Merge tag 'android-16.0.0_r1' of https://android.googlesource.com/platform/build/soong into HEAD

Android 16.0.0 release 1

Change-Id: Ide1ec574e3f9743d18ee70b928056bb7fe248f80
diff --git a/Android.bp b/Android.bp
index 434ee9f..7134591 100644
--- a/Android.bp
+++ b/Android.bp
@@ -150,10 +150,11 @@
     arch: {
         arm64: {
             cflags: [
-                // Prevent the compiler from optimizing code using SVE, as the
-                // baremetal environment might not have configured the hardware.
-                "-Xclang -target-feature",
-                "-Xclang -sve",
+                // Override the global -march= flag (as set by TARGET_ARCH_VARIANT)
+                // and explicitly use the baseline architecture (ARMv8-A is the first
+                // version with 64-bit support) to avoid emitting potentially
+                // unsupported instructions.
+                "-march=armv8-a",
             ],
         },
     },
@@ -175,12 +176,18 @@
     footer_files: [
         ":applied_backported_fixes",
     ],
+    dist: {
+        targets: [
+            "droidcore-unbundled",
+            "sdk",
+        ],
+    },
     // Currently, only microdroid, Ravenwood, and cf system image can refer to system-build.prop
     visibility: [
-        "//build/make/target/product/generic",
-        "//build/make/target/product/gsi",
+        "//build/soong/fsgen",
         "//packages/modules/Virtualization/build/microdroid",
         "//frameworks/base/ravenwood",
+        "//visibility:any_system_partition",
     ],
 }
 
@@ -190,7 +197,14 @@
     system_ext_specific: true,
     product_config: ":product_config",
     relative_install_path: "etc", // system_ext/etc/build.prop
-    visibility: ["//build/make/target/product/gsi"],
+    dist: {
+        targets: ["droidcore-unbundled"],
+        dest: "build.prop-system_ext",
+    },
+    visibility: [
+        "//build/make/target/product/gsi",
+        "//build/soong/fsgen",
+    ],
 }
 
 build_prop {
@@ -199,7 +213,14 @@
     product_specific: true,
     product_config: ":product_config",
     relative_install_path: "etc", // product/etc/build.prop
-    visibility: ["//build/make/target/product/gsi"],
+    dist: {
+        targets: ["droidcore-unbundled"],
+        dest: "build.prop-product",
+    },
+    visibility: [
+        "//build/make/target/product/gsi",
+        "//build/soong/fsgen",
+    ],
 }
 
 build_prop {
@@ -208,7 +229,11 @@
     device_specific: true,
     product_config: ":product_config",
     relative_install_path: "etc", // odm/etc/build.prop
-    visibility: ["//visibility:private"],
+    dist: {
+        targets: ["droidcore-unbundled"],
+        dest: "build.prop-odm",
+    },
+    visibility: ["//build/soong/fsgen"],
 }
 
 build_prop {
@@ -244,5 +269,17 @@
     ramdisk: true,
     product_config: ":product_config",
     relative_install_path: "etc/ramdisk", // ramdisk/system/etc/ramdisk/build.prop
+    dist: {
+        targets: ["droidcore-unbundled"],
+        dest: "build.prop-ramdisk",
+    },
     visibility: ["//visibility:private"],
 }
+
+all_apex_certs {
+    name: "all_apex_certs",
+    visibility: [
+        "//cts/tests/tests/security",
+        "//cts/hostsidetests/appsecurity",
+    ],
+}
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index 402cf16..a15fe86 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -17,6 +17,7 @@
         "aconfig_values.go",
         "aconfig_value_set.go",
         "all_aconfig_declarations.go",
+        "all_aconfig_declarations_extension.go",
         "exported_java_aconfig_library.go",
         "init.go",
         "testing.go",
@@ -25,7 +26,21 @@
         "aconfig_declarations_test.go",
         "aconfig_values_test.go",
         "aconfig_value_set_test.go",
-        "all_aconfig_declarations_test.go",
+        "all_aconfig_declarations_extension_test.go",
     ],
     pluginFor: ["soong_build"],
 }
+
+all_aconfig_declarations {
+    name: "all_aconfig_declarations",
+    visibility: [
+        "//vendor:__subpackages__", // for vendor extensions
+    ],
+    api_signature_files: [
+        ":frameworks-base-api-current.txt",
+        ":frameworks-base-api-system-current.txt",
+        ":frameworks-base-api-system-server-current.txt",
+        ":frameworks-base-api-module-lib-current.txt",
+    ],
+    finalized_flags_file: ":latest-finalized-flags",
+}
diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go
index 9a9e568..b068398 100644
--- a/aconfig/aconfig_declarations.go
+++ b/aconfig/aconfig_declarations.go
@@ -47,7 +47,7 @@
 		// are from RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS.
 		ReleaseConfigValues []AconfigReleaseConfigValue
 
-		// Container(system/vendor/apex) that this module belongs to
+		// Container(system/system_ext/vendor/apex) that this module belongs to
 		Container string
 
 		// The flags will only be repackaged if this prop is true.
@@ -88,13 +88,6 @@
 		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_declarations_test.go b/aconfig/aconfig_declarations_test.go
index e89cd31..c39008b 100644
--- a/aconfig/aconfig_declarations_test.go
+++ b/aconfig/aconfig_declarations_test.go
@@ -37,7 +37,7 @@
 	`
 	result := runTest(t, android.FixtureExpectsNoErrors, bp)
 
-	module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule)
+	module := result.ModuleForTests(t, "module_name", "").Module().(*DeclarationsModule)
 
 	// Check that the provider has the right contents
 	depData, _ := android.OtherModuleProvider(result, module, android.AconfigDeclarationsProviderKey)
@@ -66,7 +66,7 @@
 	`
 	result := runTest(t, android.FixtureExpectsNoErrors, bp)
 
-	module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule)
+	module := result.ModuleForTests(t, "module_name", "").Module().(*DeclarationsModule)
 	depData, _ := android.OtherModuleProvider(result, module, android.AconfigDeclarationsProviderKey)
 	android.AssertBoolEquals(t, "exportable", depData.Exportable, false)
 }
@@ -84,7 +84,7 @@
 	`
 	result := runTest(t, android.FixtureExpectsNoErrors, bp)
 
-	module := result.ModuleForTests("module_name", "")
+	module := result.ModuleForTests(t, "module_name", "")
 	rule := module.Rule("aconfig")
 	android.AssertStringEquals(t, "rule must contain container", rule.Args["container"], "--container com.android.foo")
 }
@@ -204,7 +204,7 @@
 			fixture = fixture.ExtendWithErrorHandler(test.errorHandler)
 		}
 		result := fixture.RunTestWithBp(t, test.bp)
-		module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule)
+		module := result.ModuleForTests(t, "module_name", "").Module().(*DeclarationsModule)
 		depData, _ := android.OtherModuleProvider(result, module, android.AconfigReleaseDeclarationsProviderKey)
 		expectedKeys := []string{""}
 		for _, rc := range strings.Split(test.buildFlags["RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS"], " ") {
diff --git a/aconfig/aconfig_value_set_test.go b/aconfig/aconfig_value_set_test.go
index 3b7281e..1f04244 100644
--- a/aconfig/aconfig_value_set_test.go
+++ b/aconfig/aconfig_value_set_test.go
@@ -37,7 +37,7 @@
 			`
 	result := runTest(t, android.FixtureExpectsNoErrors, bp)
 
-	module := result.ModuleForTests("module_name", "").Module().(*ValueSetModule)
+	module := result.ModuleForTests(t, "module_name", "").Module().(*ValueSetModule)
 
 	// Check that the provider has the right contents
 	depData, _ := android.OtherModuleProvider(result, module, valueSetProviderKey)
@@ -88,7 +88,7 @@
 
 	checkModuleHasDependency := func(name, variant, dep string) bool {
 		t.Helper()
-		module := result.ModuleForTests(name, variant).Module()
+		module := result.ModuleForTests(t, name, variant).Module()
 		depFound := false
 		result.VisitDirectDeps(module, func(m blueprint.Module) {
 			if m.Name() == dep {
diff --git a/aconfig/aconfig_values_test.go b/aconfig/aconfig_values_test.go
index ddbea57..0bec14b 100644
--- a/aconfig/aconfig_values_test.go
+++ b/aconfig/aconfig_values_test.go
@@ -30,7 +30,7 @@
 			`
 	result := runTest(t, android.FixtureExpectsNoErrors, bp)
 
-	module := result.ModuleForTests("module_name", "").Module().(*ValuesModule)
+	module := result.ModuleForTests(t, "module_name", "").Module().(*ValuesModule)
 
 	// Check that the provider has the right contents
 	depData, _ := android.OtherModuleProvider(result, module, valuesProviderKey)
diff --git a/aconfig/all_aconfig_declarations.go b/aconfig/all_aconfig_declarations.go
index 6ad54da..5a52624 100644
--- a/aconfig/all_aconfig_declarations.go
+++ b/aconfig/all_aconfig_declarations.go
@@ -19,6 +19,9 @@
 	"slices"
 
 	"android/soong/android"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 // A singleton module that collects all of the aconfig flags declared in the
@@ -28,17 +31,36 @@
 // Note that this is ALL aconfig_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 AllAconfigDeclarationsFactory() android.Singleton {
-	return &allAconfigDeclarationsSingleton{releaseMap: make(map[string]allAconfigReleaseDeclarationsSingleton)}
+func AllAconfigDeclarationsFactory() android.SingletonModule {
+	module := &allAconfigDeclarationsSingleton{releaseMap: make(map[string]allAconfigReleaseDeclarationsSingleton)}
+	module.AddProperties(&module.properties)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	return module
 }
 
+type allAconfigDeclarationsInfo struct {
+	parsedFlagsFile android.Path
+}
+
+var allAconfigDeclarationsInfoProvider = blueprint.NewProvider[allAconfigDeclarationsInfo]()
+
 type allAconfigReleaseDeclarationsSingleton struct {
 	intermediateBinaryProtoPath android.OutputPath
 	intermediateTextProtoPath   android.OutputPath
 }
 
+type ApiSurfaceContributorProperties struct {
+	Api_signature_files  proptools.Configurable[[]string] `android:"arch_variant,path"`
+	Finalized_flags_file string                           `android:"arch_variant,path"`
+}
+
 type allAconfigDeclarationsSingleton struct {
+	android.SingletonModuleBase
+
 	releaseMap map[string]allAconfigReleaseDeclarationsSingleton
+	properties ApiSurfaceContributorProperties
+
+	finalizedFlags android.OutputPath
 }
 
 func (this *allAconfigDeclarationsSingleton) sortedConfigNames() []string {
@@ -50,12 +72,80 @@
 	return names
 }
 
-func (this *allAconfigDeclarationsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+func GenerateFinalizedFlagsForApiSurface(ctx android.ModuleContext, outputPath android.WritablePath,
+	parsedFlagsFile android.Path, apiSurface ApiSurfaceContributorProperties) {
+
+	apiSignatureFiles := android.Paths{}
+	for _, apiSignatureFile := range apiSurface.Api_signature_files.GetOrDefault(ctx, nil) {
+		if path := android.PathForModuleSrc(ctx, apiSignatureFile); path != nil {
+			apiSignatureFiles = append(apiSignatureFiles, path)
+		}
+	}
+	finalizedFlagsFile := android.PathForModuleSrc(ctx, apiSurface.Finalized_flags_file)
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   RecordFinalizedFlagsRule,
+		Inputs: append(apiSignatureFiles, finalizedFlagsFile, parsedFlagsFile),
+		Output: outputPath,
+		Args: map[string]string{
+			"api_signature_files":  android.JoinPathsWithPrefix(apiSignatureFiles, "--api-signature-file "),
+			"finalized_flags_file": "--finalized-flags-file " + finalizedFlagsFile.String(),
+			"parsed_flags_file":    "--parsed-flags-file " + parsedFlagsFile.String(),
+		},
+	})
+}
+
+func GenerateExportedFlagCheck(ctx android.ModuleContext, outputPath android.WritablePath,
+	parsedFlagsFile android.Path, apiSurface ApiSurfaceContributorProperties) {
+
+	apiSignatureFiles := android.Paths{}
+	for _, apiSignatureFile := range apiSurface.Api_signature_files.GetOrDefault(ctx, nil) {
+		if path := android.PathForModuleSrc(ctx, apiSignatureFile); path != nil {
+			apiSignatureFiles = append(apiSignatureFiles, path)
+		}
+	}
+	finalizedFlagsFile := android.PathForModuleSrc(ctx, apiSurface.Finalized_flags_file)
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   ExportedFlagCheckRule,
+		Inputs: append(apiSignatureFiles, finalizedFlagsFile, parsedFlagsFile),
+		Output: outputPath,
+		Args: map[string]string{
+			"api_signature_files":  android.JoinPathsWithPrefix(apiSignatureFiles, "--api-signature-file "),
+			"finalized_flags_file": "--finalized-flags-file " + finalizedFlagsFile.String(),
+			"parsed_flags_file":    "--parsed-flags-file " + parsedFlagsFile.String(),
+		},
+	})
+}
+
+func (this *allAconfigDeclarationsSingleton) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	parsedFlagsFile := android.PathForIntermediates(ctx, "all_aconfig_declarations.pb")
+	this.finalizedFlags = android.PathForIntermediates(ctx, "finalized-flags.txt")
+	GenerateFinalizedFlagsForApiSurface(ctx, this.finalizedFlags, parsedFlagsFile, this.properties)
+
+	depsFiles := android.Paths{this.finalizedFlags}
+	if checkExportedFlag, ok := ctx.Config().GetBuildFlag("RELEASE_EXPORTED_FLAG_CHECK"); ok {
+		if checkExportedFlag == "true" {
+			invalidExportedFlags := android.PathForIntermediates(ctx, "invalid_exported_flags.txt")
+			GenerateExportedFlagCheck(ctx, invalidExportedFlags, parsedFlagsFile, this.properties)
+			depsFiles = append(depsFiles, invalidExportedFlags)
+			ctx.Phony("droidcore", invalidExportedFlags)
+		}
+	}
+
+	ctx.Phony("all_aconfig_declarations", depsFiles...)
+
+	android.SetProvider(ctx, allAconfigDeclarationsInfoProvider, allAconfigDeclarationsInfo{
+		parsedFlagsFile: parsedFlagsFile,
+	})
+}
+
+func (this *allAconfigDeclarationsSingleton) GenerateSingletonBuildActions(ctx android.SingletonContext) {
 	for _, rcName := range append([]string{""}, ctx.Config().ReleaseAconfigExtraReleaseConfigs()...) {
 		// Find all of the aconfig_declarations modules
 		var packages = make(map[string]int)
 		var cacheFiles android.Paths
-		ctx.VisitAllModules(func(module android.Module) {
+		ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
 			decl, ok := android.OtherModuleProvider(ctx, module, android.AconfigReleaseDeclarationsProviderKey)
 			if !ok {
 				return
@@ -65,15 +155,16 @@
 		})
 
 		var numOffendingPkg = 0
+		offendingPkgsMessage := ""
 		for pkg, cnt := range packages {
 			if cnt > 1 {
-				fmt.Printf("%d aconfig_declarations found for package %s\n", cnt, pkg)
+				offendingPkgsMessage += fmt.Sprintf("%d aconfig_declarations found for package %s\n", cnt, pkg)
 				numOffendingPkg++
 			}
 		}
 
 		if numOffendingPkg > 0 {
-			panic(fmt.Errorf("Only one aconfig_declarations allowed for each package."))
+			panic("Only one aconfig_declarations allowed for each package.\n" + offendingPkgsMessage)
 		}
 
 		// Generate build action for aconfig (binary proto output)
@@ -105,9 +196,7 @@
 		})
 		ctx.Phony("all_aconfig_declarations_textproto", this.releaseMap[rcName].intermediateTextProtoPath)
 	}
-}
 
-func (this *allAconfigDeclarationsSingleton) MakeVars(ctx android.MakeVarsContext) {
 	for _, rcName := range this.sortedConfigNames() {
 		ctx.DistForGoal("droid", this.releaseMap[rcName].intermediateBinaryProtoPath)
 		for _, goal := range []string{"docs", "droid", "sdk"} {
@@ -115,4 +204,5 @@
 			ctx.DistForGoalWithFilename(goal, this.releaseMap[rcName].intermediateTextProtoPath, assembleFileName(rcName, "flags.textproto"))
 		}
 	}
+	ctx.DistForGoalWithFilename("sdk", this.finalizedFlags, "finalized-flags.txt")
 }
diff --git a/aconfig/all_aconfig_declarations_extension.go b/aconfig/all_aconfig_declarations_extension.go
new file mode 100644
index 0000000..d5a4588
--- /dev/null
+++ b/aconfig/all_aconfig_declarations_extension.go
@@ -0,0 +1,88 @@
+// Copyright 2025 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 aconfig
+
+import (
+	"android/soong/android"
+	"path"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+func AllAconfigDeclarationsExtensionFactory() android.Module {
+	module := &allAconfigDeclarationsExtension{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	return module
+}
+
+type allAconfigDeclarationsExtensionProperties struct {
+	// all_aconfig_declarations module that this module extends. Defaults to
+	// all_aconfig_declarations.
+	Base *string
+
+	// Directory where the dist artifact should be placed in.
+	Dist_dir *string
+
+	ApiSurfaceContributorProperties
+}
+
+type allAconfigDeclarationsExtension struct {
+	android.ModuleBase
+
+	properties allAconfigDeclarationsExtensionProperties
+
+	finalizedFlags android.ModuleOutPath
+}
+
+type allAconfigDeclarationsDependencyTagStruct struct {
+	blueprint.BaseDependencyTag
+}
+
+var allAconfigDeclarationsDependencyTag allAconfigDeclarationsDependencyTagStruct
+
+func (ext *allAconfigDeclarationsExtension) DepsMutator(ctx android.BottomUpMutatorContext) {
+	ctx.AddDependency(ctx.Module(), allAconfigDeclarationsDependencyTag, proptools.StringDefault(ext.properties.Base, "all_aconfig_declarations"))
+}
+
+func (ext *allAconfigDeclarationsExtension) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+
+	var parsedFlagsFile android.Path
+	ctx.VisitDirectDepsProxyWithTag(allAconfigDeclarationsDependencyTag, func(proxy android.ModuleProxy) {
+		if info, ok := android.OtherModuleProvider(ctx, proxy, allAconfigDeclarationsInfoProvider); ok {
+			parsedFlagsFile = info.parsedFlagsFile
+		} else {
+			ctx.PropertyErrorf("base", "base must provide allAconfigDeclarationsInfo")
+		}
+	})
+
+	ext.finalizedFlags = android.PathForModuleOut(ctx, "finalized-flags.txt")
+
+	GenerateFinalizedFlagsForApiSurface(ctx,
+		ext.finalizedFlags,
+		parsedFlagsFile,
+		ext.properties.ApiSurfaceContributorProperties,
+	)
+
+	ctx.Phony(ctx.ModuleName(), ext.finalizedFlags)
+
+	ctx.DistForGoalWithFilename("sdk", ext.finalizedFlags, path.Join(proptools.String(ext.properties.Dist_dir), "finalized-flags.txt"))
+
+	// This module must not set any provider or call `ctx.SetOutputFiles`!
+	// This module is only used to depend on the singleton module all_aconfig_declarations and
+	// generate the custom finalized-flags.txt file in dist builds, and should not be depended
+	// by other modules.
+}
diff --git a/aconfig/all_aconfig_declarations_extension_test.go b/aconfig/all_aconfig_declarations_extension_test.go
new file mode 100644
index 0000000..5bd99d0
--- /dev/null
+++ b/aconfig/all_aconfig_declarations_extension_test.go
@@ -0,0 +1,50 @@
+// Copyright 2025 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 aconfig
+
+import (
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestAllAconfigDeclarationsExtension(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithAconfigBuildComponents,
+		android.FixtureMergeMockFs(
+			android.MockFS{
+				"a.txt":     nil,
+				"flags.txt": nil,
+			},
+		),
+	).RunTestWithBp(t, `
+		all_aconfig_declarations {
+			name: "all_aconfig_declarations",
+		}
+
+		all_aconfig_declarations_extension {
+			name: "custom_aconfig_declarations",
+			base: "all_aconfig_declarations",
+			api_signature_files: [
+				"a.txt",
+			],
+			finalized_flags_file: "flags.txt",
+		}
+	`)
+
+	finalizedFlags := result.ModuleForTests(t, "custom_aconfig_declarations", "").Output("finalized-flags.txt")
+	android.AssertStringContainsEquals(t, "must depend on all_aconfig_declarations", strings.Join(finalizedFlags.Inputs.Strings(), " "), "all_aconfig_declarations.pb", true)
+}
diff --git a/aconfig/all_aconfig_declarations_test.go b/aconfig/all_aconfig_declarations_test.go
deleted file mode 100644
index 0b2021e..0000000
--- a/aconfig/all_aconfig_declarations_test.go
+++ /dev/null
@@ -1,48 +0,0 @@
-// 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 aconfig
-
-import (
-	"testing"
-
-	"android/soong/android"
-)
-
-func TestTwoAconfigDeclarationsPerPackage(t *testing.T) {
-	bp := `
-		aconfig_declarations {
-			name: "module_name.foo",
-			package: "com.example.package",
-			container: "com.android.foo",
-			srcs: [
-				"foo.aconfig",
-			],
-		}
-
-		aconfig_declarations {
-			name: "module_name.bar",
-			package: "com.example.package",
-			container: "com.android.foo",
-			srcs: [
-				"bar.aconfig",
-			],
-		}
-	`
-	errMsg := "Only one aconfig_declarations allowed for each package."
-	android.GroupFixturePreparers(
-		PrepareForTestWithAconfigBuildComponents).
-		ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(errMsg)).
-		RunTestWithBp(t, bp)
-}
diff --git a/aconfig/build_flags/build_flags_singleton.go b/aconfig/build_flags/build_flags_singleton.go
index 3b40755..3a06e72 100644
--- a/aconfig/build_flags/build_flags_singleton.go
+++ b/aconfig/build_flags/build_flags_singleton.go
@@ -15,6 +15,8 @@
 package build_flags
 
 import (
+	"fmt"
+
 	"android/soong/android"
 )
 
@@ -41,7 +43,7 @@
 	var flagsFiles android.Paths
 	// Find all of the release_config_contribution modules
 	var contributionDirs android.Paths
-	ctx.VisitAllModules(func(module android.Module) {
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
 		decl, ok := android.OtherModuleProvider(ctx, module, BuildFlagDeclarationsProviderKey)
 		if ok {
 			flagsFiles = append(flagsFiles, decl.IntermediateCacheOutputPath)
@@ -110,9 +112,7 @@
 		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")
@@ -120,4 +120,26 @@
 		ctx.DistForGoalWithFilename(goal, this.configsBinaryProtoPath, "build_flags/all_release_config_contributions.pb")
 		ctx.DistForGoalWithFilename(goal, this.configsTextProtoPath, "build_flags/all_release_config_contributions.textproto")
 	}
+
+	if ctx.Config().HasDeviceProduct() {
+		flagsDir := android.PathForOutput(ctx, "release-config")
+		baseAllRelease := fmt.Sprintf("all_release_configs-%s", ctx.Config().DeviceProduct())
+
+		distAllReleaseConfigsArtifact := func(ext string) {
+			ctx.DistForGoalWithFilename(
+				"droid",
+				flagsDir.Join(ctx, fmt.Sprintf("%s.%s", baseAllRelease, ext)),
+				fmt.Sprintf("build_flags/all_release_configs.%s", ext),
+			)
+		}
+
+		distAllReleaseConfigsArtifact("pb")
+		distAllReleaseConfigsArtifact("textproto")
+		distAllReleaseConfigsArtifact("json")
+		ctx.DistForGoalWithFilename(
+			"droid",
+			flagsDir.Join(ctx, fmt.Sprintf("inheritance_graph-%s.dot", ctx.Config().DeviceProduct())),
+			fmt.Sprintf("build_flags/inheritance_graph-%s.dot", ctx.Config().DeviceProduct()),
+		)
+	}
 }
diff --git a/aconfig/codegen/aconfig_declarations_group_test.go b/aconfig/codegen/aconfig_declarations_group_test.go
index ef954ce..c822ef8 100644
--- a/aconfig/codegen/aconfig_declarations_group_test.go
+++ b/aconfig/codegen/aconfig_declarations_group_test.go
@@ -74,7 +74,7 @@
 
 	// Check if srcjar files are correctly passed to the reverse dependency of
 	// aconfig_declarations_group module
-	bazModule := result.ModuleForTests("baz", "android_common")
+	bazModule := result.ModuleForTests(t, "baz", "android_common")
 	bazJavacSrcjars := bazModule.Rule("javac").Args["srcJars"]
 	errorMessage := "baz javac argument expected to contain srcjar provided by aconfig_declrations_group"
 	android.AssertStringDoesContain(t, errorMessage, bazJavacSrcjars, "foo-java.srcjar")
diff --git a/aconfig/codegen/cc_aconfig_library.go b/aconfig/codegen/cc_aconfig_library.go
index 8c4bfe6..ce37456 100644
--- a/aconfig/codegen/cc_aconfig_library.go
+++ b/aconfig/codegen/cc_aconfig_library.go
@@ -22,7 +22,6 @@
 	"github.com/google/blueprint/proptools"
 
 	"fmt"
-	"strconv"
 	"strings"
 )
 
@@ -32,8 +31,6 @@
 
 var ccDeclarationsTag = ccDeclarationsTagType{}
 
-const baseLibDep = "server_configurable_flags"
-
 const libBaseDep = "libbase"
 const libLogDep = "liblog"
 const libAconfigStorageReadApiCcDep = "libaconfig_storage_read_api_cc"
@@ -86,15 +83,11 @@
 
 	// Add a dependency for the aconfig flags base library if it is not forced read only
 	if mode != "force-read-only" {
-		deps.SharedLibs = append(deps.SharedLibs, baseLibDep)
-
+		deps.SharedLibs = append(deps.SharedLibs, libAconfigStorageReadApiCcDep)
+		deps.SharedLibs = append(deps.SharedLibs, libBaseDep)
+		deps.SharedLibs = append(deps.SharedLibs, libLogDep)
 	}
 
-	// TODO: after storage migration is over, don't add these in force-read-only-mode.
-	deps.SharedLibs = append(deps.SharedLibs, libAconfigStorageReadApiCcDep)
-	deps.SharedLibs = append(deps.SharedLibs, libBaseDep)
-	deps.SharedLibs = append(deps.SharedLibs, libLogDep)
-
 	// TODO: It'd be really nice if we could reexport this library and not make everyone do it.
 
 	return deps
@@ -104,7 +97,7 @@
 	result := cc.GeneratedSource{}
 
 	// Get the values that came from the global RELEASE_ACONFIG_VALUE_SETS flag
-	declarationsModules := ctx.GetDirectDepsWithTag(ccDeclarationsTag)
+	declarationsModules := ctx.GetDirectDepsProxyWithTag(ccDeclarationsTag)
 	if len(declarationsModules) != 1 {
 		panic(fmt.Errorf("Exactly one aconfig_declarations property required"))
 	}
@@ -134,7 +127,7 @@
 
 func (this *CcAconfigLibraryCallbacks) GeneratorBuildActions(ctx cc.ModuleContext, flags cc.Flags, deps cc.PathDeps) {
 	// Get the values that came from the global RELEASE_ACONFIG_VALUE_SETS flag
-	declarationsModules := ctx.GetDirectDepsWithTag(ccDeclarationsTag)
+	declarationsModules := ctx.GetDirectDepsProxyWithTag(ccDeclarationsTag)
 	if len(declarationsModules) != 1 {
 		panic(fmt.Errorf("Exactly one aconfig_declarations property required"))
 	}
@@ -156,7 +149,6 @@
 		Args: map[string]string{
 			"gendir": this.generatedDir.String(),
 			"mode":   mode,
-			"debug":  strconv.FormatBool(ctx.Config().ReleaseReadFromNewStorage()),
 		},
 	})
 
diff --git a/aconfig/codegen/cc_aconfig_library_test.go b/aconfig/codegen/cc_aconfig_library_test.go
index c308ed4..5cb3f8b 100644
--- a/aconfig/codegen/cc_aconfig_library_test.go
+++ b/aconfig/codegen/cc_aconfig_library_test.go
@@ -81,7 +81,7 @@
 			}
 		`, bpMode))
 
-	module := result.ModuleForTests("my_cc_aconfig_library", "android_arm64_armv8-a_shared")
+	module := result.ModuleForTests(t, "my_cc_aconfig_library", "android_arm64_armv8-a_shared")
 	rule := module.Rule("cc_aconfig_library")
 	android.AssertStringEquals(t, "rule must contain test mode", rule.Args["mode"], ruleMode)
 }
@@ -209,7 +209,7 @@
 		cc.PrepareForTestWithCcDefaultModules).
 		ExtendWithErrorHandler(android.FixtureExpectsNoErrors).RunTestWithBp(t, bp)
 
-	module := result.ModuleForTests("my_cc_library", "android_vendor_arm64_armv8-a_shared").Module()
+	module := result.ModuleForTests(t, "my_cc_library", "android_vendor_arm64_armv8-a_shared").Module()
 
 	entry := android.AndroidMkInfoForTest(t, result.TestContext, module).PrimaryInfo
 
@@ -254,13 +254,13 @@
 			}
 		`))
 
-	module := result.ModuleForTests("my_cc_aconfig_library", "android_arm64_armv8-a_shared").Module()
-	dependOnBaseLib := false
+	module := result.ModuleForTests(t, "my_cc_aconfig_library", "android_arm64_armv8-a_shared").Module()
+	dependOnReadLib := false
 	result.VisitDirectDeps(module, func(dep blueprint.Module) {
-		if dep.Name() == baseLibDep {
-			dependOnBaseLib = true
+		if dep.Name() == libAconfigStorageReadApiCcDep {
+			dependOnReadLib = true
 		}
 	})
-	android.AssertBoolEquals(t, "should not have dependency on server_configuriable_flags",
-		dependOnBaseLib, false)
+	android.AssertBoolEquals(t, "should not have dependency on libaconfig_storage_read_api_cc",
+		dependOnReadLib, false)
 }
diff --git a/aconfig/codegen/init.go b/aconfig/codegen/init.go
index ed0b3ed..325e367 100644
--- a/aconfig/codegen/init.go
+++ b/aconfig/codegen/init.go
@@ -26,6 +26,7 @@
 	// For java_aconfig_library: Generate java library
 	javaRule = pctx.AndroidStaticRule("java_aconfig_library",
 		blueprint.RuleParams{
+			// LINT.IfChange
 			Command: `rm -rf ${out}.tmp` +
 				` && mkdir -p ${out}.tmp` +
 				` && ${aconfig} create-java-lib` +
@@ -33,14 +34,17 @@
 				`    --cache ${in}` +
 				`    --out ${out}.tmp` +
 				`    --allow-instrumentation ${debug}` +
+				`    --new-exported ${new_exported}` +
+				`		 --check-api-level ${check_api_level}` +
 				` && $soong_zip -write_if_changed -jar -o ${out} -C ${out}.tmp -D ${out}.tmp` +
 				` && rm -rf ${out}.tmp`,
+			// LINT.ThenChange(/aconfig/init.go)
 			CommandDeps: []string{
 				"$aconfig",
 				"$soong_zip",
 			},
 			Restat: true,
-		}, "mode", "debug")
+		}, "mode", "debug", "new_exported", "check_api_level")
 
 	// For cc_aconfig_library: Generate C++ library
 	cppRule = pctx.AndroidStaticRule("cc_aconfig_library",
@@ -50,12 +54,11 @@
 				` && ${aconfig} create-cpp-lib` +
 				`    --mode ${mode}` +
 				`    --cache ${in}` +
-				`    --out ${gendir}` +
-				`    --allow-instrumentation ${debug}`,
+				`    --out ${gendir}`,
 			CommandDeps: []string{
 				"$aconfig",
 			},
-		}, "gendir", "mode", "debug")
+		}, "gendir", "mode")
 
 	// For rust_aconfig_library: Generate Rust library
 	rustRule = pctx.AndroidStaticRule("rust_aconfig_library",
@@ -65,12 +68,11 @@
 				` && ${aconfig} create-rust-lib` +
 				`    --mode ${mode}` +
 				`    --cache ${in}` +
-				`    --allow-instrumentation ${debug}` +
 				`    --out ${gendir}`,
 			CommandDeps: []string{
 				"$aconfig",
 			},
-		}, "gendir", "mode", "debug")
+		}, "gendir", "mode")
 )
 
 func init() {
diff --git a/aconfig/codegen/java_aconfig_library.go b/aconfig/codegen/java_aconfig_library.go
index 9f399bf..7b9da8e 100644
--- a/aconfig/codegen/java_aconfig_library.go
+++ b/aconfig/codegen/java_aconfig_library.go
@@ -98,14 +98,24 @@
 		ctx.PropertyErrorf("mode", "exported mode requires its aconfig_declaration has exportable prop true")
 	}
 
+	var newExported bool
+	if useNewExported, ok := ctx.Config().GetBuildFlag("RELEASE_ACONFIG_NEW_EXPORTED"); ok {
+		// The build flag (RELEASE_ACONFIG_REQUIRE_ALL_READ_ONLY) is the negation of the aconfig flag
+		// (allow-read-write) for historical reasons.
+		// Bool build flags are always "" for false, and generally "true" for true.
+		newExported = useNewExported == "true"
+	}
+
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        javaRule,
 		Input:       declarations.IntermediateCacheOutputPath,
 		Output:      srcJarPath,
 		Description: "aconfig.srcjar",
 		Args: map[string]string{
-			"mode":  mode,
-			"debug": strconv.FormatBool(ctx.Config().ReleaseReadFromNewStorage()),
+			"mode":            mode,
+			"debug":           strconv.FormatBool(ctx.Config().ReleaseReadFromNewStorage()),
+			"new_exported":    strconv.FormatBool(newExported),
+			"check_api_level": strconv.FormatBool(ctx.Config().ReleaseAconfigCheckApiLevel()),
 		},
 	})
 
diff --git a/aconfig/codegen/java_aconfig_library_test.go b/aconfig/codegen/java_aconfig_library_test.go
index d8372f3..b9455f7 100644
--- a/aconfig/codegen/java_aconfig_library_test.go
+++ b/aconfig/codegen/java_aconfig_library_test.go
@@ -57,7 +57,7 @@
 			}
 		`)
 
-	module := result.ModuleForTests("my_module", "android_common").Module()
+	module := result.ModuleForTests(t, "my_module", "android_common").Module()
 
 	entry := android.AndroidMkEntriesForTest(t, result.TestContext, module)[0]
 
@@ -189,7 +189,7 @@
 			}
 		`, bpMode))
 
-	module := result.ModuleForTests("my_java_aconfig_library", "android_common")
+	module := result.ModuleForTests(t, "my_java_aconfig_library", "android_common")
 	rule := module.Rule("java_aconfig_library")
 	android.AssertStringEquals(t, "rule must contain test mode", rule.Args["mode"], ruleMode)
 }
@@ -260,7 +260,7 @@
 			aconfig_declarations {
 				name: "my_aconfig_declarations_bar",
 				package: "com.example.package.bar",
-				container: "vendor",
+				container: "system_ext",
 				srcs: ["bar.aconfig"],
 			}
 
@@ -282,7 +282,7 @@
 			}
 		`)
 
-	module := result.ModuleForTests("my_module", "android_common").Module()
+	module := result.ModuleForTests(t, "my_module", "android_common").Module()
 	entry := android.AndroidMkEntriesForTest(t, result.TestContext, module)[0]
 	makeVar := entry.EntryMap["LOCAL_ACONFIG_FILES"]
 	android.EnsureListContainsSuffix(t, makeVar, "my_aconfig_declarations_foo/intermediate.pb")
diff --git a/aconfig/codegen/rust_aconfig_library.go b/aconfig/codegen/rust_aconfig_library.go
index 4b896c3..53818c2 100644
--- a/aconfig/codegen/rust_aconfig_library.go
+++ b/aconfig/codegen/rust_aconfig_library.go
@@ -2,7 +2,6 @@
 
 import (
 	"fmt"
-	"strconv"
 
 	"android/soong/android"
 	"android/soong/rust"
@@ -83,7 +82,6 @@
 		Args: map[string]string{
 			"gendir": generatedDir.String(),
 			"mode":   mode,
-			"debug":  strconv.FormatBool(ctx.Config().ReleaseReadFromNewStorage()),
 		},
 	})
 	a.BaseSourceProvider.OutputFiles = android.Paths{generatedSource}
@@ -102,7 +100,6 @@
 func (a *aconfigDecorator) SourceProviderDeps(ctx rust.DepsContext, deps rust.Deps) rust.Deps {
 	deps = a.BaseSourceProvider.SourceProviderDeps(ctx, deps)
 	deps.Rustlibs = append(deps.Rustlibs, "libaconfig_storage_read_api")
-	deps.Rustlibs = append(deps.Rustlibs, "libflags_rust")
 	deps.Rustlibs = append(deps.Rustlibs, "liblazy_static")
 	deps.Rustlibs = append(deps.Rustlibs, "liblogger")
 	deps.Rustlibs = append(deps.Rustlibs, "liblog_rust")
diff --git a/aconfig/codegen/rust_aconfig_library_test.go b/aconfig/codegen/rust_aconfig_library_test.go
index 523b464..6701021 100644
--- a/aconfig/codegen/rust_aconfig_library_test.go
+++ b/aconfig/codegen/rust_aconfig_library_test.go
@@ -57,13 +57,13 @@
 			}
 		`))
 
-	sourceVariant := result.ModuleForTests("libmy_rust_aconfig_library", "android_arm64_armv8-a_source")
+	sourceVariant := result.ModuleForTests(t, "libmy_rust_aconfig_library", "android_arm64_armv8-a_source")
 	rule := sourceVariant.Rule("rust_aconfig_library")
 	android.AssertStringEquals(t, "rule must contain production mode", rule.Args["mode"], "production")
 
-	dylibVariant := result.ModuleForTests("libmy_rust_aconfig_library", "android_arm64_armv8-a_dylib")
-	rlibRlibStdVariant := result.ModuleForTests("libmy_rust_aconfig_library", "android_arm64_armv8-a_rlib_rlib-std")
-	rlibDylibStdVariant := result.ModuleForTests("libmy_rust_aconfig_library", "android_arm64_armv8-a_rlib_dylib-std")
+	dylibVariant := result.ModuleForTests(t, "libmy_rust_aconfig_library", "android_arm64_armv8-a_dylib")
+	rlibRlibStdVariant := result.ModuleForTests(t, "libmy_rust_aconfig_library", "android_arm64_armv8-a_rlib_rlib-std")
+	rlibDylibStdVariant := result.ModuleForTests(t, "libmy_rust_aconfig_library", "android_arm64_armv8-a_rlib_dylib-std")
 
 	variants := []android.TestingModule{
 		dylibVariant,
@@ -143,7 +143,7 @@
 			}
 		`, bpMode))
 
-	module := result.ModuleForTests("libmy_rust_aconfig_library", "android_arm64_armv8-a_source")
+	module := result.ModuleForTests(t, "libmy_rust_aconfig_library", "android_arm64_armv8-a_source")
 	rule := module.Rule("rust_aconfig_library")
 	android.AssertStringEquals(t, "rule must contain test mode", rule.Args["mode"], ruleMode)
 }
diff --git a/aconfig/exported_java_aconfig_library.go b/aconfig/exported_java_aconfig_library.go
index a64cac8..ffb2a0c 100644
--- a/aconfig/exported_java_aconfig_library.go
+++ b/aconfig/exported_java_aconfig_library.go
@@ -15,6 +15,8 @@
 package aconfig
 
 import (
+	"strconv"
+
 	"android/soong/android"
 )
 
@@ -29,7 +31,7 @@
 func (this *exportedJavaDeclarationsLibrarySingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	// Find all of the aconfig_declarations modules
 	var cacheFiles android.Paths
-	ctx.VisitAllModules(func(module android.Module) {
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
 		decl, ok := android.OtherModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey)
 		if !ok {
 			return
@@ -37,6 +39,16 @@
 		cacheFiles = append(cacheFiles, decl.IntermediateCacheOutputPath)
 	})
 
+	var newExported bool
+	if useNewExported, ok := ctx.Config().GetBuildFlag("RELEASE_ACONFIG_NEW_EXPORTED"); ok {
+		newExported = useNewExported == "true"
+	}
+
+	var newStorage bool
+	if useNewStorage, ok := ctx.Config().GetBuildFlag("RELEASE_READ_FROM_NEW_STORAGE"); ok {
+		newStorage = useNewStorage == "true"
+	}
+
 	// Generate build action for aconfig
 	this.intermediatePath = android.PathForIntermediates(ctx, "exported_java_aconfig_library.jar")
 	ctx.Build(pctx, android.BuildParams{
@@ -45,12 +57,12 @@
 		Output:      this.intermediatePath,
 		Description: "exported_java_aconfig_library",
 		Args: map[string]string{
-			"cache_files": android.JoinPathsWithPrefix(cacheFiles, " "),
+			"cache_files":      android.JoinPathsWithPrefix(cacheFiles, " "),
+			"use_new_storage":  strconv.FormatBool(newStorage),
+			"use_new_exported": strconv.FormatBool(newExported),
+			"check_api_level":  strconv.FormatBool(ctx.Config().ReleaseAconfigCheckApiLevel()),
 		},
 	})
 	ctx.Phony("exported_java_aconfig_library", this.intermediatePath)
-}
-
-func (this *exportedJavaDeclarationsLibrarySingleton) MakeVars(ctx android.MakeVarsContext) {
 	ctx.DistForGoalWithFilename("sdk", this.intermediatePath, "android-flags.jar")
 }
diff --git a/aconfig/init.go b/aconfig/init.go
index 621d619..d8d5470 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -70,14 +70,28 @@
 				"${aconfig}",
 			},
 		}, "cache_files")
+	RecordFinalizedFlagsRule = pctx.AndroidStaticRule("RecordFinalizedFlagsRule",
+		blueprint.RuleParams{
+			Command: `${record-finalized-flags} ${parsed_flags_file} ${finalized_flags_file} ${api_signature_files} > ${out}`,
+			CommandDeps: []string{
+				"${record-finalized-flags}",
+			},
+		}, "api_signature_files", "finalized_flags_file", "parsed_flags_file")
+	ExportedFlagCheckRule = pctx.AndroidStaticRule("ExportedFlagCheckRule",
+		blueprint.RuleParams{
+			Command: `${exported-flag-check} ${parsed_flags_file} ${finalized_flags_file} ${api_signature_files} > ${out}`,
+			CommandDeps: []string{
+				"${exported-flag-check}",
+			},
+		}, "api_signature_files", "finalized_flags_file", "parsed_flags_file")
 
 	CreateStorageRule = pctx.AndroidStaticRule("aconfig_create_storage",
 		blueprint.RuleParams{
-			Command: `${aconfig} create-storage --container ${container} --file ${file_type} --out ${out} ${cache_files}`,
+			Command: `${aconfig} create-storage --container ${container} --file ${file_type} --out ${out} ${cache_files} --version ${version}`,
 			CommandDeps: []string{
 				"${aconfig}",
 			},
-		}, "container", "file_type", "cache_files")
+		}, "container", "file_type", "cache_files", "version")
 
 	// For exported_java_aconfig_library: Generate a JAR from all
 	// java_aconfig_libraries to be consumed by apps built outside the
@@ -88,31 +102,43 @@
 		// exported flags (only). Finally collect all generated code
 		// into the ${out} JAR file.
 		blueprint.RuleParams{
+			// LINT.IfChange
 			Command: `rm -rf ${out}.tmp` +
 				`&& for cache in ${cache_files}; do ` +
 				`  if [ -n "$$(${aconfig} dump-cache --dedup --cache $$cache --filter=is_exported:true --format='{fully_qualified_name}')" ]; then ` +
-				`    ${aconfig} create-java-lib --cache $$cache --mode=exported --out ${out}.tmp; ` +
+				`    ${aconfig} create-java-lib` +
+				`        --cache $$cache` +
+				`        --mode=exported` +
+				`        --allow-instrumentation ${use_new_storage}` +
+				`        --new-exported ${use_new_exported}` +
+				`        --single-exported-file true` +
+				`        --check-api-level ${check_api_level}` +
+				`        --out ${out}.tmp; ` +
 				`  fi ` +
 				`done` +
 				`&& $soong_zip -write_if_changed -jar -o ${out} -C ${out}.tmp -D ${out}.tmp` +
 				`&& rm -rf ${out}.tmp`,
+			// LINT.ThenChange(/aconfig/codegen/init.go)
 			CommandDeps: []string{
 				"$aconfig",
 				"$soong_zip",
 			},
-		}, "cache_files")
+		}, "cache_files", "use_new_storage", "use_new_exported", "check_api_level")
 )
 
 func init() {
 	RegisterBuildComponents(android.InitRegistrationContext)
 	pctx.HostBinToolVariable("aconfig", "aconfig")
 	pctx.HostBinToolVariable("soong_zip", "soong_zip")
+	pctx.HostBinToolVariable("record-finalized-flags", "record-finalized-flags")
+	pctx.HostBinToolVariable("exported-flag-check", "exported-flag-check")
 }
 
 func RegisterBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("aconfig_declarations", DeclarationsFactory)
 	ctx.RegisterModuleType("aconfig_values", ValuesFactory)
 	ctx.RegisterModuleType("aconfig_value_set", ValueSetFactory)
-	ctx.RegisterParallelSingletonType("all_aconfig_declarations", AllAconfigDeclarationsFactory)
+	ctx.RegisterSingletonModuleType("all_aconfig_declarations", AllAconfigDeclarationsFactory)
 	ctx.RegisterParallelSingletonType("exported_java_aconfig_library", ExportedJavaDeclarationsLibraryFactory)
+	ctx.RegisterModuleType("all_aconfig_declarations_extension", AllAconfigDeclarationsExtensionFactory)
 }
diff --git a/aidl_library/aidl_library_test.go b/aidl_library/aidl_library_test.go
index 1660456..63a08b9 100644
--- a/aidl_library/aidl_library_test.go
+++ b/aidl_library/aidl_library_test.go
@@ -46,7 +46,7 @@
 		}.AddToFixture(),
 	).RunTest(t).TestContext
 
-	foo := ctx.ModuleForTests("foo", "").Module().(*AidlLibrary)
+	foo := ctx.ModuleForTests(t, "foo", "").Module().(*AidlLibrary)
 	actualInfo, _ := android.OtherModuleProvider(ctx, foo, AidlLibraryProvider)
 
 	android.AssertArrayString(
@@ -95,7 +95,7 @@
 		}.AddToFixture(),
 	).RunTest(t).TestContext
 
-	foo := ctx.ModuleForTests("foo", "").Module().(*AidlLibrary)
+	foo := ctx.ModuleForTests(t, "foo", "").Module().(*AidlLibrary)
 	actualInfo, _ := android.OtherModuleProvider(ctx, foo, AidlLibraryProvider)
 
 	android.AssertArrayString(
diff --git a/android/Android.bp b/android/Android.bp
index dfea8f9..97d634f 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -11,6 +11,8 @@
         "blueprint-depset",
         "blueprint-gobtools",
         "blueprint-metrics",
+        "blueprint-pool",
+        "blueprint-syncmap",
         "sbox_proto",
         "soong",
         "soong-android_team_proto",
@@ -79,8 +81,10 @@
         "namespace.go",
         "neverallow.go",
         "ninja_deps.go",
+        "nothing.go",
         "notices.go",
         "onceper.go",
+        "otatools_package_cert_zip.go",
         "override_module.go",
         "package.go",
         "package_ctx.go",
@@ -92,10 +96,14 @@
         "prebuilt.go",
         "prebuilt_build_tool.go",
         "product_config.go",
+        "product_packages_file.go",
         "proto.go",
         "provider.go",
+        "provider_keys.go",
         "raw_files.go",
+        "recovery_build_prop.go",
         "register.go",
+        "removed_package.go",
         "rule_builder.go",
         "sandbox.go",
         "sbom.go",
@@ -107,8 +115,10 @@
         "soong_config_modules.go",
         "team.go",
         "test_asserts.go",
+        "test_mapping_zip.go",
         "test_suites.go",
         "testing.go",
+        "transition.go",
         "util.go",
         "variable.go",
         "vendor_api_levels.go",
@@ -120,7 +130,6 @@
         "all_teams_test.go",
         "android_test.go",
         "androidmk_test.go",
-        "apex_test.go",
         "arch_test.go",
         "blueprint_e2e_test.go",
         "build_prop_test.go",
@@ -136,6 +145,7 @@
         "license_kind_test.go",
         "license_test.go",
         "licenses_test.go",
+        "makevars_test.go",
         "module_test.go",
         "mutator_test.go",
         "namespace_test.go",
@@ -154,6 +164,7 @@
         "singleton_module_test.go",
         "soong_config_modules_test.go",
         "test_suites_test.go",
+        "transition_test.go",
         "util_test.go",
         "variable_test.go",
         "vintf_fragment_test.go",
diff --git a/android/aconfig_providers.go b/android/aconfig_providers.go
index 210a656..bb73f0b 100644
--- a/android/aconfig_providers.go
+++ b/android/aconfig_providers.go
@@ -92,11 +92,11 @@
 				if asError {
 					ctx.ModuleErrorf(msg)
 				} else {
-					fmt.Printf("WARNING: " + msg)
+					fmt.Print("WARNING: " + msg)
 				}
 			} else {
 				if !asError {
-					fmt.Printf("PASSED: " + msg)
+					fmt.Print("PASSED: " + msg)
 				}
 			}
 		}
@@ -136,7 +136,7 @@
 			AconfigFiles: mergedAconfigFiles,
 			ModeInfos:    mergedModeInfos,
 		})
-		ctx.setAconfigPaths(getAconfigFilePaths(ctx.Module().base(), mergedAconfigFiles))
+		ctx.setAconfigPaths(getAconfigFilePaths(getContainer(ctx.Module()), mergedAconfigFiles))
 	}
 }
 
@@ -147,7 +147,8 @@
 		return
 	}
 	data.Extra = append(data.Extra, func(w io.Writer, outputFile Path) {
-		AndroidMkEmitAssignList(w, "LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles).Strings())
+		AndroidMkEmitAssignList(w, "LOCAL_ACONFIG_FILES", getAconfigFilePaths(
+			getContainerUsingProviders(ctx, mod), info.AconfigFiles).Strings())
 	})
 	// If there is a Custom writer, it needs to support this provider.
 	if data.Custom != nil {
@@ -179,24 +180,29 @@
 	// All of the files in the module potentially depend on the aconfig flag values.
 	for idx, _ := range *entries {
 		(*entries)[idx].ExtraEntries = append((*entries)[idx].ExtraEntries,
-			func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
-				entries.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles))
+			func(_ AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
+				entries.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(
+					getContainerUsingProviders(ctx, mod), info.AconfigFiles))
 			},
 		)
 
 	}
 }
 
+// TODO(b/397766191): Change the signature to take ModuleProxy
+// Please only access the module's internal data through providers.
 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))
+	infos.PrimaryInfo.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(
+		getContainerUsingProviders(ctx, mod), info.AconfigFiles))
 	if len(infos.ExtraInfo) > 0 {
 		for _, ei := range (*infos).ExtraInfo {
-			ei.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles))
+			ei.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(
+				getContainerUsingProviders(ctx, mod), info.AconfigFiles))
 		}
 	}
 }
@@ -224,19 +230,37 @@
 	return Paths{output}
 }
 
-func getAconfigFilePaths(m *ModuleBase, aconfigFiles map[string]Paths) (paths Paths) {
-	// TODO(b/311155208): The default container here should be system.
+func getContainer(m Module) string {
 	container := "system"
-
-	if m.SocSpecific() {
+	base := m.base()
+	if base.SocSpecific() {
 		container = "vendor"
-	} else if m.ProductSpecific() {
+	} else if base.ProductSpecific() {
 		container = "product"
-	} else if m.SystemExtSpecific() {
-		// system_ext and system partitions should be treated as one container
-		container = "system"
+	} else if base.SystemExtSpecific() {
+		container = "system_ext"
 	}
 
+	return container
+}
+
+// TODO(b/397766191): Change the signature to take ModuleProxy
+// Please only access the module's internal data through providers.
+func getContainerUsingProviders(ctx OtherModuleProviderContext, m Module) string {
+	container := "system"
+	commonInfo := OtherModulePointerProviderOrDefault(ctx, m, CommonModuleInfoProvider)
+	if commonInfo.Vendor || commonInfo.Proprietary || commonInfo.SocSpecific {
+		container = "vendor"
+	} else if commonInfo.ProductSpecific {
+		container = "product"
+	} else if commonInfo.SystemExtSpecific {
+		container = "system_ext"
+	}
+
+	return container
+}
+
+func getAconfigFilePaths(container string, aconfigFiles map[string]Paths) (paths Paths) {
 	paths = append(paths, aconfigFiles[container]...)
 	if container == "system" {
 		// TODO(b/311155208): Once the default container is system, we can drop this.
diff --git a/android/all_teams.go b/android/all_teams.go
index 01be396..18a050f 100644
--- a/android/all_teams.go
+++ b/android/all_teams.go
@@ -78,19 +78,19 @@
 	t.teams = make(map[string]teamProperties)
 	t.teams_for_mods = make(map[string]moduleTeamAndTestInfo)
 
-	ctx.VisitAllModules(func(module Module) {
+	ctx.VisitAllModuleProxies(func(module ModuleProxy) {
 		bpFile := ctx.BlueprintFile(module)
 
 		// Package Modules and Team Modules are stored in a map so we can look them up by name for
 		// modules without a team.
-		if pack, ok := module.(*packageModule); ok {
+		if pack, ok := OtherModuleProvider(ctx, module, PackageInfoProvider); ok {
 			// Packages don't have names, use the blueprint file as the key. we can't get qualifiedModuleId in t context.
 			pkgKey := bpFile
-			t.packages[pkgKey] = pack.properties
+			t.packages[pkgKey] = pack.Properties
 			return
 		}
-		if team, ok := module.(*teamModule); ok {
-			t.teams[team.Name()] = team.properties
+		if team, ok := OtherModuleProvider(ctx, module, TeamInfoProvider); ok {
+			t.teams[module.Name()] = team.Properties
 			return
 		}
 
@@ -116,7 +116,7 @@
 			testOnly:           testModInfo.TestOnly,
 			topLevelTestTarget: testModInfo.TopLevelTarget,
 			kind:               ctx.ModuleType(module),
-			teamName:           module.base().Team(),
+			teamName:           OtherModulePointerProviderOrDefault(ctx, module, CommonModuleInfoProvider).Team,
 		}
 		t.teams_for_mods[module.Name()] = entry
 
@@ -134,9 +134,6 @@
 
 	WriteFileRuleVerbatim(ctx, t.outputPath, string(data))
 	ctx.Phony("all_teams", t.outputPath)
-}
-
-func (t *allTeamsSingleton) MakeVars(ctx MakeVarsContext) {
 	ctx.DistForGoal("all_teams", t.outputPath)
 }
 
diff --git a/android/all_teams_test.go b/android/all_teams_test.go
index fa8c048..3b200f6 100644
--- a/android/all_teams_test.go
+++ b/android/all_teams_test.go
@@ -131,7 +131,7 @@
 
 func getTeamProtoOutput(t *testing.T, ctx *TestResult) *team_proto.AllTeams {
 	teams := new(team_proto.AllTeams)
-	config := ctx.SingletonForTests("all_teams")
+	config := ctx.SingletonForTests(t, "all_teams")
 	allOutputs := config.AllOutputs()
 
 	protoPath := allOutputs[0]
diff --git a/android/android_info.go b/android/android_info.go
index a8d3d4e..9a68d10 100644
--- a/android/android_info.go
+++ b/android/android_info.go
@@ -79,6 +79,7 @@
 	})
 
 	ctx.SetOutputFiles(Paths{androidInfoProp}, "")
+	ctx.SetOutputFiles(Paths{androidInfoTxt}, ".txt")
 }
 
 // android_info module generate a file named android-info.txt that contains various information
@@ -86,6 +87,6 @@
 func AndroidInfoFactory() Module {
 	module := &androidInfoModule{}
 	module.AddProperties(&module.properties)
-	InitAndroidModule(module)
+	InitAndroidArchModule(module, DeviceSupported, MultilibCommon)
 	return module
 }
diff --git a/android/androidmk.go b/android/androidmk.go
index 590cce3..e328359 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -64,7 +64,6 @@
 type AndroidMkData struct {
 	Class           string
 	SubName         string
-	DistFiles       TaggedDistFiles
 	OutputFile      OptionalPath
 	Disabled        bool
 	Include         string
@@ -79,6 +78,12 @@
 	Entries AndroidMkEntries
 }
 
+type AndroidMkDataInfo struct {
+	Class string
+}
+
+var AndroidMkDataInfoProvider = blueprint.NewProvider[AndroidMkDataInfo]()
+
 type AndroidMkExtraFunc func(w io.Writer, outputFile Path)
 
 // Interface for modules to declare their Android.mk outputs. Note that every module needs to
@@ -108,8 +113,6 @@
 	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
@@ -166,7 +169,7 @@
 
 type androidMkExtraEntriesContext struct {
 	ctx fillInEntriesContext
-	mod blueprint.Module
+	mod Module
 }
 
 func (a *androidMkExtraEntriesContext) Provider(provider blueprint.AnyProviderKey) (any, bool) {
@@ -333,36 +336,34 @@
 	dest string
 }
 
-// Compute the contributions that the module makes to the dist.
-func (a *AndroidMkEntries) getDistContributions(mod blueprint.Module) *distContributions {
-	amod := mod.(Module).base()
+func (d *distCopy) String() string {
+	if len(d.dest) == 0 {
+		return d.from.String()
+	}
+	return fmt.Sprintf("%s:%s", d.from.String(), d.dest)
+}
+
+type distCopies []distCopy
+
+func (d *distCopies) Strings() (ret []string) {
+	if d == nil {
+		return
+	}
+	for _, dist := range *d {
+		ret = append(ret, dist.String())
+	}
+	return
+}
+
+// This gets the dist contributuions from the given module that were specified in the Android.bp
+// file using the dist: property. It does not include contribututions that the module's
+// implementation may have defined with ctx.DistForGoals(), for that, see DistProvider.
+func getDistContributions(ctx ConfigAndOtherModuleProviderContext, mod Module) *distContributions {
+	amod := mod.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(a.entryContext, 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)
+	info := OtherModuleProviderOrDefault(ctx, mod, InstallFilesProvider)
+	availableTaggedDists := info.DistFiles
 
 	if len(availableTaggedDists) == 0 {
 		// Nothing dist-able for this module.
@@ -372,7 +373,7 @@
 	// Collate the contributions this module makes to the dist.
 	distContributions := &distContributions{}
 
-	if !exemptFromRequiredApplicableLicensesProperty(mod.(Module)) {
+	if !exemptFromRequiredApplicableLicensesProperty(mod) {
 		distContributions.licenseMetadataFile = info.LicenseMetadataFile
 	}
 
@@ -434,13 +435,18 @@
 				suffix = *dist.Suffix
 			}
 
-			productString := ""
-			if dist.Append_artifact_with_product != nil && *dist.Append_artifact_with_product {
-				productString = fmt.Sprintf("_%s", a.entryContext.Config().DeviceProduct())
+			prependProductString := ""
+			if proptools.Bool(dist.Prepend_artifact_with_product) {
+				prependProductString = fmt.Sprintf("%s-", ctx.Config().DeviceProduct())
 			}
 
-			if suffix != "" || productString != "" {
-				dest = strings.TrimSuffix(dest, ext) + suffix + productString + ext
+			appendProductString := ""
+			if proptools.Bool(dist.Append_artifact_with_product) {
+				appendProductString = fmt.Sprintf("_%s", ctx.Config().DeviceProduct())
+			}
+
+			if suffix != "" || appendProductString != "" || prependProductString != "" {
+				dest = prependProductString + strings.TrimSuffix(dest, ext) + suffix + appendProductString + ext
 			}
 
 			if dist.Dir != nil {
@@ -483,8 +489,8 @@
 
 // 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 *AndroidMkEntries) GetDistForGoals(mod blueprint.Module) []string {
-	distContributions := a.getDistContributions(mod)
+func (a *AndroidMkEntries) GetDistForGoals(mod Module) []string {
+	distContributions := getDistContributions(a.entryContext, mod)
 	if distContributions == nil {
 		return nil
 	}
@@ -504,12 +510,14 @@
 	HasMutatorFinished(mutatorName string) bool
 }
 
-func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
+func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod Module) {
 	a.entryContext = ctx
 	a.EntryMap = make(map[string][]string)
-	amod := mod.(Module)
-	base := amod.base()
+	base := mod.base()
 	name := base.BaseModuleName()
+	if bmn, ok := mod.(baseModuleName); ok {
+		name = bmn.BaseModuleName()
+	}
 	if a.OverrideName != "" {
 		name = a.OverrideName
 	}
@@ -517,10 +525,10 @@
 	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()...)
+	a.Required = append(a.Required, mod.RequiredModuleNames(ctx)...)
+	a.Required = append(a.Required, mod.VintfFragmentModuleNames(ctx)...)
+	a.Host_required = append(a.Host_required, mod.HostRequiredModuleNames()...)
+	a.Target_required = append(a.Target_required, mod.TargetRequiredModuleNames()...)
 
 	for _, distString := range a.GetDistForGoals(mod) {
 		fmt.Fprintln(&a.header, distString)
@@ -528,6 +536,14 @@
 
 	fmt.Fprintf(&a.header, "\ninclude $(CLEAR_VARS)  # type: %s, name: %s, variant: %s\n", ctx.ModuleType(mod), base.BaseModuleName(), ctx.ModuleSubDir(mod))
 
+	// Add the TestSuites from the provider to LOCAL_SOONG_PROVIDER_TEST_SUITES.
+	// LOCAL_SOONG_PROVIDER_TEST_SUITES will be compared against LOCAL_COMPATIBILITY_SUITES
+	// in make and enforced they're the same, to ensure we've successfully translated all
+	// LOCAL_COMPATIBILITY_SUITES usages to the provider.
+	if testSuiteInfo, ok := OtherModuleProvider(ctx, mod, TestSuiteInfoProvider); ok {
+		a.AddStrings("LOCAL_SOONG_PROVIDER_TEST_SUITES", testSuiteInfo.TestSuites...)
+	}
+
 	// Collect make variable assignment entries.
 	a.SetString("LOCAL_PATH", ctx.ModuleDir(mod))
 	a.SetString("LOCAL_MODULE", name+a.SubName)
@@ -536,7 +552,7 @@
 	a.AddStrings("LOCAL_REQUIRED_MODULES", a.Required...)
 	a.AddStrings("LOCAL_HOST_REQUIRED_MODULES", a.Host_required...)
 	a.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", a.Target_required...)
-	a.AddStrings("LOCAL_SOONG_MODULE_TYPE", ctx.ModuleType(amod))
+	a.AddStrings("LOCAL_SOONG_MODULE_TYPE", ctx.ModuleType(mod))
 
 	// If the install rule was generated by Soong tell Make about it.
 	info := OtherModuleProviderOrDefault(ctx, mod, InstallFilesProvider)
@@ -700,24 +716,29 @@
 
 type androidMkSingleton struct{}
 
-func (c *androidMkSingleton) GenerateBuildActions(ctx SingletonContext) {
-	// Skip if Soong wasn't invoked from Make.
-	if !ctx.Config().KatiEnabled() {
-		return
-	}
+func allModulesSorted(ctx SingletonContext) []Module {
+	var allModules []Module
 
-	var androidMkModulesList []blueprint.Module
-
-	ctx.VisitAllModulesBlueprint(func(module blueprint.Module) {
-		androidMkModulesList = append(androidMkModulesList, module)
+	ctx.VisitAllModules(func(module Module) {
+		allModules = append(allModules, module)
 	})
 
 	// Sort the module list by the module names to eliminate random churns, which may erroneously
 	// invoke additional build processes.
-	sort.SliceStable(androidMkModulesList, func(i, j int) bool {
-		return ctx.ModuleName(androidMkModulesList[i]) < ctx.ModuleName(androidMkModulesList[j])
+	sort.SliceStable(allModules, func(i, j int) bool {
+		return ctx.ModuleName(allModules[i]) < ctx.ModuleName(allModules[j])
 	})
 
+	return allModules
+}
+
+func (c *androidMkSingleton) GenerateBuildActions(ctx SingletonContext) {
+	// If running in soong-only mode, more limited version of this singleton is run as
+	// soong only androidmk singleton
+	if !ctx.Config().KatiEnabled() {
+		return
+	}
+
 	transMk := PathForOutput(ctx, "Android"+String(ctx.Config().productVariables.Make_suffix)+".mk")
 	if ctx.Failed() {
 		return
@@ -725,7 +746,7 @@
 
 	moduleInfoJSON := PathForOutput(ctx, "module-info"+String(ctx.Config().productVariables.Make_suffix)+".json")
 
-	err := translateAndroidMk(ctx, absolutePath(transMk.String()), moduleInfoJSON, androidMkModulesList)
+	err := translateAndroidMk(ctx, absolutePath(transMk.String()), moduleInfoJSON, allModulesSorted(ctx))
 	if err != nil {
 		ctx.Errorf(err.Error())
 	}
@@ -736,7 +757,202 @@
 	})
 }
 
-func translateAndroidMk(ctx SingletonContext, absMkFile string, moduleInfoJSONPath WritablePath, mods []blueprint.Module) error {
+type soongOnlyAndroidMkSingleton struct {
+	Singleton
+}
+
+func soongOnlyAndroidMkSingletonFactory() Singleton {
+	return &soongOnlyAndroidMkSingleton{}
+}
+
+func (so *soongOnlyAndroidMkSingleton) GenerateBuildActions(ctx SingletonContext) {
+	if !ctx.Config().KatiEnabled() {
+		so.soongOnlyBuildActions(ctx, allModulesSorted(ctx))
+	}
+}
+
+// In soong-only mode, we don't do most of the androidmk stuff. But disted files are still largely
+// defined through the androidmk mechanisms, so this function is an alternate implementation of
+// the androidmk singleton that just focuses on getting the dist contributions
+// TODO(b/397766191): Change the signature to take ModuleProxy
+// Please only access the module's internal data through providers.
+func (so *soongOnlyAndroidMkSingleton) soongOnlyBuildActions(ctx SingletonContext, mods []Module) {
+	allDistContributions, moduleInfoJSONs := getSoongOnlyDataFromMods(ctx, mods)
+
+	singletonDists := getSingletonDists(ctx.Config())
+	singletonDists.lock.Lock()
+	if contribution := distsToDistContributions(singletonDists.dists); contribution != nil {
+		allDistContributions = append(allDistContributions, *contribution)
+	}
+	singletonDists.lock.Unlock()
+
+	// Build module-info.json. Only in builds with HasDeviceProduct(), as we need a named
+	// device to have a TARGET_OUT folder.
+	if ctx.Config().HasDeviceProduct() {
+		preMergePath := PathForOutput(ctx, "module_info_pre_merging.json")
+		moduleInfoJSONPath := pathForInstall(ctx, Android, X86_64, "", "module-info.json")
+		if err := writeModuleInfoJSON(ctx, moduleInfoJSONs, preMergePath); err != nil {
+			ctx.Errorf("%s", err)
+		}
+		builder := NewRuleBuilder(pctx, ctx)
+		builder.Command().
+			BuiltTool("merge_module_info_json").
+			FlagWithOutput("-o ", moduleInfoJSONPath).
+			Input(preMergePath)
+		builder.Build("merge_module_info_json", "merge module info json")
+		ctx.Phony("module-info", moduleInfoJSONPath)
+		ctx.Phony("droidcore-unbundled", moduleInfoJSONPath)
+		allDistContributions = append(allDistContributions, distContributions{
+			copiesForGoals: []*copiesForGoals{{
+				goals: "general-tests droidcore-unbundled",
+				copies: []distCopy{{
+					from: moduleInfoJSONPath,
+					dest: "module-info.json",
+				}},
+			}},
+		})
+	}
+
+	// Build dist.mk for the packaging step to read and generate dist targets
+	distMkFile := absolutePath(filepath.Join(ctx.Config().katiPackageMkDir(), "dist.mk"))
+
+	var goalOutputPairs []string
+	var srcDstPairs []string
+	for _, contributions := range allDistContributions {
+		for _, copiesForGoal := range contributions.copiesForGoals {
+			goals := strings.Fields(copiesForGoal.goals)
+			for _, copy := range copiesForGoal.copies {
+				for _, goal := range goals {
+					goalOutputPairs = append(goalOutputPairs, fmt.Sprintf(" %s:%s", goal, copy.dest))
+				}
+				srcDstPairs = append(srcDstPairs, fmt.Sprintf(" %s:%s", copy.from.String(), copy.dest))
+			}
+		}
+	}
+	// There are duplicates in the lists that we need to remove
+	goalOutputPairs = SortedUniqueStrings(goalOutputPairs)
+	srcDstPairs = SortedUniqueStrings(srcDstPairs)
+	var buf strings.Builder
+	buf.WriteString("DIST_SRC_DST_PAIRS :=")
+	for _, srcDstPair := range srcDstPairs {
+		buf.WriteString(srcDstPair)
+	}
+	buf.WriteString("\nDIST_GOAL_OUTPUT_PAIRS :=")
+	for _, goalOutputPair := range goalOutputPairs {
+		buf.WriteString(goalOutputPair)
+	}
+	buf.WriteString("\n")
+
+	writeValueIfChanged(ctx, distMkFile, buf.String())
+}
+
+func writeValueIfChanged(ctx SingletonContext, path string, value string) {
+	if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
+		ctx.Errorf("%s\n", err)
+		return
+	}
+	previousValue := ""
+	rawPreviousValue, err := os.ReadFile(path)
+	if err == nil {
+		previousValue = string(rawPreviousValue)
+	}
+
+	if previousValue != value {
+		if err = os.WriteFile(path, []byte(value), 0666); err != nil {
+			ctx.Errorf("Failed to write: %v", err)
+		}
+	}
+}
+
+func distsToDistContributions(dists []dist) *distContributions {
+	if len(dists) == 0 {
+		return nil
+	}
+
+	copyGoals := []*copiesForGoals{}
+	for _, dist := range dists {
+		for _, goal := range dist.goals {
+			copyGoals = append(copyGoals, &copiesForGoals{
+				goals:  goal,
+				copies: dist.paths,
+			})
+		}
+	}
+
+	return &distContributions{
+		copiesForGoals: copyGoals,
+	}
+}
+
+// getSoongOnlyDataFromMods gathers data from the given modules needed in soong-only builds.
+// Currently, this is the dist contributions, and the module-info.json contents.
+func getSoongOnlyDataFromMods(ctx fillInEntriesContext, mods []Module) ([]distContributions, []*ModuleInfoJSON) {
+	var allDistContributions []distContributions
+	var moduleInfoJSONs []*ModuleInfoJSON
+	for _, mod := range mods {
+		if distInfo, ok := OtherModuleProvider(ctx, mod, DistProvider); ok {
+			if contribution := distsToDistContributions(distInfo.Dists); contribution != nil {
+				allDistContributions = append(allDistContributions, *contribution)
+			}
+		}
+
+		commonInfo := OtherModulePointerProviderOrDefault(ctx, mod, CommonModuleInfoProvider)
+		if commonInfo.SkipAndroidMkProcessing {
+			continue
+		}
+		if info, ok := OtherModuleProvider(ctx, mod, AndroidMkInfoProvider); ok {
+			// Deep copy the provider info since we need to modify the info later
+			info := deepCopyAndroidMkProviderInfo(info)
+			info.PrimaryInfo.fillInEntries(ctx, mod, commonInfo)
+			if info.PrimaryInfo.disabled() {
+				continue
+			}
+			if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+				moduleInfoJSONs = append(moduleInfoJSONs, moduleInfoJSON...)
+			}
+			if contribution := getDistContributions(ctx, mod); contribution != nil {
+				allDistContributions = append(allDistContributions, *contribution)
+			}
+		} else {
+			if x, ok := mod.(AndroidMkDataProvider); ok {
+				data := x.AndroidMk()
+
+				if data.Include == "" {
+					data.Include = "$(BUILD_PREBUILT)"
+				}
+
+				data.fillInData(ctx, mod)
+				if data.Entries.disabled() {
+					continue
+				}
+				if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+					moduleInfoJSONs = append(moduleInfoJSONs, moduleInfoJSON...)
+				}
+				if contribution := getDistContributions(ctx, mod); contribution != nil {
+					allDistContributions = append(allDistContributions, *contribution)
+				}
+			}
+			if x, ok := mod.(AndroidMkEntriesProvider); ok {
+				entriesList := x.AndroidMkEntries()
+				for _, entries := range entriesList {
+					entries.fillInEntries(ctx, mod)
+					if entries.disabled() {
+						continue
+					}
+					if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+						moduleInfoJSONs = append(moduleInfoJSONs, moduleInfoJSON...)
+					}
+					if contribution := getDistContributions(ctx, mod); contribution != nil {
+						allDistContributions = append(allDistContributions, *contribution)
+					}
+				}
+			}
+		}
+	}
+	return allDistContributions, moduleInfoJSONs
+}
+
+func translateAndroidMk(ctx SingletonContext, absMkFile string, moduleInfoJSONPath WritablePath, mods []Module) error {
 	buf := &bytes.Buffer{}
 
 	var moduleInfoJSONs []*ModuleInfoJSON
@@ -751,8 +967,8 @@
 			return err
 		}
 
-		if amod, ok := mod.(Module); ok && ctx.PrimaryModule(amod) == amod {
-			typeStats[ctx.ModuleType(amod)] += 1
+		if ctx.PrimaryModule(mod) == mod {
+			typeStats[ctx.ModuleType(mod)] += 1
 		}
 	}
 
@@ -796,7 +1012,7 @@
 	return nil
 }
 
-func translateAndroidMkModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON, mod blueprint.Module) error {
+func translateAndroidMkModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON, mod Module) error {
 	defer func() {
 		if r := recover(); r != nil {
 			panic(fmt.Errorf("%s in translateAndroidMkModule for module %s variant %s",
@@ -827,12 +1043,11 @@
 	return err
 }
 
-func (data *AndroidMkData) fillInData(ctx fillInEntriesContext, mod blueprint.Module) {
+func (data *AndroidMkData) fillInData(ctx fillInEntriesContext, mod Module) {
 	// Get the preamble content through AndroidMkEntries logic.
 	data.Entries = AndroidMkEntries{
 		Class:           data.Class,
 		SubName:         data.SubName,
-		DistFiles:       data.DistFiles,
 		OutputFile:      data.OutputFile,
 		Disabled:        data.Disabled,
 		Include:         data.Include,
@@ -851,9 +1066,9 @@
 // A support func for the deprecated AndroidMkDataProvider interface. Use AndroidMkEntryProvider
 // instead.
 func translateAndroidModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON,
-	mod blueprint.Module, provider AndroidMkDataProvider) error {
+	mod Module, provider AndroidMkDataProvider) error {
 
-	amod := mod.(Module).base()
+	amod := mod.base()
 	if shouldSkipAndroidMkProcessing(ctx, amod) {
 		return nil
 	}
@@ -865,7 +1080,7 @@
 	}
 
 	data.fillInData(ctx, mod)
-	aconfigUpdateAndroidMkData(ctx, mod.(Module), &data)
+	aconfigUpdateAndroidMkData(ctx, mod, &data)
 
 	prefix := ""
 	if amod.ArchSpecific() {
@@ -920,7 +1135,7 @@
 
 	if !data.Entries.disabled() {
 		if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
-			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
+			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON...)
 		}
 	}
 
@@ -946,23 +1161,28 @@
 }
 
 func translateAndroidMkEntriesModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON,
-	mod blueprint.Module, provider AndroidMkEntriesProvider) error {
-	if shouldSkipAndroidMkProcessing(ctx, mod.(Module).base()) {
+	mod Module, provider AndroidMkEntriesProvider) error {
+	if shouldSkipAndroidMkProcessing(ctx, mod.base()) {
 		return nil
 	}
 
 	entriesList := provider.AndroidMkEntries()
-	aconfigUpdateAndroidMkEntries(ctx, mod.(Module), &entriesList)
+	aconfigUpdateAndroidMkEntries(ctx, mod, &entriesList)
+
+	moduleInfoJSON, providesModuleInfoJSON := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider)
 
 	// Any new or special cases here need review to verify correct propagation of license information.
 	for _, entries := range entriesList {
 		entries.fillInEntries(ctx, mod)
 		entries.write(w)
-	}
 
-	if len(entriesList) > 0 && !entriesList[0].disabled() {
-		if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
-			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
+		if providesModuleInfoJSON && !entries.disabled() {
+			// append only the name matching moduleInfoJSON entry
+			for _, m := range moduleInfoJSON {
+				if m.RegisterNameOverride == entries.OverrideName && m.SubName == entries.SubName {
+					*moduleInfoJSONs = append(*moduleInfoJSONs, m)
+				}
+			}
 		}
 	}
 
@@ -1065,8 +1285,6 @@
 	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
@@ -1106,30 +1324,33 @@
 // TODO: rename it to AndroidMkEntriesProvider after AndroidMkEntriesProvider interface is gone.
 var AndroidMkInfoProvider = blueprint.NewProvider[*AndroidMkProviderInfo]()
 
+// TODO(b/397766191): Change the signature to take ModuleProxy
+// Please only access the module's internal data through providers.
 func translateAndroidMkEntriesInfoModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON,
-	mod blueprint.Module, providerInfo *AndroidMkProviderInfo) error {
-	if shouldSkipAndroidMkProcessing(ctx, mod.(Module).base()) {
+	mod Module, providerInfo *AndroidMkProviderInfo) error {
+	commonInfo := OtherModulePointerProviderOrDefault(ctx, mod, CommonModuleInfoProvider)
+	if commonInfo.SkipAndroidMkProcessing {
 		return nil
 	}
 
 	// Deep copy the provider info since we need to modify the info later
 	info := deepCopyAndroidMkProviderInfo(providerInfo)
 
-	aconfigUpdateAndroidMkInfos(ctx, mod.(Module), &info)
+	aconfigUpdateAndroidMkInfos(ctx, mod, &info)
 
 	// Any new or special cases here need review to verify correct propagation of license information.
-	info.PrimaryInfo.fillInEntries(ctx, mod)
+	info.PrimaryInfo.fillInEntries(ctx, mod, commonInfo)
 	info.PrimaryInfo.write(w)
 	if len(info.ExtraInfo) > 0 {
 		for _, ei := range info.ExtraInfo {
-			ei.fillInEntries(ctx, mod)
+			ei.fillInEntries(ctx, mod, commonInfo)
 			ei.write(w)
 		}
 	}
 
 	if !info.PrimaryInfo.disabled() {
 		if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
-			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
+			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON...)
 		}
 	}
 
@@ -1251,14 +1472,14 @@
 	a.AddStrings("LOCAL_COMPATIBILITY_SUITE", suites...)
 }
 
-func (a *AndroidMkInfo) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
+// TODO(b/397766191): Change the signature to take ModuleProxy
+// Please only access the module's internal data through providers.
+func (a *AndroidMkInfo) fillInEntries(ctx fillInEntriesContext, mod Module, commonInfo *CommonModuleInfo) {
 	helperInfo := AndroidMkInfo{
 		EntryMap: make(map[string][]string),
 	}
 
-	amod := mod.(Module)
-	base := amod.base()
-	name := base.BaseModuleName()
+	name := commonInfo.BaseModuleName
 	if a.OverrideName != "" {
 		name = a.OverrideName
 	}
@@ -1266,17 +1487,22 @@
 	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()...)
+	a.Required = append(a.Required, commonInfo.RequiredModuleNames...)
+	a.Required = append(a.Required, commonInfo.VintfFragmentModuleNames...)
+	a.Host_required = append(a.Host_required, commonInfo.HostRequiredModuleNames...)
+	a.Target_required = append(a.Target_required, commonInfo.TargetRequiredModuleNames...)
 
-	for _, distString := range a.GetDistForGoals(ctx, mod) {
-		a.HeaderStrings = append(a.HeaderStrings, distString)
+	a.HeaderStrings = append(a.HeaderStrings, a.GetDistForGoals(ctx, mod, commonInfo)...)
+	a.HeaderStrings = append(a.HeaderStrings, fmt.Sprintf("\ninclude $(CLEAR_VARS)  # type: %s, name: %s, variant: %s", ctx.ModuleType(mod), commonInfo.BaseModuleName, ctx.ModuleSubDir(mod)))
+
+	// Add the TestSuites from the provider to LOCAL_SOONG_PROVIDER_TEST_SUITES.
+	// LOCAL_SOONG_PROVIDER_TEST_SUITES will be compared against LOCAL_COMPATIBILITY_SUITES
+	// in make and enforced they're the same, to ensure we've successfully translated all
+	// LOCAL_COMPATIBILITY_SUITES usages to the provider.
+	if testSuiteInfo, ok := OtherModuleProvider(ctx, mod, TestSuiteInfoProvider); ok {
+		helperInfo.AddStrings("LOCAL_SOONG_PROVIDER_TEST_SUITES", testSuiteInfo.TestSuites...)
 	}
 
-	a.HeaderStrings = append(a.HeaderStrings, fmt.Sprintf("\ninclude $(CLEAR_VARS)  # type: %s, name: %s, variant: %s", 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)
@@ -1285,7 +1511,7 @@
 	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))
+	helperInfo.AddStrings("LOCAL_SOONG_MODULE_TYPE", ctx.ModuleType(mod))
 
 	// If the install rule was generated by Soong tell Make about it.
 	info := OtherModuleProviderOrDefault(ctx, mod, InstallFilesProvider)
@@ -1300,7 +1526,7 @@
 		// 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))
+		helperInfo.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", commonInfo.NoFullInstall)
 	}
 
 	if info.UncheckedModule {
@@ -1315,31 +1541,31 @@
 		helperInfo.AddStrings("LOCAL_TEST_DATA", androidMkDataPaths(info.TestData)...)
 	}
 
-	if am, ok := mod.(ApexModule); ok {
-		helperInfo.SetBoolIfTrue("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", am.NotAvailableForPlatform())
+	if commonInfo.IsApexModule {
+		helperInfo.SetBoolIfTrue("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", commonInfo.NotAvailableForPlatform)
 	}
 
-	archStr := base.Arch().ArchType.String()
+	archStr := commonInfo.Target.Arch.ArchType.String()
 	host := false
-	switch base.Os().Class {
+	switch commonInfo.Target.Os.Class {
 	case Host:
-		if base.Target().HostCross {
+		if commonInfo.Target.HostCross {
 			// Make cannot identify LOCAL_MODULE_HOST_CROSS_ARCH:= common.
-			if base.Arch().ArchType != Common {
+			if commonInfo.Target.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 {
+			if commonInfo.Target.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 commonInfo.Target.Arch.ArchType != Common {
+			if commonInfo.Target.NativeBridge {
+				hostArchStr := commonInfo.Target.NativeBridgeHostArchName
 				if hostArchStr != "" {
 					helperInfo.SetString("LOCAL_MODULE_TARGET_ARCH", hostArchStr)
 				}
@@ -1348,27 +1574,28 @@
 			}
 		}
 
-		if !base.InVendorRamdisk() {
+		if !commonInfo.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.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", commonInfo.Proprietary)
+		if commonInfo.Vendor || commonInfo.SocSpecific {
 			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)
+		helperInfo.SetBoolIfTrue("LOCAL_ODM_MODULE", commonInfo.DeviceSpecific)
+		helperInfo.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", commonInfo.ProductSpecific)
+		helperInfo.SetBoolIfTrue("LOCAL_SYSTEM_EXT_MODULE", commonInfo.SystemExtSpecific)
+		if commonInfo.Owner != "" {
+			helperInfo.SetString("LOCAL_MODULE_OWNER", commonInfo.Owner)
 		}
 	}
 
 	if host {
-		makeOs := base.Os().String()
-		if base.Os() == Linux || base.Os() == LinuxBionic || base.Os() == LinuxMusl {
+		os := commonInfo.Target.Os
+		makeOs := os.String()
+		if os == Linux || os == LinuxBionic || os == LinuxMusl {
 			makeOs = "linux"
 		}
 		helperInfo.SetString("LOCAL_MODULE_HOST_OS", makeOs)
@@ -1426,8 +1653,10 @@
 
 // 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)
+// TODO(b/397766191): Change the signature to take ModuleProxy
+// Please only access the module's internal data through providers.
+func (a *AndroidMkInfo) GetDistForGoals(ctx fillInEntriesContext, mod Module, commonInfo *CommonModuleInfo) []string {
+	distContributions := getDistContributions(ctx, mod)
 	if distContributions == nil {
 		return nil
 	}
@@ -1435,131 +1664,6 @@
 	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),
@@ -1577,9 +1681,8 @@
 		Class:        mkinfo.Class,
 		SubName:      mkinfo.SubName,
 		OverrideName: mkinfo.OverrideName,
-		// There is no modification on DistFiles or OutputFile, so no need to
+		// There is no modification on OutputFile, so no need to
 		// make their deep copy.
-		DistFiles:       mkinfo.DistFiles,
 		OutputFile:      mkinfo.OutputFile,
 		Disabled:        mkinfo.Disabled,
 		Include:         mkinfo.Include,
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
index f63b227..cd61133 100644
--- a/android/androidmk_test.go
+++ b/android/androidmk_test.go
@@ -34,7 +34,6 @@
 	}
 
 	data       AndroidMkData
-	distFiles  TaggedDistFiles
 	outputFile OptionalPath
 }
 
@@ -73,7 +72,6 @@
 		path := PathForTesting("default-dist.out")
 		defaultDistPaths = Paths{path}
 		m.setOutputFiles(ctx, defaultDistPaths)
-		m.distFiles = MakeDefaultDistFiles(path)
 
 	case defaultDistFiles_Tagged:
 		// Module types that set AndroidMkEntry.DistFiles to the result of calling
@@ -84,11 +82,6 @@
 		// will be the same as empty-string-tag output.
 		defaultDistPaths = PathsForTesting("one.out")
 		m.setOutputFiles(ctx, defaultDistPaths)
-
-		// This must be called after setting defaultDistPaths/outputFile as
-		// GenerateTaggedDistFiles calls into outputFiles property which may use
-		// those fields.
-		m.distFiles = m.GenerateTaggedDistFiles(ctx)
 	}
 }
 
@@ -113,7 +106,6 @@
 	return []AndroidMkEntries{
 		{
 			Class:      "CUSTOM_MODULE",
-			DistFiles:  m.distFiles,
 			OutputFile: m.outputFile,
 		},
 	}
@@ -144,7 +136,7 @@
 		FixtureWithRootAndroidBp(bp),
 	).RunTest(t)
 
-	module := result.ModuleForTests("foo", "").Module().(*customModule)
+	module := result.ModuleForTests(t, "foo", "").Module().(*customModule)
 	return result.TestContext, module
 }
 
@@ -354,7 +346,7 @@
 			if len(entries) != 1 {
 				t.Errorf("Expected a single AndroidMk entry, got %d", len(entries))
 			}
-			distContributions := entries[0].getDistContributions(module)
+			distContributions := getDistContributions(ctx, module)
 
 			if err := compareContributions(expectedContributions, distContributions); err != nil {
 				t.Errorf("%s\nExpected Contributions\n%sActualContributions\n%s",
@@ -656,8 +648,8 @@
 				default_dist_files: "none",
 				dist_output_file: false,
 				dists: [
-					// The following is silently ignored because there is not default file
-					// in either the dist files or the output file.
+					// The following will dist one.out because there's no default dist file provided
+					// (default_dist_files: "none") and one.out is the outputfile for the "" tag.
 					{
 						targets: ["my_goal"],
 					},
@@ -672,6 +664,12 @@
 			{
 				goals: "my_goal",
 				copies: []distCopy{
+					distCopyForTest("one.out", "one.out"),
+				},
+			},
+			{
+				goals: "my_goal",
+				copies: []distCopy{
 					distCopyForTest("two.out", "two.out"),
 					distCopyForTest("three/four.out", "four.out"),
 				},
diff --git a/android/apex.go b/android/apex.go
index db93912..57baff5 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"slices"
-	"sort"
 	"strconv"
 	"strings"
 	"sync"
@@ -55,23 +54,32 @@
 	// to true.
 	UsePlatformApis bool
 
-	// List of Apex variant names that this module is associated with. This initially is the
-	// same as the `ApexVariationName` field.  Then when multiple apex variants are merged in
-	// mergeApexVariations, ApexInfo struct of the merged variant holds the list of apexBundles
-	// that are merged together.
-	InApexVariants []string
-
 	// True if this is for a prebuilt_apex.
 	//
 	// If true then this will customize the apex processing to make it suitable for handling
 	// prebuilt_apex, e.g. it will prevent ApexInfos from being merged together.
 	//
-	// See Prebuilt.ApexInfoMutator for more information.
+	// Unlike the source apex module type the prebuilt_apex module type cannot share compatible variants
+	// across prebuilt_apex modules. That is because there is no way to determine whether two
+	// prebuilt_apex modules that export files for the same module are compatible. e.g. they could have
+	// been built from different source at different times or they could have been built with different
+	// build options that affect the libraries.
+	//
+	// While it may be possible to provide sufficient information to determine whether two prebuilt_apex
+	// modules were compatible it would be a lot of work and would not provide much benefit for a couple
+	// of reasons:
+	//   - The number of prebuilt_apex modules that will be exporting files for the same module will be
+	//     low as the prebuilt_apex only exports files for the direct dependencies that require it and
+	//     very few modules are direct dependencies of multiple prebuilt_apex modules, e.g. there are a
+	//     few com.android.art* apex files that contain the same contents and could export files for the
+	//     same modules but only one of them needs to do so. Contrast that with source apex modules which
+	//     need apex specific variants for every module that contributes code to the apex, whether direct
+	//     or indirect.
+	//   - The build cost of a prebuilt_apex variant is generally low as at worst it will involve some
+	//     extra copying of files. Contrast that with source apex modules that has to build each variant
+	//     from source.
 	ForPrebuiltApex bool
 
-	// Returns the name of the test apexes that this module is included in.
-	TestApexes []string
-
 	// Returns the name of the overridden apex (com.android.foo)
 	BaseApexName string
 
@@ -79,19 +87,34 @@
 	ApexAvailableName string
 }
 
-// AllApexInfo holds the ApexInfo of all apexes that include this module.
-type AllApexInfo struct {
-	ApexInfos []ApexInfo
+func (a ApexInfo) Variation() string {
+	return a.ApexVariationName
+}
+
+// Minimize is called during a transition from a module with a unique variation per apex to a module that should
+// share variations between apexes.  It returns a minimized ApexInfo that removes any apex names and replaces
+// the variation name with one computed from the remaining properties.
+func (a ApexInfo) Minimize() ApexInfo {
+	info := ApexInfo{
+		MinSdkVersion:   a.MinSdkVersion,
+		UsePlatformApis: a.UsePlatformApis,
+	}
+	info.ApexVariationName = info.mergedName()
+	return info
+}
+
+type ApexAvailableInfo struct {
+	// Returns the apex names that this module is available for
+	ApexAvailableFor []string
 }
 
 var ApexInfoProvider = blueprint.NewMutatorProvider[ApexInfo]("apex_mutate")
-var AllApexInfoProvider = blueprint.NewMutatorProvider[*AllApexInfo]("apex_info")
+var ApexAvailableInfoProvider = blueprint.NewMutatorProvider[ApexAvailableInfo]("apex_mutate")
 
 func (i ApexInfo) AddJSONData(d *map[string]interface{}) {
 	(*d)["Apex"] = map[string]interface{}{
 		"ApexVariationName": i.ApexVariationName,
 		"MinSdkVersion":     i.MinSdkVersion,
-		"InApexVariants":    i.InApexVariants,
 		"ForPrebuiltApex":   i.ForPrebuiltApex,
 	}
 }
@@ -105,6 +128,9 @@
 // thus wouldn't be merged.
 func (i ApexInfo) mergedName() string {
 	name := "apex" + strconv.Itoa(i.MinSdkVersion.FinalOrFutureInt())
+	if i.UsePlatformApis {
+		name += "_p"
+	}
 	return name
 }
 
@@ -114,49 +140,54 @@
 	return i.ApexVariationName == ""
 }
 
-// InApexVariant tells whether this apex variant of the module is part of the given apexVariant or
-// not.
-func (i ApexInfo) InApexVariant(apexVariant string) bool {
-	for _, a := range i.InApexVariants {
-		if a == apexVariant {
-			return true
-		}
-	}
-	return false
-}
-
 // To satisfy the comparable interface
 func (i ApexInfo) Equal(other any) bool {
 	otherApexInfo, ok := other.(ApexInfo)
 	return ok && i.ApexVariationName == otherApexInfo.ApexVariationName &&
 		i.MinSdkVersion == otherApexInfo.MinSdkVersion &&
 		i.Updatable == otherApexInfo.Updatable &&
-		i.UsePlatformApis == otherApexInfo.UsePlatformApis &&
-		slices.Equal(i.InApexVariants, otherApexInfo.InApexVariants)
+		i.UsePlatformApis == otherApexInfo.UsePlatformApis
 }
 
 // ApexBundleInfo contains information about the dependencies of an apex
 type ApexBundleInfo struct {
 }
 
-var ApexBundleInfoProvider = blueprint.NewMutatorProvider[ApexBundleInfo]("apex_info")
+var ApexBundleInfoProvider = blueprint.NewMutatorProvider[ApexBundleInfo]("apex_mutate")
 
-// DepIsInSameApex defines an interface that should be used to determine whether a given dependency
-// should be considered as part of the same APEX as the current module or not. Note: this was
-// extracted from ApexModule to make it easier to define custom subsets of the ApexModule interface
-// and improve code navigation within the IDE.
-type DepIsInSameApex interface {
-	// DepIsInSameApex tests if the other module 'dep' is considered as part of the same APEX as
-	// this module. For example, a static lib dependency usually returns true here, while a
+// DepInSameApexChecker defines an interface that should be used to determine whether a given dependency
+// should be considered as part of the same APEX as the current module or not.
+type DepInSameApexChecker interface {
+	// OutgoingDepIsInSameApex tests if the module depended on via 'tag' is considered as part of
+	// the same APEX as this module. For example, a static lib dependency usually returns true here, while a
 	// shared lib dependency to a stub library returns false.
 	//
 	// This method must not be called directly without first ignoring dependencies whose tags
 	// implement ExcludeFromApexContentsTag. Calls from within the func passed to WalkPayloadDeps()
 	// are fine as WalkPayloadDeps() will ignore those dependencies automatically. Otherwise, use
 	// IsDepInSameApex instead.
-	DepIsInSameApex(ctx BaseModuleContext, dep Module) bool
+	OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool
+
+	// IncomingDepIsInSameApex tests if this module depended on via 'tag' is considered as part of
+	// the same APEX as the depending module module. For example, a static lib dependency usually
+	// returns true here, while a shared lib dependency to a stub library returns false.
+	//
+	// This method must not be called directly without first ignoring dependencies whose tags
+	// implement ExcludeFromApexContentsTag. Calls from within the func passed to WalkPayloadDeps()
+	// are fine as WalkPayloadDeps() will ignore those dependencies automatically. Otherwise, use
+	// IsDepInSameApex instead.
+	IncomingDepIsInSameApex(tag blueprint.DependencyTag) bool
 }
 
+// DepInSameApexInfo is a provider that wraps around a DepInSameApexChecker that can be
+// used to check if a dependency belongs to the same apex as the module when walking
+// through the dependencies of a module.
+type DepInSameApexInfo struct {
+	Checker DepInSameApexChecker
+}
+
+var DepInSameApexInfoProvider = blueprint.NewMutatorProvider[DepInSameApexInfo]("apex_unique")
+
 func IsDepInSameApex(ctx BaseModuleContext, module, dep Module) bool {
 	depTag := ctx.OtherModuleDependencyTag(dep)
 	if _, ok := depTag.(ExcludeFromApexContentsTag); ok {
@@ -164,7 +195,25 @@
 		// apex as the parent.
 		return false
 	}
-	return module.(DepIsInSameApex).DepIsInSameApex(ctx, dep)
+
+	if !EqualModules(ctx.Module(), module) {
+		if moduleInfo, ok := OtherModuleProvider(ctx, module, DepInSameApexInfoProvider); ok {
+			if !moduleInfo.Checker.OutgoingDepIsInSameApex(depTag) {
+				return false
+			}
+		}
+	} else {
+		if m, ok := ctx.Module().(ApexModule); ok && !m.GetDepInSameApexChecker().OutgoingDepIsInSameApex(depTag) {
+			return false
+		}
+	}
+	if depInfo, ok := OtherModuleProvider(ctx, dep, DepInSameApexInfoProvider); ok {
+		if !depInfo.Checker.IncomingDepIsInSameApex(depTag) {
+			return false
+		}
+	}
+
+	return true
 }
 
 // ApexModule is the interface that a module type is expected to implement if the module has to be
@@ -182,7 +231,6 @@
 // mergedName) when the two APEXes have the same min_sdk_version requirement.
 type ApexModule interface {
 	Module
-	DepIsInSameApex
 
 	apexModuleBase() *ApexModuleBase
 
@@ -213,6 +261,12 @@
 	// apex_available property of the module.
 	AvailableFor(what string) bool
 
+	// Returns the apexes that are available for this module, valid values include
+	// "//apex_available:platform", "//apex_available:anyapex" and specific apexes.
+	// There are some differences between this one and the ApexAvailable on
+	// ApexModuleBase for cc, java library and sdkLibraryXml.
+	ApexAvailableFor() []string
+
 	// AlwaysRequiresPlatformApexVariant allows the implementing module to determine whether an
 	// APEX mutator should always be created for it.
 	//
@@ -228,16 +282,15 @@
 	// check-platform-availability mutator in the apex package.
 	SetNotAvailableForPlatform()
 
-	// Returns nil (success) if this module should support the given sdk version. Returns an
-	// error if not. No default implementation is provided for this method. A module type
-	// implementing this interface should provide an implementation. A module supports an sdk
-	// version when the module's min_sdk_version is equal to or less than the given sdk version.
-	ShouldSupportSdkVersion(ctx BaseModuleContext, sdkVersion ApiLevel) error
+	// Returns the min sdk version that the module supports, .
+	MinSdkVersionSupported(ctx BaseModuleContext) ApiLevel
 
 	// Returns true if this module needs a unique variation per apex, effectively disabling the
 	// deduping. This is turned on when, for example if use_apex_name_macro is set so that each
 	// apex variant should be built with different macro definitions.
 	UniqueApexVariations() bool
+
+	GetDepInSameApexChecker() DepInSameApexChecker
 }
 
 // Properties that are common to all module types implementing ApexModule interface.
@@ -257,16 +310,10 @@
 
 	// See ApexModule.UniqueApexVariants()
 	UniqueApexVariationsForDeps bool `blueprint:"mutated"`
-
-	// The test apexes that includes this apex variant
-	TestApexes []string `blueprint:"mutated"`
 }
 
 // Marker interface that identifies dependencies that are excluded from APEX contents.
 //
-// Unless the tag also implements the AlwaysRequireApexVariantTag this will prevent an apex variant
-// from being created for the module.
-//
 // At the moment the sdk.sdkRequirementsMutator relies on the fact that the existing tags which
 // implement this interface do not define dependencies onto members of an sdk_snapshot. If that
 // changes then sdk.sdkRequirementsMutator will need fixing.
@@ -277,17 +324,6 @@
 	ExcludeFromApexContents()
 }
 
-// Marker interface that identifies dependencies that always requires an APEX variant to be created.
-//
-// It is possible for a dependency to require an apex variant but exclude the module from the APEX
-// contents. See sdk.sdkMemberDependencyTag.
-type AlwaysRequireApexVariantTag interface {
-	blueprint.DependencyTag
-
-	// Return true if this tag requires that the target dependency has an apex variant.
-	AlwaysRequireApexVariant() bool
-}
-
 // Interface that identifies dependencies to skip Apex dependency check
 type SkipApexAllowedDependenciesCheck interface {
 	// Returns true to skip the Apex dependency check, which limits the allowed dependency in build.
@@ -306,6 +342,61 @@
 	apexInfosLock sync.Mutex // protects apexInfos during parallel apexInfoMutator
 }
 
+func (m *ApexModuleBase) ApexTransitionMutatorSplit(ctx BaseModuleContext) []ApexInfo {
+	return []ApexInfo{{}}
+}
+
+func (m *ApexModuleBase) ApexTransitionMutatorOutgoing(ctx OutgoingTransitionContext, info ApexInfo) ApexInfo {
+	if !ctx.Module().(ApexModule).GetDepInSameApexChecker().OutgoingDepIsInSameApex(ctx.DepTag()) {
+		return ApexInfo{}
+	}
+	return info
+}
+
+func (m *ApexModuleBase) ApexTransitionMutatorIncoming(ctx IncomingTransitionContext, info ApexInfo) ApexInfo {
+	module := ctx.Module().(ApexModule)
+	if !module.CanHaveApexVariants() {
+		return ApexInfo{}
+	}
+
+	if !ctx.Module().(ApexModule).GetDepInSameApexChecker().IncomingDepIsInSameApex(ctx.DepTag()) {
+		return ApexInfo{}
+	}
+
+	if info.ApexVariationName == "" {
+		return ApexInfo{}
+	}
+
+	if !ctx.Module().(ApexModule).UniqueApexVariations() && !m.ApexProperties.UniqueApexVariationsForDeps && !info.ForPrebuiltApex {
+		return info.Minimize()
+	}
+	return info
+}
+
+func (m *ApexModuleBase) ApexTransitionMutatorMutate(ctx BottomUpMutatorContext, info ApexInfo) {
+	SetProvider(ctx, ApexInfoProvider, info)
+
+	module := ctx.Module().(ApexModule)
+	base := module.apexModuleBase()
+
+	platformVariation := info.ApexVariationName == ""
+	if !platformVariation {
+		// Do some validity checks.
+		// TODO(jiyong): is this the right place?
+		base.checkApexAvailableProperty(ctx)
+
+		SetProvider(ctx, ApexAvailableInfoProvider, ApexAvailableInfo{
+			ApexAvailableFor: module.ApexAvailableFor(),
+		})
+	}
+	if platformVariation && !ctx.Host() && !module.AvailableFor(AvailableToPlatform) && module.NotAvailableForPlatform() {
+		// Do not install the module for platform, but still allow it to output
+		// uninstallable AndroidMk entries in certain cases when they have side
+		// effects.  TODO(jiyong): move this routine to somewhere else
+		module.MakeUninstallable()
+	}
+}
+
 // Initializes ApexModuleBase struct. Not calling this (even when inheriting from ApexModuleBase)
 // prevents the module from being mutated for apexBundle.
 func InitApexModule(m ApexModule) {
@@ -334,6 +425,10 @@
 	return CopyOf(availableToPlatformList)
 }
 
+func (m *ApexModuleBase) ApexAvailableFor() []string {
+	return m.ApexAvailable()
+}
+
 // Implements ApexModule
 func (m *ApexModuleBase) BuildForApex(apex ApexInfo) {
 	m.apexInfosLock.Lock()
@@ -373,11 +468,6 @@
 	return false
 }
 
-// Returns the test apexes that this module is included in.
-func (m *ApexModuleBase) TestApexes() []string {
-	return m.ApexProperties.TestApexes
-}
-
 // Implements ApexModule
 func (m *ApexModuleBase) UniqueApexVariations() bool {
 	// If needed, this will bel overridden by concrete types inheriting
@@ -386,11 +476,17 @@
 }
 
 // Implements ApexModule
-func (m *ApexModuleBase) DepIsInSameApex(ctx BaseModuleContext, dep Module) bool {
-	// By default, if there is a dependency from A to B, we try to include both in the same
-	// APEX, unless B is explicitly from outside of the APEX (i.e. a stubs lib). Thus, returning
-	// true. This is overridden by some module types like apex.ApexBundle, cc.Module,
-	// java.Module, etc.
+func (m *ApexModuleBase) GetDepInSameApexChecker() DepInSameApexChecker {
+	return BaseDepInSameApexChecker{}
+}
+
+type BaseDepInSameApexChecker struct{}
+
+func (m BaseDepInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool {
+	return true
+}
+
+func (m BaseDepInSameApexChecker) IncomingDepIsInSameApex(tag blueprint.DependencyTag) bool {
 	return true
 }
 
@@ -428,13 +524,17 @@
 		if strings.HasSuffix(apex_name, ".*") && strings.HasPrefix(what, strings.TrimSuffix(apex_name, "*")) {
 			return true
 		}
+		// TODO b/383863941: Remove once legacy name is no longer used
+		if (apex_name == "com.android.btservices" && what == "com.android.bt") || (apex_name == "com.android.bt" && what == "com.android.btservices") {
+			return true
+		}
 	}
 	return false
 }
 
 // Implements ApexModule
 func (m *ApexModuleBase) AvailableFor(what string) bool {
-	return CheckAvailableForApex(what, m.ApexProperties.Apex_available)
+	return CheckAvailableForApex(what, m.ApexAvailableFor())
 }
 
 // Implements ApexModule
@@ -494,204 +594,14 @@
 	return true
 }
 
-// mergeApexVariations deduplicates apex variations that would build identically into a common
-// variation. It returns the reduced list of variations and a list of aliases from the original
-// variation names to the new variation names.
-func mergeApexVariations(apexInfos []ApexInfo) (merged []ApexInfo, aliases [][2]string) {
-	seen := make(map[string]int)
-	for _, apexInfo := range apexInfos {
-		// If this is for a prebuilt apex then use the actual name of the apex variation to prevent this
-		// from being merged with other ApexInfo. See Prebuilt.ApexInfoMutator for more information.
-		if apexInfo.ForPrebuiltApex {
-			merged = append(merged, apexInfo)
-			continue
-		}
-
-		// Merge the ApexInfo together. If a compatible ApexInfo exists then merge the information from
-		// this one into it, otherwise create a new merged ApexInfo from this one and save it away so
-		// other ApexInfo instances can be merged into it.
-		variantName := apexInfo.ApexVariationName
-		mergedName := apexInfo.mergedName()
-		if index, exists := seen[mergedName]; exists {
-			// Variants having the same mergedName are deduped
-			merged[index].InApexVariants = append(merged[index].InApexVariants, variantName)
-			merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable
-			// Platform APIs is allowed for this module only when all APEXes containing
-			// the module are with `use_platform_apis: true`.
-			merged[index].UsePlatformApis = merged[index].UsePlatformApis && apexInfo.UsePlatformApis
-			merged[index].TestApexes = append(merged[index].TestApexes, apexInfo.TestApexes...)
-		} else {
-			seen[mergedName] = len(merged)
-			apexInfo.ApexVariationName = mergedName
-			apexInfo.InApexVariants = CopyOf(apexInfo.InApexVariants)
-			apexInfo.TestApexes = CopyOf(apexInfo.TestApexes)
-			merged = append(merged, apexInfo)
-		}
-		aliases = append(aliases, [2]string{variantName, mergedName})
-	}
-	return merged, aliases
-}
-
-// IncomingApexTransition is called by apexTransitionMutator.IncomingTransition on modules that can be in apexes.
-// The incomingVariation can be either the name of an apex if the dependency is coming directly from an apex
-// module, or it can be the name of an apex variation (e.g. apex10000) if it is coming from another module that
-// is in the apex.
-func IncomingApexTransition(ctx IncomingTransitionContext, incomingVariation string) string {
-	module := ctx.Module().(ApexModule)
-	base := module.apexModuleBase()
-
-	var apexInfos []ApexInfo
-	if allApexInfos, ok := ModuleProvider(ctx, AllApexInfoProvider); ok {
-		apexInfos = allApexInfos.ApexInfos
-	}
-
-	// Dependencies from platform variations go to the platform variation.
-	if incomingVariation == "" {
-		return ""
-	}
-
-	if len(apexInfos) == 0 {
-		if ctx.IsAddingDependency() {
-			// If this module has no apex variations we can't do any mapping on the incoming variation, just return it
-			// and let the caller get a "missing variant" error.
-			return incomingVariation
-		} else {
-			// If this module has no apex variations the use the platform variation.
-			return ""
-		}
-	}
-
-	// Convert the list of apex infos into from the AllApexInfoProvider into the merged list
-	// of apex variations and the aliases from apex names to apex variations.
-	var aliases [][2]string
-	if !module.UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps {
-		apexInfos, aliases = mergeApexVariations(apexInfos)
-	}
-
-	// Check if the incoming variation matches an apex name, and if so use the corresponding
-	// apex variation.
-	aliasIndex := slices.IndexFunc(aliases, func(alias [2]string) bool {
-		return alias[0] == incomingVariation
-	})
-	if aliasIndex >= 0 {
-		return aliases[aliasIndex][1]
-	}
-
-	// Check if the incoming variation matches an apex variation.
-	apexIndex := slices.IndexFunc(apexInfos, func(info ApexInfo) bool {
-		return info.ApexVariationName == incomingVariation
-	})
-	if apexIndex >= 0 {
-		return incomingVariation
-	}
-
-	return ""
-}
-
-func MutateApexTransition(ctx BaseModuleContext, variation string) {
-	module := ctx.Module().(ApexModule)
-	base := module.apexModuleBase()
-	platformVariation := variation == ""
-
-	var apexInfos []ApexInfo
-	if allApexInfos, ok := ModuleProvider(ctx, AllApexInfoProvider); ok {
-		apexInfos = allApexInfos.ApexInfos
-	}
-
-	// Shortcut
-	if len(apexInfos) == 0 {
-		return
-	}
-
-	// Do some validity checks.
-	// TODO(jiyong): is this the right place?
-	base.checkApexAvailableProperty(ctx)
-
-	if !module.UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps {
-		apexInfos, _ = mergeApexVariations(apexInfos)
-	}
-
-	if platformVariation && !ctx.Host() && !module.AvailableFor(AvailableToPlatform) && module.NotAvailableForPlatform() {
-		// Do not install the module for platform, but still allow it to output
-		// uninstallable AndroidMk entries in certain cases when they have side
-		// effects.  TODO(jiyong): move this routine to somewhere else
-		module.MakeUninstallable()
-	}
-	if !platformVariation {
-		var thisApexInfo ApexInfo
-
-		apexIndex := slices.IndexFunc(apexInfos, func(info ApexInfo) bool {
-			return info.ApexVariationName == variation
-		})
-		if apexIndex >= 0 {
-			thisApexInfo = apexInfos[apexIndex]
-		} else {
-			panic(fmt.Errorf("failed to find apexInfo for incoming variation %q", variation))
-		}
-
-		SetProvider(ctx, ApexInfoProvider, thisApexInfo)
-	}
-
-	// Set the value of TestApexes in every single apex variant.
-	// This allows each apex variant to be aware of the test apexes in the user provided apex_available.
-	var testApexes []string
-	for _, a := range apexInfos {
-		testApexes = append(testApexes, a.TestApexes...)
-	}
-	base.ApexProperties.TestApexes = testApexes
-
-}
-
-func ApexInfoMutator(ctx TopDownMutatorContext, module ApexModule) {
-	base := module.apexModuleBase()
-	if len(base.apexInfos) > 0 {
-		apexInfos := slices.Clone(base.apexInfos)
-		slices.SortFunc(apexInfos, func(a, b ApexInfo) int {
-			return strings.Compare(a.ApexVariationName, b.ApexVariationName)
-		})
-		SetProvider(ctx, AllApexInfoProvider, &AllApexInfo{apexInfos})
-		// base.apexInfos is only needed to propagate the list of apexes from the apex module to its
-		// contents within apexInfoMutator. Clear it so it doesn't accidentally get used later.
-		base.apexInfos = nil
-	}
-}
-
 // UpdateUniqueApexVariationsForDeps sets UniqueApexVariationsForDeps if any dependencies that are
 // in the same APEX have unique APEX variations so that the module can link against the right
 // variant.
 func UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext, am ApexModule) {
-	// anyInSameApex returns true if the two ApexInfo lists contain any values in an
-	// InApexVariants list in common. It is used instead of DepIsInSameApex because it needs to
-	// determine if the dep is in the same APEX due to being directly included, not only if it
-	// is included _because_ it is a dependency.
-	anyInSameApex := func(a, b ApexModule) bool {
-		collectApexes := func(m ApexModule) []string {
-			if allApexInfo, ok := OtherModuleProvider(mctx, m, AllApexInfoProvider); ok {
-				var ret []string
-				for _, info := range allApexInfo.ApexInfos {
-					ret = append(ret, info.InApexVariants...)
-				}
-				return ret
-			}
-			return nil
-		}
-
-		aApexes := collectApexes(a)
-		bApexes := collectApexes(b)
-		sort.Strings(bApexes)
-		for _, aApex := range aApexes {
-			index := sort.SearchStrings(bApexes, aApex)
-			if index < len(bApexes) && bApexes[index] == aApex {
-				return true
-			}
-		}
-		return false
-	}
-
 	// If any of the dependencies requires unique apex variations, so does this module.
 	mctx.VisitDirectDeps(func(dep Module) {
 		if depApexModule, ok := dep.(ApexModule); ok {
-			if anyInSameApex(depApexModule, am) &&
+			if IsDepInSameApex(mctx, am, depApexModule) &&
 				(depApexModule.UniqueApexVariations() ||
 					depApexModule.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps) {
 				am.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps = true
@@ -736,6 +646,13 @@
 	FullListPath() Path
 }
 
+type ApexBundleDepsData struct {
+	Updatable    bool
+	FlatListPath Path
+}
+
+var ApexBundleDepsDataProvider = blueprint.NewProvider[ApexBundleDepsData]()
+
 func (d *ApexBundleDepsInfo) FlatListPath() Path {
 	return d.flatListPath
 }
@@ -778,7 +695,7 @@
 // Function called while walking an APEX's payload dependencies.
 //
 // Return true if the `to` module should be visited, false otherwise.
-type PayloadDepsCallback func(ctx BaseModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool
+type PayloadDepsCallback func(ctx BaseModuleContext, from, to ModuleProxy, externalDep bool) bool
 type WalkPayloadDepsFunc func(ctx BaseModuleContext, do PayloadDepsCallback)
 
 // ModuleWithMinSdkVersionCheck represents a module that implements min_sdk_version checks
@@ -806,40 +723,63 @@
 		return
 	}
 
-	walk(ctx, func(ctx BaseModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool {
+	walk(ctx, func(ctx BaseModuleContext, from, to ModuleProxy, 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
 			// dependencies.
 			return false
 		}
-		if am, ok := from.(DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
+		if !IsDepInSameApex(ctx, from, to) {
 			return false
 		}
-		if m, ok := to.(ModuleWithMinSdkVersionCheck); ok {
-			// This dependency performs its own min_sdk_version check, just make sure it sets min_sdk_version
-			// to trigger the check.
-			if !m.MinSdkVersion(ctx).Specified() {
-				ctx.OtherModuleErrorf(m, "must set min_sdk_version")
+		if info, ok := OtherModuleProvider(ctx, to, CommonModuleInfoProvider); ok && info.ModuleWithMinSdkVersionCheck {
+			if info.MinSdkVersion.ApiLevel == nil || !info.MinSdkVersion.ApiLevel.Specified() {
+				// This dependency performs its own min_sdk_version check, just make sure it sets min_sdk_version
+				// to trigger the check.
+				ctx.OtherModuleErrorf(to, "must set min_sdk_version")
 			}
 			return false
 		}
-		if err := to.ShouldSupportSdkVersion(ctx, minSdkVersion); err != nil {
-			toName := ctx.OtherModuleName(to)
+		if err := ShouldSupportSdkVersion(ctx, to, minSdkVersion); err != nil {
 			ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v."+
 				"\n\nDependency path: %s\n\n"+
 				"Consider adding 'min_sdk_version: %q' to %q",
 				minSdkVersion, ctx.ModuleName(), err.Error(),
 				ctx.GetPathString(false),
-				minSdkVersion, toName)
+				minSdkVersion, ctx.OtherModuleName(to))
 			return false
 		}
 		return true
 	})
 }
 
+type MinSdkVersionFromValueContext interface {
+	Config() Config
+	DeviceConfig() DeviceConfig
+	ModuleErrorContext
+}
+
+// Returns nil (success) if this module should support the given sdk version. Returns an
+// error if not. No default implementation is provided for this method. A module type
+// implementing this interface should provide an implementation. A module supports an sdk
+// version when the module's min_sdk_version is equal to or less than the given sdk version.
+func ShouldSupportSdkVersion(ctx BaseModuleContext, module Module, sdkVersion ApiLevel) error {
+	info, ok := OtherModuleProvider(ctx, module, CommonModuleInfoProvider)
+	if !ok || info.MinSdkVersionSupported.IsNone() {
+		return fmt.Errorf("min_sdk_version is not specified")
+	}
+	minVer := info.MinSdkVersionSupported
+
+	if minVer.GreaterThan(sdkVersion) {
+		return fmt.Errorf("newer SDK(%v)", minVer)
+	}
+
+	return nil
+}
+
 // Construct ApiLevel object from min_sdk_version string value
-func MinSdkVersionFromValue(ctx EarlyModuleContext, value string) ApiLevel {
+func MinSdkVersionFromValue(ctx MinSdkVersionFromValueContext, value string) ApiLevel {
 	if value == "" {
 		return NoneApiLevel
 	}
@@ -880,3 +820,21 @@
 	// to generate the mainline module prebuilt.
 	Prebuilt_info_file_path string `json:",omitempty"`
 }
+
+// FragmentInApexTag is embedded into a dependency tag to allow apex modules to annotate
+// their fragments in a way that allows the java bootclasspath modules to traverse from
+// the apex to the fragment.
+type FragmentInApexTag struct{}
+
+func (FragmentInApexTag) isFragmentInApexTag() {}
+
+type isFragmentInApexTagIntf interface {
+	isFragmentInApexTag()
+}
+
+// IsFragmentInApexTag returns true if the dependency tag embeds FragmentInApexTag,
+// signifying that it is a dependency from an apex module to its fragment.
+func IsFragmentInApexTag(tag blueprint.DependencyTag) bool {
+	_, ok := tag.(isFragmentInApexTagIntf)
+	return ok
+}
diff --git a/android/apex_contributions.go b/android/apex_contributions.go
index ce34278..fe7a835 100644
--- a/android/apex_contributions.go
+++ b/android/apex_contributions.go
@@ -104,19 +104,8 @@
 	AcDepTag = apexContributionsDepTag{}
 )
 
-// Creates a dep to each selected apex_contributions
-func (a *allApexContributions) DepsMutator(ctx BottomUpMutatorContext) {
-	// Skip apex_contributions if BuildApexContributionContents is true
-	// This product config var allows some products in the same family to use mainline modules from source
-	// (e.g. shiba and shiba_fullmte)
-	// Eventually these product variants will have their own release config maps.
-	if !proptools.Bool(ctx.Config().BuildIgnoreApexContributionContents()) {
-		ctx.AddDependency(ctx.Module(), AcDepTag, ctx.Config().AllApexContributions()...)
-	}
-}
-
 // Set PrebuiltSelectionInfoProvider in post deps phase
-func (a *allApexContributions) SetPrebuiltSelectionInfoProvider(ctx BaseModuleContext) {
+func (a *allApexContributions) SetPrebuiltSelectionInfoProvider(ctx BottomUpMutatorContext) {
 	addContentsToProvider := func(p *PrebuiltSelectionInfoMap, m *apexContributions) {
 		for _, content := range m.Contents() {
 			// Verify that the module listed in contents exists in the tree
@@ -135,13 +124,23 @@
 	}
 
 	p := PrebuiltSelectionInfoMap{}
-	ctx.VisitDirectDepsWithTag(AcDepTag, func(child Module) {
-		if m, ok := child.(*apexContributions); ok {
-			addContentsToProvider(&p, m)
-		} else {
-			ctx.ModuleErrorf("%s is not an apex_contributions module\n", child.Name())
+	// Skip apex_contributions if BuildApexContributionContents is true
+	// This product config var allows some products in the same family to use mainline modules from source
+	// (e.g. shiba and shiba_fullmte)
+	// Eventually these product variants will have their own release config maps.
+	if !proptools.Bool(ctx.Config().BuildIgnoreApexContributionContents()) {
+		deps := ctx.AddDependency(ctx.Module(), AcDepTag, ctx.Config().AllApexContributions()...)
+		for _, child := range deps {
+			if child == nil {
+				continue
+			}
+			if m, ok := child.(*apexContributions); ok {
+				addContentsToProvider(&p, m)
+			} else {
+				ctx.ModuleErrorf("%s is not an apex_contributions module\n", child.Name())
+			}
 		}
-	})
+	}
 	SetProvider(ctx, PrebuiltSelectionInfoProvider, p)
 }
 
diff --git a/android/apex_test.go b/android/apex_test.go
deleted file mode 100644
index 78597b2..0000000
--- a/android/apex_test.go
+++ /dev/null
@@ -1,270 +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 android
-
-import (
-	"reflect"
-	"testing"
-)
-
-func Test_mergeApexVariations(t *testing.T) {
-	const (
-		ForPrebuiltApex    = true
-		NotForPrebuiltApex = false
-	)
-	tests := []struct {
-		name        string
-		in          []ApexInfo
-		wantMerged  []ApexInfo
-		wantAliases [][2]string
-	}{
-		{
-			name: "single",
-			in: []ApexInfo{
-				{
-					ApexVariationName: "foo",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantMerged: []ApexInfo{
-				{
-					ApexVariationName: "apex10000",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantAliases: [][2]string{
-				{"foo", "apex10000"},
-			},
-		},
-		{
-			name: "merge",
-			in: []ApexInfo{
-				{
-					ApexVariationName: "foo",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				{
-					ApexVariationName: "bar",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantMerged: []ApexInfo{
-				{
-					ApexVariationName: "apex10000",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo", "bar"},
-				}},
-			wantAliases: [][2]string{
-				{"foo", "apex10000"},
-				{"bar", "apex10000"},
-			},
-		},
-		{
-			name: "don't merge version",
-			in: []ApexInfo{
-				{
-					ApexVariationName: "foo",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				{
-					ApexVariationName: "bar",
-					MinSdkVersion:     uncheckedFinalApiLevel(30),
-					InApexVariants:    []string{"bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantMerged: []ApexInfo{
-				{
-					ApexVariationName: "apex10000",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				{
-					ApexVariationName: "apex30",
-					MinSdkVersion:     uncheckedFinalApiLevel(30),
-					InApexVariants:    []string{"bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantAliases: [][2]string{
-				{"foo", "apex10000"},
-				{"bar", "apex30"},
-			},
-		},
-		{
-			name: "merge updatable",
-			in: []ApexInfo{
-				{
-					ApexVariationName: "foo",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				{
-					ApexVariationName: "bar",
-					MinSdkVersion:     FutureApiLevel,
-					Updatable:         true,
-					InApexVariants:    []string{"bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantMerged: []ApexInfo{
-				{
-					ApexVariationName: "apex10000",
-					MinSdkVersion:     FutureApiLevel,
-					Updatable:         true,
-					InApexVariants:    []string{"foo", "bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantAliases: [][2]string{
-				{"foo", "apex10000"},
-				{"bar", "apex10000"},
-			},
-		},
-		{
-			name: "don't merge when for prebuilt_apex",
-			in: []ApexInfo{
-				{
-					ApexVariationName: "foo",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				{
-					ApexVariationName: "bar",
-					MinSdkVersion:     FutureApiLevel,
-					Updatable:         true,
-					InApexVariants:    []string{"bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				// This one should not be merged in with the others because it is for
-				// a prebuilt_apex.
-				{
-					ApexVariationName: "baz",
-					MinSdkVersion:     FutureApiLevel,
-					Updatable:         true,
-					InApexVariants:    []string{"baz"},
-					ForPrebuiltApex:   ForPrebuiltApex,
-				},
-			},
-			wantMerged: []ApexInfo{
-				{
-					ApexVariationName: "apex10000",
-					MinSdkVersion:     FutureApiLevel,
-					Updatable:         true,
-					InApexVariants:    []string{"foo", "bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				{
-					ApexVariationName: "baz",
-					MinSdkVersion:     FutureApiLevel,
-					Updatable:         true,
-					InApexVariants:    []string{"baz"},
-					ForPrebuiltApex:   ForPrebuiltApex,
-				},
-			},
-			wantAliases: [][2]string{
-				{"foo", "apex10000"},
-				{"bar", "apex10000"},
-			},
-		},
-		{
-			name: "merge different UsePlatformApis but don't allow using platform api",
-			in: []ApexInfo{
-				{
-					ApexVariationName: "foo",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				{
-					ApexVariationName: "bar",
-					MinSdkVersion:     FutureApiLevel,
-					UsePlatformApis:   true,
-					InApexVariants:    []string{"bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantMerged: []ApexInfo{
-				{
-					ApexVariationName: "apex10000",
-					MinSdkVersion:     FutureApiLevel,
-					InApexVariants:    []string{"foo", "bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantAliases: [][2]string{
-				{"foo", "apex10000"},
-				{"bar", "apex10000"},
-			},
-		},
-		{
-			name: "merge same UsePlatformApis and allow using platform api",
-			in: []ApexInfo{
-				{
-					ApexVariationName: "foo",
-					MinSdkVersion:     FutureApiLevel,
-					UsePlatformApis:   true,
-					InApexVariants:    []string{"foo"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-				{
-					ApexVariationName: "bar",
-					MinSdkVersion:     FutureApiLevel,
-					UsePlatformApis:   true,
-					InApexVariants:    []string{"bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantMerged: []ApexInfo{
-				{
-					ApexVariationName: "apex10000",
-					MinSdkVersion:     FutureApiLevel,
-					UsePlatformApis:   true,
-					InApexVariants:    []string{"foo", "bar"},
-					ForPrebuiltApex:   NotForPrebuiltApex,
-				},
-			},
-			wantAliases: [][2]string{
-				{"foo", "apex10000"},
-				{"bar", "apex10000"},
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			gotMerged, gotAliases := mergeApexVariations(tt.in)
-			if !reflect.DeepEqual(gotMerged, tt.wantMerged) {
-				t.Errorf("mergeApexVariations() gotMerged = %v, want %v", gotMerged, tt.wantMerged)
-			}
-			if !reflect.DeepEqual(gotAliases, tt.wantAliases) {
-				t.Errorf("mergeApexVariations() gotAliases = %v, want %v", gotAliases, tt.wantAliases)
-			}
-		})
-	}
-}
diff --git a/android/api_levels.go b/android/api_levels.go
index 2b1d01d..c83fae8 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -19,6 +19,8 @@
 	"fmt"
 	"strconv"
 	"strings"
+
+	"github.com/google/blueprint/gobtools"
 )
 
 func init() {
@@ -52,6 +54,34 @@
 	isPreview bool
 }
 
+type apiLevelGob struct {
+	Value     string
+	Number    int
+	IsPreview bool
+}
+
+func (a *ApiLevel) ToGob() *apiLevelGob {
+	return &apiLevelGob{
+		Value:     a.value,
+		Number:    a.number,
+		IsPreview: a.isPreview,
+	}
+}
+
+func (a *ApiLevel) FromGob(data *apiLevelGob) {
+	a.value = data.Value
+	a.number = data.Number
+	a.isPreview = data.IsPreview
+}
+
+func (a ApiLevel) GobEncode() ([]byte, error) {
+	return gobtools.CustomGobEncode[apiLevelGob](&a)
+}
+
+func (a *ApiLevel) GobDecode(data []byte) error {
+	return gobtools.CustomGobDecode[apiLevelGob](data, a)
+}
+
 func (this ApiLevel) FinalInt() int {
 	if this.IsInvalid() {
 		panic(fmt.Errorf("%v is not a recognized api_level\n", this))
@@ -252,6 +282,9 @@
 	isPreview: true,
 }
 
+// A special ApiLevel that all modules should at least support.
+var MinApiLevel = ApiLevel{number: 1}
+
 // Sentinel ApiLevel to validate that an apiLevel is either an int or a recognized codename.
 var InvalidApiLevel = NewInvalidApiLevel("invalid")
 
@@ -311,7 +344,7 @@
 
 // ApiLevelFrom converts the given string `raw` to an ApiLevel.
 // If `raw` is invalid (empty string, unrecognized codename etc.) it returns an invalid ApiLevel
-func ApiLevelFrom(ctx PathContext, raw string) ApiLevel {
+func ApiLevelFrom(ctx ConfigContext, raw string) ApiLevel {
 	ret, err := ApiLevelFromUser(ctx, raw)
 	if err != nil {
 		return NewInvalidApiLevel(raw)
@@ -333,7 +366,7 @@
 //
 // Inputs that are not "current", known previews, or convertible to an integer
 // will return an error.
-func ApiLevelFromUser(ctx PathContext, raw string) (ApiLevel, error) {
+func ApiLevelFromUser(ctx ConfigContext, raw string) (ApiLevel, error) {
 	return ApiLevelFromUserWithConfig(ctx.Config(), raw)
 }
 
@@ -413,7 +446,7 @@
 // Converts an API level string `raw` into an ApiLevel in the same method as
 // `ApiLevelFromUser`, but the input is assumed to have no errors and any errors
 // will panic instead of returning an error.
-func ApiLevelOrPanic(ctx PathContext, raw string) ApiLevel {
+func ApiLevelOrPanic(ctx ConfigContext, raw string) ApiLevel {
 	value, err := ApiLevelFromUser(ctx, raw)
 	if err != nil {
 		panic(err.Error())
@@ -465,6 +498,7 @@
 		"Tiramisu":        33,
 		"UpsideDownCake":  34,
 		"VanillaIceCream": 35,
+		"Baklava":         36,
 	}, nil
 }
 
diff --git a/android/arch.go b/android/arch.go
index 3cd6e4b..d6b2971 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -1553,7 +1553,7 @@
 	config.BuildOS = func() OsType {
 		switch runtime.GOOS {
 		case "linux":
-			if Bool(config.productVariables.HostMusl) {
+			if Bool(config.productVariables.HostMusl) || runtime.GOARCH == "arm64" {
 				return LinuxMusl
 			}
 			return Linux
@@ -1565,11 +1565,25 @@
 	}()
 
 	config.BuildArch = func() ArchType {
-		switch runtime.GOARCH {
-		case "amd64":
-			return X86_64
+		switch runtime.GOOS {
+		case "linux":
+			switch runtime.GOARCH {
+			case "amd64":
+				return X86_64
+			case "arm64":
+				return Arm64
+			default:
+				panic(fmt.Sprintf("unsupported arch: %s", runtime.GOARCH))
+			}
+		case "darwin":
+			switch runtime.GOARCH {
+			case "amd64":
+				return X86_64
+			default:
+				panic(fmt.Sprintf("unsupported arch: %s", runtime.GOARCH))
+			}
 		default:
-			panic(fmt.Sprintf("unsupported Arch: %s", runtime.GOARCH))
+			panic(fmt.Sprintf("unsupported OS: %s", runtime.GOOS))
 		}
 	}()
 
diff --git a/android/arch_list.go b/android/arch_list.go
index 389f194..8659549 100644
--- a/android/arch_list.go
+++ b/android/arch_list.go
@@ -27,6 +27,8 @@
 		"armv8-2a-dotprod",
 		"armv9-a",
 		"armv9-2a",
+		"armv9-3a",
+		"armv9-4a",
 	},
 	X86: {
 		"alderlake",
@@ -151,6 +153,12 @@
 		"armv9-2a": {
 			"dotprod",
 		},
+		"armv9-3a": {
+			"dotprod",
+		},
+		"armv9-4a": {
+			"dotprod",
+		},
 	},
 	X86: {
 		"alderlake": {
diff --git a/android/arch_test.go b/android/arch_test.go
index 7914884..adb655f 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -432,7 +432,7 @@
 		var ret []string
 		variants := ctx.ModuleVariantsForTests(name)
 		for _, variant := range variants {
-			m := ctx.ModuleForTests(name, variant)
+			m := ctx.ModuleForTests(t, name, variant)
 			if m.Module().Enabled(PanickingConfigAndErrorContext(ctx)) {
 				ret = append(ret, variant)
 			}
@@ -442,7 +442,7 @@
 
 	moduleMultiTargets := func(ctx *TestContext, name string, variant string) []string {
 		var ret []string
-		targets := ctx.ModuleForTests(name, variant).Module().MultiTargets()
+		targets := ctx.ModuleForTests(t, name, variant).Module().MultiTargets()
 		for _, t := range targets {
 			ret = append(ret, t.String())
 		}
@@ -546,7 +546,7 @@
 		var ret []string
 		variants := ctx.ModuleVariantsForTests(name)
 		for _, variant := range variants {
-			m := ctx.ModuleForTests(name, variant)
+			m := ctx.ModuleForTests(t, name, variant)
 			if m.Module().Enabled(PanickingConfigAndErrorContext(ctx)) {
 				ret = append(ret, variant)
 			}
@@ -758,7 +758,7 @@
 
 			for _, want := range tt.results {
 				t.Run(want.module+"_"+want.variant, func(t *testing.T) {
-					got := result.ModuleForTests(want.module, want.variant).Module().(*testArchPropertiesModule).properties.A
+					got := result.ModuleForTests(t, want.module, want.variant).Module().(*testArchPropertiesModule).properties.A
 					AssertArrayString(t, "arch mutator property", want.property, got)
 				})
 			}
diff --git a/android/base_module_context.go b/android/base_module_context.go
index 06819d6..5cb9e71 100644
--- a/android/base_module_context.go
+++ b/android/base_module_context.go
@@ -34,8 +34,6 @@
 
 	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
@@ -53,6 +51,9 @@
 	// dependencies on the module being visited, it returns the dependency tag used for the current dependency.
 	OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
 
+	// OtherModuleSubDir returns the string representing the variations of a module.
+	OtherModuleSubDir(m blueprint.Module) string
+
 	// OtherModuleExists returns true if a module with the specified name exists, as determined by the NameInterface
 	// passed to Context.SetNameInterface, or SimpleNameInterface if it was not called.
 	OtherModuleExists(name string) bool
@@ -88,6 +89,11 @@
 	// This method shouldn't be used directly, prefer the type-safe android.OtherModuleProvider instead.
 	otherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
 
+	// OtherModuleHasProvider returns true if the module has the given provider set. This
+	// can avoid copying the provider if the caller only cares about the existence of
+	// the provider.
+	OtherModuleHasProvider(m blueprint.Module, provider blueprint.AnyProviderKey) bool
+
 	// OtherModuleIsAutoGenerated returns true if the module is auto generated by another module
 	// instead of being defined in Android.bp file.
 	OtherModuleIsAutoGenerated(m blueprint.Module) bool
@@ -110,15 +116,14 @@
 
 	GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module
 
+	GetDirectDepsProxyWithTag(tag blueprint.DependencyTag) []ModuleProxy
+
 	// GetDirectDepWithTag returns the Module the direct dependency with the specified name, or nil if
 	// none exists.  It panics if the dependency does not have the specified tag.  It skips any
 	// dependencies that are not an android.Module.
 	GetDirectDepWithTag(name string, tag blueprint.DependencyTag) Module
 
-	// GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified
-	// name, or nil if none exists.  If there are multiple dependencies on the same module it returns
-	// the first DependencyTag.
-	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
+	GetDirectDepProxyWithTag(name string, tag blueprint.DependencyTag) *ModuleProxy
 
 	// 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
@@ -255,14 +260,17 @@
 }
 
 func getWrappedModule(module blueprint.Module) blueprint.Module {
+	if mp, isProxy := module.(*ModuleProxy); isProxy {
+		return mp.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 EqualModules(m1, m2 Module) bool {
+	return blueprint.EqualModules(getWrappedModule(m1), getWrappedModule(m2))
 }
 
 func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string {
@@ -277,6 +285,9 @@
 func (b *baseModuleContext) OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag {
 	return b.bp.OtherModuleDependencyTag(getWrappedModule(m))
 }
+func (b *baseModuleContext) OtherModuleSubDir(m blueprint.Module) string {
+	return b.bp.OtherModuleSubDir(getWrappedModule(m))
+}
 func (b *baseModuleContext) OtherModuleExists(name string) bool { return b.bp.OtherModuleExists(name) }
 func (b *baseModuleContext) OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool {
 	return b.bp.OtherModuleDependencyVariantExists(variations, name)
@@ -292,7 +303,11 @@
 }
 
 func (b *baseModuleContext) otherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
-	return b.bp.OtherModuleProvider(m, provider)
+	return b.bp.OtherModuleProvider(getWrappedModule(m), provider)
+}
+
+func (b *baseModuleContext) OtherModuleHasProvider(m blueprint.Module, provider blueprint.AnyProviderKey) bool {
+	return b.bp.OtherModuleHasProvider(getWrappedModule(m), provider)
 }
 
 func (b *baseModuleContext) OtherModuleIsAutoGenerated(m blueprint.Module) bool {
@@ -314,6 +329,13 @@
 	return nil
 }
 
+func (b *baseModuleContext) GetDirectDepProxyWithTag(name string, tag blueprint.DependencyTag) *ModuleProxy {
+	if module := b.bp.GetDirectDepProxyWithTag(name, tag); module != nil {
+		return &ModuleProxy{*module}
+	}
+	return nil
+}
+
 func (b *baseModuleContext) blueprintBaseModuleContext() blueprint.BaseModuleContext {
 	return b.bp
 }
@@ -388,7 +410,7 @@
 		return &aModule
 	}
 
-	if !OtherModuleProviderOrDefault(b, module, CommonModuleInfoKey).Enabled {
+	if !OtherModulePointerProviderOrDefault(b, module, CommonModuleInfoProvider).Enabled {
 		if t, ok := tag.(AllowDisabledModuleDependency); !ok || !t.AllowDisabledModuleDependencyProxy(b, aModule) {
 			if b.Config().AllowMissingDependencies() {
 				b.AddMissingDependencies([]string{b.OtherModuleName(aModule)})
@@ -402,53 +424,30 @@
 	return &aModule
 }
 
-type dep struct {
-	mod blueprint.Module
-	tag blueprint.DependencyTag
-}
-
-func (b *baseModuleContext) getDirectDepsInternal(name string, tag blueprint.DependencyTag) []dep {
-	var deps []dep
+func (b *baseModuleContext) getDirectDepsInternal(name string, tag blueprint.DependencyTag) []Module {
+	var deps []Module
 	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})
+				deps = append(deps, module)
 			}
 		}
 	})
 	return deps
 }
 
-func (b *baseModuleContext) getDirectDepInternal(name string, tag blueprint.DependencyTag) (blueprint.Module, blueprint.DependencyTag) {
-	deps := b.getDirectDepsInternal(name, tag)
-	if len(deps) == 1 {
-		return deps[0].mod, deps[0].tag
-	} else if len(deps) >= 2 {
-		panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q",
-			name, b.ModuleName()))
-	} else {
-		return nil, nil
-	}
-}
-
-func (b *baseModuleContext) getDirectDepFirstTag(name string) (blueprint.Module, blueprint.DependencyTag) {
-	foundDeps := b.getDirectDepsInternal(name, nil)
-	deps := map[blueprint.Module]bool{}
-	for _, dep := range foundDeps {
-		deps[dep.mod] = true
-	}
-	if len(deps) == 1 {
-		return foundDeps[0].mod, foundDeps[0].tag
-	} else if len(deps) >= 2 {
-		// this could happen if two dependencies have the same name in different namespaces
-		// TODO(b/186554727): this should not occur if namespaces are handled within
-		// getDirectDepsInternal.
-		panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q",
-			name, b.ModuleName()))
-	} else {
-		return nil, nil
-	}
+func (b *baseModuleContext) getDirectDepsProxyInternal(name string, tag blueprint.DependencyTag) []ModuleProxy {
+	var deps []ModuleProxy
+	b.VisitDirectDepsProxy(func(module ModuleProxy) {
+		if OtherModulePointerProviderOrDefault(b, module, CommonModuleInfoProvider).BaseModuleName == name {
+			returnedTag := b.OtherModuleDependencyTag(module)
+			if tag == nil || returnedTag == tag {
+				deps = append(deps, module)
+			}
+		}
+	})
+	return deps
 }
 
 func (b *baseModuleContext) GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module {
@@ -461,11 +460,14 @@
 	return deps
 }
 
-// GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified
-// name, or nil if none exists. If there are multiple dependencies on the same module it returns the
-// first DependencyTag.
-func (b *baseModuleContext) GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) {
-	return b.getDirectDepFirstTag(name)
+func (b *baseModuleContext) GetDirectDepsProxyWithTag(tag blueprint.DependencyTag) []ModuleProxy {
+	var deps []ModuleProxy
+	b.VisitDirectDepsProxy(func(module ModuleProxy) {
+		if b.OtherModuleDependencyTag(module) == tag {
+			deps = append(deps, module)
+		}
+	})
+	return deps
 }
 
 func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) {
diff --git a/android/build_prop_test.go b/android/build_prop_test.go
index e75975a..e136a1a 100644
--- a/android/build_prop_test.go
+++ b/android/build_prop_test.go
@@ -36,6 +36,6 @@
 	res := GroupFixturePreparers(
 		FixtureRegisterWithContext(registerBuildPropComponents),
 	).RunTestWithBp(t, bp)
-	buildPropCmd := res.ModuleForTests("vendor-build.prop", "").Rule("vendor-build.prop_.vendor-build.prop").RuleParams.Command
+	buildPropCmd := res.ModuleForTests(t, "vendor-build.prop", "").Rule("vendor-build.prop_.vendor-build.prop").RuleParams.Command
 	AssertStringDoesContain(t, "Could not find android-info in prop files of vendor build.prop", buildPropCmd, "--prop-files=out/soong/.intermediates/board-info/android-info.prop")
 }
diff --git a/android/compliance_metadata.go b/android/compliance_metadata.go
index 0b876c3..16a3853 100644
--- a/android/compliance_metadata.go
+++ b/android/compliance_metadata.go
@@ -18,7 +18,9 @@
 	"bytes"
 	"encoding/csv"
 	"fmt"
+	"path/filepath"
 	"slices"
+	"sort"
 	"strconv"
 	"strings"
 
@@ -43,6 +45,7 @@
 		STATIC_DEP_FILES       string
 		WHOLE_STATIC_DEPS      string
 		WHOLE_STATIC_DEP_FILES string
+		HEADER_LIBS            string
 		LICENSES               string
 
 		// module_type=package
@@ -71,6 +74,7 @@
 		"static_dep_files",
 		"whole_static_deps",
 		"whole_static_dep_files",
+		"header_libs",
 		"licenses",
 
 		"pkg_default_applicable_licenses",
@@ -106,6 +110,8 @@
 		ComplianceMetadataProp.WHOLE_STATIC_DEPS,
 		// Space separated file paths of whole static dependencies
 		ComplianceMetadataProp.WHOLE_STATIC_DEP_FILES,
+		// Space separated modules name of header libs
+		ComplianceMetadataProp.HEADER_LIBS,
 		ComplianceMetadataProp.LICENSES,
 		// module_type=package
 		ComplianceMetadataProp.PKG_DEFAULT_APPLICABLE_LICENSES,
@@ -123,27 +129,37 @@
 // dependencies, built/installed files, etc. It is a wrapper on a map[string]string with some utility
 // methods to get/set properties' values.
 type ComplianceMetadataInfo struct {
-	properties map[string]string
+	properties          map[string]string
+	filesContained      []string
+	prebuiltFilesCopied []string
 }
 
 type complianceMetadataInfoGob struct {
-	Properties map[string]string
+	Properties          map[string]string
+	FilesContained      []string
+	PrebuiltFilesCopied []string
 }
 
 func NewComplianceMetadataInfo() *ComplianceMetadataInfo {
 	return &ComplianceMetadataInfo{
-		properties: map[string]string{},
+		properties:          map[string]string{},
+		filesContained:      make([]string, 0),
+		prebuiltFilesCopied: make([]string, 0),
 	}
 }
 
 func (m *ComplianceMetadataInfo) ToGob() *complianceMetadataInfoGob {
 	return &complianceMetadataInfoGob{
-		Properties: m.properties,
+		Properties:          m.properties,
+		FilesContained:      m.filesContained,
+		PrebuiltFilesCopied: m.prebuiltFilesCopied,
 	}
 }
 
 func (m *ComplianceMetadataInfo) FromGob(data *complianceMetadataInfoGob) {
 	m.properties = data.Properties
+	m.filesContained = data.FilesContained
+	m.prebuiltFilesCopied = data.PrebuiltFilesCopied
 }
 
 func (c *ComplianceMetadataInfo) GobEncode() ([]byte, error) {
@@ -165,6 +181,22 @@
 	c.SetStringValue(propertyName, strings.TrimSpace(strings.Join(value, " ")))
 }
 
+func (c *ComplianceMetadataInfo) SetFilesContained(files []string) {
+	c.filesContained = files
+}
+
+func (c *ComplianceMetadataInfo) GetFilesContained() []string {
+	return c.filesContained
+}
+
+func (c *ComplianceMetadataInfo) SetPrebuiltFilesCopied(files []string) {
+	c.prebuiltFilesCopied = files
+}
+
+func (c *ComplianceMetadataInfo) GetPrebuiltFilesCopied() []string {
+	return c.prebuiltFilesCopied
+}
+
 func (c *ComplianceMetadataInfo) getStringValue(propertyName string) string {
 	if !slices.Contains(COMPLIANCE_METADATA_PROPS, propertyName) {
 		panic(fmt.Errorf("Unknown metadata property: %s.", propertyName))
@@ -271,16 +303,18 @@
 	writerToCsv(csvWriter, columnNames)
 
 	rowId := -1
-	ctx.VisitAllModules(func(module Module) {
-		if !module.Enabled(ctx) {
+	ctx.VisitAllModuleProxies(func(module ModuleProxy) {
+		commonInfo := OtherModulePointerProviderOrDefault(ctx, module, CommonModuleInfoProvider)
+		if !commonInfo.Enabled {
 			return
 		}
+
 		moduleType := ctx.ModuleType(module)
 		if moduleType == "package" {
 			metadataMap := map[string]string{
 				ComplianceMetadataProp.NAME:                            ctx.ModuleName(module),
 				ComplianceMetadataProp.MODULE_TYPE:                     ctx.ModuleType(module),
-				ComplianceMetadataProp.PKG_DEFAULT_APPLICABLE_LICENSES: strings.Join(module.base().primaryLicensesProperty.getStrings(), " "),
+				ComplianceMetadataProp.PKG_DEFAULT_APPLICABLE_LICENSES: strings.Join(commonInfo.PrimaryLicensesProperty.getStrings(), " "),
 			}
 			rowId = rowId + 1
 			metadata := []string{strconv.Itoa(rowId)}
@@ -290,8 +324,7 @@
 			writerToCsv(csvWriter, metadata)
 			return
 		}
-		if provider, ok := ctx.otherModuleProvider(module, ComplianceMetadataProvider); ok {
-			metadataInfo := provider.(*ComplianceMetadataInfo)
+		if metadataInfo, ok := OtherModuleProvider(ctx, module, ComplianceMetadataProvider); ok {
 			rowId = rowId + 1
 			metadata := []string{strconv.Itoa(rowId)}
 			for _, propertyName := range COMPLIANCE_METADATA_PROPS {
@@ -311,6 +344,44 @@
 	makeMetadataCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-metadata.csv")
 	makeModulesCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-modules.csv")
 
+	productOutPath := filepath.Join(ctx.Config().OutDir(), "target", "product", String(ctx.Config().productVariables.DeviceName))
+	if !ctx.Config().KatiEnabled() {
+		ctx.VisitAllModuleProxies(func(module ModuleProxy) {
+			// In soong-only build the installed file list is from android_device module
+			if androidDeviceInfo, ok := OtherModuleProvider(ctx, module, AndroidDeviceInfoProvider); ok && androidDeviceInfo.Main_device {
+				if metadataInfo, ok := OtherModuleProvider(ctx, module, ComplianceMetadataProvider); ok {
+					if len(metadataInfo.filesContained) > 0 || len(metadataInfo.prebuiltFilesCopied) > 0 {
+						allFiles := make([]string, 0, len(metadataInfo.filesContained)+len(metadataInfo.prebuiltFilesCopied))
+						allFiles = append(allFiles, metadataInfo.filesContained...)
+						prebuiltFilesSrcDest := make(map[string]string)
+						for _, srcDestPair := range metadataInfo.prebuiltFilesCopied {
+							prebuiltFilePath := filepath.Join(productOutPath, strings.Split(srcDestPair, ":")[1])
+							allFiles = append(allFiles, prebuiltFilePath)
+							prebuiltFilesSrcDest[prebuiltFilePath] = srcDestPair
+						}
+						sort.Strings(allFiles)
+
+						csvHeaders := "installed_file,module_path,is_soong_module,is_prebuilt_make_module,product_copy_files,kernel_module_copy_files,is_platform_generated,static_libs,whole_static_libs,license_text"
+						csvContent := make([]string, 0, len(allFiles)+1)
+						csvContent = append(csvContent, csvHeaders)
+						for _, file := range allFiles {
+							if _, ok := prebuiltFilesSrcDest[file]; ok {
+								srcDestPair := prebuiltFilesSrcDest[file]
+								csvContent = append(csvContent, file+",,,,"+srcDestPair+",,,,,")
+							} else {
+								csvContent = append(csvContent, file+",,Y,,,,,,,")
+							}
+						}
+
+						WriteFileRuleVerbatim(ctx, makeMetadataCsv, strings.Join(csvContent, "\n"))
+						WriteFileRuleVerbatim(ctx, makeModulesCsv, "name,module_path,module_class,module_type,static_libs,whole_static_libs,built_files,installed_files")
+					}
+					return
+				}
+			}
+		})
+	}
+
 	// Import metadata from Make and Soong to sqlite3 database
 	complianceMetadataDb := PathForOutput(ctx, "compliance-metadata", deviceProduct, "compliance-metadata.db")
 	ctx.Build(pctx, BuildParams{
diff --git a/android/config.go b/android/config.go
index b811c55..b92eb7e 100644
--- a/android/config.go
+++ b/android/config.go
@@ -22,6 +22,7 @@
 	"fmt"
 	"os"
 	"path/filepath"
+	"reflect"
 	"runtime"
 	"strconv"
 	"strings"
@@ -83,6 +84,7 @@
 	OutDir         string
 	SoongOutDir    string
 	SoongVariables string
+	KatiSuffix     string
 
 	ModuleGraphFile   string
 	ModuleActionsFile string
@@ -107,6 +109,10 @@
 
 const testKeyDir = "build/make/target/product/security"
 
+func (c Config) genericConfig() Config {
+	return Config{c.config.genericConfig}
+}
+
 // SoongOutDir returns the build output directory for the configuration.
 func (c Config) SoongOutDir() string {
 	return c.soongOutDir
@@ -199,6 +205,11 @@
 	return c.config.productVariables.ReleaseAconfigValueSets
 }
 
+// If native modules should have symbols stripped by default. Default false, enabled for build tools
+func (c Config) StripByDefault() bool {
+	return proptools.Bool(c.config.productVariables.StripByDefault)
+}
+
 func (c Config) ReleaseAconfigExtraReleaseConfigs() []string {
 	result := []string{}
 	if val, ok := c.config.productVariables.BuildFlags["RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS"]; ok {
@@ -232,11 +243,6 @@
 	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 {
@@ -289,6 +295,14 @@
 	return c.config.productVariables.GetBuildFlagBool("RELEASE_USE_SYSTEM_FEATURE_BUILD_FLAGS")
 }
 
+func (c Config) ReleaseFingerprintAconfigPackages() bool {
+	return c.config.productVariables.GetBuildFlagBool("RELEASE_FINGERPRINT_ACONFIG_PACKAGES")
+}
+
+func (c Config) ReleaseAconfigCheckApiLevel() bool {
+	return c.config.productVariables.GetBuildFlagBool("RELEASE_ACONFIG_CHECK_API_LEVEL")
+}
+
 // 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.
@@ -345,6 +359,7 @@
 	// Changes behavior based on whether Kati runs after soong_build, or if soong_build
 	// runs standalone.
 	katiEnabled bool
+	katiSuffix  string
 
 	captureBuild      bool // true for tests, saves build parameters for each module
 	ignoreEnvironment bool // true for tests, returns empty from all Getenv calls
@@ -362,7 +377,7 @@
 	// regenerate build.ninja.
 	ninjaFileDepsSet sync.Map
 
-	OncePer
+	*OncePer
 
 	// If buildFromSourceStub is true then the Java API stubs are
 	// built from the source Java files, not the signature text files.
@@ -372,18 +387,44 @@
 	// modules that aren't mixed-built for at least one variant will cause a build
 	// failure
 	ensureAllowlistIntegrity bool
+
+	// If isGeneric is true, this config is the generic config.
+	isGeneric bool
+
+	// InstallPath requires the device name.
+	// This is only for the installPath.
+	deviceNameToInstall *string
+
+	// Copy of this config struct but some product-specific variables are
+	// replaced with the generic configuration values.
+	genericConfig *config
 }
 
 type partialCompileFlags struct {
-	// Is partial compilation enabled at all?
-	enabled bool
-
 	// Whether to use d8 instead of r8
-	use_d8 bool
+	Use_d8 bool
+
+	// Whether to disable stub validation.  This is slightly more surgical
+	// than DISABLE_STUB_VALIDATION, in that it only applies to partial
+	// compile builds.
+	Disable_stub_validation bool
+
+	// Whether to disable api lint.
+	Disable_api_lint bool
 
 	// Add others as needed.
 }
 
+// These are the flags when `SOONG_PARTIAL_COMPILE` is empty or not set.
+var defaultPartialCompileFlags = partialCompileFlags{}
+
+// These are the flags when `SOONG_PARTIAL_COMPILE=true`.
+var enabledPartialCompileFlags = partialCompileFlags{
+	Use_d8:                  true,
+	Disable_stub_validation: false,
+	Disable_api_lint:        false,
+}
+
 type deviceConfig struct {
 	config *config
 	OncePer
@@ -417,11 +458,6 @@
 // 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.
-var defaultPartialCompileFlags = partialCompileFlags{
-	// Set any opt-out flags here.  Opt-in flags are off by default.
-	enabled: false,
-}
-
 func (c *config) parsePartialCompileFlags(isEngBuild bool) (partialCompileFlags, error) {
 	if !isEngBuild {
 		return partialCompileFlags{}, nil
@@ -461,16 +497,31 @@
 			state = "+"
 		}
 		switch tok {
+		case "all":
+			// Turn on **all** of the flags.
+			ret = partialCompileFlags{
+				Use_d8:                  true,
+				Disable_stub_validation: true,
+				Disable_api_lint:        true,
+			}
 		case "true":
-			ret = defaultPartialCompileFlags
-			ret.enabled = true
+			ret = enabledPartialCompileFlags
 		case "false":
 			// Set everything to false.
 			ret = partialCompileFlags{}
-		case "enabled":
-			ret.enabled = makeVal(state, defaultPartialCompileFlags.enabled)
+
+		case "api_lint", "enable_api_lint":
+			ret.Disable_api_lint = !makeVal(state, !defaultPartialCompileFlags.Disable_api_lint)
+		case "disable_api_lint":
+			ret.Disable_api_lint = makeVal(state, defaultPartialCompileFlags.Disable_api_lint)
+
+		case "stub_validation", "enable_stub_validation":
+			ret.Disable_stub_validation = !makeVal(state, !defaultPartialCompileFlags.Disable_stub_validation)
+		case "disable_stub_validation":
+			ret.Disable_stub_validation = makeVal(state, defaultPartialCompileFlags.Disable_stub_validation)
+
 		case "use_d8":
-			ret.use_d8 = makeVal(state, defaultPartialCompileFlags.use_d8)
+			ret.Use_d8 = makeVal(state, defaultPartialCompileFlags.Use_d8)
 		default:
 			return partialCompileFlags{}, fmt.Errorf("Unknown SOONG_PARTIAL_COMPILE value: %v", tok)
 		}
@@ -604,11 +655,9 @@
 	}
 }
 
-// NewConfig creates a new Config object. The srcDir argument specifies the path
-// to the root source directory. It also loads the config file, if found.
-func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) {
+func initConfig(cmdArgs CmdArgs, availableEnv map[string]string) (*config, error) {
 	// Make a config with default options.
-	config := &config{
+	newConfig := &config{
 		ProductVariablesFileName: cmdArgs.SoongVariables,
 
 		env: availableEnv,
@@ -616,75 +665,78 @@
 		outDir:            cmdArgs.OutDir,
 		soongOutDir:       cmdArgs.SoongOutDir,
 		runGoTests:        cmdArgs.RunGoTests,
+		katiSuffix:        cmdArgs.KatiSuffix,
 		multilibConflicts: make(map[ArchType]bool),
 
 		moduleListFile: cmdArgs.ModuleListFile,
 		fs:             pathtools.NewOsFs(absSrcDir),
 
+		OncePer: &OncePer{},
+
 		buildFromSourceStub: cmdArgs.BuildFromSourceStub,
 	}
 	variant, ok := os.LookupEnv("TARGET_BUILD_VARIANT")
 	isEngBuild := !ok || variant == "eng"
 
-	config.deviceConfig = &deviceConfig{
-		config: config,
+	newConfig.deviceConfig = &deviceConfig{
+		config: newConfig,
 	}
 
 	// Soundness check of the build and source directories. This won't catch strange
 	// configurations with symlinks, but at least checks the obvious case.
 	absBuildDir, err := filepath.Abs(cmdArgs.SoongOutDir)
 	if err != nil {
-		return Config{}, err
+		return &config{}, err
 	}
 
 	absSrcDir, err := filepath.Abs(".")
 	if err != nil {
-		return Config{}, err
+		return &config{}, err
 	}
 
 	if strings.HasPrefix(absSrcDir, absBuildDir) {
-		return Config{}, fmt.Errorf("Build dir must not contain source directory")
+		return &config{}, fmt.Errorf("Build dir must not contain source directory")
 	}
 
 	// Load any configurable options from the configuration file
-	err = loadConfig(config)
+	err = loadConfig(newConfig)
 	if err != nil {
-		return Config{}, err
+		return &config{}, err
 	}
 
 	KatiEnabledMarkerFile := filepath.Join(cmdArgs.SoongOutDir, ".soong.kati_enabled")
 	if _, err := os.Stat(absolutePath(KatiEnabledMarkerFile)); err == nil {
-		config.katiEnabled = true
+		newConfig.katiEnabled = true
 	}
 
-	determineBuildOS(config)
+	determineBuildOS(newConfig)
 
 	// Sets up the map of target OSes to the finer grained compilation targets
 	// that are configured from the product variables.
-	targets, err := decodeTargetProductVariables(config)
+	targets, err := decodeTargetProductVariables(newConfig)
 	if err != nil {
-		return Config{}, err
+		return &config{}, err
 	}
 
-	config.partialCompileFlags, err = config.parsePartialCompileFlags(isEngBuild)
+	newConfig.partialCompileFlags, err = newConfig.parsePartialCompileFlags(isEngBuild)
 	if err != nil {
-		return Config{}, err
+		return &config{}, err
 	}
 
 	// Make the CommonOS OsType available for all products.
 	targets[CommonOS] = []Target{commonTargetMap[CommonOS.Name]}
 
 	var archConfig []archConfig
-	if config.NdkAbis() {
+	if newConfig.NdkAbis() {
 		archConfig = getNdkAbisConfig()
-	} else if config.AmlAbis() {
+	} else if newConfig.AmlAbis() {
 		archConfig = getAmlAbisConfig()
 	}
 
 	if archConfig != nil {
 		androidTargets, err := decodeAndroidArchSettings(archConfig)
 		if err != nil {
-			return Config{}, err
+			return &config{}, err
 		}
 		targets[Android] = androidTargets
 	}
@@ -692,37 +744,113 @@
 	multilib := make(map[string]bool)
 	for _, target := range targets[Android] {
 		if seen := multilib[target.Arch.ArchType.Multilib]; seen {
-			config.multilibConflicts[target.Arch.ArchType] = true
+			newConfig.multilibConflicts[target.Arch.ArchType] = true
 		}
 		multilib[target.Arch.ArchType.Multilib] = true
 	}
 
 	// Map of OS to compilation targets.
-	config.Targets = targets
+	newConfig.Targets = targets
 
 	// Compilation targets for host tools.
-	config.BuildOSTarget = config.Targets[config.BuildOS][0]
-	config.BuildOSCommonTarget = getCommonTargets(config.Targets[config.BuildOS])[0]
+	newConfig.BuildOSTarget = newConfig.Targets[newConfig.BuildOS][0]
+	newConfig.BuildOSCommonTarget = getCommonTargets(newConfig.Targets[newConfig.BuildOS])[0]
 
 	// Compilation targets for Android.
-	if len(config.Targets[Android]) > 0 {
-		config.AndroidCommonTarget = getCommonTargets(config.Targets[Android])[0]
-		config.AndroidFirstDeviceTarget = FirstTarget(config.Targets[Android], "lib64", "lib32")[0]
+	if len(newConfig.Targets[Android]) > 0 {
+		newConfig.AndroidCommonTarget = getCommonTargets(newConfig.Targets[Android])[0]
+		newConfig.AndroidFirstDeviceTarget = FirstTarget(newConfig.Targets[Android], "lib64", "lib32")[0]
 	}
 
 	setBuildMode := func(arg string, mode SoongBuildMode) {
 		if arg != "" {
-			if config.BuildMode != AnalysisNoBazel {
+			if newConfig.BuildMode != AnalysisNoBazel {
 				fmt.Fprintf(os.Stderr, "buildMode is already set, illegal argument: %s", arg)
 				os.Exit(1)
 			}
-			config.BuildMode = mode
+			newConfig.BuildMode = mode
 		}
 	}
 	setBuildMode(cmdArgs.ModuleGraphFile, GenerateModuleGraph)
 	setBuildMode(cmdArgs.DocFile, GenerateDocFile)
 
-	config.productVariables.Build_from_text_stub = boolPtr(config.BuildFromTextStub())
+	newConfig.productVariables.Build_from_text_stub = boolPtr(newConfig.BuildFromTextStub())
+
+	newConfig.deviceNameToInstall = newConfig.productVariables.DeviceName
+
+	return newConfig, err
+}
+
+// Replace variables in config.productVariables that have tags with "generic" key.
+// A generic tag may have a string or an int value for the generic configuration.
+// If the value is "unset", generic configuration will unset the variable.
+func overrideGenericConfig(config *config) {
+	config.genericConfig.isGeneric = true
+	type_pv := reflect.TypeOf(config.genericConfig.productVariables)
+	value_pv := reflect.ValueOf(&config.genericConfig.productVariables)
+	for i := range type_pv.NumField() {
+		type_pv_field := type_pv.Field(i)
+		generic_value := type_pv_field.Tag.Get("generic")
+		// If a product variable has an annotation of "generic" tag, use the
+		// value of the tag to set the generic variable.
+		if generic_value != "" {
+			value_pv_field := value_pv.Elem().Field(i)
+
+			if generic_value == "unset" {
+				// unset the product variable
+				value_pv_field.SetZero()
+				continue
+			}
+
+			kind_of_type_pv := type_pv_field.Type.Kind()
+			is_pointer := false
+			if kind_of_type_pv == reflect.Pointer {
+				is_pointer = true
+				kind_of_type_pv = type_pv_field.Type.Elem().Kind()
+			}
+
+			switch kind_of_type_pv {
+			case reflect.String:
+				if is_pointer {
+					value_pv_field.Set(reflect.ValueOf(stringPtr(generic_value)))
+				} else {
+					value_pv_field.Set(reflect.ValueOf(generic_value))
+				}
+			case reflect.Int:
+				generic_int, err := strconv.Atoi(generic_value)
+				if err != nil {
+					panic(fmt.Errorf("Only an int value can be assigned to int variable: %s", err))
+				}
+				if is_pointer {
+					value_pv_field.Set(reflect.ValueOf(intPtr(generic_int)))
+				} else {
+					value_pv_field.Set(reflect.ValueOf(generic_int))
+				}
+			default:
+				panic(fmt.Errorf("Unknown type to replace for generic variable: %s", &kind_of_type_pv))
+			}
+		}
+	}
+
+	// OncePer must be a singleton.
+	config.genericConfig.OncePer = config.OncePer
+	// keep the device name to get the install path.
+	config.genericConfig.deviceNameToInstall = config.deviceNameToInstall
+}
+
+// NewConfig creates a new Config object. It also loads the config file, if
+// found. The Config object includes a duplicated Config object in it for the
+// generic configuration that does not provide any product specific information.
+func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) {
+	config, err := initConfig(cmdArgs, availableEnv)
+	if err != nil {
+		return Config{}, err
+	}
+
+	// Initialize generic configuration.
+	config.genericConfig, err = initConfig(cmdArgs, availableEnv)
+	// Update product specific variables with the generic configuration.
+	overrideGenericConfig(config)
 
 	return Config{config}, err
 }
@@ -764,11 +892,7 @@
 // BlueprintToolLocation returns the directory containing build system tools
 // from Blueprint, like soong_zip and merge_zips.
 func (c *config) HostToolDir() string {
-	if c.KatiEnabled() {
-		return filepath.Join(c.outDir, "host", c.PrebuiltOS(), "bin")
-	} else {
-		return filepath.Join(c.soongOutDir, "host", c.PrebuiltOS(), "bin")
-	}
+	return filepath.Join(c.outDir, "host", c.PrebuiltOS(), "bin")
 }
 
 func (c *config) HostToolPath(ctx PathContext, tool string) Path {
@@ -802,11 +926,18 @@
 func (c *config) PrebuiltOS() string {
 	switch runtime.GOOS {
 	case "linux":
-		return "linux-x86"
+		switch runtime.GOARCH {
+		case "amd64":
+			return "linux-x86"
+		case "arm64":
+			return "linux-arm64"
+		default:
+			panic(fmt.Errorf("Unknown GOARCH %s", runtime.GOARCH))
+		}
 	case "darwin":
 		return "darwin-x86"
 	default:
-		panic("Unknown GOOS")
+		panic(fmt.Errorf("Unknown GOOS %s", runtime.GOOS))
 	}
 }
 
@@ -907,7 +1038,7 @@
 // require them to run and get the current build fingerprint. This ensures they
 // don't rebuild on every incremental build when the build number changes.
 func (c *config) BuildFingerprintFile(ctx PathContext) Path {
-	return PathForArbitraryOutput(ctx, "target", "product", c.DeviceName(), String(c.productVariables.BuildFingerprintFile))
+	return PathForArbitraryOutput(ctx, "target", "product", *c.deviceNameToInstall, String(c.productVariables.BuildFingerprintFile))
 }
 
 // BuildNumberFile returns the path to a text file containing metadata
@@ -935,7 +1066,7 @@
 // require them to run and get the current build thumbprint. This ensures they
 // don't rebuild on every incremental build when the build thumbprint changes.
 func (c *config) BuildThumbprintFile(ctx PathContext) Path {
-	return PathForArbitraryOutput(ctx, "target", "product", c.DeviceName(), String(c.productVariables.BuildThumbprintFile))
+	return PathForArbitraryOutput(ctx, "target", "product", *c.deviceNameToInstall, String(c.productVariables.BuildThumbprintFile))
 }
 
 // DeviceName returns the name of the current device target.
@@ -983,6 +1114,10 @@
 	return uncheckedFinalApiLevel(*c.productVariables.Platform_sdk_version)
 }
 
+func (c *config) PlatformSdkVersionFull() string {
+	return proptools.StringDefault(c.productVariables.Platform_sdk_version_full, "")
+}
+
 func (c *config) RawPlatformSdkVersion() *int {
 	return c.productVariables.Platform_sdk_version
 }
@@ -1168,6 +1303,10 @@
 	return otaPaths
 }
 
+func (c *config) ExtraOtaRecoveryKeys() []string {
+	return c.productVariables.ExtraOtaRecoveryKeys
+}
+
 func (c *config) BuildKeys() string {
 	defaultCert := String(c.productVariables.DefaultAppCertificate)
 	if defaultCert == "" || defaultCert == filepath.Join(testKeyDir, "testkey") {
@@ -1317,7 +1456,16 @@
 }
 
 func (c *config) RunErrorProne() bool {
-	return c.IsEnvTrue("RUN_ERROR_PRONE")
+	return c.IsEnvTrue("RUN_ERROR_PRONE") || c.RunErrorProneInline()
+}
+
+// Returns if the errorprone build should be run "inline", that is, using errorprone as part
+// of the main javac compilation instead of its own separate compilation. This is good for CI
+// but bad for local development, because if you toggle errorprone+inline on/off it will repeatedly
+// clobber java files from the old configuration.
+func (c *config) RunErrorProneInline() bool {
+	value := strings.ToLower(c.Getenv("RUN_ERROR_PRONE"))
+	return c.IsEnvTrue("RUN_ERROR_PRONE_INLINE") || value == "inline"
 }
 
 // XrefCorpusName returns the Kythe cross-reference corpus name.
@@ -1503,6 +1651,10 @@
 	return c.productVariables.GetBuildFlagBool("RELEASE_BOARD_API_LEVEL_FROZEN")
 }
 
+func (c *config) katiPackageMkDir() string {
+	return filepath.Join(c.soongOutDir, "kati_packaging"+c.katiSuffix)
+}
+
 func (c *deviceConfig) Arches() []Arch {
 	var arches []Arch
 	for _, target := range c.config.Targets[Android] {
@@ -2151,12 +2303,20 @@
 	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")
+func (c *config) UseR8FullModeByDefault() bool {
+	return c.productVariables.GetBuildFlagBool("RELEASE_R8_FULL_MODE_BY_DEFAULT")
 }
 
-func (c *config) UseTransitiveJarsInClasspath() bool {
-	return c.productVariables.GetBuildFlagBool("RELEASE_USE_TRANSITIVE_JARS_IN_CLASSPATH")
+func (c *config) UseR8OnlyRuntimeVisibleAnnotations() bool {
+	return c.productVariables.GetBuildFlagBool("RELEASE_R8_ONLY_RUNTIME_VISIBLE_ANNOTATIONS")
+}
+
+func (c *config) UseR8StoreStoreFenceConstructorInlining() bool {
+	return c.productVariables.GetBuildFlagBool("RELEASE_R8_STORE_STORE_FENCE_CONSTRUCTOR_INLINING")
+}
+
+func (c *config) UseR8GlobalCheckNotNullFlags() bool {
+	return c.productVariables.GetBuildFlagBool("RELEASE_R8_GLOBAL_CHECK_NOT_NULL_FLAGS")
 }
 
 func (c *config) UseDexV41() bool {
@@ -2169,13 +2329,13 @@
 		"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES":              "com.android.adservices",
 		"RELEASE_APEX_CONTRIBUTIONS_APPSEARCH":               "com.android.appsearch",
 		"RELEASE_APEX_CONTRIBUTIONS_ART":                     "com.android.art",
-		"RELEASE_APEX_CONTRIBUTIONS_BLUETOOTH":               "com.android.btservices",
+		"RELEASE_APEX_CONTRIBUTIONS_BLUETOOTH":               "com.android.bt",
 		"RELEASE_APEX_CONTRIBUTIONS_CAPTIVEPORTALLOGIN":      "",
 		"RELEASE_APEX_CONTRIBUTIONS_CELLBROADCAST":           "com.android.cellbroadcast",
 		"RELEASE_APEX_CONTRIBUTIONS_CONFIGINFRASTRUCTURE":    "com.android.configinfrastructure",
 		"RELEASE_APEX_CONTRIBUTIONS_CONNECTIVITY":            "com.android.tethering",
 		"RELEASE_APEX_CONTRIBUTIONS_CONSCRYPT":               "com.android.conscrypt",
-		"RELEASE_APEX_CONTRIBUTIONS_CRASHRECOVERY":           "",
+		"RELEASE_APEX_CONTRIBUTIONS_CRASHRECOVERY":           "com.android.crashrecovery",
 		"RELEASE_APEX_CONTRIBUTIONS_DEVICELOCK":              "com.android.devicelock",
 		"RELEASE_APEX_CONTRIBUTIONS_DOCUMENTSUIGOOGLE":       "",
 		"RELEASE_APEX_CONTRIBUTIONS_EXTSERVICES":             "com.android.extservices",
@@ -2186,9 +2346,10 @@
 		"RELEASE_APEX_CONTRIBUTIONS_MODULE_METADATA":         "",
 		"RELEASE_APEX_CONTRIBUTIONS_NETWORKSTACKGOOGLE":      "",
 		"RELEASE_APEX_CONTRIBUTIONS_NEURALNETWORKS":          "com.android.neuralnetworks",
+		"RELEASE_APEX_CONTRIBUTIONS_NFC":                     "com.android.nfcservices",
 		"RELEASE_APEX_CONTRIBUTIONS_ONDEVICEPERSONALIZATION": "com.android.ondevicepersonalization",
 		"RELEASE_APEX_CONTRIBUTIONS_PERMISSION":              "com.android.permission",
-		"RELEASE_APEX_CONTRIBUTIONS_PRIMARY_LIBS":            "",
+		"RELEASE_APEX_CONTRIBUTIONS_PROFILING":               "com.android.profiling",
 		"RELEASE_APEX_CONTRIBUTIONS_REMOTEKEYPROVISIONING":   "com.android.rkpd",
 		"RELEASE_APEX_CONTRIBUTIONS_RESOLV":                  "com.android.resolv",
 		"RELEASE_APEX_CONTRIBUTIONS_SCHEDULING":              "com.android.scheduling",
@@ -2197,6 +2358,7 @@
 		"RELEASE_APEX_CONTRIBUTIONS_STATSD":                  "com.android.os.statsd",
 		"RELEASE_APEX_CONTRIBUTIONS_TELEMETRY_TVP":           "",
 		"RELEASE_APEX_CONTRIBUTIONS_TZDATA":                  "com.android.tzdata",
+		"RELEASE_APEX_CONTRIBUTIONS_UPROBESTATS":             "com.android.uprobestats",
 		"RELEASE_APEX_CONTRIBUTIONS_UWB":                     "com.android.uwb",
 		"RELEASE_APEX_CONTRIBUTIONS_WIFI":                    "com.android.wifi",
 	}
@@ -2239,10 +2401,18 @@
 }
 
 func (c *config) UseDebugArt() bool {
+	// If the ArtTargetIncludeDebugBuild product variable is set then return its value.
 	if c.productVariables.ArtTargetIncludeDebugBuild != nil {
 		return Bool(c.productVariables.ArtTargetIncludeDebugBuild)
 	}
 
+	// If the RELEASE_APEX_CONTRIBUTIONS_ART build flag is set to use a prebuilt ART apex
+	// then don't use the debug apex.
+	if val, ok := c.GetBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART"); ok && val != "" {
+		return false
+	}
+
+	// Default to the debug apex for eng builds.
 	return Bool(c.productVariables.Eng)
 }
 
@@ -2266,10 +2436,6 @@
 	return PathsForSource(ctx, c.productVariables.VendorPropFiles)
 }
 
-func (c *config) ExtraAllowedDepsTxt() string {
-	return String(c.productVariables.ExtraAllowedDepsTxt)
-}
-
 func (c *config) EnableUffdGc() string {
 	return String(c.productVariables.EnableUffdGc)
 }
diff --git a/android/config_test.go b/android/config_test.go
index adb5ffa..81b7c3e 100644
--- a/android/config_test.go
+++ b/android/config_test.go
@@ -77,7 +77,7 @@
 func TestProductConfigAnnotations(t *testing.T) {
 	err := validateConfigAnnotations(&ProductVariables{})
 	if err != nil {
-		t.Errorf(err.Error())
+		t.Error(err.Error())
 	}
 }
 
@@ -213,13 +213,18 @@
 	})
 }
 
-func (p partialCompileFlags) updateEnabled(value bool) partialCompileFlags {
-	p.enabled = value
+func (p partialCompileFlags) updateUseD8(value bool) partialCompileFlags {
+	p.Use_d8 = value
 	return p
 }
 
-func (p partialCompileFlags) updateUseD8(value bool) partialCompileFlags {
-	p.use_d8 = value
+func (p partialCompileFlags) updateDisableApiLint(value bool) partialCompileFlags {
+	p.Disable_api_lint = value
+	return p
+}
+
+func (p partialCompileFlags) updateDisableStubValidation(value bool) partialCompileFlags {
+	p.Disable_stub_validation = value
 	return p
 }
 
@@ -239,12 +244,31 @@
 	}{
 		{"", true, defaultPartialCompileFlags},
 		{"false", true, partialCompileFlags{}},
-		{"true", true, defaultPartialCompileFlags.updateEnabled(true)},
+		{"true", true, enabledPartialCompileFlags},
 		{"true", false, partialCompileFlags{}},
-		{"true,use_d8", true, defaultPartialCompileFlags.updateEnabled(true).updateUseD8(true)},
-		{"true,-use_d8", true, defaultPartialCompileFlags.updateEnabled(true).updateUseD8(false)},
+		{"all", true, partialCompileFlags{}.updateUseD8(true).updateDisableApiLint(true).updateDisableStubValidation(true)},
+
+		// This verifies both use_d8 and the processing order.
+		{"true,use_d8", true, enabledPartialCompileFlags.updateUseD8(true)},
+		{"true,-use_d8", true, enabledPartialCompileFlags.updateUseD8(false)},
 		{"use_d8,false", true, partialCompileFlags{}},
 		{"false,+use_d8", true, partialCompileFlags{}.updateUseD8(true)},
+
+		// disable_api_lint can be specified with any of 3 options.
+		{"false,-api_lint", true, partialCompileFlags{}.updateDisableApiLint(true)},
+		{"false,-enable_api_lint", true, partialCompileFlags{}.updateDisableApiLint(true)},
+		{"false,+disable_api_lint", true, partialCompileFlags{}.updateDisableApiLint(true)},
+		{"false,+api_lint", true, partialCompileFlags{}.updateDisableApiLint(false)},
+		{"false,+enable_api_lint", true, partialCompileFlags{}.updateDisableApiLint(false)},
+		{"false,-disable_api_lint", true, partialCompileFlags{}.updateDisableApiLint(false)},
+
+		// disable_stub_validation can be specified with any of 3 options.
+		{"false,-stub_validation", true, partialCompileFlags{}.updateDisableStubValidation(true)},
+		{"false,-enable_stub_validation", true, partialCompileFlags{}.updateDisableStubValidation(true)},
+		{"false,+disable_stub_validation", true, partialCompileFlags{}.updateDisableStubValidation(true)},
+		{"false,+stub_validation", true, partialCompileFlags{}.updateDisableStubValidation(false)},
+		{"false,+enable_stub_validation", true, partialCompileFlags{}.updateDisableStubValidation(false)},
+		{"false,-disable_stub_validation", true, partialCompileFlags{}.updateDisableStubValidation(false)},
 	}
 
 	for _, test := range tests {
@@ -257,3 +281,72 @@
 		})
 	}
 }
+
+type configTestProperties struct {
+	Use_generic_config *bool
+}
+
+type configTestModule struct {
+	ModuleBase
+	properties configTestProperties
+}
+
+func (d *configTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	deviceName := ctx.Config().DeviceName()
+	if ctx.ModuleName() == "foo" {
+		if ctx.Module().UseGenericConfig() {
+			ctx.PropertyErrorf("use_generic_config", "must not be set for this test")
+		}
+	} else if ctx.ModuleName() == "bar" {
+		if !ctx.Module().UseGenericConfig() {
+			ctx.ModuleErrorf("\"use_generic_config: true\" must be set for this test")
+		}
+	}
+
+	if ctx.Module().UseGenericConfig() {
+		if deviceName != "generic" {
+			ctx.ModuleErrorf("Device name for this module must be \"generic\" but %q\n", deviceName)
+		}
+	} else {
+		if deviceName == "generic" {
+			ctx.ModuleErrorf("Device name for this module must not be \"generic\"\n")
+		}
+	}
+}
+
+func configTestModuleFactory() Module {
+	module := &configTestModule{}
+	module.AddProperties(&module.properties)
+	InitAndroidModule(module)
+	return module
+}
+
+var prepareForConfigTest = GroupFixturePreparers(
+	FixtureRegisterWithContext(func(ctx RegistrationContext) {
+		ctx.RegisterModuleType("test", configTestModuleFactory)
+	}),
+)
+
+func TestGenericConfig(t *testing.T) {
+	bp := `
+		test {
+			name: "foo",
+		}
+
+		test {
+			name: "bar",
+			use_generic_config: true,
+		}
+	`
+
+	result := GroupFixturePreparers(
+		prepareForConfigTest,
+		FixtureWithRootAndroidBp(bp),
+	).RunTest(t)
+
+	foo := result.Module("foo", "").(*configTestModule)
+	bar := result.Module("bar", "").(*configTestModule)
+
+	AssertBoolEquals(t, "Do not use generic config", false, foo.UseGenericConfig())
+	AssertBoolEquals(t, "Use generic config", true, bar.UseGenericConfig())
+}
diff --git a/android/configurable_properties.go b/android/configurable_properties.go
index 2c794a1..bde33e9 100644
--- a/android/configurable_properties.go
+++ b/android/configurable_properties.go
@@ -7,7 +7,8 @@
 // to indicate a "default" case.
 func CreateSelectOsToBool(cases map[string]*bool) proptools.Configurable[bool] {
 	var resultCases []proptools.ConfigurableCase[bool]
-	for pattern, value := range cases {
+	for _, pattern := range SortedKeys(cases) {
+		value := cases[pattern]
 		if pattern == "" {
 			resultCases = append(resultCases, proptools.NewConfigurableCase(
 				[]proptools.ConfigurablePattern{proptools.NewDefaultConfigurablePattern()},
diff --git a/android/configured_jars.go b/android/configured_jars.go
index c7b808f..657826e 100644
--- a/android/configured_jars.go
+++ b/android/configured_jars.go
@@ -264,7 +264,7 @@
 			subdir = filepath.Join("apex", apex, "javalib")
 		}
 
-		if ostype.Class == Host {
+		if ostype.Class == Host || cfg.IsEnvTrue("ART_USE_SIMULATOR") {
 			paths[i] = filepath.Join(cfg.Getenv("OUT_DIR"), "host", cfg.PrebuiltOS(), subdir, name)
 		} else {
 			paths[i] = filepath.Join("/", subdir, name)
diff --git a/android/container.go b/android/container.go
index 27b17ed..547fe81 100644
--- a/android/container.go
+++ b/android/container.go
@@ -31,28 +31,25 @@
 // and the corresponding functions are called from [exceptionHandleFunctionsTable] map.
 // ----------------------------------------------------------------------------
 
-type exceptionHandleFunc func(ModuleContext, Module, Module) bool
+type exceptionHandleFunc func(ModuleContext, Module, ModuleProxy) bool
 
 type StubsAvailableModule interface {
 	IsStubsModule() bool
 }
 
 // Returns true if the dependency module is a stubs module
-var depIsStubsModule exceptionHandleFunc = func(_ ModuleContext, _, dep Module) bool {
-	if stubsModule, ok := dep.(StubsAvailableModule); ok {
-		return stubsModule.IsStubsModule()
-	}
-	return false
+var depIsStubsModule exceptionHandleFunc = func(mctx ModuleContext, _ Module, dep ModuleProxy) bool {
+	return OtherModulePointerProviderOrDefault(mctx, dep, CommonModuleInfoProvider).IsStubsModule
 }
 
 // Returns true if the dependency module belongs to any of the apexes.
-var depIsApexModule exceptionHandleFunc = func(mctx ModuleContext, _, dep Module) bool {
+var depIsApexModule exceptionHandleFunc = func(mctx ModuleContext, _ Module, dep ModuleProxy) bool {
 	depContainersInfo, _ := getContainerModuleInfo(mctx, dep)
 	return InList(ApexContainer, depContainersInfo.belongingContainers)
 }
 
 // Returns true if the module and the dependent module belongs to common apexes.
-var belongsToCommonApexes exceptionHandleFunc = func(mctx ModuleContext, m, dep Module) bool {
+var belongsToCommonApexes exceptionHandleFunc = func(mctx ModuleContext, m Module, dep ModuleProxy) bool {
 	mContainersInfo, _ := getContainerModuleInfo(mctx, m)
 	depContainersInfo, _ := getContainerModuleInfo(mctx, dep)
 
@@ -62,7 +59,7 @@
 // Returns true when all apexes that the module belongs to are non updatable.
 // For an apex module to be allowed to depend on a non-apex partition module,
 // all apexes that the module belong to must be non updatable.
-var belongsToNonUpdatableApex exceptionHandleFunc = func(mctx ModuleContext, m, _ Module) bool {
+var belongsToNonUpdatableApex exceptionHandleFunc = func(mctx ModuleContext, m Module, _ ModuleProxy) bool {
 	mContainersInfo, _ := getContainerModuleInfo(mctx, m)
 
 	return !mContainersInfo.UpdatableApex()
@@ -70,7 +67,7 @@
 
 // Returns true if the dependency is added via dependency tags that are not used to tag dynamic
 // dependency tags.
-var depIsNotDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m, dep Module) bool {
+var depIsNotDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m Module, dep ModuleProxy) bool {
 	mInstallable, _ := m.(InstallableModule)
 	depTag := ctx.OtherModuleDependencyTag(dep)
 	return !InList(depTag, mInstallable.DynamicDependencyTags())
@@ -79,7 +76,7 @@
 // Returns true if the dependency is added via dependency tags that are not used to tag static
 // or dynamic dependency tags. These dependencies do not affect the module in compile time or in
 // runtime, thus are not significant enough to raise an error.
-var depIsNotStaticOrDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m, dep Module) bool {
+var depIsNotStaticOrDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m Module, dep ModuleProxy) bool {
 	mInstallable, _ := m.(InstallableModule)
 	depTag := ctx.OtherModuleDependencyTag(dep)
 	return !InList(depTag, append(mInstallable.StaticDependencyTags(), mInstallable.DynamicDependencyTags()...))
@@ -106,7 +103,7 @@
 }
 
 // Returns true when the dependency is globally allowlisted for inter-container dependency
-var depIsGloballyAllowlisted exceptionHandleFunc = func(_ ModuleContext, _, dep Module) bool {
+var depIsGloballyAllowlisted exceptionHandleFunc = func(_ ModuleContext, _ Module, dep ModuleProxy) bool {
 	return InList(dep.Name(), globallyAllowlistedDependencies)
 }
 
@@ -170,8 +167,8 @@
 }
 
 var apexContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
-	_, ok := ModuleProvider(mctx, AllApexInfoProvider)
-	return ok
+	// TODO(b/394955484): a module can't determine the apexes it belongs to any more
+	return false
 }
 
 var ctsContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
@@ -197,10 +194,11 @@
 
 func determineUnstableModule(mctx ModuleContext) bool {
 	module := mctx.Module()
+
 	unstableModule := module.Name() == "framework-minus-apex"
 	if installable, ok := module.(InstallableModule); ok {
 		for _, staticDepTag := range installable.StaticDependencyTags() {
-			mctx.VisitDirectDepsWithTag(staticDepTag, func(dep Module) {
+			mctx.VisitDirectDepsProxyWithTag(staticDepTag, func(dep ModuleProxy) {
 				if unstableInfo, ok := OtherModuleProvider(mctx, dep, unstableInfoProvider); ok {
 					unstableModule = unstableModule || unstableInfo.ContainsPlatformPrivateApis
 				}
@@ -382,7 +380,7 @@
 
 func (c *ContainersInfo) ApexNames() (ret []string) {
 	for _, apex := range c.belongingApexes {
-		ret = append(ret, apex.InApexVariants...)
+		ret = append(ret, apex.BaseApexName)
 	}
 	slices.Sort(ret)
 	return ret
@@ -400,7 +398,7 @@
 
 var ContainersInfoProvider = blueprint.NewProvider[ContainersInfo]()
 
-func satisfyAllowedExceptions(ctx ModuleContext, allowedExceptionLabels []exceptionHandleFuncLabel, m, dep Module) bool {
+func satisfyAllowedExceptions(ctx ModuleContext, allowedExceptionLabels []exceptionHandleFuncLabel, m Module, dep ModuleProxy) bool {
 	for _, label := range allowedExceptionLabels {
 		if exceptionHandleFunctionsTable[label](ctx, m, dep) {
 			return true
@@ -409,7 +407,7 @@
 	return false
 }
 
-func (c *ContainersInfo) GetViolations(mctx ModuleContext, m, dep Module, depInfo ContainersInfo) []string {
+func (c *ContainersInfo) GetViolations(mctx ModuleContext, m Module, dep ModuleProxy, depInfo ContainersInfo) []string {
 	var violations []string
 
 	// Any containers that the module belongs to but the dependency does not belong to must be examined.
@@ -443,19 +441,15 @@
 		}
 	}
 
-	var belongingApexes []ApexInfo
-	if apexInfo, ok := ModuleProvider(ctx, AllApexInfoProvider); ok {
-		belongingApexes = apexInfo.ApexInfos
-	}
-
 	return ContainersInfo{
 		belongingContainers: containers,
-		belongingApexes:     belongingApexes,
+		// TODO(b/394955484): a module can't determine the apexes it belongs to any more
+		belongingApexes: nil,
 	}
 }
 
 func getContainerModuleInfo(ctx ModuleContext, module Module) (ContainersInfo, bool) {
-	if ctx.Module() == module {
+	if EqualModules(ctx.Module(), module) {
 		return ctx.getContainersInfo(), true
 	}
 
@@ -479,8 +473,8 @@
 func checkContainerViolations(ctx ModuleContext) {
 	if _, ok := ctx.Module().(InstallableModule); ok {
 		containersInfo, _ := getContainerModuleInfo(ctx, ctx.Module())
-		ctx.VisitDirectDeps(func(dep Module) {
-			if !dep.Enabled(ctx) {
+		ctx.VisitDirectDepsProxy(func(dep ModuleProxy) {
+			if !OtherModuleProviderOrDefault(ctx, dep, CommonModuleInfoProvider).Enabled {
 				return
 			}
 
diff --git a/android/container_violations.go b/android/container_violations.go
index efbc8da..4c6386d 100644
--- a/android/container_violations.go
+++ b/android/container_violations.go
@@ -32,22 +32,22 @@
 	},
 
 	"Bluetooth": {
-		"app-compat-annotations",         // apex [com.android.btservices] -> system
-		"framework-bluetooth-pre-jarjar", // apex [com.android.btservices] -> system
+		"app-compat-annotations",         // apex [com.android.bt] -> system
+		"framework-bluetooth-pre-jarjar", // apex [com.android.bt] -> system
 	},
 
 	"bluetooth-nano-protos": {
-		"libprotobuf-java-nano", // apex [com.android.btservices] -> apex [com.android.wifi, test_com.android.wifi]
+		"libprotobuf-java-nano", // apex [com.android.bt] -> apex [com.android.wifi, test_com.android.wifi]
 	},
 
 	"bluetooth.change-ids": {
-		"app-compat-annotations", // apex [com.android.btservices] -> system
+		"app-compat-annotations", // apex [com.android.bt] -> system
 	},
 
 	"CarServiceUpdatable": {
 		"modules-utils-os",                    // apex [com.android.car.framework] -> apex [com.android.permission, test_com.android.permission]
 		"modules-utils-preconditions",         // apex [com.android.car.framework] -> apex [com.android.adservices, com.android.appsearch, com.android.cellbroadcast, com.android.extservices, com.android.ondevicepersonalization, com.android.tethering, com.android.uwb, com.android.wifi, test_com.android.cellbroadcast, test_com.android.wifi]
-		"modules-utils-shell-command-handler", // apex [com.android.car.framework] -> apex [com.android.adservices, com.android.art, com.android.art.debug, com.android.art.testing, com.android.btservices, com.android.configinfrastructure, com.android.mediaprovider, com.android.nfcservices, com.android.permission, com.android.scheduling, com.android.tethering, com.android.uwb, com.android.wifi, test_com.android.mediaprovider, test_com.android.permission, test_com.android.wifi, test_imgdiag_com.android.art, test_jitzygote_com.android.art]
+		"modules-utils-shell-command-handler", // apex [com.android.car.framework] -> apex [com.android.adservices, com.android.art, com.android.art.debug, com.android.art.testing, com.android.bt, com.android.configinfrastructure, com.android.mediaprovider, com.android.nfcservices, com.android.permission, com.android.scheduling, com.android.tethering, com.android.uwb, com.android.wifi, test_com.android.mediaprovider, test_com.android.permission, test_com.android.wifi, test_imgdiag_com.android.art, test_jitzygote_com.android.art]
 	},
 
 	"cellbroadcastreceiver_aconfig_flags_lib": {
@@ -414,10 +414,6 @@
 		"framework", // cts -> unstable
 	},
 
-	"CtsMediaBetterTogetherTestCases": {
-		"framework", // cts -> unstable
-	},
-
 	"CtsMediaCodecTestCases": {
 		"framework", // cts -> unstable
 	},
@@ -478,6 +474,11 @@
 		"framework", // cts -> unstable
 	},
 
+	// TODO(b/387499846): Remove once migrated to sdk_version.
+	"CtsMediaRouterTestCases": {
+		"framework", // cts -> unstable
+	},
+
 	"CtsMediaRouterHostSideTestBluetoothPermissionsApp": {
 		"framework", // cts -> unstable
 	},
@@ -490,6 +491,11 @@
 		"framework", // cts -> unstable
 	},
 
+	// TODO(b/387500109): Remove once migrated to sdk_version.
+	"CtsMediaSessionTestCases": {
+		"framework", // cts -> unstable
+	},
+
 	"CtsMediaV2TestCases": {
 		"framework", // cts -> unstable
 	},
@@ -824,7 +830,7 @@
 	},
 
 	"devicelockcontroller-lib": {
-		"modules-utils-expresslog", // apex [com.android.devicelock] -> apex [com.android.btservices, com.android.car.framework]
+		"modules-utils-expresslog", // apex [com.android.devicelock] -> apex [com.android.bt, com.android.car.framework]
 	},
 
 	"FederatedCompute": {
@@ -836,7 +842,7 @@
 	},
 
 	"framework-bluetooth.impl": {
-		"app-compat-annotations", // apex [com.android.btservices] -> system
+		"app-compat-annotations", // apex [com.android.bt] -> system
 	},
 
 	"framework-configinfrastructure.impl": {
@@ -848,6 +854,13 @@
 		"framework-connectivity-pre-jarjar", // apex [com.android.tethering] -> system
 	},
 
+	// TODO(b/382743602): Remove "app-compat-annotations" and depend on the stub version jar
+	// TODO(b/382301972): Remove the violations and use jarjar_rename or jarjar_prefix
+	"framework-connectivity-b.impl": {
+		"app-compat-annotations",            // apex [com.android.tethering] -> system
+		"framework-connectivity-pre-jarjar", // apex [com.android.tethering] -> system
+	},
+
 	"framework-connectivity.impl": {
 		"app-compat-annotations", // apex [com.android.tethering] -> system
 	},
@@ -900,10 +913,6 @@
 		"libnativeloader_vendor_shared_lib", // system -> vendor
 	},
 
-	"MctsMediaBetterTogetherTestCases": {
-		"framework", // cts -> unstable
-	},
-
 	"MctsMediaCodecTestCases": {
 		"framework", // cts -> unstable
 	},
@@ -940,6 +949,16 @@
 		"framework", // cts -> unstable
 	},
 
+	// TODO(b/387499846): Remove once migrated to sdk_version.
+	"MctsMediaRouterTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	// TODO(b/387500109): Remove once migrated to sdk_version.
+	"MctsMediaSessionTestCases": {
+		"framework", // cts -> unstable
+	},
+
 	"MctsMediaTranscodingTestCases": {
 		"framework", // cts -> unstable
 	},
@@ -974,7 +993,11 @@
 	},
 
 	"NfcNciApex": {
+		// TODO(b/383782511): Remove the violations once the infra is fixed.
+		"android.nfc.flags-aconfig-java",        // apex [com.android.nfcservices] -> system
 		"android.permission.flags-aconfig-java", // apex [com.android.nfcservices] -> apex [com.android.permission, test_com.android.permission]
+		// TODO(b/383782511): Remove the violations once the infra is fixed.
+		"framework-nfc.impl", // apex [com.android.nfcservices] -> system
 	},
 
 	"okhttp-norepackage": {
@@ -994,7 +1017,7 @@
 	},
 
 	"PlatformProperties": {
-		"sysprop-library-stub-platform", // apex [com.android.btservices, com.android.nfcservices, com.android.tethering, com.android.virt, com.android.wifi, test_com.android.wifi] -> system
+		"sysprop-library-stub-platform", // apex [com.android.bt, com.android.nfcservices, com.android.tethering, com.android.virt, com.android.wifi, test_com.android.wifi] -> system
 	},
 
 	"safety-center-config": {
@@ -1030,8 +1053,8 @@
 	},
 
 	"service-bluetooth-pre-jarjar": {
-		"framework-bluetooth-pre-jarjar", // apex [com.android.btservices] -> system
-		"service-bluetooth.change-ids",   // apex [com.android.btservices] -> system
+		"framework-bluetooth-pre-jarjar", // apex [com.android.bt] -> system
+		"service-bluetooth.change-ids",   // apex [com.android.bt] -> system
 	},
 
 	"service-connectivity": {
@@ -1051,6 +1074,13 @@
 		"framework-connectivity-t-pre-jarjar", // apex [com.android.tethering] -> system
 	},
 
+	// TODO(b/382301972): Remove the violations and use jarjar_rename or jarjar_prefix
+	"service-connectivity-b-pre-jarjar": {
+		"framework-connectivity-pre-jarjar",   // apex [com.android.tethering] -> system
+		"framework-connectivity-b-pre-jarjar", // apex [com.android.tethering] -> system
+		"framework-connectivity-t-pre-jarjar", // apex [com.android.tethering] -> system
+	},
+
 	"service-entitlement": {
 		"auto_value_annotations", // apex [com.android.wifi, test_com.android.wifi] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus]
 	},
diff --git a/android/csuite_config_test.go b/android/csuite_config_test.go
index b8a176e..7e25aac 100644
--- a/android/csuite_config_test.go
+++ b/android/csuite_config_test.go
@@ -32,7 +32,6 @@
 	if len(variants) > 1 {
 		t.Errorf("expected 1, got %d", len(variants))
 	}
-	outputFilename := result.ModuleForTests(
-		"plain", variants[0]).Module().(*CSuiteConfig).OutputFilePath.Base()
+	outputFilename := result.ModuleForTests(t, "plain", variants[0]).Module().(*CSuiteConfig).OutputFilePath.Base()
 	AssertStringEquals(t, "output file name", "plain", outputFilename)
 }
diff --git a/android/deapexer.go b/android/deapexer.go
index 4049d2b..6d00dcd 100644
--- a/android/deapexer.go
+++ b/android/deapexer.go
@@ -75,8 +75,6 @@
 
 	// map from the name of an exported file from a prebuilt_apex to the path to that file. The
 	// exported file name is the apex relative path, e.g. javalib/core-libart.jar.
-	//
-	// See Prebuilt.ApexInfoMutator for more information.
 	exports map[string]WritablePath
 
 	// name of the java libraries exported from the apex
diff --git a/android/defaults.go b/android/defaults.go
index 510ebe0..0fc1768 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -178,6 +178,7 @@
 	module.AddProperties(
 		&hostAndDeviceProperties{},
 		commonProperties,
+		&baseProperties{},
 		&ApexProperties{},
 		&distProperties{})
 
diff --git a/android/defaults_test.go b/android/defaults_test.go
index 0ad0fb8..24f1461 100644
--- a/android/defaults_test.go
+++ b/android/defaults_test.go
@@ -123,8 +123,8 @@
 		FixtureWithRootAndroidBp(bp),
 	).RunTest(t)
 
-	missingDefaults := result.ModuleForTests("missing_defaults", "").Output("out")
-	missingTransitiveDefaults := result.ModuleForTests("missing_transitive_defaults", "").Output("out")
+	missingDefaults := result.ModuleForTests(t, "missing_defaults", "").Output("out")
+	missingTransitiveDefaults := result.ModuleForTests(t, "missing_transitive_defaults", "").Output("out")
 
 	AssertSame(t, "missing_defaults rule", ErrorRule, missingDefaults.Rule)
 
diff --git a/android/defs.go b/android/defs.go
index 9f3fb1e..57fcc9b 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -51,6 +51,14 @@
 		},
 		"cpFlags", "extraCmds")
 
+	// A copy rule wrapped with bash.
+	CpWithBash = pctx.AndroidStaticRule("CpWithBash",
+		blueprint.RuleParams{
+			Command:     "/bin/bash -c \"rm -f $out && cp $cpFlags $cpPreserveSymlinks $in $out$extraCmds\"",
+			Description: "cp $out",
+		},
+		"cpFlags", "extraCmds")
+
 	// A copy rule that doesn't preserve symlinks.
 	CpNoPreserveSymlink = pctx.AndroidStaticRule("CpNoPreserveSymlink",
 		blueprint.RuleParams{
@@ -74,6 +82,14 @@
 		},
 		"cpFlags", "extraCmds")
 
+	// A copy executable rule wrapped with bash
+	CpExecutableWithBash = pctx.AndroidStaticRule("CpExecutableWithBash",
+		blueprint.RuleParams{
+			Command:     "/bin/bash -c \"(rm -f $out && cp $cpFlags $cpPreserveSymlinks $in $out ) && (chmod +x $out$extraCmds )\"",
+			Description: "cp $out",
+		},
+		"cpFlags", "extraCmds")
+
 	// A timestamp touch rule.
 	Touch = pctx.AndroidStaticRule("Touch",
 		blueprint.RuleParams{
@@ -89,6 +105,14 @@
 		},
 		"fromPath")
 
+	// A symlink rule wrapped with bash
+	SymlinkWithBash = pctx.AndroidStaticRule("SymlinkWithBash",
+		blueprint.RuleParams{
+			Command:     "/bin/bash -c \"rm -f $out && ln -sfn $fromPath $out\"",
+			Description: "symlink $out",
+		},
+		"fromPath")
+
 	ErrorRule = pctx.AndroidStaticRule("Error",
 		blueprint.RuleParams{
 			Command:     `echo "$error" && false`,
@@ -119,3 +143,13 @@
 		return ctx.Config().RBEWrapper()
 	})
 }
+
+// CopyFileRule creates a ninja rule to copy path to outPath.
+func CopyFileRule(ctx ModuleContext, path Path, outPath OutputPath) {
+	ctx.Build(pctx, BuildParams{
+		Rule:        Cp,
+		Input:       path,
+		Output:      outPath,
+		Description: "copy " + outPath.Base(),
+	})
+}
diff --git a/android/deptag_test.go b/android/deptag_test.go
index eb4fa89..9037871 100644
--- a/android/deptag_test.go
+++ b/android/deptag_test.go
@@ -90,10 +90,10 @@
 
 	config := result.Config
 
-	hostFoo := result.ModuleForTests("foo", config.BuildOSCommonTarget.String()).Description("install")
-	hostInstallDep := result.ModuleForTests("install_dep", config.BuildOSCommonTarget.String()).Description("install")
-	hostTransitive := result.ModuleForTests("transitive", config.BuildOSCommonTarget.String()).Description("install")
-	hostDep := result.ModuleForTests("dep", config.BuildOSCommonTarget.String()).Description("install")
+	hostFoo := result.ModuleForTests(t, "foo", config.BuildOSCommonTarget.String()).Description("install")
+	hostInstallDep := result.ModuleForTests(t, "install_dep", config.BuildOSCommonTarget.String()).Description("install")
+	hostTransitive := result.ModuleForTests(t, "transitive", config.BuildOSCommonTarget.String()).Description("install")
+	hostDep := result.ModuleForTests(t, "dep", config.BuildOSCommonTarget.String()).Description("install")
 
 	if g, w := hostFoo.Implicits.Strings(), hostInstallDep.Output.String(); !InList(w, g) {
 		t.Errorf("expected host dependency %q, got %q", w, g)
@@ -111,10 +111,10 @@
 		t.Errorf("expected no host dependency %q, got %q", w, g)
 	}
 
-	deviceFoo := result.ModuleForTests("foo", "android_common").Description("install")
-	deviceInstallDep := result.ModuleForTests("install_dep", "android_common").Description("install")
-	deviceTransitive := result.ModuleForTests("transitive", "android_common").Description("install")
-	deviceDep := result.ModuleForTests("dep", "android_common").Description("install")
+	deviceFoo := result.ModuleForTests(t, "foo", "android_common").Description("install")
+	deviceInstallDep := result.ModuleForTests(t, "install_dep", "android_common").Description("install")
+	deviceTransitive := result.ModuleForTests(t, "transitive", "android_common").Description("install")
+	deviceDep := result.ModuleForTests(t, "dep", "android_common").Description("install")
 
 	if g, w := deviceFoo.OrderOnly.Strings(), deviceInstallDep.Output.String(); !InList(w, g) {
 		t.Errorf("expected device dependency %q, got %q", w, g)
diff --git a/android/early_module_context.go b/android/early_module_context.go
index 5e971ef..300edf1 100644
--- a/android/early_module_context.go
+++ b/android/early_module_context.go
@@ -146,6 +146,13 @@
 }
 
 func (e *earlyModuleContext) Config() Config {
+	// Only the system image may use the generic config.
+	// If a module builds multiple image variations, provide the generic config only for the core
+	// variant which is installed in the system partition. Other image variant may still read the
+	// original configurations.
+	if e.Module().base().UseGenericConfig() && e.Module().base().commonProperties.ImageVariation == "" {
+		return e.EarlyModuleContext.Config().(Config).genericConfig()
+	}
 	return e.EarlyModuleContext.Config().(Config)
 }
 
@@ -182,7 +189,7 @@
 }
 
 func (e *earlyModuleContext) OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{}) {
-	e.EarlyModuleContext.OtherModulePropertyErrorf(module, property, fmt, args...)
+	e.EarlyModuleContext.OtherModulePropertyErrorf(getWrappedModule(module), property, fmt, args...)
 }
 
 func (e *earlyModuleContext) HasMutatorFinished(mutatorName string) bool {
diff --git a/android/filegroup.go b/android/filegroup.go
index 67e5add1f..4fad52a 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -41,11 +41,11 @@
 
 	Exclude_srcs proptools.Configurable[[]string] `android:"path"`
 
-	// Sources the will be included in the filegroup, but any module dependencies will be added
+	// Sources that will be included in the filegroup, but any module dependencies will be added
 	// using the device os and the device's first architecture's variant.
 	Device_first_srcs proptools.Configurable[[]string] `android:"path_device_first"`
 
-	// Sources the will be included in the filegroup, but any module dependencies will be added
+	// Sources that will be included in the filegroup, but any module dependencies will be added
 	// using the device os and the common architecture's variant.
 	Device_common_srcs proptools.Configurable[[]string] `android:"path_device_common"`
 
@@ -104,7 +104,6 @@
 	if fg.properties.Path != nil {
 		srcs = PathsWithModuleSrcSubDir(ctx, srcs, String(fg.properties.Path))
 	}
-	SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()})
 
 	var aconfigDeclarations []string
 	var intermediateCacheOutputPaths Paths
@@ -132,10 +131,11 @@
 	return append(Paths{}, fg.srcs...)
 }
 
-func (fg *fileGroup) MakeVars(ctx MakeVarsModuleContext) {
+func (fg *fileGroup) MakeVars(_ MakeVarsModuleContext) []ModuleMakeVarsValue {
 	if makeVar := String(fg.properties.Export_to_make_var); makeVar != "" {
-		ctx.StrictRaw(makeVar, strings.Join(fg.srcs.Strings(), " "))
+		return []ModuleMakeVarsValue{{makeVar, strings.Join(fg.srcs.Strings(), " ")}}
 	}
+	return nil
 }
 
 // Defaults
diff --git a/android/fixture.go b/android/fixture.go
index 5ad47e8..ea52b95 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -1048,7 +1048,7 @@
 
 // Module returns the module with the specific name and of the specified variant.
 func (r *TestResult) Module(name string, variant string) Module {
-	return r.ModuleForTests(name, variant).Module()
+	return r.ModuleForTests(r.fixture.t, name, variant).Module()
 }
 
 // CollateErrs adds additional errors to the result and returns true if there is more than one
diff --git a/android/gen_notice.go b/android/gen_notice.go
index 9adde9e..45f90f4 100644
--- a/android/gen_notice.go
+++ b/android/gen_notice.go
@@ -19,6 +19,7 @@
 	"path/filepath"
 	"strings"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -35,34 +36,31 @@
 type genNoticeBuildRules struct{}
 
 func (s *genNoticeBuildRules) GenerateBuildActions(ctx SingletonContext) {
-	ctx.VisitAllModules(func(m Module) {
-		gm, ok := m.(*genNoticeModule)
+	ctx.VisitAllModuleProxies(func(m ModuleProxy) {
+		gm, ok := OtherModuleProvider(ctx, m, GenNoticeInfoProvider)
 		if !ok {
 			return
 		}
-		if len(gm.missing) > 0 {
-			missingReferencesRule(ctx, gm)
+		if len(gm.Missing) > 0 {
+			missingReferencesRule(ctx, m, &gm)
 			return
 		}
 		out := BuildNoticeTextOutputFromLicenseMetadata
-		if proptools.Bool(gm.properties.Xml) {
+		if gm.Xml {
 			out = BuildNoticeXmlOutputFromLicenseMetadata
-		} else if proptools.Bool(gm.properties.Html) {
+		} else if gm.Html {
 			out = BuildNoticeHtmlOutputFromLicenseMetadata
 		}
 		defaultName := ""
-		if len(gm.properties.For) > 0 {
-			defaultName = gm.properties.For[0]
+		if len(gm.For) > 0 {
+			defaultName = gm.For[0]
 		}
 
-		modules := make([]Module, 0)
-		for _, name := range gm.properties.For {
-			mods := ctx.ModuleVariantsFromName(gm, name)
+		modules := make([]ModuleProxy, 0)
+		for _, name := range gm.For {
+			mods := ctx.ModuleVariantsFromName(m, name)
 			for _, mod := range mods {
-				if mod == nil {
-					continue
-				}
-				if !mod.Enabled(ctx) { // don't depend on variants without build rules
+				if !OtherModulePointerProviderOrDefault(ctx, mod, CommonModuleInfoProvider).Enabled { // don't depend on variants without build rules
 					continue
 				}
 				modules = append(modules, mod)
@@ -71,8 +69,8 @@
 		if ctx.Failed() {
 			return
 		}
-		out(ctx, gm.output, ctx.ModuleName(gm),
-			proptools.StringDefault(gm.properties.ArtifactName, defaultName),
+		out(ctx, gm.Output, ctx.ModuleName(m),
+			proptools.StringDefault(gm.ArtifactName, defaultName),
 			[]string{
 				filepath.Join(ctx.Config().OutDir(), "target", "product", ctx.Config().DeviceName()) + "/",
 				ctx.Config().OutDir() + "/",
@@ -115,6 +113,22 @@
 	missing []string
 }
 
+type GenNoticeInfo struct {
+	// For specifies the modules for which to generate a notice file.
+	For []string
+	// ArtifactName specifies the internal name to use for the notice file.
+	// It appears in the "used by:" list for targets whose entire name is stripped by --strip_prefix.
+	ArtifactName *string
+	// Html indicates an html-format file is needed. The default is text. Can be Html or Xml but not both.
+	Html bool
+	// Xml indicates an xml-format file is needed. The default is text. Can be Html or Xml but not both.
+	Xml     bool
+	Output  OutputPath
+	Missing []string
+}
+
+var GenNoticeInfoProvider = blueprint.NewProvider[GenNoticeInfo]()
+
 func (m *genNoticeModule) DepsMutator(ctx BottomUpMutatorContext) {
 	if ctx.ContainsProperty("licenses") {
 		ctx.PropertyErrorf("licenses", "not supported on \"gen_notice\" modules")
@@ -176,6 +190,15 @@
 	}
 	out := m.getStem() + m.getSuffix()
 	m.output = PathForModuleOut(ctx, out).OutputPath
+
+	SetProvider(ctx, GenNoticeInfoProvider, GenNoticeInfo{
+		For:          m.properties.For,
+		ArtifactName: m.properties.ArtifactName,
+		Xml:          proptools.Bool(m.properties.Xml),
+		Html:         proptools.Bool(m.properties.Html),
+		Output:       m.output,
+		Missing:      m.missing,
+	})
 	ctx.SetOutputFiles(Paths{m.output}, "")
 }
 
@@ -205,17 +228,17 @@
 }
 
 // missingReferencesRule emits an ErrorRule for missing module references.
-func missingReferencesRule(ctx BuilderContext, m *genNoticeModule) {
-	if len(m.missing) < 1 {
+func missingReferencesRule(ctx BuilderContext, m ModuleProxy, genInfo *GenNoticeInfo) {
+	if len(genInfo.Missing) < 1 {
 		panic(fmt.Errorf("missing references rule requested with no missing references"))
 	}
 
 	ctx.Build(pctx, BuildParams{
 		Rule:        ErrorRule,
-		Output:      m.output,
-		Description: "notice for " + proptools.StringDefault(m.properties.ArtifactName, "container"),
+		Output:      genInfo.Output,
+		Description: "notice for " + proptools.StringDefault(genInfo.ArtifactName, "container"),
 		Args: map[string]string{
-			"error": m.Name() + " references missing module(s): " + strings.Join(m.missing, ", "),
+			"error": m.Name() + " references missing module(s): " + strings.Join(genInfo.Missing, ", "),
 		},
 	})
 }
diff --git a/android/hooks.go b/android/hooks.go
index f8022d0..5d509a4 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -100,12 +100,12 @@
 	l.appendPrependHelper(props, proptools.PrependMatchingProperties)
 }
 
-func (l *loadHookContext) createModule(factory blueprint.ModuleFactory, name string, props ...interface{}) blueprint.Module {
-	return l.bp.CreateModule(factory, name, props...)
+func (l *loadHookContext) createModule(factory blueprint.ModuleFactory, name string, props ...interface{}) Module {
+	return bpModuleToModule(l.bp.CreateModule(factory, name, props...))
 }
 
-func (l *loadHookContext) createModuleInDirectory(factory blueprint.ModuleFactory, name, moduleDir string, props ...interface{}) blueprint.Module {
-	return l.bp.CreateModuleInDirectory(factory, name, moduleDir, props...)
+func (l *loadHookContext) createModuleInDirectory(factory blueprint.ModuleFactory, name, moduleDir string, props ...interface{}) Module {
+	return bpModuleToModule(l.bp.CreateModuleInDirectory(factory, name, moduleDir, props...))
 }
 
 type specifyDirectory struct {
@@ -130,8 +130,8 @@
 type createModuleContext interface {
 	Module() Module
 	HasMutatorFinished(mutatorName string) bool
-	createModule(blueprint.ModuleFactory, string, ...interface{}) blueprint.Module
-	createModuleInDirectory(blueprint.ModuleFactory, string, string, ...interface{}) blueprint.Module
+	createModule(blueprint.ModuleFactory, string, ...interface{}) Module
+	createModuleInDirectory(blueprint.ModuleFactory, string, string, ...interface{}) Module
 }
 
 func createModule(ctx createModuleContext, factory ModuleFactory, ext string, specifyDirectory specifyDirectory, props ...interface{}) Module {
diff --git a/android/init.go b/android/init.go
index d3a13d0..af50323 100644
--- a/android/init.go
+++ b/android/init.go
@@ -17,6 +17,7 @@
 import "encoding/gob"
 
 func init() {
+	gob.Register(applicableLicensesPropertyImpl{})
 	gob.Register(extraFilesZip{})
 	gob.Register(InstallPath{})
 	gob.Register(ModuleGenPath{})
diff --git a/android/license.go b/android/license.go
index 5bffc25..7b4aeeb 100644
--- a/android/license.go
+++ b/android/license.go
@@ -18,6 +18,15 @@
 	"github.com/google/blueprint"
 )
 
+type LicenseInfo struct {
+	PackageName                *string
+	EffectiveLicenseText       NamedPaths
+	EffectiveLicenseKinds      []string
+	EffectiveLicenseConditions []string
+}
+
+var LicenseInfoProvider = blueprint.NewProvider[LicenseInfo]()
+
 type licenseKindDependencyTag struct {
 	blueprint.BaseDependencyTag
 }
@@ -69,16 +78,24 @@
 
 func (m *licenseModule) GenerateAndroidBuildActions(ctx ModuleContext) {
 	// license modules have no licenses, but license_kinds must refer to license_kind modules
-	mergeStringProps(&m.base().commonProperties.Effective_licenses, ctx.ModuleName())
 	namePathProps(&m.base().commonProperties.Effective_license_text, m.properties.Package_name, PathsForModuleSrc(ctx, m.properties.License_text)...)
-	for _, module := range ctx.GetDirectDepsWithTag(licenseKindTag) {
-		if lk, ok := module.(*licenseKindModule); ok {
-			mergeStringProps(&m.base().commonProperties.Effective_license_conditions, lk.properties.Conditions...)
-			mergeStringProps(&m.base().commonProperties.Effective_license_kinds, ctx.OtherModuleName(module))
+	var conditions []string
+	var kinds []string
+	for _, module := range ctx.GetDirectDepsProxyWithTag(licenseKindTag) {
+		if lk, ok := OtherModuleProvider(ctx, module, LicenseKindInfoProvider); ok {
+			conditions = append(conditions, lk.Conditions...)
+			kinds = append(kinds, ctx.OtherModuleName(module))
 		} else {
 			ctx.ModuleErrorf("license_kinds property %q is not a license_kind module", ctx.OtherModuleName(module))
 		}
 	}
+
+	SetProvider(ctx, LicenseInfoProvider, LicenseInfo{
+		PackageName:                m.properties.Package_name,
+		EffectiveLicenseText:       m.base().commonProperties.Effective_license_text,
+		EffectiveLicenseKinds:      SortedUniqueStrings(kinds),
+		EffectiveLicenseConditions: SortedUniqueStrings(conditions),
+	})
 }
 
 func LicenseFactory() Module {
diff --git a/android/license_kind.go b/android/license_kind.go
index 838dedd..1ca6954 100644
--- a/android/license_kind.go
+++ b/android/license_kind.go
@@ -14,6 +14,14 @@
 
 package android
 
+import "github.com/google/blueprint"
+
+type LicenseKindInfo struct {
+	Conditions []string
+}
+
+var LicenseKindInfoProvider = blueprint.NewProvider[LicenseKindInfo]()
+
 func init() {
 	RegisterLicenseKindBuildComponents(InitRegistrationContext)
 }
@@ -43,8 +51,10 @@
 	// Nothing to do.
 }
 
-func (m *licenseKindModule) GenerateAndroidBuildActions(ModuleContext) {
-	// Nothing to do.
+func (m *licenseKindModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	SetProvider(ctx, LicenseKindInfoProvider, LicenseKindInfo{
+		Conditions: m.properties.Conditions,
+	})
 }
 
 func LicenseKindFactory() Module {
diff --git a/android/license_metadata.go b/android/license_metadata.go
index 3df36e6..0b880dd 100644
--- a/android/license_metadata.go
+++ b/android/license_metadata.go
@@ -15,11 +15,11 @@
 package android
 
 import (
-	"github.com/google/blueprint/depset"
 	"sort"
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/depset"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -64,8 +64,8 @@
 	var allDepOutputFiles Paths
 	var allDepMetadataDepSets []depset.DepSet[Path]
 
-	ctx.VisitDirectDeps(func(dep Module) {
-		if !dep.Enabled(ctx) {
+	ctx.VisitDirectDepsProxy(func(dep ModuleProxy) {
+		if !OtherModulePointerProviderOrDefault(ctx, dep, CommonModuleInfoProvider).Enabled {
 			return
 		}
 
@@ -81,7 +81,7 @@
 
 		if info, ok := OtherModuleProvider(ctx, dep, LicenseMetadataProvider); ok {
 			allDepMetadataFiles = append(allDepMetadataFiles, info.LicenseMetadataPath)
-			if isContainer || isInstallDepNeeded(dep, ctx.OtherModuleDependencyTag(dep)) {
+			if isContainer || isInstallDepNeeded(ctx, dep) {
 				allDepMetadataDepSets = append(allDepMetadataDepSets, info.LicenseMetadataDepSet)
 			}
 
diff --git a/android/licenses.go b/android/licenses.go
index 53d0555..3877921 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -22,6 +22,7 @@
 	"sync"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/gobtools"
 )
 
 // Adds cross-cutting licenses dependency to propagate license metadata through the build system.
@@ -67,6 +68,31 @@
 	licensesProperty *[]string
 }
 
+type applicableLicensesPropertyImplGob struct {
+	Name             string
+	LicensesProperty []string
+}
+
+func (a *applicableLicensesPropertyImpl) ToGob() *applicableLicensesPropertyImplGob {
+	return &applicableLicensesPropertyImplGob{
+		Name:             a.name,
+		LicensesProperty: *a.licensesProperty,
+	}
+}
+
+func (a *applicableLicensesPropertyImpl) FromGob(data *applicableLicensesPropertyImplGob) {
+	a.name = data.Name
+	a.licensesProperty = &data.LicensesProperty
+}
+
+func (a applicableLicensesPropertyImpl) GobEncode() ([]byte, error) {
+	return gobtools.CustomGobEncode[applicableLicensesPropertyImplGob](&a)
+}
+
+func (a *applicableLicensesPropertyImpl) GobDecode(data []byte) error {
+	return gobtools.CustomGobDecode[applicableLicensesPropertyImplGob](data, a)
+}
+
 func newApplicableLicensesProperty(name string, licensesProperty *[]string) applicableLicensesProperty {
 	return applicableLicensesPropertyImpl{
 		name:             name,
@@ -227,16 +253,18 @@
 	}
 
 	var licenses []string
-	for _, module := range ctx.GetDirectDepsWithTag(licensesTag) {
-		if l, ok := module.(*licenseModule); ok {
+	var texts NamedPaths
+	var conditions []string
+	var kinds []string
+	for _, module := range ctx.GetDirectDepsProxyWithTag(licensesTag) {
+		if l, ok := OtherModuleProvider(ctx, module, LicenseInfoProvider); ok {
 			licenses = append(licenses, ctx.OtherModuleName(module))
-			if m.base().commonProperties.Effective_package_name == nil && l.properties.Package_name != nil {
-				m.base().commonProperties.Effective_package_name = l.properties.Package_name
+			if m.base().commonProperties.Effective_package_name == nil && l.PackageName != nil {
+				m.base().commonProperties.Effective_package_name = l.PackageName
 			}
-			mergeStringProps(&m.base().commonProperties.Effective_licenses, module.base().commonProperties.Effective_licenses...)
-			mergeNamedPathProps(&m.base().commonProperties.Effective_license_text, module.base().commonProperties.Effective_license_text...)
-			mergeStringProps(&m.base().commonProperties.Effective_license_kinds, module.base().commonProperties.Effective_license_kinds...)
-			mergeStringProps(&m.base().commonProperties.Effective_license_conditions, module.base().commonProperties.Effective_license_conditions...)
+			texts = append(texts, l.EffectiveLicenseText...)
+			kinds = append(kinds, l.EffectiveLicenseKinds...)
+			conditions = append(conditions, l.EffectiveLicenseConditions...)
 		} else {
 			propertyName := "licenses"
 			primaryProperty := m.base().primaryLicensesProperty
@@ -247,17 +275,15 @@
 		}
 	}
 
+	m.base().commonProperties.Effective_license_text = SortedUniqueNamedPaths(texts)
+	m.base().commonProperties.Effective_license_kinds = SortedUniqueStrings(kinds)
+	m.base().commonProperties.Effective_license_conditions = SortedUniqueStrings(conditions)
+
 	// Make the license information available for other modules.
-	licenseInfo := LicenseInfo{
+	licenseInfo := LicensesInfo{
 		Licenses: licenses,
 	}
-	SetProvider(ctx, LicenseInfoProvider, licenseInfo)
-}
-
-// Update a property string array with a distinct union of its values and a list of new values.
-func mergeStringProps(prop *[]string, values ...string) {
-	*prop = append(*prop, values...)
-	*prop = SortedUniqueStrings(*prop)
+	SetProvider(ctx, LicensesInfoProvider, licenseInfo)
 }
 
 // Update a property NamedPath array with a distinct union of its values and a list of new values.
@@ -274,12 +300,6 @@
 	*prop = SortedUniqueNamedPaths(*prop)
 }
 
-// Update a property NamedPath array with a distinct union of its values and a list of new values.
-func mergeNamedPathProps(prop *NamedPaths, values ...NamedPath) {
-	*prop = append(*prop, values...)
-	*prop = SortedUniqueNamedPaths(*prop)
-}
-
 // Get the licenses property falling back to the package default.
 func getLicenses(ctx BaseModuleContext, module Module) []string {
 	if exemptFromRequiredApplicableLicensesProperty(module) {
@@ -336,14 +356,14 @@
 	return true
 }
 
-// LicenseInfo contains information about licenses for a specific module.
-type LicenseInfo struct {
+// LicensesInfo contains information about licenses for a specific module.
+type LicensesInfo struct {
 	// The list of license modules this depends upon, either explicitly or through default package
 	// configuration.
 	Licenses []string
 }
 
-var LicenseInfoProvider = blueprint.NewProvider[LicenseInfo]()
+var LicensesInfoProvider = blueprint.NewProvider[LicensesInfo]()
 
 func init() {
 	RegisterMakeVarsProvider(pctx, licensesMakeVarsProvider)
@@ -357,9 +377,7 @@
 	ctx.Strict("HTMLNOTICE", ctx.Config().HostToolPath(ctx, "htmlnotice").String())
 	ctx.Strict("XMLNOTICE", ctx.Config().HostToolPath(ctx, "xmlnotice").String())
 	ctx.Strict("TEXTNOTICE", ctx.Config().HostToolPath(ctx, "textnotice").String())
-	ctx.Strict("COMPLIANCENOTICE_BOM", ctx.Config().HostToolPath(ctx, "compliancenotice_bom").String())
 	ctx.Strict("COMPLIANCENOTICE_SHIPPEDLIBS", ctx.Config().HostToolPath(ctx, "compliancenotice_shippedlibs").String())
 	ctx.Strict("COMPLIANCE_LISTSHARE", ctx.Config().HostToolPath(ctx, "compliance_listshare").String())
 	ctx.Strict("COMPLIANCE_CHECKSHARE", ctx.Config().HostToolPath(ctx, "compliance_checkshare").String())
-	ctx.Strict("COMPLIANCE_SBOM", ctx.Config().HostToolPath(ctx, "compliance_sbom").String())
 }
diff --git a/android/licenses_test.go b/android/licenses_test.go
index 8a81e12..0c371e8 100644
--- a/android/licenses_test.go
+++ b/android/licenses_test.go
@@ -7,15 +7,13 @@
 )
 
 var licensesTests = []struct {
-	name                       string
-	fs                         MockFS
-	expectedErrors             []string
-	effectiveLicenses          map[string][]string
-	effectiveInheritedLicenses map[string][]string
-	effectivePackage           map[string]string
-	effectiveNotices           map[string][]string
-	effectiveKinds             map[string][]string
-	effectiveConditions        map[string][]string
+	name                string
+	fs                  MockFS
+	expectedErrors      []string
+	effectivePackage    map[string]string
+	effectiveNotices    map[string][]string
+	effectiveKinds      map[string][]string
+	effectiveConditions map[string][]string
 }{
 	{
 		name: "invalid module type without licenses property",
@@ -69,11 +67,6 @@
 					licenses: ["top_Apache2"],
 				}`),
 		},
-		effectiveLicenses: map[string][]string{
-			"libexample1": []string{"top_Apache2"},
-			"libnested":   []string{"top_Apache2"},
-			"libother":    []string{"top_Apache2"},
-		},
 		effectiveKinds: map[string][]string{
 			"libexample1": []string{"notice"},
 			"libnested":   []string{"notice"},
@@ -146,18 +139,6 @@
 					deps: ["libexample"],
 				}`),
 		},
-		effectiveLicenses: map[string][]string{
-			"libexample":     []string{"nested_other", "top_other"},
-			"libsamepackage": []string{},
-			"libnested":      []string{},
-			"libother":       []string{},
-		},
-		effectiveInheritedLicenses: map[string][]string{
-			"libexample":     []string{"nested_other", "top_other"},
-			"libsamepackage": []string{"nested_other", "top_other"},
-			"libnested":      []string{"nested_other", "top_other"},
-			"libother":       []string{"nested_other", "top_other"},
-		},
 		effectiveKinds: map[string][]string{
 			"libexample":     []string{"nested_notice", "top_notice"},
 			"libsamepackage": []string{},
@@ -217,20 +198,6 @@
 					deps: ["libexample"],
 				}`),
 		},
-		effectiveLicenses: map[string][]string{
-			"libexample":     []string{"other", "top_nested"},
-			"libsamepackage": []string{},
-			"libnested":      []string{},
-			"libother":       []string{},
-			"liboutsider":    []string{},
-		},
-		effectiveInheritedLicenses: map[string][]string{
-			"libexample":     []string{"other", "top_nested"},
-			"libsamepackage": []string{"other", "top_nested"},
-			"libnested":      []string{"other", "top_nested"},
-			"libother":       []string{"other", "top_nested"},
-			"liboutsider":    []string{"other", "top_nested"},
-		},
 		effectiveKinds: map[string][]string{
 			"libexample":     []string{},
 			"libsamepackage": []string{},
@@ -284,14 +251,6 @@
 					defaults: ["top_defaults"],
 				}`),
 		},
-		effectiveLicenses: map[string][]string{
-			"libexample":  []string{"by_exception_only"},
-			"libdefaults": []string{"notice"},
-		},
-		effectiveInheritedLicenses: map[string][]string{
-			"libexample":  []string{"by_exception_only"},
-			"libdefaults": []string{"notice"},
-		},
 	},
 
 	// Package default_applicable_licenses tests
@@ -326,14 +285,6 @@
 					deps: ["libexample"],
 				}`),
 		},
-		effectiveLicenses: map[string][]string{
-			"libexample":  []string{"top_notice"},
-			"liboutsider": []string{},
-		},
-		effectiveInheritedLicenses: map[string][]string{
-			"libexample":  []string{"top_notice"},
-			"liboutsider": []string{"top_notice"},
-		},
 	},
 	{
 		name: "package default_applicable_licenses not inherited to subpackages",
@@ -369,18 +320,6 @@
 					deps: ["libexample", "libother", "libnested"],
 				}`),
 		},
-		effectiveLicenses: map[string][]string{
-			"libexample":  []string{"top_notice"},
-			"libnested":   []string{"outsider"},
-			"libother":    []string{},
-			"liboutsider": []string{},
-		},
-		effectiveInheritedLicenses: map[string][]string{
-			"libexample":  []string{"top_notice"},
-			"libnested":   []string{"outsider"},
-			"libother":    []string{},
-			"liboutsider": []string{"top_notice", "outsider"},
-		},
 	},
 	{
 		name: "verify that prebuilt dependencies are included",
@@ -409,12 +348,6 @@
 					deps: [":module"],
 				}`),
 		},
-		effectiveLicenses: map[string][]string{
-			"other": []string{},
-		},
-		effectiveInheritedLicenses: map[string][]string{
-			"other": []string{"prebuilt", "top_sources"},
-		},
 	},
 	{
 		name: "verify that prebuilt dependencies are ignored for licenses reasons (preferred)",
@@ -444,13 +377,6 @@
 					deps: [":module"],
 				}`),
 		},
-		effectiveLicenses: map[string][]string{
-			"other": []string{},
-		},
-		effectiveInheritedLicenses: map[string][]string{
-			"module": []string{"prebuilt", "top_sources"},
-			"other":  []string{"prebuilt", "top_sources"},
-		},
 	},
 }
 
@@ -470,10 +396,6 @@
 				ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
 				RunTest(t)
 
-			if test.effectiveLicenses != nil {
-				checkEffectiveLicenses(t, result, test.effectiveLicenses)
-			}
-
 			if test.effectivePackage != nil {
 				checkEffectivePackage(t, result, test.effectivePackage)
 			}
@@ -489,114 +411,10 @@
 			if test.effectiveConditions != nil {
 				checkEffectiveConditions(t, result, test.effectiveConditions)
 			}
-
-			if test.effectiveInheritedLicenses != nil {
-				checkEffectiveInheritedLicenses(t, result, test.effectiveInheritedLicenses)
-			}
 		})
 	}
 }
 
-func checkEffectiveLicenses(t *testing.T, result *TestResult, effectiveLicenses map[string][]string) {
-	actualLicenses := make(map[string][]string)
-	result.Context.Context.VisitAllModules(func(m blueprint.Module) {
-		if _, ok := m.(*licenseModule); ok {
-			return
-		}
-		if _, ok := m.(*licenseKindModule); ok {
-			return
-		}
-		if _, ok := m.(*packageModule); ok {
-			return
-		}
-		module, ok := m.(Module)
-		if !ok {
-			t.Errorf("%q not a module", m.Name())
-			return
-		}
-		base := module.base()
-		if base == nil {
-			return
-		}
-		actualLicenses[m.Name()] = base.commonProperties.Effective_licenses
-	})
-
-	for moduleName, expectedLicenses := range effectiveLicenses {
-		licenses, ok := actualLicenses[moduleName]
-		if !ok {
-			licenses = []string{}
-		}
-		if !compareUnorderedStringArrays(expectedLicenses, licenses) {
-			t.Errorf("effective licenses mismatch for module %q: expected %q, found %q", moduleName, expectedLicenses, licenses)
-		}
-	}
-}
-
-func checkEffectiveInheritedLicenses(t *testing.T, result *TestResult, effectiveInheritedLicenses map[string][]string) {
-	actualLicenses := make(map[string][]string)
-	result.Context.Context.VisitAllModules(func(m blueprint.Module) {
-		if _, ok := m.(*licenseModule); ok {
-			return
-		}
-		if _, ok := m.(*licenseKindModule); ok {
-			return
-		}
-		if _, ok := m.(*packageModule); ok {
-			return
-		}
-		module, ok := m.(Module)
-		if !ok {
-			t.Errorf("%q not a module", m.Name())
-			return
-		}
-		base := module.base()
-		if base == nil {
-			return
-		}
-		inherited := make(map[string]bool)
-		for _, l := range base.commonProperties.Effective_licenses {
-			inherited[l] = true
-		}
-		result.Context.Context.VisitDepsDepthFirst(m, func(c blueprint.Module) {
-			if _, ok := c.(*licenseModule); ok {
-				return
-			}
-			if _, ok := c.(*licenseKindModule); ok {
-				return
-			}
-			if _, ok := c.(*packageModule); ok {
-				return
-			}
-			cmodule, ok := c.(Module)
-			if !ok {
-				t.Errorf("%q not a module", c.Name())
-				return
-			}
-			cbase := cmodule.base()
-			if cbase == nil {
-				return
-			}
-			for _, l := range cbase.commonProperties.Effective_licenses {
-				inherited[l] = true
-			}
-		})
-		actualLicenses[m.Name()] = []string{}
-		for l := range inherited {
-			actualLicenses[m.Name()] = append(actualLicenses[m.Name()], l)
-		}
-	})
-
-	for moduleName, expectedInheritedLicenses := range effectiveInheritedLicenses {
-		licenses, ok := actualLicenses[moduleName]
-		if !ok {
-			licenses = []string{}
-		}
-		if !compareUnorderedStringArrays(expectedInheritedLicenses, licenses) {
-			t.Errorf("effective inherited licenses mismatch for module %q: expected %q, found %q", moduleName, expectedInheritedLicenses, licenses)
-		}
-	}
-}
-
 func checkEffectivePackage(t *testing.T, result *TestResult, effectivePackage map[string]string) {
 	actualPackage := make(map[string]string)
 	result.Context.Context.VisitAllModules(func(m blueprint.Module) {
diff --git a/android/logtags.go b/android/logtags.go
index 7929057..074f402 100644
--- a/android/logtags.go
+++ b/android/logtags.go
@@ -14,7 +14,11 @@
 
 package android
 
-import "github.com/google/blueprint"
+import (
+	"strings"
+
+	"github.com/google/blueprint"
+)
 
 func init() {
 	RegisterParallelSingletonType("logtags", LogtagsSingleton)
@@ -38,19 +42,28 @@
 
 func (l *logtagsSingleton) GenerateBuildActions(ctx SingletonContext) {
 	var allLogtags Paths
-	ctx.VisitAllModules(func(module Module) {
-		if !module.ExportedToMake() {
+	ctx.VisitAllModuleProxies(func(module ModuleProxy) {
+		if !OtherModulePointerProviderOrDefault(ctx, module, CommonModuleInfoProvider).ExportedToMake {
 			return
 		}
 		if logtagsInfo, ok := OtherModuleProvider(ctx, module, LogtagsProviderKey); ok {
 			allLogtags = append(allLogtags, logtagsInfo.Logtags...)
 		}
 	})
+	allLogtags = SortedUniquePaths(allLogtags)
+	filteredLogTags := make([]Path, 0, len(allLogtags))
+	for _, p := range allLogtags {
+		// Logic copied from make:
+		// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=987;drc=0585bb1bcf4c89065adaf709f48acc8b869fd3ce
+		if !strings.HasPrefix(p.String(), "vendor/") && !strings.HasPrefix(p.String(), "device/") && !strings.HasPrefix(p.String(), "out/") {
+			filteredLogTags = append(filteredLogTags, p)
+		}
+	}
 
 	builder := NewRuleBuilder(pctx, ctx)
 	builder.Command().
 		BuiltTool("merge-event-log-tags").
 		FlagWithOutput("-o ", MergedLogtagsPath(ctx)).
-		Inputs(SortedUniquePaths(allLogtags))
+		Inputs(filteredLogTags)
 	builder.Build("all-event-log-tags.txt", "merge logtags")
 }
diff --git a/android/makevars.go b/android/makevars.go
index 8305d8e..7017e7d 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -65,24 +65,6 @@
 	// dependencies to be added to it.  Phony can be called on the same name multiple
 	// times to add additional dependencies.
 	Phony(names string, deps ...Path)
-
-	// DistForGoal creates a rule to copy one or more Paths to the artifacts
-	// directory on the build server when the specified goal is built.
-	DistForGoal(goal string, paths ...Path)
-
-	// DistForGoalWithFilename creates a rule to copy a Path to the artifacts
-	// directory on the build server with the given filename when the specified
-	// goal is built.
-	DistForGoalWithFilename(goal string, path Path, filename string)
-
-	// DistForGoals creates a rule to copy one or more Paths to the artifacts
-	// directory on the build server when any of the specified goals are built.
-	DistForGoals(goals []string, paths ...Path)
-
-	// DistForGoalsWithFilename creates a rule to copy a Path to the artifacts
-	// directory on the build server with the given filename when any of the
-	// specified goals are built.
-	DistForGoalsWithFilename(goals []string, path Path, filename string)
 }
 
 // MakeVarsContext contains the set of functions available for MakeVarsProvider
@@ -102,6 +84,7 @@
 	Errorf(format string, args ...interface{})
 
 	VisitAllModules(visit func(Module))
+	VisitAllModuleProxies(visit func(proxy ModuleProxy))
 	VisitAllModulesIf(pred func(Module) bool, visit func(Module))
 
 	// Verify the make variable matches the Soong version, fail the build
@@ -126,7 +109,7 @@
 // MakeVarsModuleContext contains the set of functions available for modules
 // implementing the ModuleMakeVarsProvider interface.
 type MakeVarsModuleContext interface {
-	BaseMakeVarsContext
+	Config() Config
 }
 
 var _ PathContext = MakeVarsContext(nil)
@@ -168,14 +151,21 @@
 	return func(ctx MakeVarsContext) { singleton.MakeVars(ctx) }
 }
 
+type ModuleMakeVarsValue struct {
+	// Make variable name.
+	Name string
+	// Make variable value.
+	Value string
+}
+
 // ModuleMakeVarsProvider is a Module with an extra method to provide extra values to be exported to Make.
 type ModuleMakeVarsProvider interface {
-	Module
-
 	// MakeVars uses a MakeVarsModuleContext to provide extra values to be exported to Make.
-	MakeVars(ctx MakeVarsModuleContext)
+	MakeVars(ctx MakeVarsModuleContext) []ModuleMakeVarsValue
 }
 
+var ModuleMakeVarsInfoProvider = blueprint.NewProvider[[]ModuleMakeVarsValue]()
+
 // /////////////////////////////////////////////////////////////////////////////
 
 func makeVarsSingletonFunc() Singleton {
@@ -185,6 +175,7 @@
 type makeVarsSingleton struct {
 	varsForTesting     []makeVarsVariable
 	installsForTesting []byte
+	lateForTesting     []byte
 }
 
 type makeVarsProvider struct {
@@ -197,11 +188,9 @@
 
 type makeVarsContext struct {
 	SingletonContext
-	config  Config
 	pctx    PackageContext
 	vars    []makeVarsVariable
 	phonies []phony
-	dists   []dist
 }
 
 var _ MakeVarsContext = &makeVarsContext{}
@@ -220,7 +209,7 @@
 
 type dist struct {
 	goals []string
-	paths []string
+	paths distCopies
 }
 
 func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) {
@@ -262,29 +251,41 @@
 
 		vars = append(vars, mctx.vars...)
 		phonies = append(phonies, mctx.phonies...)
-		dists = append(dists, mctx.dists...)
 	}
 
-	ctx.VisitAllModules(func(m Module) {
-		if provider, ok := m.(ModuleMakeVarsProvider); ok && m.Enabled(ctx) {
+	singletonDists := getSingletonDists(ctx.Config())
+	singletonDists.lock.Lock()
+	dists = append(dists, singletonDists.dists...)
+	singletonDists.lock.Unlock()
+
+	ctx.VisitAllModuleProxies(func(m ModuleProxy) {
+		commonInfo := OtherModulePointerProviderOrDefault(ctx, m, CommonModuleInfoProvider)
+		if provider, ok := OtherModuleProvider(ctx, m, ModuleMakeVarsInfoProvider); ok &&
+			commonInfo.Enabled {
 			mctx := &makeVarsContext{
 				SingletonContext: ctx,
 			}
-
-			provider.MakeVars(mctx)
+			for _, val := range provider {
+				if val.Name != "" {
+					mctx.StrictRaw(val.Name, val.Value)
+				}
+			}
 
 			vars = append(vars, mctx.vars...)
 			phonies = append(phonies, mctx.phonies...)
-			dists = append(dists, mctx.dists...)
 		}
 
-		if m.ExportedToMake() {
+		if commonInfo.ExportedToMake {
 			info := OtherModuleProviderOrDefault(ctx, m, InstallFilesProvider)
 			katiInstalls = append(katiInstalls, info.KatiInstalls...)
 			katiInitRcInstalls = append(katiInitRcInstalls, info.KatiInitRcInstalls...)
 			katiVintfManifestInstalls = append(katiVintfManifestInstalls, info.KatiVintfInstalls...)
 			katiSymlinks = append(katiSymlinks, info.KatiSymlinks...)
 		}
+
+		if distInfo, ok := OtherModuleProvider(ctx, m, DistProvider); ok {
+			dists = append(dists, distInfo.Dists...)
+		}
 	})
 
 	compareKatiInstalls := func(a, b katiInstall) int {
@@ -318,19 +319,12 @@
 	sort.Slice(phonies, func(i, j int) bool {
 		return phonies[i].name < phonies[j].name
 	})
-	lessArr := func(a, b []string) bool {
-		if len(a) == len(b) {
-			for i := range a {
-				if a[i] < b[i] {
-					return true
-				}
-			}
-			return false
-		}
-		return len(a) < len(b)
-	}
 	sort.Slice(dists, func(i, j int) bool {
-		return lessArr(dists[i].goals, dists[j].goals) || lessArr(dists[i].paths, dists[j].paths)
+		goals := slices.Compare(dists[i].goals, dists[j].goals)
+		if goals != 0 {
+			return goals < 0
+		}
+		return slices.Compare(dists[i].paths.Strings(), dists[j].paths.Strings()) < 0
 	})
 
 	outBytes := s.writeVars(vars)
@@ -354,6 +348,7 @@
 	if ctx.Config().RunningInsideUnitTest() {
 		s.varsForTesting = vars
 		s.installsForTesting = installsBytes
+		s.lateForTesting = lateOutBytes
 	}
 }
 
@@ -458,7 +453,7 @@
 	for _, dist := range dists {
 		fmt.Fprintf(buf, ".PHONY: %s\n", strings.Join(dist.goals, " "))
 		fmt.Fprintf(buf, "$(call dist-for-goals,%s,%s)\n",
-			strings.Join(dist.goals, " "), strings.Join(dist.paths, " "))
+			strings.Join(dist.goals, " "), strings.Join(dist.paths.Strings(), " "))
 	}
 
 	return buf.Bytes()
@@ -607,13 +602,6 @@
 	c.phonies = append(c.phonies, phony{name, deps})
 }
 
-func (c *makeVarsContext) addDist(goals []string, paths []string) {
-	c.dists = append(c.dists, dist{
-		goals: goals,
-		paths: paths,
-	})
-}
-
 func (c *makeVarsContext) Strict(name, ninjaStr string) {
 	c.addVariable(name, ninjaStr, true, false)
 }
@@ -637,19 +625,3 @@
 func (c *makeVarsContext) Phony(name string, deps ...Path) {
 	c.addPhony(name, Paths(deps).Strings())
 }
-
-func (c *makeVarsContext) DistForGoal(goal string, paths ...Path) {
-	c.DistForGoals([]string{goal}, paths...)
-}
-
-func (c *makeVarsContext) DistForGoalWithFilename(goal string, path Path, filename string) {
-	c.DistForGoalsWithFilename([]string{goal}, path, filename)
-}
-
-func (c *makeVarsContext) DistForGoals(goals []string, paths ...Path) {
-	c.addDist(goals, Paths(paths).Strings())
-}
-
-func (c *makeVarsContext) DistForGoalsWithFilename(goals []string, path Path, filename string) {
-	c.addDist(goals, []string{path.String() + ":" + filename})
-}
diff --git a/android/makevars_test.go b/android/makevars_test.go
new file mode 100644
index 0000000..387d457
--- /dev/null
+++ b/android/makevars_test.go
@@ -0,0 +1,96 @@
+package android
+
+import (
+	"regexp"
+	"testing"
+)
+
+func TestDistFilesInGenerateAndroidBuildActions(t *testing.T) {
+	result := GroupFixturePreparers(
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterModuleType("my_module_type", newDistFileModule)
+			ctx.RegisterParallelSingletonType("my_singleton", newDistFileSingleton)
+			ctx.RegisterParallelSingletonModuleType("my_singleton_module", newDistFileSingletonModule)
+		}),
+		FixtureModifyConfig(SetKatiEnabledForTests),
+		PrepareForTestWithMakevars,
+	).RunTestWithBp(t, `
+	my_module_type {
+		name: "foo",
+	}
+	my_singleton_module {
+		name: "bar"
+	}
+	`)
+
+	lateContents := string(result.SingletonForTests(t, "makevars").Singleton().(*makeVarsSingleton).lateForTesting)
+	matched, err := regexp.MatchString(`call dist-for-goals,my_goal,.*/my_file.txt:my_file.txt\)`, lateContents)
+	if err != nil || !matched {
+		t.Fatalf("Expected a dist of my_file.txt, but got: %s", lateContents)
+	}
+	matched, err = regexp.MatchString(`call dist-for-goals,my_singleton_goal,.*/my_singleton_file.txt:my_singleton_file.txt\)`, lateContents)
+	if err != nil || !matched {
+		t.Fatalf("Expected a dist of my_singleton_file.txt, but got: %s", lateContents)
+	}
+	matched, err = regexp.MatchString(`call dist-for-goals,my_singleton_module_module_goal,.*/my_singleton_module_module_file.txt:my_singleton_module_module_file.txt\)`, lateContents)
+	if err != nil || !matched {
+		t.Fatalf("Expected a dist of my_singleton_module_module_file.txt, but got: %s", lateContents)
+	}
+	matched, err = regexp.MatchString(`call dist-for-goals,my_singleton_module_singleton_goal,.*/my_singleton_module_singleton_file.txt:my_singleton_module_singleton_file.txt\)`, lateContents)
+	if err != nil || !matched {
+		t.Fatalf("Expected a dist of my_singleton_module_singleton_file.txt, but got: %s", lateContents)
+	}
+}
+
+type distFileModule struct {
+	ModuleBase
+}
+
+func newDistFileModule() Module {
+	m := &distFileModule{}
+	InitAndroidModule(m)
+	return m
+}
+
+func (m *distFileModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	out := PathForModuleOut(ctx, "my_file.txt")
+	WriteFileRule(ctx, out, "Hello, world!")
+	ctx.DistForGoal("my_goal", out)
+}
+
+type distFileSingleton struct {
+}
+
+func newDistFileSingleton() Singleton {
+	return &distFileSingleton{}
+}
+
+func (d *distFileSingleton) GenerateBuildActions(ctx SingletonContext) {
+	out := PathForOutput(ctx, "my_singleton_file.txt")
+	WriteFileRule(ctx, out, "Hello, world!")
+	ctx.DistForGoal("my_singleton_goal", out)
+}
+
+type distFileSingletonModule struct {
+	SingletonModuleBase
+}
+
+func newDistFileSingletonModule() SingletonModule {
+	sm := &distFileSingletonModule{}
+	InitAndroidSingletonModule(sm)
+	return sm
+}
+
+// GenerateAndroidBuildActions implements SingletonModule.
+func (d *distFileSingletonModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	out := PathForModuleOut(ctx, "my_singleton_module_module_file.txt")
+	WriteFileRule(ctx, out, "Hello, world!")
+	ctx.DistForGoal("my_singleton_module_module_goal", out)
+}
+
+// GenerateSingletonBuildActions implements SingletonModule.
+func (d *distFileSingletonModule) GenerateSingletonBuildActions(ctx SingletonContext) {
+	out := PathForOutput(ctx, "my_singleton_module_singleton_file.txt")
+	WriteFileRule(ctx, out, "Hello, world!")
+	ctx.DistForGoal("my_singleton_module_singleton_goal", out)
+}
diff --git a/android/metrics.go b/android/metrics.go
index 6834b1b..dc51703 100644
--- a/android/metrics.go
+++ b/android/metrics.go
@@ -57,8 +57,8 @@
 
 func (soongMetricsSingleton) GenerateBuildActions(ctx SingletonContext) {
 	metrics := getSoongMetrics(ctx.Config())
-	ctx.VisitAllModules(func(m Module) {
-		if ctx.PrimaryModule(m) == m {
+	ctx.VisitAllModuleProxies(func(m ModuleProxy) {
+		if ctx.PrimaryModuleProxy(m) == m {
 			metrics.modules++
 		}
 		metrics.variants++
diff --git a/android/module.go b/android/module.go
index a9f6b94..1538861 100644
--- a/android/module.go
+++ b/android/module.go
@@ -15,12 +15,14 @@
 package android
 
 import (
+	"errors"
 	"fmt"
 	"net/url"
 	"path/filepath"
 	"reflect"
 	"slices"
 	"sort"
+	"strconv"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -43,6 +45,14 @@
 	// For more information, see Module.GenerateBuildActions within Blueprint's module_ctx.go
 	GenerateAndroidBuildActions(ModuleContext)
 
+	// CleanupAfterBuildActions is called after ModuleBase.GenerateBuildActions is finished.
+	// If all interactions with this module are handled via providers instead of direct access
+	// to the module then it can free memory attached to the module.
+	// This is a temporary measure to reduce memory usage, eventually blueprint's reference
+	// to the Module should be dropped after GenerateAndroidBuildActions once all accesses
+	// can be done through providers.
+	CleanupAfterBuildActions()
+
 	// Add dependencies to the components of a module, i.e. modules that are created
 	// by the module and which are considered to be part of the creating module.
 	//
@@ -94,7 +104,6 @@
 	ReplacedByPrebuilt()
 	IsReplacedByPrebuilt() bool
 	ExportedToMake() bool
-	EffectiveLicenseKinds() []string
 	EffectiveLicenseFiles() Paths
 
 	AddProperties(props ...interface{})
@@ -128,6 +137,9 @@
 	// WARNING: This should not be used outside build/soong/fsgen
 	// Overrides returns the list of modules which should not be installed if this module is installed.
 	Overrides() []string
+
+	// If this is true, the module must not read product-specific configurations.
+	UseGenericConfig() bool
 }
 
 // Qualified id for a module
@@ -196,6 +208,12 @@
 	// no change to the artifact file name.
 	Append_artifact_with_product *bool `android:"arch_variant"`
 
+	// If true, then the artifact file will be prepended with <product name>-. For
+	// example, if the product is coral and the module is an android_app module
+	// of name foo, then the artifact would be coral-foo.apk. If false, there is
+	// no change to the artifact file name.
+	Prepend_artifact_with_product *bool `android:"arch_variant"`
+
 	// A string tag to select the OutputFiles associated with the tag.
 	//
 	// If no tag is specified then it will select the default dist paths provided
@@ -257,6 +275,8 @@
 	Name *string
 }
 
+// Properties common to all modules inheriting from ModuleBase. These properties are automatically
+// inherited by sub-modules created with ctx.CreateModule()
 type commonProperties struct {
 	// emit build rules for this module
 	//
@@ -315,8 +335,6 @@
 	// Describes the licenses applicable to this module. Must reference license modules.
 	Licenses []string
 
-	// Flattened from direct license dependencies. Equal to Licenses unless particular module adds more.
-	Effective_licenses []string `blueprint:"mutated"`
 	// Override of module name when reporting licenses
 	Effective_package_name *string `blueprint:"mutated"`
 	// Notice files
@@ -409,15 +427,6 @@
 	// VINTF manifest fragments to be installed if this module is installed
 	Vintf_fragments proptools.Configurable[[]string] `android:"path"`
 
-	// names of other modules to install if this module is installed
-	Required proptools.Configurable[[]string] `android:"arch_variant"`
-
-	// names of other modules to install on host if this module is installed
-	Host_required []string `android:"arch_variant"`
-
-	// names of other modules to install on target if this module is installed
-	Target_required []string `android:"arch_variant"`
-
 	// The OsType of artifacts that this module variant is responsible for creating.
 	//
 	// Set by osMutator
@@ -516,6 +525,28 @@
 	// List of module names that are prevented from being installed when this module gets
 	// installed.
 	Overrides []string
+
+	// Set to true if this module must be generic and does not require product-specific information.
+	// To be included in the system image, this property must be set to true.
+	Use_generic_config *bool
+}
+
+// Properties common to all modules inheriting from ModuleBase. Unlike commonProperties, these
+// properties are NOT automatically inherited by sub-modules created with ctx.CreateModule()
+type baseProperties struct {
+	// names of other modules to install if this module is installed
+	Required proptools.Configurable[[]string] `android:"arch_variant"`
+
+	// names of other modules to install on host if this module is installed
+	Host_required []string `android:"arch_variant"`
+
+	// names of other modules to install on target if this module is installed
+	Target_required []string `android:"arch_variant"`
+
+	// If this is a soong config module, this property will be set to the name of the original
+	// module type. This is used by neverallow to ensure you can't bypass a ModuleType() matcher
+	// just by creating a soong config module type.
+	Soong_config_base_module_type *string `blueprint:"mutated"`
 }
 
 type distProperties struct {
@@ -610,17 +641,6 @@
 	return t
 }
 
-func MakeDefaultDistFiles(paths ...Path) TaggedDistFiles {
-	for _, p := range paths {
-		if p == nil {
-			panic("The path to a dist file cannot be nil.")
-		}
-	}
-
-	// The default OutputFile tag is the empty "" string.
-	return TaggedDistFiles{DefaultDistTag: paths}
-}
-
 type hostAndDeviceProperties struct {
 	// If set to true, build a variant of the module for the host.  Defaults to false.
 	Host_supported *bool
@@ -716,6 +736,7 @@
 	m.AddProperties(
 		&base.nameProperties,
 		&base.commonProperties,
+		&base.baseProperties,
 		&base.distProperties)
 
 	initProductVariableModule(m)
@@ -834,6 +855,7 @@
 
 	nameProperties          nameProperties
 	commonProperties        commonProperties
+	baseProperties          baseProperties
 	distProperties          distProperties
 	variableProperties      interface{}
 	hostAndDeviceProperties hostAndDeviceProperties
@@ -990,8 +1012,9 @@
 	// 2. `boot_signer` is `required` by modules like `build_image` which is explicitly list as
 	// the top-level build goal (in the shell file that invokes Soong).
 	// 3. `boot_signer` depends on `bouncycastle-unbundled` which is in the missing git project.
-	// 4. aosp_kernel-build-tools invokes soong with `--skip-make`. Therefore, the absence of
-	// ALLOW_MISSING_DEPENDENCIES didn't cause a problem.
+	// 4. aosp_kernel-build-tools invokes soong with `--soong-only`. Therefore, the absence of
+	// ALLOW_MISSING_DEPENDENCIES didn't cause a problem, as previously only make processed required
+	// dependencies.
 	// 5. Now, since Soong understands `required` deps, it tries to build `boot_signer` and the
 	// absence of external/bouncycastle fails the build.
 	//
@@ -1001,11 +1024,19 @@
 	pv := ctx.Config().productVariables
 	fullManifest := pv.DeviceArch != nil && pv.DeviceName != nil
 	if fullManifest {
-		addRequiredDeps(ctx)
 		addVintfFragmentDeps(ctx)
 	}
 }
 
+// required property can be overridden too; handle it separately
+func (m *ModuleBase) baseOverridablePropertiesDepsMutator(ctx BottomUpMutatorContext) {
+	pv := ctx.Config().productVariables
+	fullManifest := pv.DeviceArch != nil && pv.DeviceName != nil
+	if fullManifest {
+		addRequiredDeps(ctx)
+	}
+}
+
 // addRequiredDeps adds required, target_required, and host_required as dependencies.
 func addRequiredDeps(ctx BottomUpMutatorContext) {
 	addDep := func(target Target, depName string) {
@@ -1049,7 +1080,7 @@
 	hostTargets = append(hostTargets, ctx.Config().BuildOSCommonTarget)
 
 	if ctx.Device() {
-		for _, depName := range ctx.Module().RequiredModuleNames(ctx) {
+		for _, depName := range append(ctx.Module().RequiredModuleNames(ctx), ctx.Module().VintfFragmentModuleNames(ctx)...) {
 			for _, target := range deviceTargets {
 				addDep(target, depName)
 			}
@@ -1062,7 +1093,7 @@
 	}
 
 	if ctx.Host() {
-		for _, depName := range ctx.Module().RequiredModuleNames(ctx) {
+		for _, depName := range append(ctx.Module().RequiredModuleNames(ctx), ctx.Module().VintfFragmentModuleNames(ctx)...) {
 			for _, target := range hostTargets {
 				// When a host module requires another host module, don't make a
 				// dependency if they have different OSes (i.e. hostcross).
@@ -1085,6 +1116,10 @@
 	InstallAlwaysNeededDependencyTag
 }{}
 
+func IsVintfDepTag(depTag blueprint.DependencyTag) bool {
+	return depTag == vintfDepTag
+}
+
 func addVintfFragmentDeps(ctx BottomUpMutatorContext) {
 	// Vintf manifests in the recovery partition will be ignored.
 	if !ctx.Device() || ctx.Module().InstallInRecovery() {
@@ -1103,7 +1138,7 @@
 			// of nil pointer dereference errors, but we should resolve the missing dependencies.
 			continue
 		}
-		if vintfModule, ok := vintf.(*vintfFragmentModule); ok {
+		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.",
@@ -1220,6 +1255,13 @@
 		tag := proptools.StringDefault(dist.Tag, DefaultDistTag)
 
 		distFileForTagFromProvider, err := outputFilesForModuleFromProvider(ctx, m.module, tag)
+
+		// If the module doesn't define output files for the DefaultDistTag, try the files under
+		// the "" tag.
+		if tag == DefaultDistTag && errors.Is(err, ErrUnsupportedOutputTag) {
+			distFileForTagFromProvider, err = outputFilesForModuleFromProvider(ctx, m.module, "")
+		}
+
 		if err != OutputFilesProviderNotSet {
 			if err != nil && tag != DefaultDistTag {
 				ctx.PropertyErrorf("dist.tag", "%s", err.Error())
@@ -1458,10 +1500,6 @@
 	return m.commonProperties.NamespaceExportedToMake
 }
 
-func (m *ModuleBase) EffectiveLicenseKinds() []string {
-	return m.commonProperties.Effective_license_kinds
-}
-
 func (m *ModuleBase) EffectiveLicenseFiles() Paths {
 	result := make(Paths, 0, len(m.commonProperties.Effective_license_text))
 	for _, p := range m.commonProperties.Effective_license_text {
@@ -1475,12 +1513,13 @@
 func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]depset.DepSet[InstallPath], []depset.DepSet[PackagingSpec]) {
 	var installDeps []depset.DepSet[InstallPath]
 	var packagingSpecs []depset.DepSet[PackagingSpec]
-	ctx.VisitDirectDeps(func(dep Module) {
-		if isInstallDepNeeded(dep, ctx.OtherModuleDependencyTag(dep)) {
+	ctx.VisitDirectDepsProxy(func(dep ModuleProxy) {
+		if isInstallDepNeeded(ctx, dep) {
 			// Installation is still handled by Make, so anything hidden from Make is not
 			// installable.
 			info := OtherModuleProviderOrDefault(ctx, dep, InstallFilesProvider)
-			if !dep.IsHideFromMake() && !dep.IsSkipInstall() {
+			commonInfo := OtherModulePointerProviderOrDefault(ctx, dep, CommonModuleInfoProvider)
+			if !commonInfo.HideFromMake && !commonInfo.SkipInstall {
 				installDeps = append(installDeps, info.TransitiveInstallFiles)
 			}
 			// Add packaging deps even when the dependency is not installed so that uninstallable
@@ -1494,13 +1533,13 @@
 
 // isInstallDepNeeded returns true if installing the output files of the current module
 // should also install the output files of the given dependency and dependency tag.
-func isInstallDepNeeded(dep Module, tag blueprint.DependencyTag) bool {
+func isInstallDepNeeded(ctx ModuleContext, dep ModuleProxy) bool {
 	// Don't add a dependency from the platform to a library provided by an apex.
-	if dep.base().commonProperties.UninstallableApexPlatformVariant {
+	if OtherModulePointerProviderOrDefault(ctx, dep, CommonModuleInfoProvider).UninstallableApexPlatformVariant {
 		return false
 	}
 	// Only install modules if the dependency tag is an InstallDepNeeded tag.
-	return IsInstallDepNeededTag(tag)
+	return IsInstallDepNeededTag(ctx.OtherModuleDependencyTag(dep))
 }
 
 func (m *ModuleBase) NoAddressSanitizer() bool {
@@ -1617,15 +1656,15 @@
 }
 
 func (m *ModuleBase) RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string {
-	return m.base().commonProperties.Required.GetOrDefault(m.ConfigurableEvaluator(ctx), nil)
+	return m.base().baseProperties.Required.GetOrDefault(m.ConfigurableEvaluator(ctx), nil)
 }
 
 func (m *ModuleBase) HostRequiredModuleNames() []string {
-	return m.base().commonProperties.Host_required
+	return m.base().baseProperties.Host_required
 }
 
 func (m *ModuleBase) TargetRequiredModuleNames() []string {
-	return m.base().commonProperties.Target_required
+	return m.base().baseProperties.Target_required
 }
 
 func (m *ModuleBase) VintfFragmentModuleNames(ctx ConfigurableEvaluatorContext) []string {
@@ -1650,55 +1689,41 @@
 
 }
 
+// generateModuleTarget generates phony targets so that you can do `m <module-name>`.
+// It will be run on every variant of the module, so it relies on the fact that phony targets
+// are deduped to merge all the deps from different variants together.
 func (m *ModuleBase) generateModuleTarget(ctx *moduleContext) {
-	var allInstalledFiles InstallPaths
-	var allCheckbuildTargets Paths
-	ctx.VisitAllModuleVariantProxies(func(module ModuleProxy) {
-		var checkbuildTarget Path
-		var uncheckedModule bool
-		var skipAndroidMkProcessing bool
-		if ctx.EqualModules(m.module, module) {
-			allInstalledFiles = append(allInstalledFiles, ctx.installFiles...)
-			checkbuildTarget = ctx.checkbuildTarget
-			uncheckedModule = ctx.uncheckedModule
-			skipAndroidMkProcessing = shouldSkipAndroidMkProcessing(ctx, m)
-		} else {
-			info := OtherModuleProviderOrDefault(ctx, module, InstallFilesProvider)
-			allInstalledFiles = append(allInstalledFiles, info.InstallFiles...)
-			checkbuildTarget = info.CheckbuildTarget
-			uncheckedModule = info.UncheckedModule
-			skipAndroidMkProcessing = OtherModuleProviderOrDefault(ctx, module, CommonModuleInfoKey).SkipAndroidMkProcessing
-		}
-		// A module's -checkbuild phony targets should
-		// not be created if the module is not exported to make.
-		// Those could depend on the build target and fail to compile
-		// for the current build target.
-		if (!ctx.Config().KatiEnabled() || !skipAndroidMkProcessing) && !uncheckedModule && checkbuildTarget != nil {
-			allCheckbuildTargets = append(allCheckbuildTargets, checkbuildTarget)
-		}
-	})
-
-	var deps Paths
-
 	var namespacePrefix string
 	nameSpace := ctx.Namespace().Path
 	if nameSpace != "." {
 		namespacePrefix = strings.ReplaceAll(nameSpace, "/", ".") + "-"
 	}
 
-	var info FinalModuleBuildTargetsInfo
+	var deps Paths
+	var info ModuleBuildTargetsInfo
 
-	if len(allInstalledFiles) > 0 {
+	if len(ctx.installFiles) > 0 {
 		name := namespacePrefix + ctx.ModuleName() + "-install"
-		ctx.Phony(name, allInstalledFiles.Paths()...)
+		installFiles := ctx.installFiles.Paths()
+		ctx.Phony(name, installFiles...)
 		info.InstallTarget = PathForPhony(ctx, name)
-		deps = append(deps, info.InstallTarget)
+		deps = append(deps, installFiles...)
 	}
 
-	if len(allCheckbuildTargets) > 0 {
+	// A module's -checkbuild phony targets should
+	// not be created if the module is not exported to make.
+	// Those could depend on the build target and fail to compile
+	// for the current build target.
+	if (!ctx.Config().KatiEnabled() || !shouldSkipAndroidMkProcessing(ctx, m)) && !ctx.uncheckedModule && ctx.checkbuildTarget != nil {
 		name := namespacePrefix + ctx.ModuleName() + "-checkbuild"
-		ctx.Phony(name, allCheckbuildTargets...)
-		deps = append(deps, PathForPhony(ctx, name))
+		ctx.Phony(name, ctx.checkbuildTarget)
+		deps = append(deps, ctx.checkbuildTarget)
+	}
+
+	if outputFiles, err := outputFilesForModule(ctx, ctx.Module(), ""); err == nil && len(outputFiles) > 0 {
+		name := namespacePrefix + ctx.ModuleName() + "-outputs"
+		ctx.Phony(name, outputFiles...)
+		deps = append(deps, outputFiles...)
 	}
 
 	if len(deps) > 0 {
@@ -1708,9 +1733,20 @@
 		}
 
 		ctx.Phony(namespacePrefix+ctx.ModuleName()+suffix, deps...)
+		if ctx.Device() {
+			// Generate a target suffix for use in atest etc.
+			ctx.Phony(namespacePrefix+ctx.ModuleName()+"-target"+suffix, deps...)
+		} else {
+			// Generate a host suffix for use in atest etc.
+			ctx.Phony(namespacePrefix+ctx.ModuleName()+"-host"+suffix, deps...)
+			if ctx.Target().HostCross {
+				// Generate a host-cross suffix for use in atest etc.
+				ctx.Phony(namespacePrefix+ctx.ModuleName()+"-host-cross"+suffix, deps...)
+			}
+		}
 
 		info.BlueprintDir = ctx.ModuleDir()
-		SetProvider(ctx, FinalModuleBuildTargetsProvider, info)
+		SetProvider(ctx, ModuleBuildTargetsProvider, info)
 	}
 }
 
@@ -1850,42 +1886,112 @@
 	Srcs Paths
 }
 
-var SourceFilesInfoKey = blueprint.NewProvider[SourceFilesInfo]()
+var SourceFilesInfoProvider = blueprint.NewProvider[SourceFilesInfo]()
 
-type FinalModuleBuildTargetsInfo struct {
-	// Used by buildTargetSingleton to create checkbuild and per-directory build targets
-	// Only set on the final variant of each module
+// ModuleBuildTargetsInfo is used by buildTargetSingleton to create checkbuild and
+// per-directory build targets.
+type ModuleBuildTargetsInfo struct {
 	InstallTarget    WritablePath
 	CheckbuildTarget WritablePath
 	BlueprintDir     string
 }
 
-var FinalModuleBuildTargetsProvider = blueprint.NewProvider[FinalModuleBuildTargetsInfo]()
+var ModuleBuildTargetsProvider = blueprint.NewProvider[ModuleBuildTargetsInfo]()
 
 type CommonModuleInfo struct {
 	Enabled bool
 	// Whether the module has been replaced by a prebuilt
 	ReplacedByPrebuilt bool
 	// The Target of artifacts that this module variant is responsible for creating.
-	CompileTarget           Target
+	Target                  Target
 	SkipAndroidMkProcessing bool
 	BaseModuleName          string
 	CanHaveApexVariants     bool
+	MinSdkVersion           ApiLevelOrPlatform
+	SdkVersion              string
+	NotAvailableForPlatform bool
+	// There some subtle differences between this one and the one above.
+	NotInPlatform bool
+	// UninstallableApexPlatformVariant is set by MakeUninstallable called by the apex
+	// mutator.  MakeUninstallable also sets HideFromMake.  UninstallableApexPlatformVariant
+	// is used to avoid adding install or packaging dependencies into libraries provided
+	// by apexes.
+	UninstallableApexPlatformVariant bool
+	MinSdkVersionSupported           ApiLevel
+	ModuleWithMinSdkVersionCheck     bool
+	// Tests if this module can be installed to APEX as a file. For example, this would return
+	// true for shared libs while return false for static libs because static libs are not
+	// installable module (but it can still be mutated for APEX)
+	IsInstallableToApex bool
+	HideFromMake        bool
+	SkipInstall         bool
+	IsStubsModule       bool
+	Host                bool
+	IsApexModule        bool
+	// The primary licenses property, may be nil, records license metadata for the module.
+	PrimaryLicensesProperty applicableLicensesProperty
+	Owner                   string
+	Vendor                  bool
+	Proprietary             bool
+	SocSpecific             bool
+	ProductSpecific         bool
+	SystemExtSpecific       bool
+	DeviceSpecific          bool
+	// 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.
+	NoFullInstall                                bool
+	InVendorRamdisk                              bool
+	ExemptFromRequiredApplicableLicensesProperty bool
+	RequiredModuleNames                          []string
+	HostRequiredModuleNames                      []string
+	TargetRequiredModuleNames                    []string
+	VintfFragmentModuleNames                     []string
+	Dists                                        []Dist
+	ExportedToMake                               bool
+	Team                                         string
+	PartitionTag                                 string
 }
 
-var CommonModuleInfoKey = blueprint.NewProvider[CommonModuleInfo]()
-
-type PrebuiltModuleProviderData struct {
-	// Empty for now
+type ApiLevelOrPlatform struct {
+	ApiLevel   *ApiLevel
+	IsPlatform bool
 }
 
-var PrebuiltModuleProviderKey = blueprint.NewProvider[PrebuiltModuleProviderData]()
+var CommonModuleInfoProvider = blueprint.NewProvider[*CommonModuleInfo]()
 
-type HostToolProviderData struct {
+type PrebuiltModuleInfo struct {
+	SourceExists bool
+	UsePrebuilt  bool
+}
+
+var PrebuiltModuleInfoProvider = blueprint.NewProvider[PrebuiltModuleInfo]()
+
+type HostToolProviderInfo struct {
 	HostToolPath OptionalPath
 }
 
-var HostToolProviderKey = blueprint.NewProvider[HostToolProviderData]()
+var HostToolProviderInfoProvider = blueprint.NewProvider[HostToolProviderInfo]()
+
+type DistInfo struct {
+	Dists []dist
+}
+
+var DistProvider = blueprint.NewProvider[DistInfo]()
+
+type SourceFileGenerator interface {
+	GeneratedSourceFiles() Paths
+	GeneratedHeaderDirs() Paths
+	GeneratedDeps() Paths
+}
+
+type GeneratedSourceInfo struct {
+	GeneratedSourceFiles Paths
+	GeneratedHeaderDirs  Paths
+	GeneratedDeps        Paths
+}
+
+var GeneratedSourceInfoProvider = blueprint.NewProvider[GeneratedSourceInfo]()
 
 func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) {
 	ctx := &moduleContext{
@@ -2058,14 +2164,19 @@
 	}
 
 	if sourceFileProducer, ok := m.module.(SourceFileProducer); ok {
-		SetProvider(ctx, SourceFilesInfoKey, SourceFilesInfo{Srcs: sourceFileProducer.Srcs()})
+		srcs := sourceFileProducer.Srcs()
+		for _, src := range srcs {
+			if src == nil {
+				ctx.ModuleErrorf("SourceFileProducer cannot return nil srcs")
+				return
+			}
+		}
+		SetProvider(ctx, SourceFilesInfoProvider, SourceFilesInfo{Srcs: sourceFileProducer.Srcs()})
 	}
 
-	if ctx.IsFinalModule(m.module) {
-		m.generateModuleTarget(ctx)
-		if ctx.Failed() {
-			return
-		}
+	m.generateModuleTarget(ctx)
+	if ctx.Failed() {
+		return
 	}
 
 	ctx.TransitiveInstallFiles = depset.New[InstallPath](depset.TOPOLOGICAL, ctx.installFiles, dependencyInstallFiles)
@@ -2075,47 +2186,85 @@
 	SetProvider(ctx, InstallFilesProvider, installFiles)
 	buildLicenseMetadata(ctx, ctx.licenseMetadataFile)
 
-	if ctx.moduleInfoJSON != nil {
-		var installed InstallPaths
-		installed = append(installed, ctx.katiInstalls.InstallPaths()...)
-		installed = append(installed, ctx.katiSymlinks.InstallPaths()...)
-		installed = append(installed, ctx.katiInitRcInstalls.InstallPaths()...)
-		installed = append(installed, ctx.katiVintfInstalls.InstallPaths()...)
-		installedStrings := installed.Strings()
+	if len(ctx.moduleInfoJSON) > 0 {
+		for _, moduleInfoJSON := range ctx.moduleInfoJSON {
+			if moduleInfoJSON.Disabled {
+				continue
+			}
+			var installed InstallPaths
+			installed = append(installed, ctx.katiInstalls.InstallPaths()...)
+			installed = append(installed, ctx.katiSymlinks.InstallPaths()...)
+			installed = append(installed, ctx.katiInitRcInstalls.InstallPaths()...)
+			installed = append(installed, ctx.katiVintfInstalls.InstallPaths()...)
+			installedStrings := installed.Strings()
 
-		var targetRequired, hostRequired []string
-		if ctx.Host() {
-			targetRequired = m.commonProperties.Target_required
-		} else {
-			hostRequired = m.commonProperties.Host_required
-		}
+			var targetRequired, hostRequired []string
+			if ctx.Host() {
+				targetRequired = m.baseProperties.Target_required
+			} else {
+				hostRequired = m.baseProperties.Host_required
+			}
+			hostRequired = append(hostRequired, moduleInfoJSON.ExtraHostRequired...)
 
-		var data []string
-		for _, d := range ctx.testData {
-			data = append(data, d.ToRelativeInstallPath())
-		}
+			var data []string
+			for _, d := range ctx.testData {
+				data = append(data, d.ToRelativeInstallPath())
+			}
 
-		if ctx.moduleInfoJSON.Uninstallable {
-			installedStrings = nil
-			if len(ctx.moduleInfoJSON.CompatibilitySuites) == 1 && ctx.moduleInfoJSON.CompatibilitySuites[0] == "null-suite" {
-				ctx.moduleInfoJSON.CompatibilitySuites = nil
-				ctx.moduleInfoJSON.TestConfig = nil
-				ctx.moduleInfoJSON.AutoTestConfig = nil
-				data = nil
+			if moduleInfoJSON.Uninstallable {
+				installedStrings = nil
+				if len(moduleInfoJSON.CompatibilitySuites) == 1 && moduleInfoJSON.CompatibilitySuites[0] == "null-suite" {
+					moduleInfoJSON.CompatibilitySuites = nil
+					moduleInfoJSON.TestConfig = nil
+					moduleInfoJSON.AutoTestConfig = nil
+					data = nil
+				}
+			}
+
+			// 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. This was inherited from
+			// AndroidMkEntries.AddCompatibilityTestSuites.
+			suites := moduleInfoJSON.CompatibilitySuites
+			if PrefixInList(suites, "mts-") && !InList("mts", suites) {
+				suites = append(suites, "mts")
+			}
+			if PrefixInList(suites, "mcts-") && !InList("mcts", suites) {
+				suites = append(suites, "mcts")
+			}
+			moduleInfoJSON.CompatibilitySuites = suites
+
+			required := append(m.RequiredModuleNames(ctx), m.VintfFragmentModuleNames(ctx)...)
+			required = append(required, moduleInfoJSON.ExtraRequired...)
+
+			registerName := moduleInfoJSON.RegisterNameOverride
+			if len(registerName) == 0 {
+				registerName = m.moduleInfoRegisterName(ctx, moduleInfoJSON.SubName)
+			}
+
+			moduleName := moduleInfoJSON.ModuleNameOverride
+			if len(moduleName) == 0 {
+				moduleName = m.BaseModuleName() + moduleInfoJSON.SubName
+			}
+
+			supportedVariants := moduleInfoJSON.SupportedVariantsOverride
+			if moduleInfoJSON.SupportedVariantsOverride == nil {
+				supportedVariants = []string{m.moduleInfoVariant(ctx)}
+			}
+
+			moduleInfoJSON.core = CoreModuleInfoJSON{
+				RegisterName:       registerName,
+				Path:               []string{ctx.ModuleDir()},
+				Installed:          installedStrings,
+				ModuleName:         moduleName,
+				SupportedVariants:  supportedVariants,
+				TargetDependencies: targetRequired,
+				HostDependencies:   hostRequired,
+				Data:               data,
+				Required:           required,
 			}
 		}
 
-		ctx.moduleInfoJSON.core = CoreModuleInfoJSON{
-			RegisterName:       m.moduleInfoRegisterName(ctx, ctx.moduleInfoJSON.SubName),
-			Path:               []string{ctx.ModuleDir()},
-			Installed:          installedStrings,
-			ModuleName:         m.BaseModuleName() + ctx.moduleInfoJSON.SubName,
-			SupportedVariants:  []string{m.moduleInfoVariant(ctx)},
-			TargetDependencies: targetRequired,
-			HostDependencies:   hostRequired,
-			Data:               data,
-			Required:           append(m.RequiredModuleNames(ctx), m.VintfFragmentModuleNames(ctx)...),
-		}
 		SetProvider(ctx, ModuleInfoJSONProvider, ctx.moduleInfoJSON)
 	}
 
@@ -2133,35 +2282,131 @@
 			Phonies: ctx.phonies,
 		})
 	}
+
+	if len(ctx.dists) > 0 {
+		SetProvider(ctx, DistProvider, DistInfo{
+			Dists: ctx.dists,
+		})
+	}
+
 	buildComplianceMetadataProvider(ctx, m)
 
 	commonData := CommonModuleInfo{
-		ReplacedByPrebuilt:      m.commonProperties.ReplacedByPrebuilt,
-		CompileTarget:           m.commonProperties.CompileTarget,
-		SkipAndroidMkProcessing: shouldSkipAndroidMkProcessing(ctx, m),
-		BaseModuleName:          m.BaseModuleName(),
+		Enabled:                          m.Enabled(ctx),
+		ReplacedByPrebuilt:               m.commonProperties.ReplacedByPrebuilt,
+		Target:                           m.commonProperties.CompileTarget,
+		SkipAndroidMkProcessing:          shouldSkipAndroidMkProcessing(ctx, m),
+		UninstallableApexPlatformVariant: m.commonProperties.UninstallableApexPlatformVariant,
+		HideFromMake:                     m.commonProperties.HideFromMake,
+		SkipInstall:                      m.commonProperties.SkipInstall,
+		Host:                             m.Host(),
+		PrimaryLicensesProperty:          m.primaryLicensesProperty,
+		Owner:                            m.module.Owner(),
+		SocSpecific:                      Bool(m.commonProperties.Soc_specific),
+		Vendor:                           Bool(m.commonProperties.Vendor),
+		Proprietary:                      Bool(m.commonProperties.Proprietary),
+		ProductSpecific:                  Bool(m.commonProperties.Product_specific),
+		SystemExtSpecific:                Bool(m.commonProperties.System_ext_specific),
+		DeviceSpecific:                   Bool(m.commonProperties.Device_specific),
+		NoFullInstall:                    proptools.Bool(m.commonProperties.No_full_install),
+		InVendorRamdisk:                  m.InVendorRamdisk(),
+		ExemptFromRequiredApplicableLicensesProperty: exemptFromRequiredApplicableLicensesProperty(m.module),
+		RequiredModuleNames:                          m.module.RequiredModuleNames(ctx),
+		HostRequiredModuleNames:                      m.module.HostRequiredModuleNames(),
+		TargetRequiredModuleNames:                    m.module.TargetRequiredModuleNames(),
+		VintfFragmentModuleNames:                     m.module.VintfFragmentModuleNames(ctx),
+		Dists:                                        m.Dists(),
+		ExportedToMake:                               m.ExportedToMake(),
+		Team:                                         m.Team(),
+		PartitionTag:                                 m.PartitionTag(ctx.DeviceConfig()),
 	}
-	if m.commonProperties.ForcedDisabled {
-		commonData.Enabled = false
-	} else {
-		commonData.Enabled = m.commonProperties.Enabled.GetOrDefault(m.ConfigurableEvaluator(ctx), !m.Os().DefaultDisabled)
+	if mm, ok := m.module.(interface {
+		MinSdkVersion(ctx EarlyModuleContext) ApiLevel
+	}); ok {
+		ver := mm.MinSdkVersion(ctx)
+		commonData.MinSdkVersion.ApiLevel = &ver
+	} else if mm, ok := m.module.(interface{ MinSdkVersion() string }); ok {
+		ver := mm.MinSdkVersion()
+		// Compile against the current platform
+		if ver == "" {
+			commonData.MinSdkVersion.IsPlatform = true
+		} else {
+			api := ApiLevelFrom(ctx, ver)
+			commonData.MinSdkVersion.ApiLevel = &api
+		}
 	}
-	am, ok := m.module.(ApexModule)
-	commonData.CanHaveApexVariants = ok && am.CanHaveApexVariants()
-	SetProvider(ctx, CommonModuleInfoKey, commonData)
+
+	if mm, ok := m.module.(interface {
+		SdkVersion(ctx EarlyModuleContext) ApiLevel
+	}); ok {
+		ver := mm.SdkVersion(ctx)
+		if !ver.IsNone() {
+			commonData.SdkVersion = ver.String()
+		}
+	} else if mm, ok := m.module.(interface{ SdkVersion() string }); ok {
+		commonData.SdkVersion = mm.SdkVersion()
+	}
+
+	if am, ok := m.module.(ApexModule); ok {
+		commonData.CanHaveApexVariants = am.CanHaveApexVariants()
+		commonData.NotAvailableForPlatform = am.NotAvailableForPlatform()
+		commonData.NotInPlatform = am.NotInPlatform()
+		commonData.MinSdkVersionSupported = am.MinSdkVersionSupported(ctx)
+		commonData.IsInstallableToApex = am.IsInstallableToApex()
+		commonData.IsApexModule = true
+	}
+
+	if _, ok := m.module.(ModuleWithMinSdkVersionCheck); ok {
+		commonData.ModuleWithMinSdkVersionCheck = true
+	}
+
+	if st, ok := m.module.(StubsAvailableModule); ok {
+		commonData.IsStubsModule = st.IsStubsModule()
+	}
+	if mm, ok := m.module.(interface{ BaseModuleName() string }); ok {
+		commonData.BaseModuleName = mm.BaseModuleName()
+	}
+	SetProvider(ctx, CommonModuleInfoProvider, &commonData)
 	if p, ok := m.module.(PrebuiltInterface); ok && p.Prebuilt() != nil {
-		SetProvider(ctx, PrebuiltModuleProviderKey, PrebuiltModuleProviderData{})
+		SetProvider(ctx, PrebuiltModuleInfoProvider, PrebuiltModuleInfo{
+			SourceExists: p.Prebuilt().SourceExists(),
+			UsePrebuilt:  p.Prebuilt().UsePrebuilt(),
+		})
 	}
 	if h, ok := m.module.(HostToolProvider); ok {
-		SetProvider(ctx, HostToolProviderKey, HostToolProviderData{
+		SetProvider(ctx, HostToolProviderInfoProvider, HostToolProviderInfo{
 			HostToolPath: h.HostToolPath()})
 	}
 
 	if p, ok := m.module.(AndroidMkProviderInfoProducer); ok && !commonData.SkipAndroidMkProcessing {
 		SetProvider(ctx, AndroidMkInfoProvider, p.PrepareAndroidMKProviderInfo(ctx.Config()))
 	}
+
+	if s, ok := m.module.(SourceFileGenerator); ok {
+		SetProvider(ctx, GeneratedSourceInfoProvider, GeneratedSourceInfo{
+			GeneratedSourceFiles: s.GeneratedSourceFiles(),
+			GeneratedHeaderDirs:  s.GeneratedHeaderDirs(),
+			GeneratedDeps:        s.GeneratedDeps(),
+		})
+	}
+
+	if m.Enabled(ctx) {
+		if v, ok := m.module.(ModuleMakeVarsProvider); ok {
+			SetProvider(ctx, ModuleMakeVarsInfoProvider, v.MakeVars(ctx))
+		}
+
+		if am, ok := m.module.(AndroidMkDataProvider); ok {
+			SetProvider(ctx, AndroidMkDataInfoProvider, AndroidMkDataInfo{
+				Class: am.AndroidMk().Class,
+			})
+		}
+	}
+
+	m.module.CleanupAfterBuildActions()
 }
 
+func (m *ModuleBase) CleanupAfterBuildActions() {}
+
 func SetJarJarPrefixHandler(handler func(ModuleContext)) {
 	if jarJarPrefixHandler != nil {
 		panic("jarJarPrefixHandler already set")
@@ -2183,7 +2428,7 @@
 	arches = slices.DeleteFunc(arches, func(target Target) bool {
 		return target.NativeBridge != ctx.Target().NativeBridge
 	})
-	if len(arches) > 0 && ctx.Arch().ArchType != arches[0].Arch.ArchType {
+	if len(arches) > 0 && ctx.Arch().ArchType != arches[0].Arch.ArchType && ctx.Arch().ArchType != Common {
 		if ctx.Arch().ArchType.Multilib == "lib32" {
 			suffix = "_32"
 		} else {
@@ -2369,6 +2614,10 @@
 	return m.commonProperties.Overrides
 }
 
+func (m *ModuleBase) UseGenericConfig() bool {
+	return proptools.Bool(m.commonProperties.Use_generic_config)
+}
+
 type ConfigContext interface {
 	Config() Config
 }
@@ -2436,11 +2685,15 @@
 			return proptools.ConfigurableValueBool(ctx.Config().BuildFromTextStub())
 		case "debuggable":
 			return proptools.ConfigurableValueBool(ctx.Config().Debuggable())
+		case "eng":
+			return proptools.ConfigurableValueBool(ctx.Config().Eng())
 		case "use_debug_art":
 			// TODO(b/234351700): Remove once ART does not have separated debug APEX
 			return proptools.ConfigurableValueBool(ctx.Config().UseDebugArt())
 		case "selinux_ignore_neverallows":
 			return proptools.ConfigurableValueBool(ctx.Config().SelinuxIgnoreNeverallows())
+		case "always_use_prebuilt_sdks":
+			return proptools.ConfigurableValueBool(ctx.Config().AlwaysUsePrebuiltSdks())
 		default:
 			// TODO(b/323382414): Might add these on a case-by-case basis
 			ctx.OtherModulePropertyErrorf(m, property, fmt.Sprintf("TODO(b/323382414): Product variable %q is not yet supported in selects", variable))
@@ -2465,6 +2718,13 @@
 					return proptools.ConfigurableValueString(v)
 				case "bool":
 					return proptools.ConfigurableValueBool(v == "true")
+				case "int":
+					i, err := strconv.ParseInt(v, 10, 64)
+					if err != nil {
+						ctx.OtherModulePropertyErrorf(m, property, "integer soong_config_variable was not an int: %q", v)
+						return proptools.ConfigurableValueUndefined()
+					}
+					return proptools.ConfigurableValueInt(i)
 				case "string_list":
 					return proptools.ConfigurableValueStringList(strings.Split(v, " "))
 				default:
@@ -2674,6 +2934,8 @@
 
 // OutputFileForModule returns the output file paths with the given tag.  On error, including if the
 // module produced zero or multiple paths, it reports errors to the ctx and returns nil.
+// TODO(b/397766191): Change the signature to take ModuleProxy
+// Please only access the module's internal data through providers.
 func OutputFileForModule(ctx PathContext, module Module, tag string) Path {
 	paths, err := outputFilesForModule(ctx, module, tag)
 	if err != nil {
@@ -2711,9 +2973,10 @@
 	OtherModuleProviderContext
 	Module() Module
 	GetOutputFiles() OutputFilesInfo
-	EqualModules(m1, m2 Module) bool
 }
 
+// TODO(b/397766191): Change the signature to take ModuleProxy
+// Please only access the module's internal data through providers.
 func outputFilesForModule(ctx PathContext, module Module, tag string) (Paths, error) {
 	outputFilesFromProvider, err := outputFilesForModuleFromProvider(ctx, module, tag)
 	if outputFilesFromProvider != nil || err != OutputFilesProviderNotSet {
@@ -2721,11 +2984,12 @@
 	}
 
 	if octx, ok := ctx.(OutputFilesProviderModuleContext); ok {
-		if octx.EqualModules(octx.Module(), module) {
+		if EqualModules(octx.Module(), module) {
+			// It is the current module, we can access the srcs through interface
 			if sourceFileProducer, ok := module.(SourceFileProducer); ok {
 				return sourceFileProducer.Srcs(), nil
 			}
-		} else if sourceFiles, ok := OtherModuleProvider(octx, module, SourceFilesInfoKey); ok {
+		} else if sourceFiles, ok := OtherModuleProvider(octx, module, SourceFilesInfoProvider); ok {
 			if tag != "" {
 				return nil, fmt.Errorf("module %q is a SourceFileProducer, which does not support tag %q", pathContextName(ctx, module), tag)
 			}
@@ -2745,14 +3009,12 @@
 // If a module doesn't have the OutputFilesProvider, nil is returned.
 func outputFilesForModuleFromProvider(ctx PathContext, module Module, tag string) (Paths, error) {
 	var outputFiles OutputFilesInfo
-	fromProperty := false
 
 	if mctx, isMctx := ctx.(OutputFilesProviderModuleContext); isMctx {
-		if !mctx.EqualModules(mctx.Module(), module) {
+		if !EqualModules(mctx.Module(), module) {
 			outputFiles, _ = OtherModuleProvider(mctx, module, OutputFilesProvider)
 		} else {
 			outputFiles = mctx.GetOutputFiles()
-			fromProperty = true
 		}
 	} else if cta, isCta := ctx.(*singletonContextAdaptor); isCta {
 		outputFiles, _ = OtherModuleProvider(cta, module, OutputFilesProvider)
@@ -2769,10 +3031,8 @@
 	} else if taggedOutputFiles, hasTag := outputFiles.TaggedOutputFiles[tag]; hasTag {
 		return taggedOutputFiles, nil
 	} else {
-		if fromProperty {
-			return nil, fmt.Errorf("unsupported tag %q for module getting its own output files", tag)
-		} else {
-			return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+		return nil, UnsupportedOutputTagError{
+			tag: tag,
 		}
 	}
 }
@@ -2791,8 +3051,24 @@
 
 var OutputFilesProvider = blueprint.NewProvider[OutputFilesInfo]()
 
+type UnsupportedOutputTagError struct {
+	tag string
+}
+
+func (u UnsupportedOutputTagError) Error() string {
+	return fmt.Sprintf("unsupported output tag %q", u.tag)
+}
+
+func (u UnsupportedOutputTagError) Is(e error) bool {
+	_, ok := e.(UnsupportedOutputTagError)
+	return ok
+}
+
+var _ error = UnsupportedOutputTagError{}
+
 // This is used to mark the case where OutputFilesProvider is not set on some modules.
 var OutputFilesProviderNotSet = fmt.Errorf("No output files from provider")
+var ErrUnsupportedOutputTag = UnsupportedOutputTagError{}
 
 // Modules can implement HostToolProvider and return a valid OptionalPath from HostToolPath() to
 // specify that they can be used as a tool by a genrule module.
@@ -2849,14 +3125,17 @@
 func (c *buildTargetSingleton) GenerateBuildActions(ctx SingletonContext) {
 	var checkbuildDeps Paths
 
+	// Create a top level partialcompileclean target for modules to add dependencies to.
+	ctx.Phony("partialcompileclean")
+
 	mmTarget := func(dir string) string {
 		return "MODULES-IN-" + strings.Replace(filepath.Clean(dir), "/", "-", -1)
 	}
 
 	modulesInDir := make(map[string]Paths)
 
-	ctx.VisitAllModules(func(module Module) {
-		info := OtherModuleProviderOrDefault(ctx, module, FinalModuleBuildTargetsProvider)
+	ctx.VisitAllModuleProxies(func(module ModuleProxy) {
+		info := OtherModuleProviderOrDefault(ctx, module, ModuleBuildTargetsProvider)
 
 		if info.CheckbuildTarget != nil {
 			checkbuildDeps = append(checkbuildDeps, info.CheckbuildTarget)
@@ -2938,14 +3217,6 @@
 	BaseModuleName() string
 }
 
-// Extract the base module name from the Import name.
-// Often the Import name has a prefix "prebuilt_".
-// Remove the prefix explicitly if needed
-// until we find a better solution to get the Import name.
-type IDECustomizedModuleName interface {
-	IDECustomizedModuleName() string
-}
-
 // Collect information for opening IDE project files in java/jdeps.go.
 type IdeInfo struct {
 	BaseModuleName    string   `json:"-"`
diff --git a/android/module_context.go b/android/module_context.go
index ae7b54f..0a23a74 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -24,6 +24,7 @@
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/depset"
 	"github.com/google/blueprint/proptools"
+	"github.com/google/blueprint/uniquelist"
 )
 
 // BuildParameters describes the set of potential parameters to build a Ninja rule.
@@ -75,11 +76,13 @@
 	// Validations is a slice of output path for a validation action. Validation outputs imply lower
 	// non-blocking priority to building non-validation outputs.
 	Validations Paths
-	// Whether to skip outputting a default target statement which will be built by Ninja when no
+	// Whether to output a default target statement which will be built by Ninja when no
 	// targets are specified on Ninja's command line.
 	Default bool
 	// Args is a key value mapping for replacements of variables within the Rule
 	Args map[string]string
+	// PhonyOutput marks this build as `phony_output = true`
+	PhonyOutput bool
 }
 
 type ModuleBuildParams BuildParams
@@ -230,6 +233,10 @@
 	// the module-info.json generated by Make, and Make will not generate its own data for this module.
 	ModuleInfoJSON() *ModuleInfoJSON
 
+	// Simiar to ModuleInfoJSON, ExtraModuleInfoJSON also returns a pointer to the ModuleInfoJSON struct.
+	// This should only be called by a module that generates multiple AndroidMkEntries struct.
+	ExtraModuleInfoJSON() *ModuleInfoJSON
+
 	// SetOutputFiles stores the outputFiles to outputFiles property, which is used
 	// to set the OutputFilesProvider later.
 	SetOutputFiles(outputFiles Paths, tag string)
@@ -250,6 +257,24 @@
 	setContainersInfo(info ContainersInfo)
 
 	setAconfigPaths(paths Paths)
+
+	// DistForGoals creates a rule to copy one or more Paths to the artifacts
+	// directory on the build server when any of the specified goals are built.
+	DistForGoal(goal string, paths ...Path)
+
+	// DistForGoalWithFilename creates a rule to copy a Path to the artifacts
+	// directory on the build server with the given filename when the specified
+	// goal is built.
+	DistForGoalWithFilename(goal string, path Path, filename string)
+
+	// DistForGoals creates a rule to copy one or more Paths to the artifacts
+	// directory on the build server when any of the specified goals are built.
+	DistForGoals(goals []string, paths ...Path)
+
+	// DistForGoalsWithFilename creates a rule to copy a Path to the artifacts
+	// directory on the build server with the given filename when any of the
+	// specified goals are built.
+	DistForGoalsWithFilename(goals []string, path Path, filename string)
 }
 
 type moduleContext struct {
@@ -295,7 +320,7 @@
 
 	// moduleInfoJSON can be filled out by GenerateAndroidBuildActions to write a JSON file that will
 	// be included in the final module-info.json produced by Make.
-	moduleInfoJSON *ModuleInfoJSON
+	moduleInfoJSON []*ModuleInfoJSON
 
 	// containersInfo stores the information about the containers and the information of the
 	// apexes the module belongs to.
@@ -307,6 +332,8 @@
 	// complianceMetadataInfo is for different module types to dump metadata.
 	// See android.ModuleContext interface.
 	complianceMetadataInfo *ComplianceMetadataInfo
+
+	dists []dist
 }
 
 var _ ModuleContext = &moduleContext{}
@@ -343,7 +370,8 @@
 		OrderOnly:       params.OrderOnly.Strings(),
 		Validations:     params.Validations.Strings(),
 		Args:            params.Args,
-		Optional:        !params.Default,
+		Default:         params.Default,
+		PhonyOutput:     params.PhonyOutput,
 	}
 
 	if params.Depfile != nil {
@@ -428,6 +456,11 @@
 }
 
 func (m *moduleContext) Phony(name string, deps ...Path) {
+	for _, dep := range deps {
+		if dep == nil {
+			panic("Phony dep cannot be nil")
+		}
+	}
 	m.phonies[name] = append(m.phonies[name], deps...)
 }
 
@@ -440,10 +473,27 @@
 }
 
 func (m *moduleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) Module {
-	if module, _ := m.getDirectDepInternal(name, tag); module != nil {
-		return module.(Module)
+	deps := m.getDirectDepsInternal(name, tag)
+	if len(deps) == 1 {
+		return deps[0]
+	} else if len(deps) >= 2 {
+		panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q",
+			name, m.ModuleName()))
+	} else {
+		return nil
 	}
-	return nil
+}
+
+func (m *moduleContext) GetDirectDepProxyWithTag(name string, tag blueprint.DependencyTag) *ModuleProxy {
+	deps := m.getDirectDepsProxyInternal(name, tag)
+	if len(deps) == 1 {
+		return &deps[0]
+	} else if len(deps) >= 2 {
+		panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q",
+			name, m.ModuleName()))
+	} else {
+		return nil
+	}
 }
 
 func (m *moduleContext) ModuleSubDir() string {
@@ -569,11 +619,11 @@
 
 func (m *moduleContext) PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec {
 	fullInstallPath := installPath.Join(m, name)
-	return m.packageFile(fullInstallPath, srcPath, false)
+	return m.packageFile(fullInstallPath, srcPath, false, false)
 }
 
-func (m *moduleContext) getAconfigPaths() *Paths {
-	return &m.aconfigFilePaths
+func (m *moduleContext) getAconfigPaths() Paths {
+	return m.aconfigFilePaths
 }
 
 func (m *moduleContext) setAconfigPaths(paths Paths) {
@@ -593,7 +643,7 @@
 	return owner, overrides
 }
 
-func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, executable bool) PackagingSpec {
+func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, executable bool, requiresFullInstall bool) PackagingSpec {
 	licenseFiles := m.Module().EffectiveLicenseFiles()
 	owner, overrides := m.getOwnerAndOverrides()
 	spec := PackagingSpec{
@@ -601,13 +651,16 @@
 		srcPath:               srcPath,
 		symlinkTarget:         "",
 		executable:            executable,
-		effectiveLicenseFiles: &licenseFiles,
+		effectiveLicenseFiles: uniquelist.Make(licenseFiles),
 		partition:             fullInstallPath.partition,
 		skipInstall:           m.skipInstall(),
-		aconfigPaths:          m.getAconfigPaths(),
+		aconfigPaths:          uniquelist.Make(m.getAconfigPaths()),
 		archType:              m.target.Arch.ArchType,
-		overrides:             &overrides,
+		overrides:             uniquelist.Make(overrides),
 		owner:                 owner,
+		requiresFullInstall:   requiresFullInstall,
+		fullInstallPath:       fullInstallPath,
+		variation:             m.ModuleSubDir(),
 	}
 	m.packagingSpecs = append(m.packagingSpecs, spec)
 	return spec
@@ -615,6 +668,9 @@
 
 func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path, deps []InstallPath,
 	executable bool, hooks bool, checkbuild bool, extraZip *extraFilesZip) InstallPath {
+	if _, ok := srcPath.(InstallPath); ok {
+		m.ModuleErrorf("Src path cannot be another installed file. Please use a path from source or intermediates instead.")
+	}
 
 	fullInstallPath := installPath.Join(m, name)
 	if hooks {
@@ -623,8 +679,10 @@
 
 	if m.requiresFullInstall() {
 		deps = append(deps, InstallPaths(m.TransitiveInstallFiles.ToList())...)
-		deps = append(deps, m.installedInitRcPaths...)
-		deps = append(deps, m.installedVintfFragmentsPaths...)
+		if m.config.KatiEnabled() {
+			deps = append(deps, m.installedInitRcPaths...)
+			deps = append(deps, m.installedVintfFragmentsPaths...)
+		}
 
 		var implicitDeps, orderOnlyDeps Paths
 
@@ -636,22 +694,24 @@
 			orderOnlyDeps = InstallPaths(deps).Paths()
 		}
 
-		if m.Config().KatiEnabled() {
-			// When creating the install rule in Soong but embedding in Make, write the rule to a
-			// makefile instead of directly to the ninja file so that main.mk can add the
-			// dependencies from the `required` property that are hard to resolve in Soong.
-			m.katiInstalls = append(m.katiInstalls, katiInstall{
-				from:          srcPath,
-				to:            fullInstallPath,
-				implicitDeps:  implicitDeps,
-				orderOnlyDeps: orderOnlyDeps,
-				executable:    executable,
-				extraFiles:    extraZip,
-			})
-		} else {
-			rule := Cp
+		// When creating the install rule in Soong but embedding in Make, write the rule to a
+		// makefile instead of directly to the ninja file so that main.mk can add the
+		// dependencies from the `required` property that are hard to resolve in Soong.
+		// In soong-only builds, the katiInstall will still be created for semi-legacy code paths
+		// such as module-info.json or compliance, but it will not be used for actually installing
+		// the file.
+		m.katiInstalls = append(m.katiInstalls, katiInstall{
+			from:          srcPath,
+			to:            fullInstallPath,
+			implicitDeps:  implicitDeps,
+			orderOnlyDeps: orderOnlyDeps,
+			executable:    executable,
+			extraFiles:    extraZip,
+		})
+		if !m.Config().KatiEnabled() {
+			rule := CpWithBash
 			if executable {
-				rule = CpExecutable
+				rule = CpExecutableWithBash
 			}
 
 			extraCmds := ""
@@ -662,6 +722,17 @@
 				implicitDeps = append(implicitDeps, extraZip.zip)
 			}
 
+			var cpFlags = "-f"
+
+			// If this is a device file, copy while preserving timestamps. This is to support
+			// adb sync in soong-only builds. Because soong-only builds have 2 different staging
+			// directories, the out/target/product one and the out/soong/.intermediates one,
+			// we need to ensure the files in them have the same timestamps so that adb sync doesn't
+			// update the files on device.
+			if strings.Contains(fullInstallPath.String(), "target/product") {
+				cpFlags += " -p"
+			}
+
 			m.Build(pctx, BuildParams{
 				Rule:        rule,
 				Description: "install " + fullInstallPath.Base(),
@@ -669,9 +740,9 @@
 				Input:       srcPath,
 				Implicits:   implicitDeps,
 				OrderOnly:   orderOnlyDeps,
-				Default:     !m.Config().KatiEnabled(),
 				Args: map[string]string{
 					"extraCmds": extraCmds,
+					"cpFlags":   cpFlags,
 				},
 			})
 		}
@@ -679,9 +750,12 @@
 		m.installFiles = append(m.installFiles, fullInstallPath)
 	}
 
-	m.packageFile(fullInstallPath, srcPath, executable)
+	m.packageFile(fullInstallPath, srcPath, executable, m.requiresFullInstall())
 
 	if checkbuild {
+		if srcPath == nil {
+			panic("srcPath cannot be nil")
+		}
 		m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
 	}
 
@@ -698,25 +772,26 @@
 	}
 	if m.requiresFullInstall() {
 
-		if m.Config().KatiEnabled() {
-			// When creating the symlink rule in Soong but embedding in Make, write the rule to a
-			// makefile instead of directly to the ninja file so that main.mk can add the
-			// dependencies from the `required` property that are hard to resolve in Soong.
-			m.katiSymlinks = append(m.katiSymlinks, katiInstall{
-				from: srcPath,
-				to:   fullInstallPath,
-			})
-		} else {
+		// When creating the symlink rule in Soong but embedding in Make, write the rule to a
+		// makefile instead of directly to the ninja file so that main.mk can add the
+		// dependencies from the `required` property that are hard to resolve in Soong.
+		// In soong-only builds, the katiInstall will still be created for semi-legacy code paths
+		// such as module-info.json or compliance, but it will not be used for actually installing
+		// the file.
+		m.katiSymlinks = append(m.katiSymlinks, katiInstall{
+			from: srcPath,
+			to:   fullInstallPath,
+		})
+		if !m.Config().KatiEnabled() {
 			// The symlink doesn't need updating when the target is modified, but we sometimes
 			// have a dependency on a symlink to a binary instead of to the binary directly, and
 			// the mtime of the symlink must be updated when the binary is modified, so use a
 			// normal dependency here instead of an order-only dependency.
 			m.Build(pctx, BuildParams{
-				Rule:        Symlink,
+				Rule:        SymlinkWithBash,
 				Description: "install symlink " + fullInstallPath.Base(),
 				Output:      fullInstallPath,
 				Input:       srcPath,
-				Default:     !m.Config().KatiEnabled(),
 				Args: map[string]string{
 					"fromPath": relPath,
 				},
@@ -728,16 +803,19 @@
 
 	owner, overrides := m.getOwnerAndOverrides()
 	m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{
-		relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
-		srcPath:          nil,
-		symlinkTarget:    relPath,
-		executable:       false,
-		partition:        fullInstallPath.partition,
-		skipInstall:      m.skipInstall(),
-		aconfigPaths:     m.getAconfigPaths(),
-		archType:         m.target.Arch.ArchType,
-		overrides:        &overrides,
-		owner:            owner,
+		relPathInPackage:    Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
+		srcPath:             nil,
+		symlinkTarget:       relPath,
+		executable:          false,
+		partition:           fullInstallPath.partition,
+		skipInstall:         m.skipInstall(),
+		aconfigPaths:        uniquelist.Make(m.getAconfigPaths()),
+		archType:            m.target.Arch.ArchType,
+		overrides:           uniquelist.Make(overrides),
+		owner:               owner,
+		requiresFullInstall: m.requiresFullInstall(),
+		fullInstallPath:     fullInstallPath,
+		variation:           m.ModuleSubDir(),
 	})
 
 	return fullInstallPath
@@ -750,20 +828,21 @@
 	m.module.base().hooks.runInstallHooks(m, nil, fullInstallPath, true)
 
 	if m.requiresFullInstall() {
-		if m.Config().KatiEnabled() {
-			// When creating the symlink rule in Soong but embedding in Make, write the rule to a
-			// makefile instead of directly to the ninja file so that main.mk can add the
-			// dependencies from the `required` property that are hard to resolve in Soong.
-			m.katiSymlinks = append(m.katiSymlinks, katiInstall{
-				absFrom: absPath,
-				to:      fullInstallPath,
-			})
-		} else {
+		// When creating the symlink rule in Soong but embedding in Make, write the rule to a
+		// makefile instead of directly to the ninja file so that main.mk can add the
+		// dependencies from the `required` property that are hard to resolve in Soong.
+		// In soong-only builds, the katiInstall will still be created for semi-legacy code paths
+		// such as module-info.json or compliance, but it will not be used for actually installing
+		// the file.
+		m.katiSymlinks = append(m.katiSymlinks, katiInstall{
+			absFrom: absPath,
+			to:      fullInstallPath,
+		})
+		if !m.Config().KatiEnabled() {
 			m.Build(pctx, BuildParams{
-				Rule:        Symlink,
+				Rule:        SymlinkWithBash,
 				Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath,
 				Output:      fullInstallPath,
-				Default:     !m.Config().KatiEnabled(),
 				Args: map[string]string{
 					"fromPath": absPath,
 				},
@@ -775,16 +854,19 @@
 
 	owner, overrides := m.getOwnerAndOverrides()
 	m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{
-		relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
-		srcPath:          nil,
-		symlinkTarget:    absPath,
-		executable:       false,
-		partition:        fullInstallPath.partition,
-		skipInstall:      m.skipInstall(),
-		aconfigPaths:     m.getAconfigPaths(),
-		archType:         m.target.Arch.ArchType,
-		overrides:        &overrides,
-		owner:            owner,
+		relPathInPackage:    Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
+		srcPath:             nil,
+		symlinkTarget:       absPath,
+		executable:          false,
+		partition:           fullInstallPath.partition,
+		skipInstall:         m.skipInstall(),
+		aconfigPaths:        uniquelist.Make(m.getAconfigPaths()),
+		archType:            m.target.Arch.ArchType,
+		overrides:           uniquelist.Make(overrides),
+		owner:               owner,
+		requiresFullInstall: m.requiresFullInstall(),
+		fullInstallPath:     fullInstallPath,
+		variation:           m.ModuleSubDir(),
 	})
 
 	return fullInstallPath
@@ -805,6 +887,11 @@
 
 // CheckbuildFile specifies the output files that should be built by checkbuild.
 func (m *moduleContext) CheckbuildFile(srcPaths ...Path) {
+	for _, srcPath := range srcPaths {
+		if srcPath == nil {
+			panic("CheckbuildFile() files cannot be nil")
+		}
+	}
 	m.checkbuildFiles = append(m.checkbuildFiles, srcPaths...)
 }
 
@@ -822,11 +909,20 @@
 }
 
 func (m *moduleContext) ModuleInfoJSON() *ModuleInfoJSON {
-	if moduleInfoJSON := m.moduleInfoJSON; moduleInfoJSON != nil {
-		return moduleInfoJSON
+	if len(m.moduleInfoJSON) == 0 {
+		moduleInfoJSON := &ModuleInfoJSON{}
+		m.moduleInfoJSON = append(m.moduleInfoJSON, moduleInfoJSON)
 	}
+	return m.moduleInfoJSON[0]
+}
+
+func (m *moduleContext) ExtraModuleInfoJSON() *ModuleInfoJSON {
+	if len(m.moduleInfoJSON) == 0 {
+		panic("call ModuleInfoJSON() instead")
+	}
+
 	moduleInfoJSON := &ModuleInfoJSON{}
-	m.moduleInfoJSON = moduleInfoJSON
+	m.moduleInfoJSON = append(m.moduleInfoJSON, moduleInfoJSON)
 	return moduleInfoJSON
 }
 
@@ -913,3 +1009,32 @@
 func (m *moduleContext) setContainersInfo(info ContainersInfo) {
 	m.containersInfo = info
 }
+
+func (c *moduleContext) DistForGoal(goal string, paths ...Path) {
+	c.DistForGoals([]string{goal}, paths...)
+}
+
+func (c *moduleContext) DistForGoalWithFilename(goal string, path Path, filename string) {
+	c.DistForGoalsWithFilename([]string{goal}, path, filename)
+}
+
+func (c *moduleContext) DistForGoals(goals []string, paths ...Path) {
+	var copies distCopies
+	for _, path := range paths {
+		copies = append(copies, distCopy{
+			from: path,
+			dest: path.Base(),
+		})
+	}
+	c.dists = append(c.dists, dist{
+		goals: slices.Clone(goals),
+		paths: copies,
+	})
+}
+
+func (c *moduleContext) DistForGoalsWithFilename(goals []string, path Path, filename string) {
+	c.dists = append(c.dists, dist{
+		goals: slices.Clone(goals),
+		paths: distCopies{{from: path, dest: filename}},
+	})
+}
diff --git a/android/module_info_json.go b/android/module_info_json.go
index d102dd2..50c961a 100644
--- a/android/module_info_json.go
+++ b/android/module_info_json.go
@@ -34,15 +34,23 @@
 	SrcJars             []string `json:"srcjars,omitempty"`               // $(sort $(ALL_MODULES.$(m).SRCJARS))
 	ClassesJar          []string `json:"classes_jar,omitempty"`           // $(sort $(ALL_MODULES.$(m).CLASSES_JAR))
 	TestMainlineModules []string `json:"test_mainline_modules,omitempty"` // $(sort $(ALL_MODULES.$(m).TEST_MAINLINE_MODULES))
-	IsUnitTest          bool     `json:"is_unit_test,omitempty"`          // $(ALL_MODULES.$(m).IS_UNIT_TEST)
+	IsUnitTest          string   `json:"is_unit_test,omitempty"`          // $(ALL_MODULES.$(m).IS_UNIT_TEST)
 	TestOptionsTags     []string `json:"test_options_tags,omitempty"`     // $(sort $(ALL_MODULES.$(m).TEST_OPTIONS_TAGS))
 	RuntimeDependencies []string `json:"runtime_dependencies,omitempty"`  // $(sort $(ALL_MODULES.$(m).LOCAL_RUNTIME_LIBRARIES))
 	StaticDependencies  []string `json:"static_dependencies,omitempty"`   // $(sort $(ALL_MODULES.$(m).LOCAL_STATIC_LIBRARIES))
 	DataDependencies    []string `json:"data_dependencies,omitempty"`     // $(sort $(ALL_MODULES.$(m).TEST_DATA_BINS))
 
-	CompatibilitySuites []string `json:"compatibility_suites,omitempty"` // $(sort $(ALL_MODULES.$(m).COMPATIBILITY_SUITES))
-	AutoTestConfig      []string `json:"auto_test_config,omitempty"`     // $(ALL_MODULES.$(m).auto_test_config)
-	TestConfig          []string `json:"test_config,omitempty"`          // $(strip $(ALL_MODULES.$(m).TEST_CONFIG) $(ALL_MODULES.$(m).EXTRA_TEST_CONFIGS)
+	CompatibilitySuites  []string `json:"compatibility_suites,omitempty"` // $(sort $(ALL_MODULES.$(m).COMPATIBILITY_SUITES))
+	AutoTestConfig       []string `json:"auto_test_config,omitempty"`     // $(ALL_MODULES.$(m).auto_test_config)
+	TestConfig           []string `json:"test_config,omitempty"`          // $(strip $(ALL_MODULES.$(m).TEST_CONFIG) $(ALL_MODULES.$(m).EXTRA_TEST_CONFIGS)
+	TestModuleConfigBase string   `json:"test_module_config_base,omitempty"`
+	ExtraRequired        []string `json:"-"`
+	ExtraHostRequired    []string `json:"-"`
+
+	SupportedVariantsOverride []string `json:"-"`
+	Disabled                  bool     `json:"-"`
+	RegisterNameOverride      string   `json:"-"`
+	ModuleNameOverride        string   `json:"-"`
 }
 
 type ModuleInfoJSON struct {
@@ -127,4 +135,12 @@
 	return gobtools.CustomGobDecode[combinedModuleInfoJSON](data, m)
 }
 
-var ModuleInfoJSONProvider = blueprint.NewProvider[*ModuleInfoJSON]()
+func (m *ModuleInfoJSON) GetInstalled() []string {
+	return m.core.Installed
+}
+
+func (m *ModuleInfoJSON) GetClass() []string {
+	return m.Class
+}
+
+var ModuleInfoJSONProvider = blueprint.NewProvider[[]*ModuleInfoJSON]()
diff --git a/android/module_proxy.go b/android/module_proxy.go
index 30459b9..81d90e9 100644
--- a/android/module_proxy.go
+++ b/android/module_proxy.go
@@ -11,6 +11,10 @@
 
 var _ Module = (*ModuleProxy)(nil)
 
+func (m ModuleProxy) IsNil() bool {
+	return m.module.IsNil()
+}
+
 func (m ModuleProxy) Name() string {
 	return m.module.Name()
 }
@@ -23,6 +27,10 @@
 	panic("method is not implemented on ModuleProxy")
 }
 
+func (m ModuleProxy) CleanupAfterBuildActions() {
+	panic("method is not implemented on ModuleProxy")
+}
+
 func (m ModuleProxy) ComponentDepsMutator(ctx BottomUpMutatorContext) {
 	panic("method is not implemented on ModuleProxy")
 }
@@ -160,10 +168,6 @@
 	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")
 }
@@ -189,7 +193,7 @@
 }
 
 func (m ModuleProxy) String() string {
-	return m.module.Name()
+	return m.module.String()
 }
 
 func (m ModuleProxy) qualifiedModuleId(ctx BaseModuleContext) qualifiedModuleName {
@@ -231,3 +235,7 @@
 func (m ModuleProxy) VintfFragments(ctx ConfigurableEvaluatorContext) []string {
 	panic("method is not implemented on ModuleProxy")
 }
+
+func (m ModuleProxy) UseGenericConfig() bool {
+	panic("method is not implemented on ModuleProxy")
+}
diff --git a/android/module_test.go b/android/module_test.go
index d5bf941..5331e49 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -321,27 +321,27 @@
 		if host {
 			variant = result.Config.BuildOSCommonTarget.String()
 		}
-		return result.ModuleForTests(name, variant)
+		return result.ModuleForTests(t, name, variant)
 	}
 
 	outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) }
 
 	installRule := func(name string) TestingBuildParams {
-		return module(name, false).Output(filepath.Join("out/soong/target/product/test_device/system", name))
+		return module(name, false).Output(filepath.Join("out/target/product/test_device/system", name))
 	}
 
 	symlinkRule := func(name string) TestingBuildParams {
-		return module(name, false).Output(filepath.Join("out/soong/target/product/test_device/system/symlinks", name))
+		return module(name, false).Output(filepath.Join("out/target/product/test_device/system/symlinks", name))
 	}
 
 	hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) }
 
 	hostInstallRule := func(name string) TestingBuildParams {
-		return module(name, true).Output(filepath.Join("out/soong/host/linux-x86", name))
+		return module(name, true).Output(filepath.Join("out/host/linux-x86", name))
 	}
 
 	hostSymlinkRule := func(name string) TestingBuildParams {
-		return module(name, true).Output(filepath.Join("out/soong/host/linux-x86/symlinks", name))
+		return module(name, true).Output(filepath.Join("out/host/linux-x86/symlinks", name))
 	}
 
 	assertInputs := func(params TestingBuildParams, inputs ...Path) {
@@ -434,11 +434,12 @@
 	rules := result.InstallMakeRulesForTesting(t)
 
 	module := func(name string, host bool) TestingModule {
+		t.Helper()
 		variant := "android_common"
 		if host {
 			variant = result.Config.BuildOSCommonTarget.String()
 		}
-		return result.ModuleForTests(name, variant)
+		return result.ModuleForTests(t, name, variant)
 	}
 
 	outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) }
@@ -743,7 +744,7 @@
 				FixtureWithRootAndroidBp(tc.bp),
 			).RunTest(t)
 
-			foo := result.ModuleForTests("foo", "").Module().base()
+			foo := result.ModuleForTests(t, "foo", "").Module().base()
 
 			AssertDeepEquals(t, "foo ", tc.expectedProps, foo.propertiesWithValues())
 		})
@@ -1078,7 +1079,7 @@
 				PathContext:                PathContextForTesting(config),
 				OtherModuleProviderContext: result.TestContext.OtherModuleProviderAdaptor(),
 			}
-			got := OutputFileForModule(ctx, result.ModuleForTests("test_module", "").Module(), tt.tag)
+			got := OutputFileForModule(ctx, result.ModuleForTests(t, "test_module", "").Module(), tt.tag)
 			AssertPathRelativeToTopEquals(t, "expected output path", tt.expected, got)
 			AssertArrayString(t, "expected missing deps", tt.missingDeps, ctx.missingDeps)
 		})
@@ -1110,3 +1111,14 @@
 			"Module .+ and Vintf_fragment .+ are installed to different partitions.")).
 		RunTestWithBp(t, bp)
 }
+
+func TestInvalidModuleName(t *testing.T) {
+	bp := `
+		deps {
+			name: "fo o",
+		}
+	`
+	prepareForModuleTests.
+		ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`should use a valid name`)).
+		RunTestWithBp(t, bp)
+}
diff --git a/android/mutator.go b/android/mutator.go
index fdd16a8..12861c0 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -15,9 +15,8 @@
 package android
 
 import (
-	"sync"
-
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/pool"
 )
 
 // Phases:
@@ -67,10 +66,10 @@
 }
 
 type RegisterMutatorsContext interface {
-	TopDown(name string, m TopDownMutator) MutatorHandle
 	BottomUp(name string, m BottomUpMutator) MutatorHandle
 	BottomUpBlueprint(name string, m blueprint.BottomUpMutator) MutatorHandle
-	Transition(name string, m TransitionMutator) TransitionMutatorHandle
+	Transition(name string, m VariationTransitionMutator) TransitionMutatorHandle
+	InfoBasedTransition(name string, m androidTransitionMutator) TransitionMutatorHandle
 }
 
 type RegisterMutatorFunc func(RegisterMutatorsContext)
@@ -158,6 +157,7 @@
 
 var preDeps = []RegisterMutatorFunc{
 	registerArchMutator,
+	RegisterPrebuiltsPreDepsMutators,
 }
 
 var postDeps = []RegisterMutatorFunc{
@@ -193,17 +193,6 @@
 	finalDeps = append(finalDeps, f)
 }
 
-type TopDownMutator func(TopDownMutatorContext)
-
-type TopDownMutatorContext interface {
-	BaseModuleContext
-}
-
-type topDownMutatorContext struct {
-	bp blueprint.TopDownMutatorContext
-	baseModuleContext
-}
-
 type BottomUpMutator func(BottomUpMutatorContext)
 
 type BottomUpMutatorContext interface {
@@ -213,7 +202,7 @@
 	// dependency (some entries may be nil).
 	//
 	// This method will pause until the new dependencies have had the current mutator called on them.
-	AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) []blueprint.Module
+	AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) []Module
 
 	// AddReverseDependency adds a dependency from the destination to the given module.
 	// Does not affect the ordering of the current mutator pass, but will be ordered
@@ -229,7 +218,7 @@
 	// all the non-local variations of the current module, plus the variations argument.
 	//
 	// This method will pause until the new dependencies have had the current mutator called on them.
-	AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag, names ...string) []blueprint.Module
+	AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag, names ...string) []Module
 
 	// AddReverseVariationDependency 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
@@ -252,7 +241,7 @@
 	// dependency only needs to match the supplied variations.
 	//
 	// This method will pause until the new dependencies have had the current mutator called on them.
-	AddFarVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string) []blueprint.Module
+	AddFarVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string) []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.
@@ -279,22 +268,12 @@
 }
 
 // An outgoingTransitionContextImpl and incomingTransitionContextImpl is created for every dependency of every module
-// for each transition mutator.  bottomUpMutatorContext and topDownMutatorContext are created once for every module
-// for every BottomUp or TopDown mutator.  Use a global pool for each to avoid reallocating every time.
+// for each transition mutator.  bottomUpMutatorContext is created once for every module for every BottomUp mutator.
+// Use a global pool for each to avoid reallocating every time.
 var (
-	outgoingTransitionContextPool = sync.Pool{
-		New: func() any { return &outgoingTransitionContextImpl{} },
-	}
-	incomingTransitionContextPool = sync.Pool{
-		New: func() any { return &incomingTransitionContextImpl{} },
-	}
-	bottomUpMutatorContextPool = sync.Pool{
-		New: func() any { return &bottomUpMutatorContext{} },
-	}
-
-	topDownMutatorContextPool = sync.Pool{
-		New: func() any { return &topDownMutatorContext{} },
-	}
+	outgoingTransitionContextPool = pool.New[outgoingTransitionContextImpl]()
+	incomingTransitionContextPool = pool.New[incomingTransitionContextImpl]()
+	bottomUpMutatorContextPool    = pool.New[bottomUpMutatorContext]()
 )
 
 type bottomUpMutatorContext struct {
@@ -305,10 +284,10 @@
 
 // callers must immediately follow the call to this function with defer bottomUpMutatorContextPool.Put(mctx).
 func bottomUpMutatorContextFactory(ctx blueprint.BottomUpMutatorContext, a Module,
-	finalPhase bool) BottomUpMutatorContext {
+	finalPhase bool) *bottomUpMutatorContext {
 
 	moduleContext := a.base().baseModuleContextFactory(ctx)
-	mctx := bottomUpMutatorContextPool.Get().(*bottomUpMutatorContext)
+	mctx := bottomUpMutatorContextPool.Get()
 	*mctx = bottomUpMutatorContext{
 		bp:                ctx,
 		baseModuleContext: moduleContext,
@@ -337,250 +316,22 @@
 	return mutator
 }
 
-type IncomingTransitionContext interface {
-	ArchModuleContext
-	ModuleProviderContext
-	ModuleErrorContext
-
-	// Module returns the target of the dependency edge for which the transition
-	// is being computed
-	Module() Module
-
-	// Config returns the configuration for the build.
-	Config() Config
-
-	DeviceConfig() DeviceConfig
-
-	// IsAddingDependency returns true if the transition is being called while adding a dependency
-	// after the transition mutator has already run, or false if it is being called when the transition
-	// mutator is running.  This should be used sparingly, all uses will have to be removed in order
-	// to support creating variants on demand.
-	IsAddingDependency() bool
-}
-
-type OutgoingTransitionContext interface {
-	ArchModuleContext
-	ModuleProviderContext
-
-	// Module returns the target of the dependency edge for which the transition
-	// is being computed
-	Module() Module
-
-	// DepTag() Returns the dependency tag through which this dependency is
-	// reached
-	DepTag() blueprint.DependencyTag
-
-	// Config returns the configuration for the build.
-	Config() Config
-
-	DeviceConfig() DeviceConfig
-}
-
-// TransitionMutator implements a top-down mechanism where a module tells its
-// direct dependencies what variation they should be built in but the dependency
-// has the final say.
-//
-// When implementing a transition mutator, one needs to implement four methods:
-//   - Split() that tells what variations a module has by itself
-//   - OutgoingTransition() where a module tells what it wants from its
-//     dependency
-//   - IncomingTransition() where a module has the final say about its own
-//     variation
-//   - Mutate() that changes the state of a module depending on its variation
-//
-// That the effective variation of module B when depended on by module A is the
-// composition the outgoing transition of module A and the incoming transition
-// of module B.
-//
-// the outgoing transition should not take the properties of the dependency into
-// account, only those of the module that depends on it. For this reason, the
-// dependency is not even passed into it as an argument. Likewise, the incoming
-// transition should not take the properties of the depending module into
-// account and is thus not informed about it. This makes for a nice
-// decomposition of the decision logic.
-//
-// A given transition mutator only affects its own variation; other variations
-// stay unchanged along the dependency edges.
-//
-// Soong makes sure that all modules are created in the desired variations and
-// that dependency edges are set up correctly. This ensures that "missing
-// variation" errors do not happen and allows for more flexible changes in the
-// value of the variation among dependency edges (as oppposed to bottom-up
-// mutators where if module A in variation X depends on module B and module B
-// has that variation X, A must depend on variation X of B)
-//
-// The limited power of the context objects passed to individual mutators
-// methods also makes it more difficult to shoot oneself in the foot. Complete
-// safety is not guaranteed because no one prevents individual transition
-// mutators from mutating modules in illegal ways and for e.g. Split() or
-// Mutate() to run their own visitations of the transitive dependency of the
-// module and both of these are bad ideas, but it's better than no guardrails at
-// all.
-//
-// This model is pretty close to Bazel's configuration transitions. The mapping
-// between concepts in Soong and Bazel is as follows:
-//   - Module == configured target
-//   - Variant == configuration
-//   - Variation name == configuration flag
-//   - Variation == configuration flag value
-//   - Outgoing transition == attribute transition
-//   - Incoming transition == rule transition
-//
-// The Split() method does not have a Bazel equivalent and Bazel split
-// transitions do not have a Soong equivalent.
-//
-// Mutate() does not make sense in Bazel due to the different models of the
-// two systems: when creating new variations, Soong clones the old module and
-// thus some way is needed to change it state whereas Bazel creates each
-// configuration of a given configured target anew.
-type TransitionMutator interface {
-	// Split returns the set of variations that should be created for a module no
-	// matter who depends on it. Used when Make depends on a particular variation
-	// or when the module knows its variations just based on information given to
-	// it in the Blueprint file. This method should not mutate the module it is
-	// called on.
-	Split(ctx BaseModuleContext) []string
-
-	// OutgoingTransition is called on a module to determine which variation it wants
-	// from its direct dependencies. The dependency itself can override this decision.
-	// This method should not mutate the module itself.
-	OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string
-
-	// IncomingTransition is called on a module to determine which variation it should
-	// be in based on the variation modules that depend on it want. This gives the module
-	// a final say about its own variations. This method should not mutate the module
-	// itself.
-	IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string
-
-	// Mutate is called after a module was split into multiple variations on each variation.
-	// It should not split the module any further but adding new dependencies is
-	// fine. Unlike all the other methods on TransitionMutator, this method is
-	// allowed to mutate the module.
-	Mutate(ctx BottomUpMutatorContext, variation string)
-}
-
-type androidTransitionMutator struct {
-	finalPhase bool
-	mutator    TransitionMutator
-	name       string
-}
-
-func (a *androidTransitionMutator) Split(ctx blueprint.BaseModuleContext) []string {
-	if a.finalPhase {
-		panic("TransitionMutator not allowed in FinalDepsMutators")
+func (x *registerMutatorsContext) Transition(name string, m VariationTransitionMutator) TransitionMutatorHandle {
+	atm := &androidTransitionMutatorAdapter{
+		finalPhase: x.finalPhase,
+		mutator:    variationTransitionMutatorAdapter{m},
+		name:       name,
 	}
-	if m, ok := ctx.Module().(Module); ok {
-		moduleContext := m.base().baseModuleContextFactory(ctx)
-		return a.mutator.Split(&moduleContext)
-	} else {
-		return []string{""}
+	mutator := &mutator{
+		name:              name,
+		transitionMutator: atm,
 	}
+	x.mutators = append(x.mutators, mutator)
+	return mutator
 }
 
-type outgoingTransitionContextImpl struct {
-	archModuleContext
-	bp blueprint.OutgoingTransitionContext
-}
-
-func (c *outgoingTransitionContextImpl) Module() Module {
-	return c.bp.Module().(Module)
-}
-
-func (c *outgoingTransitionContextImpl) DepTag() blueprint.DependencyTag {
-	return c.bp.DepTag()
-}
-
-func (c *outgoingTransitionContextImpl) Config() Config {
-	return c.bp.Config().(Config)
-}
-
-func (c *outgoingTransitionContextImpl) DeviceConfig() DeviceConfig {
-	return DeviceConfig{c.bp.Config().(Config).deviceConfig}
-}
-
-func (c *outgoingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) {
-	return c.bp.Provider(provider)
-}
-
-func (a *androidTransitionMutator) OutgoingTransition(bpctx blueprint.OutgoingTransitionContext, sourceVariation string) string {
-	if m, ok := bpctx.Module().(Module); ok {
-		ctx := outgoingTransitionContextPool.Get().(*outgoingTransitionContextImpl)
-		defer outgoingTransitionContextPool.Put(ctx)
-		*ctx = outgoingTransitionContextImpl{
-			archModuleContext: m.base().archModuleContextFactory(bpctx),
-			bp:                bpctx,
-		}
-		return a.mutator.OutgoingTransition(ctx, sourceVariation)
-	} else {
-		return ""
-	}
-}
-
-type incomingTransitionContextImpl struct {
-	archModuleContext
-	bp blueprint.IncomingTransitionContext
-}
-
-func (c *incomingTransitionContextImpl) Module() Module {
-	return c.bp.Module().(Module)
-}
-
-func (c *incomingTransitionContextImpl) Config() Config {
-	return c.bp.Config().(Config)
-}
-
-func (c *incomingTransitionContextImpl) DeviceConfig() DeviceConfig {
-	return DeviceConfig{c.bp.Config().(Config).deviceConfig}
-}
-
-func (c *incomingTransitionContextImpl) IsAddingDependency() bool {
-	return c.bp.IsAddingDependency()
-}
-
-func (c *incomingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) {
-	return c.bp.Provider(provider)
-}
-
-func (c *incomingTransitionContextImpl) ModuleErrorf(fmt string, args ...interface{}) {
-	c.bp.ModuleErrorf(fmt, args)
-}
-
-func (c *incomingTransitionContextImpl) PropertyErrorf(property, fmt string, args ...interface{}) {
-	c.bp.PropertyErrorf(property, fmt, args)
-}
-
-func (a *androidTransitionMutator) IncomingTransition(bpctx blueprint.IncomingTransitionContext, incomingVariation string) string {
-	if m, ok := bpctx.Module().(Module); ok {
-		ctx := incomingTransitionContextPool.Get().(*incomingTransitionContextImpl)
-		defer incomingTransitionContextPool.Put(ctx)
-		*ctx = incomingTransitionContextImpl{
-			archModuleContext: m.base().archModuleContextFactory(bpctx),
-			bp:                bpctx,
-		}
-		return a.mutator.IncomingTransition(ctx, incomingVariation)
-	} else {
-		return ""
-	}
-}
-
-func (a *androidTransitionMutator) Mutate(ctx blueprint.BottomUpMutatorContext, variation string) {
-	if am, ok := ctx.Module().(Module); ok {
-		if variation != "" {
-			// TODO: this should really be checking whether the TransitionMutator affected this module, not
-			//  the empty variant, but TransitionMutator has no concept of skipping a module.
-			base := am.base()
-			base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, a.name)
-			base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variation)
-		}
-
-		mctx := bottomUpMutatorContextFactory(ctx, am, a.finalPhase)
-		defer bottomUpMutatorContextPool.Put(mctx)
-		a.mutator.Mutate(mctx, variation)
-	}
-}
-
-func (x *registerMutatorsContext) Transition(name string, m TransitionMutator) TransitionMutatorHandle {
-	atm := &androidTransitionMutator{
+func (x *registerMutatorsContext) InfoBasedTransition(name string, m androidTransitionMutator) TransitionMutatorHandle {
+	atm := &androidTransitionMutatorAdapter{
 		finalPhase: x.finalPhase,
 		mutator:    m,
 		name:       name,
@@ -597,24 +348,6 @@
 	return name
 }
 
-func (x *registerMutatorsContext) TopDown(name string, m TopDownMutator) MutatorHandle {
-	f := func(ctx blueprint.TopDownMutatorContext) {
-		if a, ok := ctx.Module().(Module); ok {
-			moduleContext := a.base().baseModuleContextFactory(ctx)
-			actx := topDownMutatorContextPool.Get().(*topDownMutatorContext)
-			defer topDownMutatorContextPool.Put(actx)
-			*actx = topDownMutatorContext{
-				bp:                ctx,
-				baseModuleContext: moduleContext,
-			}
-			m(actx)
-		}
-	}
-	mutator := &mutator{name: x.mutatorName(name), topDownMutator: f}
-	x.mutators = append(x.mutators, mutator)
-	return mutator
-}
-
 func (mutator *mutator) componentName() string {
 	return mutator.name
 }
@@ -624,8 +357,6 @@
 	var handle blueprint.MutatorHandle
 	if mutator.bottomUpMutator != nil {
 		handle = blueprintCtx.RegisterBottomUpMutator(mutator.name, mutator.bottomUpMutator)
-	} else if mutator.topDownMutator != nil {
-		handle = blueprintCtx.RegisterTopDownMutator(mutator.name, mutator.topDownMutator)
 	} else if mutator.transitionMutator != nil {
 		handle := blueprintCtx.RegisterTransitionMutator(mutator.name, mutator.transitionMutator)
 		if mutator.neverFar {
@@ -755,22 +486,22 @@
 	ctx.BottomUp("deps", depsMutator).UsesReverseDependencies()
 }
 
-// android.topDownMutatorContext either has to embed blueprint.TopDownMutatorContext, in which case every method that
+// android.bottomUpMutatorContext either has to embed blueprint.BottomUpMutatorContext, in which case every method that
 // has an overridden version in android.BaseModuleContext has to be manually forwarded to BaseModuleContext to avoid
-// ambiguous method errors, or it has to store a blueprint.TopDownMutatorContext non-embedded, in which case every
+// ambiguous method errors, or it has to store a blueprint.BottomUpMutatorContext non-embedded, in which case every
 // non-overridden method has to be forwarded.  There are fewer non-overridden methods, so use the latter.  The following
-// methods forward to the identical blueprint versions for topDownMutatorContext and bottomUpMutatorContext.
+// methods forward to the identical blueprint versions for bottomUpMutatorContext.
 
 func (b *bottomUpMutatorContext) Rename(name string) {
 	b.bp.Rename(name)
 	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 blueprint.ModuleFactory, name string, props ...interface{}) Module {
+	return bpModuleToModule(b.bp.CreateModule(factory, name, props...))
 }
 
-func (b *bottomUpMutatorContext) createModuleInDirectory(factory blueprint.ModuleFactory, name string, _ string, props ...interface{}) blueprint.Module {
+func (b *bottomUpMutatorContext) createModuleInDirectory(factory blueprint.ModuleFactory, name string, _ string, props ...interface{}) Module {
 	panic("createModuleInDirectory is not implemented for bottomUpMutatorContext")
 }
 
@@ -778,11 +509,11 @@
 	return createModule(b, factory, "_bottomUpMutatorModule", doesNotSpecifyDirectory(), props...)
 }
 
-func (b *bottomUpMutatorContext) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) []blueprint.Module {
+func (b *bottomUpMutatorContext) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) []Module {
 	if b.baseModuleContext.checkedMissingDeps() {
 		panic("Adding deps not allowed after checking for missing deps")
 	}
-	return b.bp.AddDependency(module, tag, name...)
+	return bpModulesToModules(b.bp.AddDependency(module, tag, name...))
 }
 
 func (b *bottomUpMutatorContext) AddReverseDependency(module blueprint.Module, tag blueprint.DependencyTag, name string) {
@@ -800,20 +531,20 @@
 }
 
 func (b *bottomUpMutatorContext) AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag,
-	names ...string) []blueprint.Module {
+	names ...string) []Module {
 	if b.baseModuleContext.checkedMissingDeps() {
 		panic("Adding deps not allowed after checking for missing deps")
 	}
-	return b.bp.AddVariationDependencies(variations, tag, names...)
+	return bpModulesToModules(b.bp.AddVariationDependencies(variations, tag, names...))
 }
 
 func (b *bottomUpMutatorContext) AddFarVariationDependencies(variations []blueprint.Variation,
-	tag blueprint.DependencyTag, names ...string) []blueprint.Module {
+	tag blueprint.DependencyTag, names ...string) []Module {
 	if b.baseModuleContext.checkedMissingDeps() {
 		panic("Adding deps not allowed after checking for missing deps")
 	}
 
-	return b.bp.AddFarVariationDependencies(variations, tag, names...)
+	return bpModulesToModules(b.bp.AddFarVariationDependencies(variations, tag, names...))
 }
 
 func (b *bottomUpMutatorContext) ReplaceDependencies(name string) {
@@ -829,3 +560,18 @@
 	}
 	b.bp.ReplaceDependenciesIf(name, predicate)
 }
+
+func bpModulesToModules(bpModules []blueprint.Module) []Module {
+	modules := make([]Module, len(bpModules))
+	for i, bpModule := range bpModules {
+		modules[i] = bpModuleToModule(bpModule)
+	}
+	return modules
+}
+
+func bpModuleToModule(bpModule blueprint.Module) Module {
+	if bpModule != nil {
+		return bpModule.(Module)
+	}
+	return nil
+}
diff --git a/android/mutator_test.go b/android/mutator_test.go
index 1d5f890..f7ee7d8 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -54,7 +54,7 @@
 	ctx.AddDependency(ctx.Module(), nil, m.props.Deps_missing_deps...)
 }
 
-func addMissingDependenciesMutator(ctx TopDownMutatorContext) {
+func addMissingDependenciesMutator(ctx BottomUpMutatorContext) {
 	ctx.AddMissingDependencies(ctx.Module().(*mutatorTestModule).props.Mutator_missing_deps)
 }
 
@@ -72,143 +72,17 @@
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 			ctx.RegisterModuleType("test", mutatorTestModuleFactory)
 			ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-				ctx.TopDown("add_missing_dependencies", addMissingDependenciesMutator)
+				ctx.BottomUp("add_missing_dependencies", addMissingDependenciesMutator)
 			})
 		}),
 		FixtureWithRootAndroidBp(bp),
 	).RunTest(t)
 
-	foo := result.ModuleForTests("foo", "").Module().(*mutatorTestModule)
+	foo := result.ModuleForTests(t, "foo", "").Module().(*mutatorTestModule)
 
 	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 {
-			name: "foo",
-		}
-	`
-
-	var moduleStrings []string
-
-	GroupFixturePreparers(
-		FixtureRegisterWithContext(func(ctx RegistrationContext) {
-
-			ctx.PreArchMutators(func(ctx RegisterMutatorsContext) {
-				ctx.Transition("pre_arch", &testTransitionMutator{
-					split: func(ctx BaseModuleContext) []string {
-						moduleStrings = append(moduleStrings, ctx.Module().String())
-						return []string{"a", "b"}
-					},
-				})
-			})
-
-			ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-				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.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())
-					ctx.Rename(ctx.Module().base().Name() + "_renamed1")
-				}).UsesRename()
-				ctx.BottomUp("final", func(ctx BottomUpMutatorContext) {
-					moduleStrings = append(moduleStrings, ctx.Module().String())
-				})
-			})
-
-			ctx.RegisterModuleType("test", mutatorTestModuleFactory)
-		}),
-		FixtureWithRootAndroidBp(bp),
-	).RunTest(t)
-
-	want := []string{
-		// Initial name.
-		"foo{}",
-
-		// After pre_arch (reversed because rename_top_down is TopDown so it visits in reverse order).
-		"foo{pre_arch:b}",
-		"foo{pre_arch:a}",
-
-		// After pre_deps (reversed because post_deps TransitionMutator.Split is TopDown).
-		"foo{pre_arch:b,pre_deps:d}",
-		"foo{pre_arch:b,pre_deps:c}",
-		"foo{pre_arch:a,pre_deps:d}",
-		"foo{pre_arch:a,pre_deps:c}",
-
-		// After post_deps.
-		"foo{pre_arch:a,pre_deps:c,post_deps:e}",
-		"foo{pre_arch:a,pre_deps:c,post_deps:f}",
-		"foo{pre_arch:a,pre_deps:d,post_deps:e}",
-		"foo{pre_arch:a,pre_deps:d,post_deps:f}",
-		"foo{pre_arch:b,pre_deps:c,post_deps:e}",
-		"foo{pre_arch:b,pre_deps:c,post_deps:f}",
-		"foo{pre_arch:b,pre_deps:d,post_deps:e}",
-		"foo{pre_arch:b,pre_deps:d,post_deps:f}",
-
-		// After rename_bottom_up.
-		"foo_renamed1{pre_arch:a,pre_deps:c,post_deps:e}",
-		"foo_renamed1{pre_arch:a,pre_deps:c,post_deps:f}",
-		"foo_renamed1{pre_arch:a,pre_deps:d,post_deps:e}",
-		"foo_renamed1{pre_arch:a,pre_deps:d,post_deps:f}",
-		"foo_renamed1{pre_arch:b,pre_deps:c,post_deps:e}",
-		"foo_renamed1{pre_arch:b,pre_deps:c,post_deps:f}",
-		"foo_renamed1{pre_arch:b,pre_deps:d,post_deps:e}",
-		"foo_renamed1{pre_arch:b,pre_deps:d,post_deps:f}",
-	}
-
-	AssertDeepEquals(t, "module String() values", want, moduleStrings)
-}
-
 func TestFinalDepsPhase(t *testing.T) {
 	bp := `
 		test {
@@ -288,22 +162,3 @@
 
 	AssertDeepEquals(t, "final", finalWant, finalGotMap)
 }
-
-func TestTransitionMutatorInFinalDeps(t *testing.T) {
-	GroupFixturePreparers(
-		FixtureRegisterWithContext(func(ctx RegistrationContext) {
-			ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
-				ctx.Transition("vars", &testTransitionMutator{
-					split: func(ctx BaseModuleContext) []string {
-						return []string{"a", "b"}
-					},
-				})
-			})
-
-			ctx.RegisterModuleType("test", mutatorTestModuleFactory)
-		}),
-		FixtureWithRootAndroidBp(`test {name: "foo"}`),
-	).
-		ExtendWithErrorHandler(FixtureExpectsOneErrorPattern("not allowed in FinalDepsMutators")).
-		RunTest(t)
-}
diff --git a/android/namespace.go b/android/namespace.go
index c3ee20f..d0e90b0 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -335,7 +335,7 @@
 	if isAbs {
 		// if the user gave a fully-qualified name, we don't need to look for other
 		// modules that they might have been referring to
-		return fmt.Errorf(text)
+		return fmt.Errorf("%s", text)
 	}
 
 	// determine which namespaces the module can be found in
@@ -371,7 +371,7 @@
 		text += fmt.Sprintf("\nOr did you mean %q?", guess)
 	}
 
-	return fmt.Errorf(text)
+	return fmt.Errorf("%s", text)
 }
 
 func (r *NameResolver) GetNamespace(ctx blueprint.NamespaceContext) blueprint.Namespace {
diff --git a/android/namespace_test.go b/android/namespace_test.go
index 0327e78..a183bbf 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -683,7 +683,7 @@
 }
 
 func getModule(result *TestResult, moduleName string) TestingModule {
-	return result.ModuleForTests(moduleName, "")
+	return result.ModuleForTests(result.fixture.t, moduleName, "")
 }
 
 func findModuleById(result *TestResult, id string) (module TestingModule) {
@@ -691,7 +691,7 @@
 		testModule, ok := candidate.(*testModule)
 		if ok {
 			if testModule.properties.Id == id {
-				module = newTestingModule(result.config, testModule)
+				module = newTestingModule(result.fixture.t, result.config, testModule)
 			}
 		}
 	}
diff --git a/android/neverallow.go b/android/neverallow.go
index 3136a15..98b443e 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -60,11 +60,13 @@
 	AddNeverAllowRules(createCcStubsRule())
 	AddNeverAllowRules(createProhibitHeaderOnlyRule())
 	AddNeverAllowRules(createLimitNdkExportRule()...)
-	AddNeverAllowRules(createLimitDirgroupRule()...)
+	AddNeverAllowRules(createLimitDirgroupRules()...)
+	AddNeverAllowRules(createLimitGenruleRules()...)
 	AddNeverAllowRules(createFilesystemIsAutoGeneratedRule())
 	AddNeverAllowRules(createKotlinPluginRule()...)
 	AddNeverAllowRules(createPrebuiltEtcBpDefineRule())
 	AddNeverAllowRules(createAutogenRroBpDefineRule())
+	AddNeverAllowRules(createNoSha1HashRule())
 }
 
 // Add a NeverAllow rule to the set of rules to apply.
@@ -230,7 +232,7 @@
 func createUncompressDexRules() []Rule {
 	return []Rule{
 		NeverAllow().
-			NotIn("art").
+			NotIn("art", "cts/hostsidetests/compilation").
 			WithMatcher("uncompress_dex", isSetMatcherInstance).
 			Because("uncompress_dex is only allowed for certain jars for test in art."),
 	}
@@ -287,29 +289,49 @@
 	}
 }
 
-func createLimitDirgroupRule() []Rule {
-	reason := "dirgroup module and dir_srcs / keep_gendir property of genrule is allowed only to Trusty build rule."
+func createLimitDirgroupRules() []Rule {
+	reason := "The dirgroup module can only be used with Trusty visibility"
+	scriptsDirsList := []string{"//trusty/vendor/google/aosp/scripts", "//trusty/vendor/google/proprietary/scripts"}
 	return []Rule{
 		NeverAllow().
 			ModuleType("dirgroup").
-			WithMatcher("visibility", NotInList([]string{"//trusty/vendor/google/aosp/scripts"})).Because(reason),
+			WithMatcher("visibility", NotInList(scriptsDirsList)).Because(reason),
 		NeverAllow().
 			ModuleType("dirgroup").
-			Without("visibility", "//trusty/vendor/google/aosp/scripts").Because(reason),
+			WithoutMatcher("visibility", InAllowedList(scriptsDirsList)).Because(reason),
+	}
+}
+
+func createLimitGenruleRules() []Rule {
+	dirSrcsReason := "The `dir_srcs` property in a `genrule` module can only be used by Trusty"
+	keepGendirReason := "The `keep_gendir` property in a `genrule` module can only be used by Trusty"
+	allowedModuleNameList := []string{
+		// Trusty TEE target names
+		"trusty_tee_package_goog",
+		"trusty_tee_package",
+		// Trusty vm target names
+		"trusty_desktop_vm_arm64.bin",
+		"trusty_desktop_vm_x86_64.bin",
+		"trusty_desktop_test_vm_arm64.bin",
+		"trusty_desktop_test_vm_x86_64.bin",
+		"trusty_test_vm_arm64.bin",
+		"trusty_test_vm_x86_64.elf",
+		"trusty_test_vm_os_arm64.bin",
+		"trusty_test_vm_os_x86_64.elf",
+		"trusty_security_vm_arm64.bin",
+		"trusty_security_vm_x86_64.elf",
+		"trusty_widevine_vm_arm64.bin",
+		"trusty_widevine_vm_x86_64.elf",
+	}
+	return []Rule{
 		NeverAllow().
 			ModuleType("genrule").
-			Without("name", "trusty-arm64.lk.elf.gen").
-			Without("name", "trusty-arm64-virt-test-debug.lk.elf.gen").
-			Without("name", "trusty-x86_64.lk.elf.gen").
-			Without("name", "trusty-x86_64-test.lk.elf.gen").
-			WithMatcher("dir_srcs", isSetMatcherInstance).Because(reason),
+			WithoutMatcher("name", InAllowedList(allowedModuleNameList)).
+			WithMatcher("dir_srcs", isSetMatcherInstance).Because(dirSrcsReason),
 		NeverAllow().
 			ModuleType("genrule").
-			Without("name", "trusty-arm64.lk.elf.gen").
-			Without("name", "trusty-arm64-virt-test-debug.lk.elf.gen").
-			Without("name", "trusty-x86_64.lk.elf.gen").
-			Without("name", "trusty-x86_64-test.lk.elf.gen").
-			With("keep_gendir", "true").Because(reason),
+			WithoutMatcher("name", InAllowedList(allowedModuleNameList)).
+			With("keep_gendir", "true").Because(keepGendirReason),
 	}
 }
 
@@ -321,11 +343,16 @@
 		Because("is_auto_generated property is only allowed for filesystem modules in build/soong/fsgen directory")
 }
 
+func createNoSha1HashRule() Rule {
+	return NeverAllow().
+		ModuleType("filesystem", "android_filesystem").
+		ModuleType("filesystem", "android_system_image").
+		With("avb_hash_algorithm", "sha1").
+		Because("sha1 is discouraged")
+}
+
 func createKotlinPluginRule() []Rule {
 	kotlinPluginProjectsAllowedList := []string{
-		// TODO: Migrate compose plugin to the bundled compiler plugin
-		// Actual path prebuilts/sdk/current/androidx/m2repository/androidx/compose/compiler/compiler-hosted
-		"prebuilts/sdk/current/androidx",
 		"external/kotlinc",
 	}
 
@@ -358,6 +385,10 @@
 			"prebuilt_sbin",
 			"prebuilt_system",
 			"prebuilt_first_stage_ramdisk",
+			"prebuilt_radio",
+			"prebuilt_gpu",
+			"prebuilt_vendor_overlay",
+			"prebuilt_tee",
 		).
 		DefinedInBpFile().
 		Because("module type not allowed to be defined in bp file")
@@ -389,7 +420,8 @@
 			continue
 		}
 
-		if !n.appliesToModuleType(ctx.ModuleType()) {
+		modType := proptools.StringDefault(m.base().baseProperties.Soong_config_base_module_type, ctx.ModuleType())
+		if !n.appliesToModuleType(modType) {
 			continue
 		}
 
@@ -479,6 +511,18 @@
 	return ".not-in-list(" + strings.Join(m.allowed, ",") + ")"
 }
 
+type InListMatcher struct {
+	allowed []string
+}
+
+func (m *InListMatcher) Test(value string) bool {
+	return InList(value, m.allowed)
+}
+
+func (m *InListMatcher) String() string {
+	return ".in-list(" + strings.Join(m.allowed, ",") + ")"
+}
+
 type isSetMatcher struct{}
 
 func (m *isSetMatcher) Test(value string) bool {
@@ -757,6 +801,10 @@
 	return &notInListMatcher{allowed}
 }
 
+func InAllowedList(allowed []string) ValueMatcher {
+	return &InListMatcher{allowed}
+}
+
 // assorted utils
 
 func cleanPaths(paths []string) []string {
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index c74d5ff..3ccc883 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -388,6 +388,30 @@
 			`module type not allowed to be defined in bp file`,
 		},
 	},
+	// Test the a neverallowed module type can't be smuggled through a soong config module type
+	{
+		name: `smuggling module types through soong config modules`,
+		fs: map[string][]byte{
+			"a/b/Android.bp": []byte(`
+				soong_config_bool_variable {
+					name: "my_var",
+				}
+				soong_config_module_type {
+					name: "smuggled_prebuilt_usr_srec",
+					module_type: "prebuilt_usr_srec",
+					config_namespace: "ANDROID",
+					variables: ["my_var"],
+					properties: ["enabled"],
+				}
+				smuggled_prebuilt_usr_srec {
+					name: "foo",
+				}
+			`),
+		},
+		expectedErrors: []string{
+			`module type not allowed to be defined in bp file`,
+		},
+	},
 }
 
 var prepareForNeverAllowTest = GroupFixturePreparers(
@@ -399,6 +423,7 @@
 		ctx.RegisterModuleType("filesystem", newMockFilesystemModule)
 		ctx.RegisterModuleType("prebuilt_usr_srec", newMockPrebuiltUsrSrecModule)
 	}),
+	PrepareForTestWithSoongConfigModuleBuildComponents,
 )
 
 func TestNeverallow(t *testing.T) {
diff --git a/android/nothing.go b/android/nothing.go
new file mode 100644
index 0000000..18bf85b
--- /dev/null
+++ b/android/nothing.go
@@ -0,0 +1,34 @@
+// Copyright 2025 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() {
+	RegisterParallelSingletonType("nothing_singleton", nothingSingletonFactory)
+}
+
+func nothingSingletonFactory() Singleton {
+	return &nothingSingleton{}
+}
+
+type nothingSingleton struct{}
+
+func (s *nothingSingleton) GenerateBuildActions(ctx SingletonContext) {
+	rule := NewRuleBuilder(pctx, ctx)
+	rule.SetPhonyOutput()
+	rule.Command().
+		Text("echo Successfully read the makefiles.").
+		ImplicitOutput(PathForPhony(ctx, "nothing"))
+	rule.Build("nothing", "nothing")
+}
diff --git a/android/notices.go b/android/notices.go
index 3c41d92..dc2290c 100644
--- a/android/notices.go
+++ b/android/notices.go
@@ -18,9 +18,11 @@
 	"fmt"
 	"path/filepath"
 	"strings"
+
+	"github.com/google/blueprint"
 )
 
-func modulesOutputDirs(ctx BuilderContext, modules ...Module) []string {
+func modulesOutputDirs(ctx BuilderContext, modules ...ModuleProxy) []string {
 	dirs := make([]string, 0, len(modules))
 	for _, module := range modules {
 		paths, err := outputFilesForModule(ctx, module, "")
@@ -41,12 +43,12 @@
 	OtherModuleProviderContext
 }
 
-func modulesLicenseMetadata(ctx OtherModuleProviderContext, modules ...Module) Paths {
+func modulesLicenseMetadata(ctx OtherModuleProviderContext, modules ...ModuleProxy) Paths {
 	result := make(Paths, 0, len(modules))
 	mctx, isMctx := ctx.(ModuleContext)
 	for _, module := range modules {
 		var mf Path
-		if isMctx && mctx.Module() == module {
+		if isMctx && EqualModules(mctx.Module(), module) {
 			mf = mctx.LicenseMetadataFile()
 		} else {
 			mf = OtherModuleProviderOrDefault(ctx, module, InstallFilesProvider).LicenseMetadataFile
@@ -61,12 +63,12 @@
 // buildNoticeOutputFromLicenseMetadata writes out a notice file.
 func buildNoticeOutputFromLicenseMetadata(
 	ctx BuilderAndOtherModuleProviderContext, tool, ruleName string, outputFile WritablePath,
-	libraryName string, stripPrefix []string, modules ...Module) {
+	libraryName string, stripPrefix []string, modules ...ModuleProxy) {
 	depsFile := outputFile.ReplaceExtension(ctx, strings.TrimPrefix(outputFile.Ext()+".d", "."))
 	rule := NewRuleBuilder(pctx, ctx)
 	if len(modules) == 0 {
 		if mctx, ok := ctx.(ModuleContext); ok {
-			modules = []Module{mctx.Module()}
+			modules = []ModuleProxy{{blueprint.CreateModuleProxy(mctx.Module())}}
 		} else {
 			panic(fmt.Errorf("%s %q needs a module to generate the notice for", ruleName, libraryName))
 		}
@@ -97,7 +99,7 @@
 // current context module if none given.
 func BuildNoticeTextOutputFromLicenseMetadata(
 	ctx BuilderAndOtherModuleProviderContext, outputFile WritablePath, ruleName, libraryName string,
-	stripPrefix []string, modules ...Module) {
+	stripPrefix []string, modules ...ModuleProxy) {
 	buildNoticeOutputFromLicenseMetadata(ctx, "textnotice", "text_notice_"+ruleName,
 		outputFile, libraryName, stripPrefix, modules...)
 }
@@ -107,7 +109,7 @@
 // current context module if none given.
 func BuildNoticeHtmlOutputFromLicenseMetadata(
 	ctx BuilderAndOtherModuleProviderContext, outputFile WritablePath, ruleName, libraryName string,
-	stripPrefix []string, modules ...Module) {
+	stripPrefix []string, modules ...ModuleProxy) {
 	buildNoticeOutputFromLicenseMetadata(ctx, "htmlnotice", "html_notice_"+ruleName,
 		outputFile, libraryName, stripPrefix, modules...)
 }
@@ -117,7 +119,7 @@
 // current context module if none given.
 func BuildNoticeXmlOutputFromLicenseMetadata(
 	ctx BuilderAndOtherModuleProviderContext, outputFile WritablePath, ruleName, libraryName string,
-	stripPrefix []string, modules ...Module) {
+	stripPrefix []string, modules ...ModuleProxy) {
 	buildNoticeOutputFromLicenseMetadata(ctx, "xmlnotice", "xml_notice_"+ruleName,
 		outputFile, libraryName, stripPrefix, modules...)
 }
diff --git a/android/otatools_package_cert_zip.go b/android/otatools_package_cert_zip.go
new file mode 100644
index 0000000..3a654cf
--- /dev/null
+++ b/android/otatools_package_cert_zip.go
@@ -0,0 +1,62 @@
+// Copyright 2025 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
+
+import (
+	"github.com/google/blueprint"
+)
+
+func init() {
+	RegisterOtatoolsPackageBuildComponents(InitRegistrationContext)
+	pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
+}
+
+func RegisterOtatoolsPackageBuildComponents(ctx RegistrationContext) {
+	ctx.RegisterModuleType("otatools_package_cert_files", OtatoolsPackageFactory)
+}
+
+type OtatoolsPackage struct {
+	ModuleBase
+}
+
+func OtatoolsPackageFactory() Module {
+	module := &OtatoolsPackage{}
+	InitAndroidModule(module)
+	return module
+}
+
+var (
+	otatoolsPackageCertRule = pctx.AndroidStaticRule("otatools_package_cert_files", blueprint.RuleParams{
+		Command:     "echo '$out : ' $$(cat $in) > ${out}.d && ${SoongZipCmd} -o $out -l $in",
+		CommandDeps: []string{"${SoongZipCmd}"},
+		Depfile:     "${out}.d",
+		Description: "Zip otatools-package cert files",
+	})
+)
+
+func (fg *OtatoolsPackage) GenerateAndroidBuildActions(ctx ModuleContext) {
+	if ctx.ModuleDir() != "build/make/tools/otatools_package" {
+		ctx.ModuleErrorf("There can only be one otatools_package_cert_files module in build/make/tools/otatools_package")
+		return
+	}
+	fileListFile := PathForArbitraryOutput(ctx, ".module_paths", "OtaToolsCertFiles.list")
+	otatoolsPackageCertZip := PathForModuleOut(ctx, "otatools_package_cert_files.zip")
+	ctx.Build(pctx, BuildParams{
+		Rule:   otatoolsPackageCertRule,
+		Input:  fileListFile,
+		Output: otatoolsPackageCertZip,
+	})
+	ctx.SetOutputFiles([]Path{otatoolsPackageCertZip}, "")
+}
diff --git a/android/override_module.go b/android/override_module.go
index 50ddc9b..96620ef 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -367,6 +367,7 @@
 }
 
 func overridableModuleDepsMutator(ctx BottomUpMutatorContext) {
+	ctx.Module().base().baseOverridablePropertiesDepsMutator(ctx)
 	if b, ok := ctx.Module().(OverridableModule); ok && b.Enabled(ctx) {
 		b.OverridablePropertiesDepsMutator(ctx)
 	}
diff --git a/android/package.go b/android/package.go
index eb76751..52bddf9 100644
--- a/android/package.go
+++ b/android/package.go
@@ -38,6 +38,12 @@
 	Default_team                *string `android:"path"`
 }
 
+type PackageInfo struct {
+	Properties packageProperties
+}
+
+var PackageInfoProvider = blueprint.NewProvider[PackageInfo]()
+
 type packageModule struct {
 	ModuleBase
 
@@ -56,7 +62,14 @@
 }
 
 func (p *packageModule) GenerateBuildActions(ctx blueprint.ModuleContext) {
-	// Nothing to do.
+	ctx.SetProvider(CommonModuleInfoProvider, &CommonModuleInfo{
+		Enabled:                 true,
+		PrimaryLicensesProperty: p.primaryLicensesProperty,
+	})
+
+	ctx.SetProvider(PackageInfoProvider, PackageInfo{
+		Properties: p.properties,
+	})
 }
 
 func (p *packageModule) qualifiedModuleId(ctx BaseModuleContext) qualifiedModuleName {
diff --git a/android/packaging.go b/android/packaging.go
index d96cccd..bf18409 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -22,6 +22,7 @@
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/gobtools"
 	"github.com/google/blueprint/proptools"
+	"github.com/google/blueprint/uniquelist"
 )
 
 // PackagingSpec abstracts a request to place a built artifact at a certain path in a package. A
@@ -42,7 +43,7 @@
 	// Whether relPathInPackage should be marked as executable or not
 	executable bool
 
-	effectiveLicenseFiles *Paths
+	effectiveLicenseFiles uniquelist.UniqueList[Path]
 
 	partition string
 
@@ -52,16 +53,28 @@
 	skipInstall bool
 
 	// Paths of aconfig files for the built artifact
-	aconfigPaths *Paths
+	aconfigPaths uniquelist.UniqueList[Path]
 
 	// ArchType of the module which produced this packaging spec
 	archType ArchType
 
 	// List of module names that this packaging spec overrides
-	overrides *[]string
+	overrides uniquelist.UniqueList[string]
 
 	// Name of the module where this packaging spec is output of
 	owner string
+
+	// If the ninja rule creating the FullInstallPath has already been emitted or not. Do not use,
+	// for the soong-only migration.
+	requiresFullInstall bool
+
+	// The path to the installed file in out/target/product. This is for legacy purposes, with
+	// tools that want to interact with these files outside of the build. You should not use it
+	// inside of the build. Will be nil if this module doesn't require a "full install".
+	fullInstallPath InstallPath
+
+	// String representation of the variation of the module where this packaging spec is output of
+	variation string
 }
 
 type packagingSpecGob struct {
@@ -69,13 +82,24 @@
 	SrcPath               Path
 	SymlinkTarget         string
 	Executable            bool
-	EffectiveLicenseFiles *Paths
+	EffectiveLicenseFiles Paths
 	Partition             string
 	SkipInstall           bool
-	AconfigPaths          *Paths
+	AconfigPaths          Paths
 	ArchType              ArchType
-	Overrides             *[]string
+	Overrides             []string
 	Owner                 string
+	RequiresFullInstall   bool
+	FullInstallPath       InstallPath
+	Variation             string
+}
+
+func (p *PackagingSpec) Owner() string {
+	return p.owner
+}
+
+func (p *PackagingSpec) Variation() string {
+	return p.variation
 }
 
 func (p *PackagingSpec) ToGob() *packagingSpecGob {
@@ -84,13 +108,16 @@
 		SrcPath:               p.srcPath,
 		SymlinkTarget:         p.symlinkTarget,
 		Executable:            p.executable,
-		EffectiveLicenseFiles: p.effectiveLicenseFiles,
+		EffectiveLicenseFiles: p.effectiveLicenseFiles.ToSlice(),
 		Partition:             p.partition,
 		SkipInstall:           p.skipInstall,
-		AconfigPaths:          p.aconfigPaths,
+		AconfigPaths:          p.aconfigPaths.ToSlice(),
 		ArchType:              p.archType,
-		Overrides:             p.overrides,
+		Overrides:             p.overrides.ToSlice(),
 		Owner:                 p.owner,
+		RequiresFullInstall:   p.requiresFullInstall,
+		FullInstallPath:       p.fullInstallPath,
+		Variation:             p.variation,
 	}
 }
 
@@ -99,13 +126,16 @@
 	p.srcPath = data.SrcPath
 	p.symlinkTarget = data.SymlinkTarget
 	p.executable = data.Executable
-	p.effectiveLicenseFiles = data.EffectiveLicenseFiles
+	p.effectiveLicenseFiles = uniquelist.Make(data.EffectiveLicenseFiles)
 	p.partition = data.Partition
 	p.skipInstall = data.SkipInstall
-	p.aconfigPaths = data.AconfigPaths
+	p.aconfigPaths = uniquelist.Make(data.AconfigPaths)
 	p.archType = data.ArchType
-	p.overrides = data.Overrides
+	p.overrides = uniquelist.Make(data.Overrides)
 	p.owner = data.Owner
+	p.requiresFullInstall = data.RequiresFullInstall
+	p.fullInstallPath = data.FullInstallPath
+	p.variation = data.Variation
 }
 
 func (p *PackagingSpec) GobEncode() ([]byte, error) {
@@ -154,10 +184,7 @@
 }
 
 func (p *PackagingSpec) EffectiveLicenseFiles() Paths {
-	if p.effectiveLicenseFiles == nil {
-		return Paths{}
-	}
-	return *p.effectiveLicenseFiles
+	return p.effectiveLicenseFiles.ToSlice()
 }
 
 func (p *PackagingSpec) Partition() string {
@@ -174,7 +201,30 @@
 
 // Paths of aconfig files for the built artifact
 func (p *PackagingSpec) GetAconfigPaths() Paths {
-	return *p.aconfigPaths
+	return p.aconfigPaths.ToSlice()
+}
+
+// The path to the installed file in out/target/product. This is for legacy purposes, with
+// tools that want to interact with these files outside of the build. You should not use it
+// inside of the build. Will be nil if this module doesn't require a "full install".
+func (p *PackagingSpec) FullInstallPath() InstallPath {
+	return p.fullInstallPath
+}
+
+// If the ninja rule creating the FullInstallPath has already been emitted or not. Do not use,
+// for the soong-only migration.
+func (p *PackagingSpec) RequiresFullInstall() bool {
+	return p.requiresFullInstall
+}
+
+// The source file to be copied to the FullInstallPath. Do not use, for the soong-only migration.
+func (p *PackagingSpec) SrcPath() Path {
+	return p.srcPath
+}
+
+// The symlink target of the PackagingSpec. Do not use, for the soong-only migration.
+func (p *PackagingSpec) SymlinkTarget() string {
+	return p.symlinkTarget
 }
 
 type PackageModule interface {
@@ -456,13 +506,11 @@
 	// all packaging specs gathered from the high priority deps.
 	var highPriorities []PackagingSpec
 
-	// Name of the dependency which requested the packaging spec.
-	// If this dep is overridden, the packaging spec will not be installed via this dependency chain.
-	// (the packaging spec might still be installed if there are some other deps which depend on it).
-	var depNames []string
-
 	// list of module names overridden
-	var overridden []string
+	overridden := make(map[string]bool)
+
+	// all installed modules which are not overridden.
+	modulesToInstall := make(map[string]bool)
 
 	var arches []ArchType
 	for _, target := range getSupportedTargets(ctx) {
@@ -479,6 +527,7 @@
 		return false
 	}
 
+	// find all overridden modules and packaging specs
 	ctx.VisitDirectDepsProxy(func(child ModuleProxy) {
 		depTag := ctx.OtherModuleDependencyTag(child)
 		if pi, ok := depTag.(PackagingItem); !ok || !pi.IsPackagingItem() {
@@ -506,22 +555,32 @@
 				regularPriorities = append(regularPriorities, ps)
 			}
 
-			depNames = append(depNames, child.Name())
-			if ps.overrides != nil {
-				overridden = append(overridden, *ps.overrides...)
+			for o := range ps.overrides.Iter() {
+				overridden[o] = true
 			}
 		}
 	})
 
-	filterOverridden := func(input []PackagingSpec) []PackagingSpec {
-		// input minus packaging specs that are overridden
-		var filtered []PackagingSpec
-		for index, ps := range input {
-			if ps.owner != "" && InList(ps.owner, overridden) {
-				continue
+	// gather modules to install, skipping overridden modules
+	ctx.WalkDeps(func(child, parent Module) bool {
+		owner := ctx.OtherModuleName(child)
+		if o, ok := child.(OverridableModule); ok {
+			if overriddenBy := o.GetOverriddenBy(); overriddenBy != "" {
+				owner = overriddenBy
 			}
-			// The dependency which requested this packaging spec has been overridden.
-			if InList(depNames[index], overridden) {
+		}
+		if overridden[owner] {
+			return false
+		}
+		modulesToInstall[owner] = true
+		return true
+	})
+
+	filterOverridden := func(input []PackagingSpec) []PackagingSpec {
+		// input minus packaging specs that are not installed
+		var filtered []PackagingSpec
+		for _, ps := range input {
+			if !modulesToInstall[ps.owner] {
 				continue
 			}
 			filtered = append(filtered, ps)
@@ -575,12 +634,12 @@
 func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir WritablePath) (entries []string) {
 	dirsToSpecs := make(map[WritablePath]map[string]PackagingSpec)
 	dirsToSpecs[dir] = specs
-	return p.CopySpecsToDirs(ctx, builder, dirsToSpecs)
+	return p.CopySpecsToDirs(ctx, builder, dirsToSpecs, false)
 }
 
 // CopySpecsToDirs is a helper that will add commands to the rule builder to copy the PackagingSpec
 // entries into corresponding directories.
-func (p *PackagingBase) CopySpecsToDirs(ctx ModuleContext, builder *RuleBuilder, dirsToSpecs map[WritablePath]map[string]PackagingSpec) (entries []string) {
+func (p *PackagingBase) CopySpecsToDirs(ctx ModuleContext, builder *RuleBuilder, dirsToSpecs map[WritablePath]map[string]PackagingSpec, preserveTimestamps bool) (entries []string) {
 	empty := true
 	for _, specs := range dirsToSpecs {
 		if len(specs) > 0 {
@@ -614,7 +673,11 @@
 				builder.Command().Textf("mkdir -p %s", destDir)
 			}
 			if ps.symlinkTarget == "" {
-				builder.Command().Text("cp").Input(ps.srcPath).Text(destPath)
+				cmd := builder.Command().Text("cp")
+				if preserveTimestamps {
+					cmd.Flag("-p")
+				}
+				cmd.Input(ps.srcPath).Text(destPath)
 			} else {
 				builder.Command().Textf("ln -sf %s %s", ps.symlinkTarget, destPath)
 			}
diff --git a/android/path_properties.go b/android/path_properties.go
index 55a4dc0..d769d58 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -54,12 +54,14 @@
 	var pathDeviceFirstPrefer32Properties []string
 	var pathDeviceCommonProperties []string
 	var pathCommonOsProperties []string
+	var pathHostCommonProperties []string
 	for _, ps := range props {
 		pathProperties = append(pathProperties, taggedPropertiesForPropertyStruct(ctx, ps, "path")...)
 		pathDeviceFirstProperties = append(pathDeviceFirstProperties, taggedPropertiesForPropertyStruct(ctx, ps, "path_device_first")...)
 		pathDeviceFirstPrefer32Properties = append(pathDeviceFirstPrefer32Properties, taggedPropertiesForPropertyStruct(ctx, ps, "path_device_first_prefer32")...)
 		pathDeviceCommonProperties = append(pathDeviceCommonProperties, taggedPropertiesForPropertyStruct(ctx, ps, "path_device_common")...)
 		pathCommonOsProperties = append(pathCommonOsProperties, taggedPropertiesForPropertyStruct(ctx, ps, "path_common_os")...)
+		pathHostCommonProperties = append(pathHostCommonProperties, taggedPropertiesForPropertyStruct(ctx, ps, "path_host_common")...)
 	}
 
 	// Remove duplicates to avoid multiple dependencies.
@@ -68,6 +70,7 @@
 	pathDeviceFirstPrefer32Properties = FirstUniqueStrings(pathDeviceFirstPrefer32Properties)
 	pathDeviceCommonProperties = FirstUniqueStrings(pathDeviceCommonProperties)
 	pathCommonOsProperties = FirstUniqueStrings(pathCommonOsProperties)
+	pathHostCommonProperties = FirstUniqueStrings(pathHostCommonProperties)
 
 	// Add dependencies to anything that is a module reference.
 	for _, s := range pathProperties {
@@ -108,6 +111,12 @@
 			ctx.AddVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), sourceOrOutputDepTag(m, t), m)
 		}
 	}
+	// properties tagged "path_host_common" get the host common variant
+	for _, s := range pathHostCommonProperties {
+		if m, t := SrcIsModuleWithTag(s); m != "" {
+			ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), sourceOrOutputDepTag(m, t), m)
+		}
+	}
 	// properties tagged "path_common_os" get the CommonOs variant
 	for _, s := range pathCommonOsProperties {
 		if m, t := SrcIsModuleWithTag(s); m != "" {
diff --git a/android/paths.go b/android/paths.go
index bbf10d1..a254717 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -209,6 +209,10 @@
 
 var _ ModuleErrorfContext = blueprint.ModuleContext(nil)
 
+type AddMissingDependenciesContext interface {
+	AddMissingDependencies([]string)
+}
+
 // reportPathError will register an error with the attached context. It
 // attempts ctx.ModuleErrorf for a better error message first, then falls
 // back to ctx.Errorf.
@@ -220,7 +224,9 @@
 // attempts ctx.ModuleErrorf for a better error message first, then falls
 // back to ctx.Errorf.
 func ReportPathErrorf(ctx PathContext, format string, args ...interface{}) {
-	if mctx, ok := ctx.(ModuleErrorfContext); ok {
+	if mctx, ok := ctx.(AddMissingDependenciesContext); ok && ctx.Config().AllowMissingDependencies() {
+		mctx.AddMissingDependencies([]string{fmt.Sprintf(format, args...)})
+	} else if mctx, ok := ctx.(ModuleErrorfContext); ok {
 		mctx.ModuleErrorf(format, args...)
 	} else if ectx, ok := ctx.(errorfContext); ok {
 		ectx.Errorf(format, args...)
@@ -229,6 +235,8 @@
 	}
 }
 
+// TODO(b/397766191): Change the signature to take ModuleProxy
+// Please only access the module's internal data through providers.
 func pathContextName(ctx PathContext, module blueprint.Module) string {
 	if x, ok := ctx.(interface{ ModuleName(blueprint.Module) string }); ok {
 		return x.ModuleName(module)
@@ -675,7 +683,7 @@
 	if module == nil {
 		return nil, missingDependencyError{[]string{moduleName}}
 	}
-	if !OtherModuleProviderOrDefault(ctx, *module, CommonModuleInfoKey).Enabled {
+	if !OtherModulePointerProviderOrDefault(ctx, *module, CommonModuleInfoProvider).Enabled {
 		return nil, missingDependencyError{[]string{moduleName}}
 	}
 
@@ -1378,7 +1386,7 @@
 
 // PathForArbitraryOutput creates a path for the given components. Unlike PathForOutput,
 // the path is relative to the root of the output folder, not the out/soong folder.
-func PathForArbitraryOutput(ctx PathContext, pathComponents ...string) Path {
+func PathForArbitraryOutput(ctx PathContext, pathComponents ...string) WritablePath {
 	path, err := validatePath(pathComponents...)
 	if err != nil {
 		reportPathError(ctx, err)
@@ -1487,33 +1495,6 @@
 	return p.withRel(path)
 }
 
-// OverlayPath returns the overlay for `path' if it exists. This assumes that the
-// SourcePath is the path to a resource overlay directory.
-func (p SourcePath) OverlayPath(ctx ModuleMissingDepsPathContext, path Path) OptionalPath {
-	var relDir string
-	if srcPath, ok := path.(SourcePath); ok {
-		relDir = srcPath.path
-	} else {
-		ReportPathErrorf(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
-		// No need to put the error message into the returned path since it has been reported already.
-		return OptionalPath{}
-	}
-	dir := filepath.Join(p.path, relDir)
-	// Use Glob so that we are run again if the directory is added.
-	if pathtools.IsGlob(dir) {
-		ReportPathErrorf(ctx, "Path may not contain a glob: %s", dir)
-	}
-	paths, err := ctx.GlobWithDeps(dir, nil)
-	if err != nil {
-		ReportPathErrorf(ctx, "glob: %s", err.Error())
-		return OptionalPath{}
-	}
-	if len(paths) == 0 {
-		return InvalidOptionalPath(dir + " does not exist")
-	}
-	return OptionalPathForPath(PathForSource(ctx, paths[0]))
-}
-
 // OutputPath is a Path representing an intermediates file path rooted from the build directory
 type OutputPath struct {
 	basePath
@@ -2123,7 +2104,7 @@
 	var partitionPaths []string
 
 	if os.Class == Device {
-		partitionPaths = []string{"target", "product", ctx.Config().DeviceName(), partition}
+		partitionPaths = []string{"target", "product", *ctx.Config().deviceNameToInstall, partition}
 	} else {
 		osName := os.String()
 		if os == Linux {
@@ -2153,7 +2134,7 @@
 		reportPathError(ctx, err)
 	}
 
-	base := pathForPartitionInstallDir(ctx, partition, partitionPath, ctx.Config().KatiEnabled())
+	base := pathForPartitionInstallDir(ctx, partition, partitionPath, true)
 	return base.Join(ctx, pathComponents...)
 }
 
diff --git a/android/paths_test.go b/android/paths_test.go
index 5e618f9..b125c4e 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -1269,7 +1269,7 @@
 				ExtendWithErrorHandler(errorHandler).
 				RunTest(t)
 
-			m := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule)
+			m := result.ModuleForTests(t, "foo", "").Module().(*pathForModuleSrcTestModule)
 
 			AssertStringPathsRelativeToTopEquals(t, "srcs", result.Config, test.srcs, m.srcs)
 			AssertStringPathsRelativeToTopEquals(t, "rels", result.Config, test.rels, m.rels)
@@ -1533,13 +1533,13 @@
 		FixtureWithRootAndroidBp(bp),
 	).RunTest(t)
 
-	foo := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule)
+	foo := result.ModuleForTests(t, "foo", "").Module().(*pathForModuleSrcTestModule)
 
 	AssertArrayString(t, "foo missing deps", []string{"a", "b", "c"}, foo.missingDeps)
 	AssertArrayString(t, "foo srcs", []string{}, foo.srcs)
 	AssertStringEquals(t, "foo src", "", foo.src)
 
-	bar := result.ModuleForTests("bar", "").Module().(*pathForModuleSrcTestModule)
+	bar := result.ModuleForTests(t, "bar", "").Module().(*pathForModuleSrcTestModule)
 
 	AssertArrayString(t, "bar missing deps", []string{"d", "e"}, bar.missingDeps)
 	AssertArrayString(t, "bar srcs", []string{}, bar.srcs)
@@ -1561,7 +1561,7 @@
 
 	t.Run("install for soong", func(t *testing.T) {
 		p := PathForModuleInstall(ctx, "install/path")
-		AssertPathRelativeToTopEquals(t, "install path for soong", "out/soong/target/product/test_device/system/install/path", p)
+		AssertPathRelativeToTopEquals(t, "install path for soong", "out/target/product/test_device/system/install/path", p)
 	})
 	t.Run("install for make", func(t *testing.T) {
 		p := PathForModuleInstall(ctx, "install/path")
@@ -1584,7 +1584,7 @@
 		}
 
 		expected := []string{
-			"out/soong/target/product/test_device/system/install/path",
+			"out/target/product/test_device/system/install/path",
 			"out/soong/output/path",
 			"source/path",
 		}
diff --git a/android/phony.go b/android/phony.go
index f8db88d..99ff0aa 100644
--- a/android/phony.go
+++ b/android/phony.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"strings"
 	"sync"
 
 	"github.com/google/blueprint"
@@ -54,7 +55,7 @@
 
 func (p *phonySingleton) GenerateBuildActions(ctx SingletonContext) {
 	p.phonyMap = getSingletonPhonyMap(ctx.Config())
-	ctx.VisitAllModules(func(m Module) {
+	ctx.VisitAllModuleProxies(func(m ModuleProxy) {
 		if info, ok := OtherModuleProvider(ctx, m, ModulePhonyProvider); ok {
 			for k, v := range info.Phonies {
 				p.phonyMap[k] = append(p.phonyMap[k], v...)
@@ -68,13 +69,25 @@
 	}
 
 	if !ctx.Config().KatiEnabled() {
+		// In soong-only builds, the phonies can conflict with dist targets that will
+		// be generated in the packaging step. Instead of emitting a blueprint/ninja phony directly,
+		// create a makefile that defines the phonies that will be included in the packaging step.
+		// Make will dedup the phonies there.
+		var buildPhonyFileContents strings.Builder
 		for _, phony := range p.phonyList {
-			ctx.Build(pctx, BuildParams{
-				Rule:      blueprint.Phony,
-				Outputs:   []WritablePath{PathForPhony(ctx, phony)},
-				Implicits: p.phonyMap[phony],
-			})
+			buildPhonyFileContents.WriteString(".PHONY: ")
+			buildPhonyFileContents.WriteString(phony)
+			buildPhonyFileContents.WriteString("\n")
+			buildPhonyFileContents.WriteString(phony)
+			buildPhonyFileContents.WriteString(":")
+			for _, dep := range p.phonyMap[phony] {
+				buildPhonyFileContents.WriteString(" ")
+				buildPhonyFileContents.WriteString(dep.String())
+			}
+			buildPhonyFileContents.WriteString("\n")
 		}
+		buildPhonyFile := PathForOutput(ctx, "soong_phony_targets.mk")
+		writeValueIfChanged(ctx, absolutePath(buildPhonyFile.String()), buildPhonyFileContents.String())
 	}
 }
 
diff --git a/android/plugin.go b/android/plugin.go
index d62fc94..4348f14 100644
--- a/android/plugin.go
+++ b/android/plugin.go
@@ -135,6 +135,6 @@
 		disallowedPlugins[name] = true
 	})
 	if len(disallowedPlugins) > 0 {
-		ctx.Errorf("New plugins are not supported; however %q were found. Please reach out to the build team or use BUILD_BROKEN_PLUGIN_VALIDATION (see Changes.md for more info).", SortedStringKeys(disallowedPlugins))
+		ctx.Errorf("New plugins are not supported; however %q were found. Please reach out to the build team or use BUILD_BROKEN_PLUGIN_VALIDATION (see Changes.md for more info).", SortedKeys(disallowedPlugins))
 	}
 }
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 0ac67b3..1ff009b 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -28,6 +28,7 @@
 
 func RegisterPrebuiltMutators(ctx RegistrationContext) {
 	ctx.PreArchMutators(RegisterPrebuiltsPreArchMutators)
+	ctx.PreDepsMutators(RegisterPrebuiltsPreDepsMutators)
 	ctx.PostDepsMutators(RegisterPrebuiltsPostDepsMutators)
 }
 
@@ -195,6 +196,10 @@
 	return p.properties.UsePrebuilt
 }
 
+func (p *Prebuilt) SetUsePrebuilt(use bool) {
+	p.properties.UsePrebuilt = use
+}
+
 // Called to provide the srcs value for the prebuilt module.
 //
 // This can be called with a context for any module not just the prebuilt one itself. It can also be
@@ -352,6 +357,17 @@
 	return true
 }
 
+func IsModulePreferredProxy(ctx OtherModuleProviderContext, module ModuleProxy) bool {
+	if OtherModulePointerProviderOrDefault(ctx, module, CommonModuleInfoProvider).ReplacedByPrebuilt {
+		// A source module that has been replaced by a prebuilt counterpart.
+		return false
+	}
+	if p, ok := OtherModuleProvider(ctx, module, PrebuiltModuleInfoProvider); ok {
+		return p.UsePrebuilt
+	}
+	return true
+}
+
 // IsModulePrebuilt returns true if the module implements PrebuiltInterface and
 // has been initialized as a prebuilt and so returns a non-nil value from the
 // PrebuiltInterface.Prebuilt() method.
@@ -381,10 +397,10 @@
 // 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 !OtherModuleProviderOrDefault(ctx, module, CommonModuleInfoKey).ReplacedByPrebuilt {
+	if !OtherModulePointerProviderOrDefault(ctx, module, CommonModuleInfoProvider).ReplacedByPrebuilt {
 		return module
 	}
-	if _, ok := OtherModuleProvider(ctx, module, PrebuiltModuleProviderKey); ok {
+	if _, ok := OtherModuleProvider(ctx, module, PrebuiltModuleInfoProvider); ok {
 		// If we're given a prebuilt then assume there's no source module around.
 		return module
 	}
@@ -396,7 +412,7 @@
 		if prebuiltMod != nil {
 			return false
 		}
-		if ctx.EqualModules(parent, ctx.Module()) {
+		if EqualModules(parent, ctx.Module()) {
 			// First level: Only recurse if the module is found as a direct dependency.
 			sourceModDepFound = child == module
 			return sourceModDepFound
@@ -422,9 +438,12 @@
 	ctx.BottomUp("prebuilt_rename", PrebuiltRenameMutator).UsesRename()
 }
 
-func RegisterPrebuiltsPostDepsMutators(ctx RegisterMutatorsContext) {
+func RegisterPrebuiltsPreDepsMutators(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("prebuilt_source", PrebuiltSourceDepsMutator).UsesReverseDependencies()
 	ctx.BottomUp("prebuilt_select", PrebuiltSelectModuleMutator)
+}
+
+func RegisterPrebuiltsPostDepsMutators(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("prebuilt_postdeps", PrebuiltPostDepsMutator).UsesReplaceDependencies()
 }
 
@@ -468,7 +487,7 @@
 			bmn, _ := m.(baseModuleName)
 			name := bmn.BaseModuleName()
 			if ctx.OtherModuleReverseDependencyVariantExists(name) {
-				ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name)
+				ctx.AddReverseVariationDependency(nil, PrebuiltDepTag, name)
 				p.properties.SourceExists = true
 			}
 		}
@@ -588,11 +607,6 @@
 		if !moduleInFamily.ExportedToMake() {
 			continue
 		}
-		// Skip for the top-level java_sdk_library_(_import). This has some special cases that need to be addressed first.
-		// This does not run into non-determinism because PrebuiltPostDepsMutator also has the special case
-		if sdkLibrary, ok := moduleInFamily.(interface{ SdkLibraryName() *string }); ok && sdkLibrary.SdkLibraryName() != nil {
-			continue
-		}
 		if p := GetEmbeddedPrebuilt(moduleInFamily); p != nil && p.properties.UsePrebuilt {
 			if selectedPrebuilt == nil {
 				selectedPrebuilt = moduleInFamily
@@ -604,6 +618,13 @@
 	}
 }
 
+func IsDontReplaceSourceWithPrebuiltTag(tag blueprint.DependencyTag) bool {
+	if t, ok := tag.(ReplaceSourceWithPrebuilt); ok {
+		return !t.ReplaceSourceWithPrebuilt()
+	}
+	return false
+}
+
 // PrebuiltPostDepsMutator replaces dependencies on the source module with dependencies on the
 // prebuilt when both modules exist and the prebuilt should be used.  When the prebuilt should not
 // be used, disable installing it.
@@ -612,26 +633,10 @@
 	if p := GetEmbeddedPrebuilt(m); p != nil {
 		bmn, _ := m.(baseModuleName)
 		name := bmn.BaseModuleName()
-		psi := PrebuiltSelectionInfoMap{}
-		ctx.VisitDirectDepsWithTag(AcDepTag, func(am Module) {
-			psi, _ = OtherModuleProvider(ctx, am, PrebuiltSelectionInfoProvider)
-		})
 
 		if p.properties.UsePrebuilt {
 			if p.properties.SourceExists {
 				ctx.ReplaceDependenciesIf(name, func(from blueprint.Module, tag blueprint.DependencyTag, to blueprint.Module) bool {
-					if sdkLibrary, ok := m.(interface{ SdkLibraryName() *string }); ok && sdkLibrary.SdkLibraryName() != nil {
-						// Do not replace deps to the top-level prebuilt java_sdk_library hook.
-						// This hook has been special-cased in #isSelected to be _always_ active, even in next builds
-						// for dexpreopt and hiddenapi processing.
-						// If we do not special-case this here, rdeps referring to a java_sdk_library in next builds via libs
-						// will get prebuilt stubs
-						// TODO (b/308187268): Remove this after the apexes have been added to apex_contributions
-						if psi.IsSelected(name) {
-							return false
-						}
-					}
-
 					if t, ok := tag.(ReplaceSourceWithPrebuilt); ok {
 						return t.ReplaceSourceWithPrebuilt()
 					}
@@ -653,23 +658,13 @@
 // java_sdk_library_import is a macro that creates
 // 1. top-level "impl" library
 // 2. stub libraries (suffixed with .stubs...)
-//
-// the impl of java_sdk_library_import is a "hook" for hiddenapi and dexpreopt processing. It does not have an impl jar, but acts as a shim
-// to provide the jar deapxed from the prebuilt apex
-//
-// isSelected uses `all_apex_contributions` to supersede source vs prebuilts selection of the stub libraries. It does not supersede the
-// selection of the top-level "impl" library so that this hook can work
-//
-// TODO (b/308174306) - Fix this when we need to support multiple prebuilts in main
 func isSelected(psi PrebuiltSelectionInfoMap, m Module) bool {
 	if sdkLibrary, ok := m.(interface{ SdkLibraryName() *string }); ok && sdkLibrary.SdkLibraryName() != nil {
 		sln := proptools.String(sdkLibrary.SdkLibraryName())
 
 		// This is the top-level library
-		// Do not supersede the existing prebuilts vs source selection mechanisms
-		// TODO (b/308187268): Remove this after the apexes have been added to apex_contributions
 		if bmn, ok := m.(baseModuleName); ok && sln == bmn.BaseModuleName() {
-			return false
+			return psi.IsSelected(m.Name())
 		}
 
 		// Stub library created by java_sdk_library_import
diff --git a/android/prebuilt_build_tool.go b/android/prebuilt_build_tool.go
index 17b3230..7773bf8 100644
--- a/android/prebuilt_build_tool.go
+++ b/android/prebuilt_build_tool.go
@@ -84,13 +84,12 @@
 	t.toolPath = OptionalPathForPath(installedPath)
 }
 
-func (t *prebuiltBuildTool) MakeVars(ctx MakeVarsModuleContext) {
-	if makeVar := String(t.properties.Export_to_make_var); makeVar != "" {
-		if t.Target().Os != ctx.Config().BuildOS {
-			return
-		}
-		ctx.StrictRaw(makeVar, t.toolPath.String())
+func (t *prebuiltBuildTool) MakeVars(ctx MakeVarsModuleContext) []ModuleMakeVarsValue {
+	if makeVar := String(t.properties.Export_to_make_var); makeVar != "" &&
+		t.Target().Os == ctx.Config().BuildOS {
+		return []ModuleMakeVarsValue{{makeVar, t.toolPath.String()}}
 	}
+	return nil
 }
 
 func (t *prebuiltBuildTool) HostToolPath() OptionalPath {
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index b90ef3b..d31fc4f 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -335,7 +335,7 @@
 			).RunTestWithBp(t, bp)
 
 			for _, variant := range result.ModuleVariantsForTests("foo") {
-				foo := result.ModuleForTests("foo", variant)
+				foo := result.ModuleForTests(t, "foo", variant)
 				t.Run(foo.Module().Target().Os.String(), func(t *testing.T) {
 					var dependsOnSourceModule, dependsOnPrebuiltModule bool
 					result.VisitDirectDeps(foo.Module(), func(m blueprint.Module) {
@@ -551,6 +551,9 @@
 }
 
 func (s *sourceModule) Srcs() Paths {
+	if s.src == nil {
+		return nil
+	}
 	return Paths{s.src}
 }
 
diff --git a/android/product_packages_file.go b/android/product_packages_file.go
new file mode 100644
index 0000000..c7c18a2
--- /dev/null
+++ b/android/product_packages_file.go
@@ -0,0 +1,39 @@
+// 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
+
+import (
+	"strings"
+)
+
+func init() {
+	RegisterParallelSingletonType("product_packages_file_singleton", productPackagesFileSingletonFactory)
+}
+
+func productPackagesFileSingletonFactory() Singleton {
+	return &productPackagesFileSingleton{}
+}
+
+type productPackagesFileSingleton struct{}
+
+func (s *productPackagesFileSingleton) GenerateBuildActions(ctx SingletonContext) {
+	// There's no HasDeviceName() function, but the device name and device product should always
+	// both be present or not.
+	if ctx.Config().HasDeviceProduct() {
+		productPackages := ctx.Config().productVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.ProductPackages
+		output := PathForArbitraryOutput(ctx, "target", "product", ctx.Config().DeviceName(), "product_packages.txt")
+		WriteFileRule(ctx, output, strings.Join(productPackages, "\n"))
+	}
+}
diff --git a/android/proto.go b/android/proto.go
index 66faa20..91d6732 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -75,7 +75,7 @@
 	}
 
 	ctx.VisitDirectDepsProxyWithTag(ProtoPluginDepTag, func(dep ModuleProxy) {
-		if h, ok := OtherModuleProvider(ctx, dep, HostToolProviderKey); !ok || !h.HostToolPath.Valid() {
+		if h, ok := OtherModuleProvider(ctx, dep, HostToolProviderInfoProvider); !ok || !h.HostToolPath.Valid() {
 			ctx.PropertyErrorf("proto.plugin", "module %q is not a host tool provider",
 				ctx.OtherModuleName(dep))
 		} else {
diff --git a/android/provider.go b/android/provider.go
index 81d17a1..aae93ef 100644
--- a/android/provider.go
+++ b/android/provider.go
@@ -4,8 +4,8 @@
 	"github.com/google/blueprint"
 )
 
-// OtherModuleProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
-// TopDownMutatorContext for use in OtherModuleProvider.
+// OtherModuleProviderContext is a helper interface that is a subset of ModuleContext or BottomUpMutatorContext
+// for use in OtherModuleProvider.
 type OtherModuleProviderContext interface {
 	otherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
 }
@@ -13,16 +13,20 @@
 var _ OtherModuleProviderContext = BaseModuleContext(nil)
 var _ OtherModuleProviderContext = ModuleContext(nil)
 var _ OtherModuleProviderContext = BottomUpMutatorContext(nil)
-var _ OtherModuleProviderContext = TopDownMutatorContext(nil)
 var _ OtherModuleProviderContext = SingletonContext(nil)
 var _ OtherModuleProviderContext = (*TestContext)(nil)
 
+// ConfigAndOtherModuleProviderContext is OtherModuleProviderContext + ConfigContext
+type ConfigAndOtherModuleProviderContext interface {
+	OtherModuleProviderContext
+	ConfigContext
+}
+
 // OtherModuleProvider reads the provider for the given module.  If the provider has been set the value is
 // returned and the boolean is true.  If it has not been set the zero value of the provider's type  is returned
 // and the boolean is false.  The value returned may be a deep copy of the value originally passed to SetProvider.
 //
-// OtherModuleProviderContext is a helper interface that accepts ModuleContext, BottomUpMutatorContext, or
-// TopDownMutatorContext.
+// OtherModuleProviderContext is a helper interface that accepts ModuleContext or BottomUpMutatorContext.
 func OtherModuleProvider[K any](ctx OtherModuleProviderContext, module blueprint.Module, provider blueprint.ProviderKey[K]) (K, bool) {
 	value, ok := ctx.otherModuleProvider(getWrappedModule(module), provider)
 	if !ok {
@@ -37,8 +41,16 @@
 	return value
 }
 
-// ModuleProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
-// TopDownMutatorContext for use in ModuleProvider.
+func OtherModulePointerProviderOrDefault[K *T, T any](ctx OtherModuleProviderContext, module blueprint.Module, provider blueprint.ProviderKey[K]) K {
+	if value, ok := OtherModuleProvider(ctx, module, provider); ok {
+		return value
+	}
+	var val T
+	return &val
+}
+
+// ModuleProviderContext is a helper interface that is a subset of ModuleContext or BottomUpMutatorContext
+// for use in ModuleProvider.
 type ModuleProviderContext interface {
 	provider(provider blueprint.AnyProviderKey) (any, bool)
 }
@@ -46,14 +58,12 @@
 var _ ModuleProviderContext = BaseModuleContext(nil)
 var _ ModuleProviderContext = ModuleContext(nil)
 var _ ModuleProviderContext = BottomUpMutatorContext(nil)
-var _ ModuleProviderContext = TopDownMutatorContext(nil)
 
 // ModuleProvider reads the provider for the current module.  If the provider has been set the value is
 // returned and the boolean is true.  If it has not been set the zero value of the provider's type  is returned
 // and the boolean is false.  The value returned may be a deep copy of the value originally passed to SetProvider.
 //
-// ModuleProviderContext is a helper interface that accepts ModuleContext, BottomUpMutatorContext, or
-// TopDownMutatorContext.
+// ModuleProviderContext is a helper interface that accepts ModuleContext or BottomUpMutatorContext.
 func ModuleProvider[K any](ctx ModuleProviderContext, provider blueprint.ProviderKey[K]) (K, bool) {
 	value, ok := ctx.provider(provider)
 	if !ok {
@@ -63,8 +73,8 @@
 	return value.(K), ok
 }
 
-// SetProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
-// TopDownMutatorContext for use in SetProvider.
+// SetProviderContext is a helper interface that is a subset of ModuleContext or BottomUpMutatorContext
+// for use in SetProvider.
 type SetProviderContext interface {
 	setProvider(provider blueprint.AnyProviderKey, value any)
 }
@@ -72,15 +82,13 @@
 var _ SetProviderContext = BaseModuleContext(nil)
 var _ SetProviderContext = ModuleContext(nil)
 var _ SetProviderContext = BottomUpMutatorContext(nil)
-var _ SetProviderContext = TopDownMutatorContext(nil)
 
 // SetProvider sets the value for a provider for the current module.  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 value has already been set.  The value should not
 // be modified after being passed to SetProvider.
 //
-// SetProviderContext is a helper interface that accepts ModuleContext, BottomUpMutatorContext, or
-// TopDownMutatorContext.
+// SetProviderContext is a helper interface that accepts ModuleContext or BottomUpMutatorContext.
 func SetProvider[K any](ctx SetProviderContext, provider blueprint.ProviderKey[K], value K) {
 	ctx.setProvider(provider, value)
 }
diff --git a/genrule/allowlists.go b/android/provider_keys.go
similarity index 67%
rename from genrule/allowlists.go
rename to android/provider_keys.go
index 45a7f72..60b383f 100644
--- a/genrule/allowlists.go
+++ b/android/provider_keys.go
@@ -1,4 +1,4 @@
-// Copyright 2023 Google Inc. All rights reserved.
+// Copyright 2025 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.
@@ -12,11 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package genrule
+package android
 
-var (
-	SandboxingDenyModuleList = []string{
-		// go/keep-sorted start
-		// go/keep-sorted end
-	}
-)
+import "github.com/google/blueprint"
+
+// Providers of package filesystem
+type AndroidDeviceInfo struct {
+	Main_device bool
+}
+
+var AndroidDeviceInfoProvider = blueprint.NewProvider[AndroidDeviceInfo]()
diff --git a/android/raw_files.go b/android/raw_files.go
index 9d7f5e8..ebba4d1 100644
--- a/android/raw_files.go
+++ b/android/raw_files.go
@@ -18,7 +18,6 @@
 	"crypto/sha1"
 	"encoding/hex"
 	"fmt"
-	"github.com/google/blueprint"
 	"io"
 	"io/fs"
 	"os"
@@ -26,25 +25,28 @@
 	"strings"
 	"testing"
 
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/syncmap"
+
 	"github.com/google/blueprint/proptools"
 )
 
 // WriteFileRule creates a ninja rule to write contents to a file by immediately writing the
 // contents, plus a trailing newline, to a file in out/soong/raw-${TARGET_PRODUCT}, and then creating
 // a ninja rule to copy the file into place.
-func WriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) {
-	writeFileRule(ctx, outputFile, content, true, false)
+func WriteFileRule(ctx BuilderContext, outputFile WritablePath, content string, validations ...Path) {
+	writeFileRule(ctx, outputFile, content, true, false, validations)
 }
 
 // WriteFileRuleVerbatim creates a ninja rule to write contents to a file by immediately writing the
 // contents to a file in out/soong/raw-${TARGET_PRODUCT}, and then creating a ninja rule to copy the file into place.
-func WriteFileRuleVerbatim(ctx BuilderContext, outputFile WritablePath, content string) {
-	writeFileRule(ctx, outputFile, content, false, false)
+func WriteFileRuleVerbatim(ctx BuilderContext, outputFile WritablePath, content string, validations ...Path) {
+	writeFileRule(ctx, outputFile, content, false, false, validations)
 }
 
 // WriteExecutableFileRuleVerbatim is the same as WriteFileRuleVerbatim, but runs chmod +x on the result
-func WriteExecutableFileRuleVerbatim(ctx BuilderContext, outputFile WritablePath, content string) {
-	writeFileRule(ctx, outputFile, content, false, true)
+func WriteExecutableFileRuleVerbatim(ctx BuilderContext, outputFile WritablePath, content string, validations ...Path) {
+	writeFileRule(ctx, outputFile, content, false, true, validations)
 }
 
 // tempFile provides a testable wrapper around a file in out/soong/.temp.  It writes to a temporary file when
@@ -124,7 +126,7 @@
 	return tempFile, hex.EncodeToString(hash.Sum(nil))
 }
 
-func writeFileRule(ctx BuilderContext, outputFile WritablePath, content string, newline bool, executable bool) {
+func writeFileRule(ctx BuilderContext, outputFile WritablePath, content string, newline bool, executable bool, validations Paths) {
 	// Write the contents to a temporary file while computing its hash.
 	tempFile, hash := writeContentToTempFileAndHash(ctx, content, newline)
 
@@ -186,6 +188,7 @@
 		Input:       rawPath,
 		Output:      outputFile,
 		Description: "raw " + outputFile.Base(),
+		Validations: validations,
 	})
 }
 
@@ -211,10 +214,10 @@
 
 var rawFileSetKey OnceKey = NewOnceKey("raw file set")
 
-func getRawFileSet(config Config) *SyncMap[string, rawFileInfo] {
+func getRawFileSet(config Config) *syncmap.SyncMap[string, rawFileInfo] {
 	return config.Once(rawFileSetKey, func() any {
-		return &SyncMap[string, rawFileInfo]{}
-	}).(*SyncMap[string, rawFileInfo])
+		return &syncmap.SyncMap[string, rawFileInfo]{}
+	}).(*syncmap.SyncMap[string, rawFileInfo])
 }
 
 // ContentFromFileRuleForTests returns the content that was passed to a WriteFileRule for use
diff --git a/android/recovery_build_prop.go b/android/recovery_build_prop.go
new file mode 100644
index 0000000..ac7d2ec
--- /dev/null
+++ b/android/recovery_build_prop.go
@@ -0,0 +1,113 @@
+// 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
+
+import "github.com/google/blueprint/proptools"
+
+func init() {
+	RegisterModuleType("recovery_build_prop", RecoveryBuildPropModuleFactory)
+}
+
+type recoveryBuildPropProperties struct {
+	// Path to the system build.prop file
+	System_build_prop *string `android:"path"`
+
+	// Path to the vendor build.prop file
+	Vendor_build_prop *string `android:"path"`
+
+	// Path to the odm build.prop file
+	Odm_build_prop *string `android:"path"`
+
+	// Path to the product build.prop file
+	Product_build_prop *string `android:"path"`
+
+	// Path to the system_ext build.prop file
+	System_ext_build_prop *string `android:"path"`
+}
+
+type recoveryBuildPropModule struct {
+	ModuleBase
+	properties recoveryBuildPropProperties
+
+	outputFilePath ModuleOutPath
+
+	installPath InstallPath
+}
+
+func RecoveryBuildPropModuleFactory() Module {
+	module := &recoveryBuildPropModule{}
+	module.AddProperties(&module.properties)
+	InitAndroidArchModule(module, DeviceSupported, MultilibCommon)
+	return module
+}
+
+// Overrides ctx.Module().InstallInRoot().
+// recovery_build_prop module always installs in root so that the prop.default
+// file is installed in recovery/root instead of recovery/root/system
+func (r *recoveryBuildPropModule) InstallInRoot() bool {
+	return true
+}
+
+func (r *recoveryBuildPropModule) appendRecoveryUIProperties(ctx ModuleContext, rule *RuleBuilder) {
+	rule.Command().Text("echo '#' >>").Output(r.outputFilePath)
+	rule.Command().Text("echo '# RECOVERY UI BUILD PROPERTIES' >>").Output(r.outputFilePath)
+	rule.Command().Text("echo '#' >>").Output(r.outputFilePath)
+
+	for propName, val := range ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.PrivateRecoveryUiProperties {
+		if len(val) > 0 {
+			rule.Command().
+				Textf("echo ro.recovery.ui.%s=%s >>", propName, val).
+				Output(r.outputFilePath)
+		}
+	}
+}
+
+func (r *recoveryBuildPropModule) getBuildProps(ctx ModuleContext) Paths {
+	var buildProps Paths
+	for _, buildProp := range []*string{
+		r.properties.System_build_prop,
+		r.properties.Vendor_build_prop,
+		r.properties.Odm_build_prop,
+		r.properties.Product_build_prop,
+		r.properties.System_ext_build_prop,
+	} {
+		if buildProp != nil {
+			if buildPropPath := PathForModuleSrc(ctx, proptools.String(buildProp)); buildPropPath != nil {
+				buildProps = append(buildProps, buildPropPath)
+			}
+		}
+	}
+	return buildProps
+}
+
+func (r *recoveryBuildPropModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	if !r.InstallInRecovery() {
+		ctx.ModuleErrorf("recovery_build_prop module must set `recovery` property to true")
+	}
+	r.outputFilePath = PathForModuleOut(ctx, ctx.ModuleName(), "prop.default")
+
+	// Replicates the logic in https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2733;drc=0585bb1bcf4c89065adaf709f48acc8b869fd3ce
+	rule := NewRuleBuilder(pctx, ctx)
+	rule.Command().Text("rm").FlagWithOutput("-f ", r.outputFilePath)
+	rule.Command().Text("cat").
+		Inputs(r.getBuildProps(ctx)).
+		Text(">>").
+		Output(r.outputFilePath)
+	r.appendRecoveryUIProperties(ctx, rule)
+
+	rule.Build(ctx.ModuleName(), "generating recovery prop.default")
+	r.installPath = PathForModuleInstall(ctx)
+	ctx.InstallFile(r.installPath, "prop.default", r.outputFilePath)
+}
diff --git a/android/register.go b/android/register.go
index 8d2f19e..10c9114 100644
--- a/android/register.go
+++ b/android/register.go
@@ -89,7 +89,6 @@
 type mutator struct {
 	name              string
 	bottomUpMutator   blueprint.BottomUpMutator
-	topDownMutator    blueprint.TopDownMutator
 	transitionMutator blueprint.TransitionMutator
 
 	usesRename              bool
@@ -192,6 +191,11 @@
 func collateGloballyRegisteredSingletons() sortableComponents {
 	allSingletons := append(sortableComponents(nil), singletons...)
 	allSingletons = append(allSingletons,
+		// Soong only androidmk is registered later than other singletons in order to collect
+		// dist contributions from other singletons. This singleton is registered just before
+		// phony so that its phony rules can be collected by the phony singleton.
+		singleton{parallel: false, name: "soongonlyandroidmk", factory: soongOnlyAndroidMkSingletonFactory},
+
 		// Register phony just before makevars so it can write out its phony rules as Make rules
 		singleton{parallel: false, name: "phony", factory: phonySingletonFactory},
 
diff --git a/android/removed_package.go b/android/removed_package.go
new file mode 100644
index 0000000..aa54c2a
--- /dev/null
+++ b/android/removed_package.go
@@ -0,0 +1,60 @@
+package android
+
+import (
+	"fmt"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+func init() {
+	InitRegistrationContext.RegisterModuleType("removed_package", removedPackageModuleFactory)
+}
+
+type removedPackageModuleProps struct {
+	// The error message to display when this module is built. This is optional, there is a
+	// reasonable default message.
+	Message *string
+}
+
+type removedPackageModule struct {
+	ModuleBase
+	properties removedPackageModuleProps
+}
+
+// removed_package will cause a build failure when it's included in PRODUCT_PACKAGES. It's needed
+// because currently you can add non-existent packages to PRODUCT_PACKAGES, and the build will
+// not notice/complain, unless you opt-into enforcement via $(call enforce-product-packages-exist).
+// Opting into the enforcement is difficult in some cases, because a package exists on some source
+// trees but not on others. removed_package is an intermediate solution that allows you to remove
+// a package and still get an error if it remains in PRODUCT_PACKAGES somewhere.
+func removedPackageModuleFactory() Module {
+	m := &removedPackageModule{}
+	InitAndroidModule(m)
+	m.AddProperties(&m.properties)
+	return m
+}
+
+var removedPackageRule = pctx.AndroidStaticRule("removed_package", blueprint.RuleParams{
+	Command: "echo $message && false",
+}, "message")
+
+func (m *removedPackageModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	// Unchecked module so that checkbuild doesn't fail
+	ctx.UncheckedModule()
+
+	out := PathForModuleOut(ctx, "out.txt")
+	message := fmt.Sprintf("%s has been removed, and can no longer be used.", ctx.ModuleName())
+	if m.properties.Message != nil {
+		message = *m.properties.Message
+	}
+	ctx.Build(pctx, BuildParams{
+		Rule:   removedPackageRule,
+		Output: out,
+		Args: map[string]string{
+			"message": proptools.ShellEscape(message),
+		},
+	})
+
+	ctx.InstallFile(PathForModuleInstall(ctx, "removed_module"), ctx.ModuleName(), out)
+}
diff --git a/android/rule_builder.go b/android/rule_builder.go
index db56c3f..01fe6d8 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -660,12 +660,17 @@
 			}
 			for _, c := range r.commands {
 				for _, tool := range c.packagedTools {
-					command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
-						From:       proto.String(tool.srcPath.String()),
-						To:         proto.String(sboxPathForPackagedToolRel(tool)),
-						Executable: proto.Bool(tool.executable),
-					})
-					tools = append(tools, tool.srcPath)
+					if tool.srcPath != nil {
+						command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
+							From:       proto.String(tool.srcPath.String()),
+							To:         proto.String(sboxPathForPackagedToolRel(tool)),
+							Executable: proto.Bool(tool.executable),
+						})
+						tools = append(tools, tool.srcPath)
+					} else if tool.SymlinkTarget() == "" {
+						// We ignore symlinks for now, could be added later if needed
+						panic("Expected tool packagingSpec to either be a file or symlink")
+					}
 				}
 			}
 		}
@@ -1187,7 +1192,11 @@
 // Textf adds the specified formatted text to the command line.  The text should not contain input or output paths or
 // the rule will not have them listed in its dependencies or outputs.
 func (c *RuleBuilderCommand) Textf(format string, a ...interface{}) *RuleBuilderCommand {
-	return c.Text(fmt.Sprintf(format, a...))
+	if c.buf.Len() > 0 {
+		c.buf.WriteByte(' ')
+	}
+	fmt.Fprintf(&c.buf, format, a...)
+	return c
 }
 
 // Flag adds the specified raw text to the command line.  The text should not contain input or output paths or the
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index e1a1e08..5f3b9be 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -358,7 +358,7 @@
 			"command3 input3 out_local/soong/module/output2 out_local/soong/module/output3 input3 out_local/soong/module/output2",
 		}
 
-		wantDepMergerCommand := "out_local/soong/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer " +
+		wantDepMergerCommand := "out_local/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer " +
 			"out_local/soong/module/DepFile out_local/soong/module/depfile out_local/soong/module/ImplicitDepFile out_local/soong/module/depfile2"
 
 		AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
@@ -388,7 +388,7 @@
 			"command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3 input3 __SBOX_SANDBOX_DIR__/out/output2",
 		}
 
-		wantDepMergerCommand := "out_local/soong/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
+		wantDepMergerCommand := "out_local/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
 
 		AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
 
@@ -651,7 +651,7 @@
 		outFile := "out/soong/.intermediates/foo/gen/foo"
 		rspFile := "out/soong/.intermediates/foo/rsp"
 		rspFile2 := "out/soong/.intermediates/foo/rsp2"
-		module := result.ModuleForTests("foo", "")
+		module := result.ModuleForTests(t, "foo", "")
 		check(t, module.Rule("rule"), module.Output(rspFile2),
 			"cp in "+outFile+" @"+rspFile+" @"+rspFile2,
 			outFile, outFile+".d", rspFile, rspFile2, true, nil, nil)
@@ -664,11 +664,11 @@
 		rspFile := filepath.Join(outDir, "rsp")
 		rspFile2 := filepath.Join(outDir, "rsp2")
 		manifest := filepath.Join(outDir, "sbox.textproto")
-		sbox := filepath.Join("out", "soong", "host", result.Config.PrebuiltOS(), "bin/sbox")
+		sbox := filepath.Join("out", "host", result.Config.PrebuiltOS(), "bin/sbox")
 		sandboxPath := shared.TempDirForOutDir("out/soong")
 
 		cmd := sbox + ` --sandbox-path ` + sandboxPath + ` --output-dir ` + sboxOutDir + ` --manifest ` + manifest
-		module := result.ModuleForTests("foo_sbox", "")
+		module := result.ModuleForTests(t, "foo_sbox", "")
 		check(t, module.Output("gen/foo_sbox"), module.Output(rspFile2),
 			cmd, outFile, depFile, rspFile, rspFile2, false, []string{manifest}, []string{sbox})
 	})
@@ -680,12 +680,12 @@
 		rspFile := filepath.Join(outDir, "rsp")
 		rspFile2 := filepath.Join(outDir, "rsp2")
 		manifest := filepath.Join(outDir, "sbox.textproto")
-		sbox := filepath.Join("out", "soong", "host", result.Config.PrebuiltOS(), "bin/sbox")
+		sbox := filepath.Join("out", "host", result.Config.PrebuiltOS(), "bin/sbox")
 		sandboxPath := shared.TempDirForOutDir("out/soong")
 
 		cmd := sbox + ` --sandbox-path ` + sandboxPath + ` --output-dir ` + sboxOutDir + ` --manifest ` + manifest
 
-		module := result.ModuleForTests("foo_sbox_inputs", "")
+		module := result.ModuleForTests(t, "foo_sbox_inputs", "")
 		check(t, module.Output("gen/foo_sbox_inputs"), module.Output(rspFile2),
 			cmd, outFile, depFile, rspFile, rspFile2, false, []string{manifest}, []string{sbox})
 	})
@@ -693,7 +693,7 @@
 		outFile := filepath.Join("out/soong/singleton/gen/baz")
 		rspFile := filepath.Join("out/soong/singleton/rsp")
 		rspFile2 := filepath.Join("out/soong/singleton/rsp2")
-		singleton := result.SingletonForTests("rule_builder_test")
+		singleton := result.SingletonForTests(t, "rule_builder_test")
 		check(t, singleton.Rule("rule"), singleton.Output(rspFile2),
 			"cp in "+outFile+" @"+rspFile+" @"+rspFile2,
 			outFile, outFile+".d", rspFile, rspFile2, true, nil, nil)
@@ -756,14 +756,14 @@
 	for _, test := range testcases {
 		t.Run(test.name, func(t *testing.T) {
 			t.Run("sbox", func(t *testing.T) {
-				gen := result.ModuleForTests(test.name+"_sbox", "")
+				gen := result.ModuleForTests(t, test.name+"_sbox", "")
 				manifest := RuleBuilderSboxProtoForTests(t, result.TestContext, gen.Output("sbox.textproto"))
 				hash := manifest.Commands[0].GetInputHash()
 
 				AssertStringEquals(t, "hash", test.expectedHash, hash)
 			})
 			t.Run("", func(t *testing.T) {
-				gen := result.ModuleForTests(test.name+"", "")
+				gen := result.ModuleForTests(t, test.name+"", "")
 				command := gen.Output("gen/" + test.name).RuleParams.Command
 				if g, w := command, " # hash of input list: "+test.expectedHash; !strings.HasSuffix(g, w) {
 					t.Errorf("Expected command line to end with %q, got %q", w, g)
diff --git a/android/sbom.go b/android/sbom.go
index f2b9c0f..fc61c41 100644
--- a/android/sbom.go
+++ b/android/sbom.go
@@ -84,12 +84,6 @@
 			Inputs: []Path{this.sbomFile},
 			Output: PathForPhony(ctx, "sbom"),
 		})
-	}
-}
-
-func (this *sbomSingleton) MakeVars(ctx MakeVarsContext) {
-	// When building SBOM of products
-	if !ctx.Config().UnbundledBuildApps() {
 		ctx.DistForGoalWithFilename("droid", this.sbomFile, "sbom/sbom.spdx.json")
 	}
 }
diff --git a/android/sdk_version.go b/android/sdk_version.go
index a9b88fb..fa3abaa 100644
--- a/android/sdk_version.go
+++ b/android/sdk_version.go
@@ -123,6 +123,31 @@
 	}
 }
 
+func JavaLibraryNameToSdkKind(name string) (SdkKind, bool) {
+	if name == SdkPublic.DefaultJavaLibraryName() {
+		return SdkPublic, true
+	}
+	if name == SdkSystem.DefaultJavaLibraryName() {
+		return SdkSystem, true
+	}
+	if name == SdkTest.DefaultJavaLibraryName() {
+		return SdkTest, true
+	}
+	if name == SdkTestFrameworksCore.DefaultJavaLibraryName() {
+		return SdkTestFrameworksCore, true
+	}
+	if name == SdkCore.DefaultJavaLibraryName() {
+		return SdkCore, true
+	}
+	if name == SdkModule.DefaultJavaLibraryName() {
+		return SdkModule, true
+	}
+	if name == SdkSystemServer.DefaultJavaLibraryName() {
+		return SdkSystemServer, true
+	}
+	return SdkInvalid, false
+}
+
 func (k SdkKind) DefaultExportableJavaLibraryName() string {
 	switch k {
 	case SdkPublic, SdkSystem, SdkTest, SdkModule, SdkSystemServer:
diff --git a/android/selects_test.go b/android/selects_test.go
index 1397ed8..8e469f8 100644
--- a/android/selects_test.go
+++ b/android/selects_test.go
@@ -666,6 +666,81 @@
 			},
 		},
 		{
+			name: "Select on integer soong config variable",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
+					34: "34",
+					default: "other",
+				}),
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "34",
+				},
+			},
+			vendorVarTypes: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "int",
+				},
+			},
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("34"),
+			},
+		},
+		{
+			name: "Select on integer soong config variable default",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
+					34: "34",
+					default: "other",
+				}),
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "5",
+				},
+			},
+			vendorVarTypes: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "int",
+				},
+			},
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("other"),
+			},
+		},
+		{
+			name: "Assign to integer property",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_int64: select(soong_config_variable("my_namespace", "my_variable"), {
+					any @ val: val,
+					default: "other",
+				}),
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "5",
+				},
+			},
+			vendorVarTypes: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "int",
+				},
+			},
+			provider: selectsTestProvider{
+				my_int64: proptools.Int64Ptr(5),
+			},
+		},
+		{
 			name: "Mismatched condition types",
 			bp: `
 			my_module_type {
@@ -1118,7 +1193,7 @@
 
 				for moduleName := range tc.providers {
 					expected := tc.providers[moduleName]
-					m := result.ModuleForTests(moduleName, "android_arm64_armv8-a")
+					m := result.ModuleForTests(t, moduleName, "android_arm64_armv8-a")
 					p, _ := OtherModuleProvider(result.testContext.OtherModuleProviderAdaptor(), m.Module(), selectsTestProviderKey)
 					if !reflect.DeepEqual(p, expected) {
 						t.Errorf("Expected:\n  %q\ngot:\n  %q", expected.String(), p.String())
@@ -1132,6 +1207,7 @@
 type selectsTestProvider struct {
 	my_bool                        *bool
 	my_string                      *string
+	my_int64                       *int64
 	my_string_list                 *[]string
 	my_paths                       *[]string
 	replacing_string_list          *[]string
@@ -1181,6 +1257,7 @@
 
 type selectsMockModuleProperties struct {
 	My_bool                        proptools.Configurable[bool]
+	My_int64                       proptools.Configurable[int64]
 	My_string                      proptools.Configurable[string]
 	My_string_list                 proptools.Configurable[[]string]
 	My_paths                       proptools.Configurable[[]string] `android:"path"`
@@ -1213,6 +1290,7 @@
 	SetProvider(ctx, selectsTestProviderKey, selectsTestProvider{
 		my_bool:                        optionalToPtr(p.properties.My_bool.Get(ctx)),
 		my_string:                      optionalToPtr(p.properties.My_string.Get(ctx)),
+		my_int64:                       optionalToPtr(p.properties.My_int64.Get(ctx)),
 		my_string_list:                 optionalToPtr(p.properties.My_string_list.Get(ctx)),
 		my_paths:                       optionalToPtr(p.properties.My_paths.Get(ctx)),
 		replacing_string_list:          optionalToPtr(p.properties.Replacing_string_list.Get(ctx)),
diff --git a/android/singleton.go b/android/singleton.go
index 0754b0c..e5f2684 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -15,6 +15,9 @@
 package android
 
 import (
+	"slices"
+	"sync"
+
 	"github.com/google/blueprint"
 )
 
@@ -33,7 +36,7 @@
 
 	// ModuleVariantsFromName returns the list of module variants named `name` in the same namespace as `referer` enforcing visibility rules.
 	// Allows generating build actions for `referer` based on the metadata for `name` deferred until the singleton context.
-	ModuleVariantsFromName(referer Module, name string) []Module
+	ModuleVariantsFromName(referer ModuleProxy, name string) []ModuleProxy
 
 	otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
 
@@ -81,6 +84,9 @@
 	VisitAllModuleVariantProxies(module Module, visit func(proxy ModuleProxy))
 
 	PrimaryModule(module Module) Module
+
+	PrimaryModuleProxy(module ModuleProxy) ModuleProxy
+
 	IsFinalModule(module Module) bool
 
 	AddNinjaFileDeps(deps ...string)
@@ -97,6 +103,24 @@
 	// HasMutatorFinished returns true if the given mutator has finished running.
 	// It will panic if given an invalid mutator name.
 	HasMutatorFinished(mutatorName string) bool
+
+	// DistForGoals creates a rule to copy one or more Paths to the artifacts
+	// directory on the build server when any of the specified goals are built.
+	DistForGoal(goal string, paths ...Path)
+
+	// DistForGoalWithFilename creates a rule to copy a Path to the artifacts
+	// directory on the build server with the given filename when the specified
+	// goal is built.
+	DistForGoalWithFilename(goal string, path Path, filename string)
+
+	// DistForGoals creates a rule to copy one or more Paths to the artifacts
+	// directory on the build server when any of the specified goals are built.
+	DistForGoals(goals []string, paths ...Path)
+
+	// DistForGoalsWithFilename creates a rule to copy a Path to the artifacts
+	// directory on the build server with the given filename when any of the
+	// specified goals are built.
+	DistForGoalsWithFilename(goals []string, path Path, filename string)
 }
 
 type singletonAdaptor struct {
@@ -118,6 +142,13 @@
 
 	s.buildParams = sctx.buildParams
 	s.ruleParams = sctx.ruleParams
+
+	if len(sctx.dists) > 0 {
+		dists := getSingletonDists(sctx.Config())
+		dists.lock.Lock()
+		defer dists.lock.Unlock()
+		dists.dists = append(dists.dists, sctx.dists...)
+	}
 }
 
 func (s *singletonAdaptor) BuildParamsForTests() []BuildParams {
@@ -128,6 +159,19 @@
 	return s.ruleParams
 }
 
+var singletonDistsKey = NewOnceKey("singletonDistsKey")
+
+type singletonDistsAndLock struct {
+	dists []dist
+	lock  sync.Mutex
+}
+
+func getSingletonDists(config Config) *singletonDistsAndLock {
+	return config.Once(singletonDistsKey, func() interface{} {
+		return &singletonDistsAndLock{}
+	}).(*singletonDistsAndLock)
+}
+
 type Singleton interface {
 	GenerateBuildActions(SingletonContext)
 }
@@ -137,6 +181,7 @@
 
 	buildParams []BuildParams
 	ruleParams  map[blueprint.Rule]blueprint.RuleParams
+	dists       []dist
 }
 
 func (s *singletonContextAdaptor) blueprintSingletonContext() blueprint.SingletonContext {
@@ -229,6 +274,26 @@
 	}
 }
 
+func (s *singletonContextAdaptor) ModuleName(module blueprint.Module) string {
+	return s.SingletonContext.ModuleName(getWrappedModule(module))
+}
+
+func (s *singletonContextAdaptor) ModuleDir(module blueprint.Module) string {
+	return s.SingletonContext.ModuleDir(getWrappedModule(module))
+}
+
+func (s *singletonContextAdaptor) ModuleSubDir(module blueprint.Module) string {
+	return s.SingletonContext.ModuleSubDir(getWrappedModule(module))
+}
+
+func (s *singletonContextAdaptor) ModuleType(module blueprint.Module) string {
+	return s.SingletonContext.ModuleType(getWrappedModule(module))
+}
+
+func (s *singletonContextAdaptor) BlueprintFile(module blueprint.Module) string {
+	return s.SingletonContext.BlueprintFile(getWrappedModule(module))
+}
+
 func (s *singletonContextAdaptor) VisitAllModulesBlueprint(visit func(blueprint.Module)) {
 	s.SingletonContext.VisitAllModules(visit)
 }
@@ -266,40 +331,42 @@
 }
 
 func (s *singletonContextAdaptor) VisitAllModuleVariantProxies(module Module, visit func(proxy ModuleProxy)) {
-	s.SingletonContext.VisitAllModuleVariantProxies(module, visitProxyAdaptor(visit))
+	s.SingletonContext.VisitAllModuleVariantProxies(getWrappedModule(module), visitProxyAdaptor(visit))
 }
 
 func (s *singletonContextAdaptor) PrimaryModule(module Module) Module {
 	return s.SingletonContext.PrimaryModule(module).(Module)
 }
 
-func (s *singletonContextAdaptor) IsFinalModule(module Module) bool {
-	return s.SingletonContext.IsFinalModule(module)
+func (s *singletonContextAdaptor) PrimaryModuleProxy(module ModuleProxy) ModuleProxy {
+	return ModuleProxy{s.SingletonContext.PrimaryModuleProxy(module.module)}
 }
 
-func (s *singletonContextAdaptor) ModuleVariantsFromName(referer Module, name string) []Module {
-	// get module reference for visibility enforcement
-	qualified := createVisibilityModuleReference(s.ModuleName(referer), s.ModuleDir(referer), referer)
+func (s *singletonContextAdaptor) IsFinalModule(module Module) bool {
+	return s.SingletonContext.IsFinalModule(getWrappedModule(module))
+}
 
-	modules := s.SingletonContext.ModuleVariantsFromName(referer, name)
-	result := make([]Module, 0, len(modules))
-	for _, m := range modules {
-		if module, ok := m.(Module); ok {
-			// enforce visibility
-			depName := s.ModuleName(module)
-			depDir := s.ModuleDir(module)
-			depQualified := qualifiedModuleName{depDir, depName}
-			// Targets are always visible to other targets in their own package.
-			if depQualified.pkg != qualified.name.pkg {
-				rule := effectiveVisibilityRules(s.Config(), depQualified)
-				if !rule.matches(qualified) {
-					s.ModuleErrorf(referer, "module %q references %q which is not visible to this module\nYou may need to add %q to its visibility",
-						referer.Name(), depQualified, "//"+s.ModuleDir(referer))
-					continue
-				}
+func (s *singletonContextAdaptor) ModuleVariantsFromName(referer ModuleProxy, name string) []ModuleProxy {
+	// get module reference for visibility enforcement
+	qualified := createVisibilityModuleProxyReference(s, s.ModuleName(referer), s.ModuleDir(referer), referer)
+
+	modules := s.SingletonContext.ModuleVariantsFromName(referer.module, name)
+	result := make([]ModuleProxy, 0, len(modules))
+	for _, module := range modules {
+		// enforce visibility
+		depName := s.ModuleName(module)
+		depDir := s.ModuleDir(module)
+		depQualified := qualifiedModuleName{depDir, depName}
+		// Targets are always visible to other targets in their own package.
+		if depQualified.pkg != qualified.name.pkg {
+			rule := effectiveVisibilityRules(s.Config(), depQualified)
+			if !rule.matches(qualified) {
+				s.ModuleErrorf(referer, "module %q references %q which is not visible to this module\nYou may need to add %q to its visibility",
+					referer.Name(), depQualified, "//"+s.ModuleDir(referer))
+				continue
 			}
-			result = append(result, module)
 		}
+		result = append(result, ModuleProxy{module})
 	}
 	return result
 }
@@ -315,3 +382,31 @@
 func (s *singletonContextAdaptor) HasMutatorFinished(mutatorName string) bool {
 	return s.blueprintSingletonContext().HasMutatorFinished(mutatorName)
 }
+func (s *singletonContextAdaptor) DistForGoal(goal string, paths ...Path) {
+	s.DistForGoals([]string{goal}, paths...)
+}
+
+func (s *singletonContextAdaptor) DistForGoalWithFilename(goal string, path Path, filename string) {
+	s.DistForGoalsWithFilename([]string{goal}, path, filename)
+}
+
+func (s *singletonContextAdaptor) DistForGoals(goals []string, paths ...Path) {
+	var copies distCopies
+	for _, path := range paths {
+		copies = append(copies, distCopy{
+			from: path,
+			dest: path.Base(),
+		})
+	}
+	s.dists = append(s.dists, dist{
+		goals: slices.Clone(goals),
+		paths: copies,
+	})
+}
+
+func (s *singletonContextAdaptor) DistForGoalsWithFilename(goals []string, path Path, filename string) {
+	s.dists = append(s.dists, dist{
+		goals: slices.Clone(goals),
+		paths: distCopies{{from: path, dest: filename}},
+	})
+}
diff --git a/android/singleton_module_test.go b/android/singleton_module_test.go
index 3b8c6b2..6f61a3b 100644
--- a/android/singleton_module_test.go
+++ b/android/singleton_module_test.go
@@ -61,7 +61,7 @@
 		FixtureWithRootAndroidBp(bp),
 	).RunTest(t)
 
-	ops := result.ModuleForTests("test_singleton_module", "").Module().(*testSingletonModule).ops
+	ops := result.ModuleForTests(t, "test_singleton_module", "").Module().(*testSingletonModule).ops
 	wantOps := []string{"GenerateAndroidBuildActions", "GenerateSingletonBuildActions", "MakeVars"}
 	AssertDeepEquals(t, "operations", wantOps, ops)
 }
@@ -88,7 +88,7 @@
 		prepareForSingletonModuleTest,
 	).RunTest(t)
 
-	singleton := result.SingletonForTests("test_singleton_module").Singleton()
+	singleton := result.SingletonForTests(t, "test_singleton_module").Singleton()
 	sm := singleton.(*singletonModuleSingletonAdaptor).sm
 	ops := sm.(*testSingletonModule).ops
 	if ops != nil {
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index e0b1d7c..a61c9d3 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -506,6 +506,10 @@
 		conditionalProps := proptools.CloneEmptyProperties(conditionalFactoryProps)
 		props = append(props, conditionalProps.Interface())
 
+		if m, ok := module.(Module); ok {
+			m.base().baseProperties.Soong_config_base_module_type = &moduleType.BaseModuleType
+		}
+
 		// Regular Soong operation wraps the existing module factory with a
 		// conditional on Soong config variables by reading the product
 		// config variables from Make.
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index 04aafde..f98e02b 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -321,10 +321,10 @@
 					FixtureWithRootAndroidBp(bp),
 				).RunTest(t)
 
-				foo := result.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
+				foo := result.ModuleForTests(t, "foo", "").Module().(*soongConfigTestModule)
 				AssertDeepEquals(t, "foo cflags", tc.fooExpectedFlags, foo.props.Cflags)
 
-				fooDefaults := result.ModuleForTests("foo_with_defaults", "").Module().(*soongConfigTestModule)
+				fooDefaults := result.ModuleForTests(t, "foo_with_defaults", "").Module().(*soongConfigTestModule)
 				AssertDeepEquals(t, "foo_with_defaults cflags", tc.fooDefaultsExpectedFlags, fooDefaults.props.Cflags)
 			})
 		}
@@ -499,8 +499,8 @@
 			).RunTest(t)
 
 			// Make sure that the singleton was created.
-			result.SingletonForTests("test_singleton")
-			m := result.ModuleForTests("wiley", "").module.(*soongConfigTestSingletonModule)
+			result.SingletonForTests(t, "test_singleton")
+			m := result.ModuleForTests(t, "wiley", "").module.(*soongConfigTestSingletonModule)
 			AssertStringEquals(t, "fragments", test.expectedFragments, fmt.Sprintf("%+v", m.props.Fragments))
 		})
 	}
diff --git a/android/team.go b/android/team.go
index c273dc6..ad37f28 100644
--- a/android/team.go
+++ b/android/team.go
@@ -32,6 +32,12 @@
 	Trendy_team_id *string `json:"trendy_team_id"`
 }
 
+type TeamInfo struct {
+	Properties teamProperties
+}
+
+var TeamInfoProvider = blueprint.NewProvider[TeamInfo]()
+
 type teamModule struct {
 	ModuleBase
 	DefaultableModuleBase
@@ -48,7 +54,11 @@
 
 // Real work is done for the module that depends on us.
 // If needed, the team can serialize the config to json/proto file as well.
-func (t *teamModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
+func (t *teamModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	SetProvider(ctx, TeamInfoProvider, TeamInfo{
+		Properties: t.properties,
+	})
+}
 
 func (t *teamModule) TrendyTeamId(ctx ModuleContext) string {
 	return *t.properties.Trendy_team_id
diff --git a/android/team_proto/OWNERS b/android/team_proto/OWNERS
index 2beb4f4..1eb820b 100644
--- a/android/team_proto/OWNERS
+++ b/android/team_proto/OWNERS
@@ -1,5 +1,4 @@
 dariofreni@google.com
 joeo@google.com
 ronish@google.com
-caditya@google.com
 rbraunstein@google.com
diff --git a/android/team_test.go b/android/team_test.go
index ccfcaaa..dcc1c99 100644
--- a/android/team_test.go
+++ b/android/team_test.go
@@ -61,9 +61,9 @@
 	`)
 
 	// Assert the rule from GenerateAndroidBuildActions exists.
-	m := ctx.ModuleForTests("main_test", "")
+	m := ctx.ModuleForTests(t, "main_test", "")
 	AssertStringEquals(t, "msg", m.Module().base().Team(), "someteam")
-	m = ctx.ModuleForTests("tool", "")
+	m = ctx.ModuleForTests(t, "tool", "")
 	AssertStringEquals(t, "msg", m.Module().base().Team(), "team2")
 }
 
diff --git a/android/test_asserts.go b/android/test_asserts.go
index c33ade5..22472c5 100644
--- a/android/test_asserts.go
+++ b/android/test_asserts.go
@@ -33,6 +33,23 @@
 	}
 }
 
+// AssertSame checks if the expected and actual values are equal and if they are not then
+// it reports an error prefixed with the supplied message and including a reason for why it failed.
+func AssertSameArray[T comparable](t *testing.T, message string, expected []T, actual []T) {
+	t.Helper()
+	if len(actual) != len(expected) {
+		t.Errorf("%s: expected %d (%v), actual (%d) %v", message, len(expected), expected, len(actual), actual)
+		return
+	}
+	for i := range actual {
+		if actual[i] != expected[i] {
+			t.Errorf("%s: expected %d-th, %v (%v), actual %v (%v)",
+				message, i, expected[i], expected, actual[i], actual)
+			return
+		}
+	}
+}
+
 // AssertBoolEquals checks if the expected and actual values are equal and if they are not then it
 // reports an error prefixed with the supplied message and including a reason for why it failed.
 func AssertBoolEquals(t *testing.T, message string, expected bool, actual bool) {
diff --git a/android/test_config.go b/android/test_config.go
index 3609e6b..5d79df0 100644
--- a/android/test_config.go
+++ b/android/test_config.go
@@ -23,8 +23,7 @@
 	"github.com/google/blueprint/proptools"
 )
 
-// TestConfig returns a Config object for testing.
-func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
+func initTestConfig(buildDir string, env map[string]string) *config {
 	envCopy := make(map[string]string)
 	for k, v := range env {
 		envCopy[k] = v
@@ -58,6 +57,7 @@
 		soongOutDir:  filepath.Join(buildDir, "soong"),
 		captureBuild: true,
 		env:          envCopy,
+		OncePer:      &OncePer{},
 
 		// Set testAllowNonExistentPaths so that test contexts don't need to specify every path
 		// passed to PathForSource or PathForModuleSrc.
@@ -69,10 +69,21 @@
 		config: config,
 	}
 	config.TestProductVariables = &config.productVariables
+	config.deviceNameToInstall = config.TestProductVariables.DeviceName
+
+	determineBuildOS(config)
+
+	return config
+}
+
+// TestConfig returns a Config object for testing.
+func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
+	config := initTestConfig(buildDir, env)
 
 	config.mockFileSystem(bp, fs)
 
-	determineBuildOS(config)
+	config.genericConfig = initTestConfig(buildDir, env)
+	overrideGenericConfig(config)
 
 	return Config{config}
 }
diff --git a/android/test_mapping_zip.go b/android/test_mapping_zip.go
new file mode 100644
index 0000000..8dc70d7
--- /dev/null
+++ b/android/test_mapping_zip.go
@@ -0,0 +1,49 @@
+// Copyright 2025 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() {
+	InitRegistrationContext.RegisterSingletonType("test_mapping_zip_singleton", testMappingZipSingletonFactory)
+}
+
+func testMappingZipSingletonFactory() Singleton {
+	return &testMappingZipSingleton{}
+}
+
+type testMappingZipSingleton struct{}
+
+func (s *testMappingZipSingleton) GenerateBuildActions(ctx SingletonContext) {
+	fileListFile := PathForArbitraryOutput(ctx, ".module_paths", "TEST_MAPPING.list")
+	out := PathForOutput(ctx, "test_mappings.zip")
+	dep := PathForOutput(ctx, "test_mappings.zip.d")
+
+	// disabled-presubmit-tests used to be filled out based on modules that set
+	// LOCAL_PRESUBMIT_DISABLED. But that's no longer used and there was never a soong equivalent
+	// anyways, so just always create an empty file.
+	disabledPresubmitTestsFile := PathForOutput(ctx, "disabled-presubmit-tests")
+	WriteFileRule(ctx, disabledPresubmitTestsFile, "")
+
+	builder := NewRuleBuilder(pctx, ctx)
+	builder.Command().BuiltTool("soong_zip").
+		FlagWithOutput("-o ", out).
+		FlagWithInput("-l ", fileListFile).
+		FlagWithArg("-e ", "disabled-presubmit-tests").
+		FlagWithInput("-f ", disabledPresubmitTestsFile)
+	builder.Command().Textf("echo '%s : ' $(cat %s) > ", out, fileListFile).DepFile(dep)
+	builder.Build("test_mappings_zip", "build TEST_MAPPING zip")
+
+	ctx.Phony("test_mapping", out)
+	ctx.DistForGoals([]string{"dist_files", "test_mapping"}, out)
+}
diff --git a/android/test_suites.go b/android/test_suites.go
index 936d2b6..dbcd48c 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -17,6 +17,8 @@
 import (
 	"path/filepath"
 	"strings"
+
+	"github.com/google/blueprint"
 )
 
 func init() {
@@ -27,51 +29,57 @@
 	return &testSuiteFiles{}
 }
 
-type testSuiteFiles struct {
-	robolectric []Path
-	ravenwood   []Path
-}
+type testSuiteFiles struct{}
 
 type TestSuiteModule interface {
 	Module
 	TestSuites() []string
 }
 
+type TestSuiteInfo struct {
+	TestSuites []string
+}
+
+var TestSuiteInfoProvider = blueprint.NewProvider[TestSuiteInfo]()
+
+type SupportFilesInfo struct {
+	SupportFiles InstallPaths
+}
+
+var SupportFilesInfoProvider = blueprint.NewProvider[SupportFilesInfo]()
+
 func (t *testSuiteFiles) GenerateBuildActions(ctx SingletonContext) {
 	files := make(map[string]map[string]InstallPaths)
 
-	ctx.VisitAllModules(func(m Module) {
-		if tsm, ok := m.(TestSuiteModule); ok {
-			for _, testSuite := range tsm.TestSuites() {
+	ctx.VisitAllModuleProxies(func(m ModuleProxy) {
+		if tsm, ok := OtherModuleProvider(ctx, m, TestSuiteInfoProvider); ok {
+			for _, testSuite := range tsm.TestSuites {
 				if files[testSuite] == nil {
 					files[testSuite] = make(map[string]InstallPaths)
 				}
 				name := ctx.ModuleName(m)
 				files[testSuite][name] = append(files[testSuite][name],
-					OtherModuleProviderOrDefault(ctx, tsm, InstallFilesProvider).InstallFiles...)
+					OtherModuleProviderOrDefault(ctx, m, InstallFilesProvider).InstallFiles...)
 			}
 		}
 	})
 
-	t.robolectric = robolectricTestSuite(ctx, files["robolectric-tests"])
-	ctx.Phony("robolectric-tests", t.robolectric...)
+	robolectricZip, robolectrictListZip := buildTestSuite(ctx, "robolectric-tests", files["robolectric-tests"])
+	ctx.Phony("robolectric-tests", robolectricZip, robolectrictListZip)
+	ctx.DistForGoal("robolectric-tests", robolectricZip, robolectrictListZip)
 
-	t.ravenwood = ravenwoodTestSuite(ctx, files["ravenwood-tests"])
-	ctx.Phony("ravenwood-tests", t.ravenwood...)
+	ravenwoodZip, ravenwoodListZip := buildTestSuite(ctx, "ravenwood-tests", files["ravenwood-tests"])
+	ctx.Phony("ravenwood-tests", ravenwoodZip, ravenwoodListZip)
+	ctx.DistForGoal("ravenwood-tests", ravenwoodZip, ravenwoodListZip)
 }
 
-func (t *testSuiteFiles) MakeVars(ctx MakeVarsContext) {
-	ctx.DistForGoal("robolectric-tests", t.robolectric...)
-	ctx.DistForGoal("ravenwood-tests", t.ravenwood...)
-}
-
-func robolectricTestSuite(ctx SingletonContext, files map[string]InstallPaths) []Path {
+func buildTestSuite(ctx SingletonContext, suiteName string, files map[string]InstallPaths) (Path, Path) {
 	var installedPaths InstallPaths
 	for _, module := range SortedKeys(files) {
 		installedPaths = append(installedPaths, files[module]...)
 	}
 
-	outputFile := pathForPackaging(ctx, "robolectric-tests.zip")
+	outputFile := pathForPackaging(ctx, suiteName+".zip")
 	rule := NewRuleBuilder(pctx, ctx)
 	rule.Command().BuiltTool("soong_zip").
 		FlagWithOutput("-o ", outputFile).
@@ -80,8 +88,8 @@
 		FlagWithRspFileInputList("-r ", outputFile.ReplaceExtension(ctx, "rsp"), installedPaths.Paths()).
 		Flag("-sha256") // necessary to save cas_uploader's time
 
-	testList := buildTestList(ctx, "robolectric-tests_list", installedPaths)
-	testListZipOutputFile := pathForPackaging(ctx, "robolectric-tests_list.zip")
+	testList := buildTestList(ctx, suiteName+"_list", installedPaths)
+	testListZipOutputFile := pathForPackaging(ctx, suiteName+"_list.zip")
 
 	rule.Command().BuiltTool("soong_zip").
 		FlagWithOutput("-o ", testListZipOutputFile).
@@ -89,38 +97,9 @@
 		FlagWithInput("-f ", testList).
 		Flag("-sha256")
 
-	rule.Build("robolectric_tests_zip", "robolectric-tests.zip")
+	rule.Build(strings.ReplaceAll(suiteName, "-", "_")+"_zip", suiteName+".zip")
 
-	return []Path{outputFile, testListZipOutputFile}
-}
-
-func ravenwoodTestSuite(ctx SingletonContext, files map[string]InstallPaths) []Path {
-	var installedPaths InstallPaths
-	for _, module := range SortedKeys(files) {
-		installedPaths = append(installedPaths, files[module]...)
-	}
-
-	outputFile := pathForPackaging(ctx, "ravenwood-tests.zip")
-	rule := NewRuleBuilder(pctx, ctx)
-	rule.Command().BuiltTool("soong_zip").
-		FlagWithOutput("-o ", outputFile).
-		FlagWithArg("-P ", "host/testcases").
-		FlagWithArg("-C ", pathForTestCases(ctx).String()).
-		FlagWithRspFileInputList("-r ", outputFile.ReplaceExtension(ctx, "rsp"), installedPaths.Paths()).
-		Flag("-sha256") // necessary to save cas_uploader's time
-
-	testList := buildTestList(ctx, "ravenwood-tests_list", installedPaths)
-	testListZipOutputFile := pathForPackaging(ctx, "ravenwood-tests_list.zip")
-
-	rule.Command().BuiltTool("soong_zip").
-		FlagWithOutput("-o ", testListZipOutputFile).
-		FlagWithArg("-C ", pathForPackaging(ctx).String()).
-		FlagWithInput("-f ", testList).
-		Flag("-sha256")
-
-	rule.Build("ravenwood_tests_zip", "ravenwood-tests.zip")
-
-	return []Path{outputFile, testListZipOutputFile}
+	return outputFile, testListZipOutputFile
 }
 
 func buildTestList(ctx SingletonContext, listFile string, installedPaths InstallPaths) Path {
diff --git a/android/test_suites_test.go b/android/test_suites_test.go
index db9a34d..03aa424 100644
--- a/android/test_suites_test.go
+++ b/android/test_suites_test.go
@@ -52,7 +52,7 @@
 		}
 	`)
 
-	config := ctx.SingletonForTests("testsuites")
+	config := ctx.SingletonForTests(t, "testsuites")
 	allOutputs := config.AllOutputs()
 
 	wantContents := map[string]string{
@@ -108,8 +108,13 @@
 
 func (f *fake_module) GenerateAndroidBuildActions(ctx ModuleContext) {
 	for _, output := range f.props.Outputs {
-		ctx.InstallFile(pathForTestCases(ctx), output, nil)
+		f := PathForModuleOut(ctx, output)
+		ctx.InstallFile(pathForTestCases(ctx), output, f)
 	}
+
+	SetProvider(ctx, TestSuiteInfoProvider, TestSuiteInfo{
+		TestSuites: f.TestSuites(),
+	})
 }
 
 func (f *fake_module) TestSuites() []string {
diff --git a/android/testing.go b/android/testing.go
index 765839f..d2949ec 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -530,7 +530,8 @@
 // both have the same value. Both the module and the map are allowed to have
 // extra variations that the other doesn't have. Panics if not exactly one
 // module variant matches.
-func (ctx *TestContext) ModuleVariantForTests(name string, matchVariations map[string]string) TestingModule {
+func (ctx *TestContext) ModuleVariantForTests(t *testing.T, name string, matchVariations map[string]string) TestingModule {
+	t.Helper()
 	modules := []Module{}
 	ctx.VisitAllModules(func(m blueprint.Module) {
 		if ctx.ModuleName(m) == name {
@@ -562,12 +563,12 @@
 		})
 
 		if len(allVariants) == 0 {
-			panic(fmt.Errorf("failed to find module %q. All modules:\n  %s",
-				name, strings.Join(SortedUniqueStrings(allModuleNames), "\n  ")))
+			t.Fatalf("failed to find module %q. All modules:\n  %s",
+				name, strings.Join(SortedUniqueStrings(allModuleNames), "\n  "))
 		} else {
 			sort.Strings(allVariants)
-			panic(fmt.Errorf("failed to find module %q matching %v. All variants:\n  %s",
-				name, matchVariations, strings.Join(allVariants, "\n  ")))
+			t.Fatalf("failed to find module %q matching %v. All variants:\n  %s",
+				name, matchVariations, strings.Join(allVariants, "\n  "))
 		}
 	}
 
@@ -577,14 +578,15 @@
 			moduleStrings = append(moduleStrings, m.String())
 		}
 		sort.Strings(moduleStrings)
-		panic(fmt.Errorf("module %q has more than one variant that match %v:\n  %s",
-			name, matchVariations, strings.Join(moduleStrings, "\n  ")))
+		t.Fatalf("module %q has more than one variant that match %v:\n  %s",
+			name, matchVariations, strings.Join(moduleStrings, "\n  "))
 	}
 
-	return newTestingModule(ctx.config, modules[0])
+	return newTestingModule(t, ctx.config, modules[0])
 }
 
-func (ctx *TestContext) ModuleForTests(name, variant string) TestingModule {
+func (ctx *TestContext) ModuleForTests(t *testing.T, name, variant string) TestingModule {
+	t.Helper()
 	var module Module
 	ctx.VisitAllModules(func(m blueprint.Module) {
 		if ctx.ModuleName(m) == name && ctx.ModuleSubDir(m) == variant {
@@ -605,15 +607,15 @@
 		sort.Strings(allVariants)
 
 		if len(allVariants) == 0 {
-			panic(fmt.Errorf("failed to find module %q. All modules:\n  %s",
-				name, strings.Join(SortedUniqueStrings(allModuleNames), "\n  ")))
+			t.Fatalf("failed to find module %q. All modules:\n  %s",
+				name, strings.Join(SortedUniqueStrings(allModuleNames), "\n  "))
 		} else {
-			panic(fmt.Errorf("failed to find module %q variant %q. All variants:\n  %s",
-				name, variant, strings.Join(allVariants, "\n  ")))
+			t.Fatalf("failed to find module %q variant %q. All variants:\n  %s",
+				name, variant, strings.Join(allVariants, "\n  "))
 		}
 	}
 
-	return newTestingModule(ctx.config, module)
+	return newTestingModule(t, ctx.config, module)
 }
 
 func (ctx *TestContext) ModuleVariantsForTests(name string) []string {
@@ -627,21 +629,24 @@
 }
 
 // SingletonForTests returns a TestingSingleton for the singleton registered with the given name.
-func (ctx *TestContext) SingletonForTests(name string) TestingSingleton {
+func (ctx *TestContext) SingletonForTests(t *testing.T, name string) TestingSingleton {
+	t.Helper()
 	allSingletonNames := []string{}
 	for _, s := range ctx.Singletons() {
 		n := ctx.SingletonName(s)
 		if n == name {
 			return TestingSingleton{
-				baseTestingComponent: newBaseTestingComponent(ctx.config, s.(testBuildProvider)),
+				baseTestingComponent: newBaseTestingComponent(t, ctx.config, s.(testBuildProvider)),
 				singleton:            s.(*singletonAdaptor).Singleton,
 			}
 		}
 		allSingletonNames = append(allSingletonNames, n)
 	}
 
-	panic(fmt.Errorf("failed to find singleton %q."+
-		"\nall singletons: %v", name, allSingletonNames))
+	t.Fatalf("failed to find singleton %q."+
+		"\nall singletons: %v", name, allSingletonNames)
+
+	return TestingSingleton{}
 }
 
 type InstallMakeRule struct {
@@ -651,6 +656,7 @@
 }
 
 func parseMkRules(t *testing.T, config Config, nodes []mkparser.Node) []InstallMakeRule {
+	t.Helper()
 	var rules []InstallMakeRule
 	for _, node := range nodes {
 		if mkParserRule, ok := node.(*mkparser.Rule); ok {
@@ -688,7 +694,8 @@
 }
 
 func (ctx *TestContext) InstallMakeRulesForTesting(t *testing.T) []InstallMakeRule {
-	installs := ctx.SingletonForTests("makevars").Singleton().(*makeVarsSingleton).installsForTesting
+	t.Helper()
+	installs := ctx.SingletonForTests(t, "makevars").Singleton().(*makeVarsSingleton).installsForTesting
 	buf := bytes.NewBuffer(append([]byte(nil), installs...))
 	parser := mkparser.NewParser("makevars", buf)
 
@@ -728,8 +735,9 @@
 //
 // It is necessary to use PrepareForTestAccessingMakeVars in tests that want to call this function.
 // Along with any other preparers needed to add the make vars.
-func (ctx *TestContext) MakeVarsForTesting(filter func(variable MakeVarVariable) bool) []MakeVarVariable {
-	vars := ctx.SingletonForTests("makevars").Singleton().(*makeVarsSingleton).varsForTesting
+func (ctx *TestContext) MakeVarsForTesting(t *testing.T, filter func(variable MakeVarVariable) bool) []MakeVarVariable {
+	t.Helper()
+	vars := ctx.SingletonForTests(t, "makevars").Singleton().(*makeVarsSingleton).varsForTesting
 	result := make([]MakeVarVariable, 0, len(vars))
 	for _, v := range vars {
 		if filter(v) {
@@ -846,12 +854,13 @@
 
 // baseTestingComponent provides functionality common to both TestingModule and TestingSingleton.
 type baseTestingComponent struct {
+	t        *testing.T
 	config   Config
 	provider testBuildProvider
 }
 
-func newBaseTestingComponent(config Config, provider testBuildProvider) baseTestingComponent {
-	return baseTestingComponent{config, provider}
+func newBaseTestingComponent(t *testing.T, config Config, provider testBuildProvider) baseTestingComponent {
+	return baseTestingComponent{t, config, provider}
 }
 
 // A function that will normalize a string containing paths, e.g. ninja command, by replacing
@@ -922,9 +931,10 @@
 }
 
 func (b baseTestingComponent) buildParamsFromRule(rule string) TestingBuildParams {
+	b.t.Helper()
 	p, searchRules := b.maybeBuildParamsFromRule(rule)
 	if p.Rule == nil {
-		panic(fmt.Errorf("couldn't find rule %q.\nall rules:\n%s", rule, strings.Join(searchRules, "\n")))
+		b.t.Fatalf("couldn't find rule %q.\nall rules:\n%s", rule, strings.Join(searchRules, "\n"))
 	}
 	return p
 }
@@ -941,9 +951,10 @@
 }
 
 func (b baseTestingComponent) buildParamsFromDescription(desc string) TestingBuildParams {
+	b.t.Helper()
 	p, searchedDescriptions := b.maybeBuildParamsFromDescription(desc)
 	if p.Rule == nil {
-		panic(fmt.Errorf("couldn't find description %q\nall descriptions:\n%s", desc, strings.Join(searchedDescriptions, "\n")))
+		b.t.Fatalf("couldn't find description %q\nall descriptions:\n%s", desc, strings.Join(searchedDescriptions, "\n"))
 	}
 	return p
 }
@@ -974,10 +985,11 @@
 }
 
 func (b baseTestingComponent) buildParamsFromOutput(file string) TestingBuildParams {
+	b.t.Helper()
 	p, searchedOutputs := b.maybeBuildParamsFromOutput(file)
 	if p.Rule == nil {
-		panic(fmt.Errorf("couldn't find output %q.\nall outputs:\n    %s\n",
-			file, strings.Join(searchedOutputs, "\n    ")))
+		b.t.Fatalf("couldn't find output %q.\nall outputs:\n    %s\n",
+			file, strings.Join(searchedOutputs, "\n    "))
 	}
 	return p
 }
@@ -999,6 +1011,7 @@
 
 // Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name.  Panics if no rule is found.
 func (b baseTestingComponent) Rule(rule string) TestingBuildParams {
+	b.t.Helper()
 	return b.buildParamsFromRule(rule)
 }
 
@@ -1012,6 +1025,7 @@
 // Description finds a call to ctx.Build with BuildParams.Description set to a the given string.  Panics if no rule is
 // found.
 func (b baseTestingComponent) Description(desc string) TestingBuildParams {
+	b.t.Helper()
 	return b.buildParamsFromDescription(desc)
 }
 
@@ -1025,6 +1039,7 @@
 // Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
 // value matches the provided string.  Panics if no rule is found.
 func (b baseTestingComponent) Output(file string) TestingBuildParams {
+	b.t.Helper()
 	return b.buildParamsFromOutput(file)
 }
 
@@ -1040,9 +1055,9 @@
 	module Module
 }
 
-func newTestingModule(config Config, module Module) TestingModule {
+func newTestingModule(t *testing.T, config Config, module Module) TestingModule {
 	return TestingModule{
-		newBaseTestingComponent(config, module),
+		newBaseTestingComponent(t, config, module),
 		module,
 	}
 }
@@ -1152,24 +1167,24 @@
 	config.katiEnabled = true
 }
 
-func AndroidMkEntriesForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) []AndroidMkEntries {
+func AndroidMkEntriesForTest(t *testing.T, ctx *TestContext, mod Module) []AndroidMkEntries {
 	t.Helper()
 	var p AndroidMkEntriesProvider
 	var ok bool
 	if p, ok = mod.(AndroidMkEntriesProvider); !ok {
-		t.Errorf("module does not implement AndroidMkEntriesProvider: " + mod.Name())
+		t.Error("module does not implement AndroidMkEntriesProvider: " + mod.Name())
 	}
 
 	entriesList := p.AndroidMkEntries()
-	aconfigUpdateAndroidMkEntries(ctx, mod.(Module), &entriesList)
+	aconfigUpdateAndroidMkEntries(ctx, mod, &entriesList)
 	for i := range entriesList {
 		entriesList[i].fillInEntries(ctx, mod)
 	}
 	return entriesList
 }
 
-func AndroidMkInfoForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) *AndroidMkProviderInfo {
-	if runtime.GOOS == "darwin" && mod.(Module).base().Os() != Darwin {
+func AndroidMkInfoForTest(t *testing.T, ctx *TestContext, mod Module) *AndroidMkProviderInfo {
+	if runtime.GOOS == "darwin" && mod.base().Os() != Darwin {
 		// The AndroidMkInfo provider is not set in this case.
 		t.Skip("AndroidMkInfo provider is not set on darwin")
 	}
@@ -1177,31 +1192,32 @@
 	t.Helper()
 	var ok bool
 	if _, ok = mod.(AndroidMkProviderInfoProducer); !ok {
-		t.Errorf("module does not implement AndroidMkProviderInfoProducer: " + mod.Name())
+		t.Error("module does not implement AndroidMkProviderInfoProducer: " + mod.Name())
 	}
 
 	info := OtherModuleProviderOrDefault(ctx, mod, AndroidMkInfoProvider)
-	aconfigUpdateAndroidMkInfos(ctx, mod.(Module), info)
-	info.PrimaryInfo.fillInEntries(ctx, mod)
+	aconfigUpdateAndroidMkInfos(ctx, mod, info)
+	commonInfo := OtherModulePointerProviderOrDefault(ctx, mod, CommonModuleInfoProvider)
+	info.PrimaryInfo.fillInEntries(ctx, mod, commonInfo)
 	if len(info.ExtraInfo) > 0 {
 		for _, ei := range info.ExtraInfo {
-			ei.fillInEntries(ctx, mod)
+			ei.fillInEntries(ctx, mod, commonInfo)
 		}
 	}
 
 	return info
 }
 
-func AndroidMkDataForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) AndroidMkData {
+func AndroidMkDataForTest(t *testing.T, ctx *TestContext, mod Module) AndroidMkData {
 	t.Helper()
 	var p AndroidMkDataProvider
 	var ok bool
 	if p, ok = mod.(AndroidMkDataProvider); !ok {
-		t.Fatalf("module does not implement AndroidMkDataProvider: " + mod.Name())
+		t.Fatal("module does not implement AndroidMkDataProvider: " + mod.Name())
 	}
 	data := p.AndroidMk()
 	data.fillInData(ctx, mod)
-	aconfigUpdateAndroidMkData(ctx, mod.(Module), &data)
+	aconfigUpdateAndroidMkData(ctx, mod, &data)
 	return data
 }
 
diff --git a/android/transition.go b/android/transition.go
new file mode 100644
index 0000000..0677ca1
--- /dev/null
+++ b/android/transition.go
@@ -0,0 +1,410 @@
+// 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
+
+import "github.com/google/blueprint"
+
+// TransitionMutator implements a top-down mechanism where a module tells its
+// direct dependencies what variation they should be built in but the dependency
+// has the final say.
+//
+// When implementing a transition mutator, one needs to implement four methods:
+//   - Split() that tells what variations a module has by itself
+//   - OutgoingTransition() where a module tells what it wants from its
+//     dependency
+//   - IncomingTransition() where a module has the final say about its own
+//     variation
+//   - Mutate() that changes the state of a module depending on its variation
+//
+// That the effective variation of module B when depended on by module A is the
+// composition the outgoing transition of module A and the incoming transition
+// of module B.
+//
+// the outgoing transition should not take the properties of the dependency into
+// account, only those of the module that depends on it. For this reason, the
+// dependency is not even passed into it as an argument. Likewise, the incoming
+// transition should not take the properties of the depending module into
+// account and is thus not informed about it. This makes for a nice
+// decomposition of the decision logic.
+//
+// A given transition mutator only affects its own variation; other variations
+// stay unchanged along the dependency edges.
+//
+// Soong makes sure that all modules are created in the desired variations and
+// that dependency edges are set up correctly. This ensures that "missing
+// variation" errors do not happen and allows for more flexible changes in the
+// value of the variation among dependency edges (as oppposed to bottom-up
+// mutators where if module A in variation X depends on module B and module B
+// has that variation X, A must depend on variation X of B)
+//
+// The limited power of the context objects passed to individual mutators
+// methods also makes it more difficult to shoot oneself in the foot. Complete
+// safety is not guaranteed because no one prevents individual transition
+// mutators from mutating modules in illegal ways and for e.g. Split() or
+// Mutate() to run their own visitations of the transitive dependency of the
+// module and both of these are bad ideas, but it's better than no guardrails at
+// all.
+//
+// This model is pretty close to Bazel's configuration transitions. The mapping
+// between concepts in Soong and Bazel is as follows:
+//   - Module == configured target
+//   - Variant == configuration
+//   - Variation name == configuration flag
+//   - Variation == configuration flag value
+//   - Outgoing transition == attribute transition
+//   - Incoming transition == rule transition
+//
+// The Split() method does not have a Bazel equivalent and Bazel split
+// transitions do not have a Soong equivalent.
+//
+// Mutate() does not make sense in Bazel due to the different models of the
+// two systems: when creating new variations, Soong clones the old module and
+// thus some way is needed to change it state whereas Bazel creates each
+// configuration of a given configured target anew.
+type TransitionMutator[T blueprint.TransitionInfo] interface {
+	// Split returns the set of variations that should be created for a module no
+	// matter who depends on it. Used when Make depends on a particular variation
+	// or when the module knows its variations just based on information given to
+	// it in the Blueprint file. This method should not mutate the module it is
+	// called on.
+	Split(ctx BaseModuleContext) []T
+
+	// OutgoingTransition is called on a module to determine which variation it wants
+	// from its direct dependencies. The dependency itself can override this decision.
+	// This method should not mutate the module itself.
+	OutgoingTransition(ctx OutgoingTransitionContext, sourceTransitionInfo T) T
+
+	// IncomingTransition is called on a module to determine which variation it should
+	// be in based on the variation modules that depend on it want. This gives the module
+	// a final say about its own variations. This method should not mutate the module
+	// itself.
+	IncomingTransition(ctx IncomingTransitionContext, incomingTransitionInfo T) T
+
+	// Mutate is called after a module was split into multiple variations on each variation.
+	// It should not split the module any further but adding new dependencies is
+	// fine. Unlike all the other methods on TransitionMutator, this method is
+	// allowed to mutate the module.
+	Mutate(ctx BottomUpMutatorContext, transitionInfo T)
+
+	// TransitionInfoFromVariation is called when adding dependencies with an explicit variation after the
+	// TransitionMutator has already run.  It takes a variation name and returns a TransitionInfo for that
+	// variation.  It may not be possible for some TransitionMutators to generate an appropriate TransitionInfo
+	// if the variation does not contain all the information from the TransitionInfo, in which case the
+	// TransitionMutator can panic in TransitionInfoFromVariation, and adding dependencies with explicit variations
+	// for this TransitionMutator is not supported.
+	TransitionInfoFromVariation(variation string) T
+}
+
+// androidTransitionMutator is a copy of blueprint.TransitionMutator with the context argument types changed
+// from blueprint.BaseModuleContext to BaseModuleContext, etc.
+type androidTransitionMutator interface {
+	Split(ctx BaseModuleContext) []blueprint.TransitionInfo
+	OutgoingTransition(ctx OutgoingTransitionContext, sourceTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo
+	IncomingTransition(ctx IncomingTransitionContext, incomingTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo
+	Mutate(ctx BottomUpMutatorContext, transitionInfo blueprint.TransitionInfo)
+	TransitionInfoFromVariation(variation string) blueprint.TransitionInfo
+}
+
+// VariationTransitionMutator is a simpler version of androidTransitionMutator that passes variation strings instead
+// of a blueprint.TransitionInfo object.
+type VariationTransitionMutator interface {
+	Split(ctx BaseModuleContext) []string
+	OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string
+	IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string
+	Mutate(ctx BottomUpMutatorContext, variation string)
+}
+
+type IncomingTransitionContext interface {
+	ArchModuleContext
+	ModuleProviderContext
+	ModuleErrorContext
+
+	// Module returns the target of the dependency edge for which the transition
+	// is being computed
+	Module() Module
+
+	// ModuleName returns the name of the module.  This is generally the value that was returned by Module.Name() when
+	// the module was created, but may have been modified by calls to BottomUpMutatorContext.Rename.
+	ModuleName() string
+
+	// DepTag() Returns the dependency tag through which this dependency is
+	// reached
+	DepTag() blueprint.DependencyTag
+
+	// Config returns the configuration for the build.
+	Config() Config
+
+	DeviceConfig() DeviceConfig
+
+	// IsAddingDependency returns true if the transition is being called while adding a dependency
+	// after the transition mutator has already run, or false if it is being called when the transition
+	// mutator is running.  This should be used sparingly, all uses will have to be removed in order
+	// to support creating variants on demand.
+	IsAddingDependency() bool
+}
+
+type OutgoingTransitionContext interface {
+	ArchModuleContext
+	ModuleProviderContext
+
+	// Module returns the target of the dependency edge for which the transition
+	// is being computed
+	Module() Module
+
+	// ModuleName returns the name of the module.  This is generally the value that was returned by Module.Name() when
+	// the module was created, but may have been modified by calls to BottomUpMutatorContext.Rename.
+	ModuleName() string
+
+	// DepTag() Returns the dependency tag through which this dependency is
+	// reached
+	DepTag() blueprint.DependencyTag
+
+	// Config returns the configuration for the build.
+	Config() Config
+
+	DeviceConfig() DeviceConfig
+}
+
+// androidTransitionMutatorAdapter wraps an androidTransitionMutator to convert it to a blueprint.TransitionInfo
+// by converting the blueprint.*Context objects into android.*Context objects.
+type androidTransitionMutatorAdapter struct {
+	finalPhase bool
+	mutator    androidTransitionMutator
+	name       string
+}
+
+func (a *androidTransitionMutatorAdapter) Split(ctx blueprint.BaseModuleContext) []blueprint.TransitionInfo {
+	if a.finalPhase {
+		panic("TransitionMutator not allowed in FinalDepsMutators")
+	}
+	m := ctx.Module().(Module)
+	moduleContext := m.base().baseModuleContextFactory(ctx)
+	return a.mutator.Split(&moduleContext)
+}
+
+func (a *androidTransitionMutatorAdapter) OutgoingTransition(bpctx blueprint.OutgoingTransitionContext,
+	sourceTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo {
+	m := bpctx.Module().(Module)
+	ctx := outgoingTransitionContextPool.Get()
+	defer outgoingTransitionContextPool.Put(ctx)
+	*ctx = outgoingTransitionContextImpl{
+		archModuleContext: m.base().archModuleContextFactory(bpctx),
+		bp:                bpctx,
+	}
+	return a.mutator.OutgoingTransition(ctx, sourceTransitionInfo)
+}
+
+func (a *androidTransitionMutatorAdapter) IncomingTransition(bpctx blueprint.IncomingTransitionContext,
+	incomingTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo {
+	m := bpctx.Module().(Module)
+	ctx := incomingTransitionContextPool.Get()
+	defer incomingTransitionContextPool.Put(ctx)
+	*ctx = incomingTransitionContextImpl{
+		archModuleContext: m.base().archModuleContextFactory(bpctx),
+		bp:                bpctx,
+	}
+	return a.mutator.IncomingTransition(ctx, incomingTransitionInfo)
+}
+
+func (a *androidTransitionMutatorAdapter) Mutate(ctx blueprint.BottomUpMutatorContext, transitionInfo blueprint.TransitionInfo) {
+	am := ctx.Module().(Module)
+	variation := transitionInfo.Variation()
+	if variation != "" {
+		// TODO: this should really be checking whether the TransitionMutator affected this module, not
+		//  the empty variant, but TransitionMutator has no concept of skipping a module.
+		base := am.base()
+		base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, a.name)
+		base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variation)
+	}
+
+	mctx := bottomUpMutatorContextFactory(ctx, am, a.finalPhase)
+	defer bottomUpMutatorContextPool.Put(mctx)
+	a.mutator.Mutate(mctx, transitionInfo)
+}
+
+func (a *androidTransitionMutatorAdapter) TransitionInfoFromVariation(variation string) blueprint.TransitionInfo {
+	return a.mutator.TransitionInfoFromVariation(variation)
+}
+
+// variationTransitionMutatorAdapter wraps a VariationTransitionMutator to convert it to an androidTransitionMutator
+// by wrapping the string info object used by VariationTransitionMutator with variationTransitionInfo to convert it into
+// blueprint.TransitionInfo.
+type variationTransitionMutatorAdapter struct {
+	m VariationTransitionMutator
+}
+
+func (v variationTransitionMutatorAdapter) Split(ctx BaseModuleContext) []blueprint.TransitionInfo {
+	variations := v.m.Split(ctx)
+	transitionInfos := make([]blueprint.TransitionInfo, 0, len(variations))
+	for _, variation := range variations {
+		transitionInfos = append(transitionInfos, variationTransitionInfo{variation})
+	}
+	return transitionInfos
+}
+
+func (v variationTransitionMutatorAdapter) OutgoingTransition(ctx OutgoingTransitionContext,
+	sourceTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo {
+
+	sourceVariationTransitionInfo, _ := sourceTransitionInfo.(variationTransitionInfo)
+	outgoingVariation := v.m.OutgoingTransition(ctx, sourceVariationTransitionInfo.variation)
+	return variationTransitionInfo{outgoingVariation}
+}
+
+func (v variationTransitionMutatorAdapter) IncomingTransition(ctx IncomingTransitionContext,
+	incomingTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo {
+
+	incomingVariationTransitionInfo, _ := incomingTransitionInfo.(variationTransitionInfo)
+	variation := v.m.IncomingTransition(ctx, incomingVariationTransitionInfo.variation)
+	return variationTransitionInfo{variation}
+}
+
+func (v variationTransitionMutatorAdapter) Mutate(ctx BottomUpMutatorContext, transitionInfo blueprint.TransitionInfo) {
+	variationTransitionInfo, _ := transitionInfo.(variationTransitionInfo)
+	v.m.Mutate(ctx, variationTransitionInfo.variation)
+}
+
+func (v variationTransitionMutatorAdapter) TransitionInfoFromVariation(variation string) blueprint.TransitionInfo {
+	return variationTransitionInfo{variation}
+}
+
+// variationTransitionInfo is a blueprint.TransitionInfo that contains a single variation string.
+type variationTransitionInfo struct {
+	variation string
+}
+
+func (v variationTransitionInfo) Variation() string {
+	return v.variation
+}
+
+// genericTransitionMutatorAdapter wraps a TransitionMutator to convert it to an androidTransitionMutator
+type genericTransitionMutatorAdapter[T blueprint.TransitionInfo] struct {
+	m TransitionMutator[T]
+}
+
+// NewGenericTransitionMutatorAdapter is used to convert a generic TransitionMutator[T] into an androidTransitionMutator
+// that can be passed to RegisterMutatorsContext.InfoBasedTransition.
+func NewGenericTransitionMutatorAdapter[T blueprint.TransitionInfo](m TransitionMutator[T]) androidTransitionMutator {
+	return &genericTransitionMutatorAdapter[T]{m}
+}
+
+func (g *genericTransitionMutatorAdapter[T]) convertTransitionInfoToT(transitionInfo blueprint.TransitionInfo) T {
+	if transitionInfo == nil {
+		var zero T
+		return zero
+	}
+	return transitionInfo.(T)
+}
+
+func (g *genericTransitionMutatorAdapter[T]) Split(ctx BaseModuleContext) []blueprint.TransitionInfo {
+	transitionInfos := g.m.Split(ctx)
+	bpTransitionInfos := make([]blueprint.TransitionInfo, 0, len(transitionInfos))
+	for _, transitionInfo := range transitionInfos {
+		bpTransitionInfos = append(bpTransitionInfos, transitionInfo)
+	}
+	return bpTransitionInfos
+}
+
+func (g *genericTransitionMutatorAdapter[T]) OutgoingTransition(ctx OutgoingTransitionContext, sourceTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo {
+	sourceTransitionInfoT := g.convertTransitionInfoToT(sourceTransitionInfo)
+	return g.m.OutgoingTransition(ctx, sourceTransitionInfoT)
+}
+
+func (g *genericTransitionMutatorAdapter[T]) IncomingTransition(ctx IncomingTransitionContext, incomingTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo {
+	incomingTransitionInfoT := g.convertTransitionInfoToT(incomingTransitionInfo)
+	return g.m.IncomingTransition(ctx, incomingTransitionInfoT)
+}
+
+func (g *genericTransitionMutatorAdapter[T]) Mutate(ctx BottomUpMutatorContext, transitionInfo blueprint.TransitionInfo) {
+	transitionInfoT := g.convertTransitionInfoToT(transitionInfo)
+	g.m.Mutate(ctx, transitionInfoT)
+}
+
+func (g *genericTransitionMutatorAdapter[T]) TransitionInfoFromVariation(variation string) blueprint.TransitionInfo {
+	return g.m.TransitionInfoFromVariation(variation)
+}
+
+// incomingTransitionContextImpl wraps a blueprint.IncomingTransitionContext to convert it to an
+// IncomingTransitionContext.
+type incomingTransitionContextImpl struct {
+	archModuleContext
+	bp blueprint.IncomingTransitionContext
+}
+
+func (c *incomingTransitionContextImpl) Module() Module {
+	return c.bp.Module().(Module)
+}
+
+func (c *incomingTransitionContextImpl) ModuleName() string {
+	return c.bp.ModuleName()
+}
+
+func (c *incomingTransitionContextImpl) DepTag() blueprint.DependencyTag {
+	return c.bp.DepTag()
+}
+
+func (c *incomingTransitionContextImpl) Config() Config {
+	return c.bp.Config().(Config)
+}
+
+func (c *incomingTransitionContextImpl) DeviceConfig() DeviceConfig {
+	return DeviceConfig{c.bp.Config().(Config).deviceConfig}
+}
+
+func (c *incomingTransitionContextImpl) IsAddingDependency() bool {
+	return c.bp.IsAddingDependency()
+}
+
+func (c *incomingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) {
+	return c.bp.Provider(provider)
+}
+
+func (c *incomingTransitionContextImpl) ModuleErrorf(fmt string, args ...interface{}) {
+	c.bp.ModuleErrorf(fmt, args)
+}
+
+func (c *incomingTransitionContextImpl) PropertyErrorf(property, fmt string, args ...interface{}) {
+	c.bp.PropertyErrorf(property, fmt, args)
+}
+
+// outgoingTransitionContextImpl wraps a blueprint.OutgoingTransitionContext to convert it to an
+// OutgoingTransitionContext.
+type outgoingTransitionContextImpl struct {
+	archModuleContext
+	bp blueprint.OutgoingTransitionContext
+}
+
+func (c *outgoingTransitionContextImpl) Module() Module {
+	return c.bp.Module().(Module)
+}
+
+func (c *outgoingTransitionContextImpl) ModuleName() string {
+	return c.bp.ModuleName()
+}
+
+func (c *outgoingTransitionContextImpl) DepTag() blueprint.DependencyTag {
+	return c.bp.DepTag()
+}
+
+func (c *outgoingTransitionContextImpl) Config() Config {
+	return c.bp.Config().(Config)
+}
+
+func (c *outgoingTransitionContextImpl) DeviceConfig() DeviceConfig {
+	return DeviceConfig{c.bp.Config().(Config).deviceConfig}
+}
+
+func (c *outgoingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) {
+	return c.bp.Provider(provider)
+}
diff --git a/android/transition_test.go b/android/transition_test.go
new file mode 100644
index 0000000..f7618f3
--- /dev/null
+++ b/android/transition_test.go
@@ -0,0 +1,162 @@
+// 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
+
+import "testing"
+
+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 {
+			name: "foo",
+		}
+	`
+
+	var moduleStrings []string
+
+	GroupFixturePreparers(
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+
+			ctx.PreArchMutators(func(ctx RegisterMutatorsContext) {
+				ctx.Transition("pre_arch", &testTransitionMutator{
+					split: func(ctx BaseModuleContext) []string {
+						moduleStrings = append(moduleStrings, ctx.Module().String())
+						return []string{"a", "b"}
+					},
+				})
+			})
+
+			ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+				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.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())
+					ctx.Rename(ctx.Module().base().Name() + "_renamed1")
+				}).UsesRename()
+				ctx.BottomUp("final", func(ctx BottomUpMutatorContext) {
+					moduleStrings = append(moduleStrings, ctx.Module().String())
+				})
+			})
+
+			ctx.RegisterModuleType("test", mutatorTestModuleFactory)
+		}),
+		FixtureWithRootAndroidBp(bp),
+	).RunTest(t)
+
+	want := []string{
+		// Initial name.
+		"foo{}",
+
+		// After pre_arch (reversed because rename_top_down is TopDown so it visits in reverse order).
+		"foo{pre_arch:b}",
+		"foo{pre_arch:a}",
+
+		// After pre_deps (reversed because post_deps TransitionMutator.Split is TopDown).
+		"foo{pre_arch:b,pre_deps:d}",
+		"foo{pre_arch:b,pre_deps:c}",
+		"foo{pre_arch:a,pre_deps:d}",
+		"foo{pre_arch:a,pre_deps:c}",
+
+		// After post_deps.
+		"foo{pre_arch:a,pre_deps:c,post_deps:e}",
+		"foo{pre_arch:a,pre_deps:c,post_deps:f}",
+		"foo{pre_arch:a,pre_deps:d,post_deps:e}",
+		"foo{pre_arch:a,pre_deps:d,post_deps:f}",
+		"foo{pre_arch:b,pre_deps:c,post_deps:e}",
+		"foo{pre_arch:b,pre_deps:c,post_deps:f}",
+		"foo{pre_arch:b,pre_deps:d,post_deps:e}",
+		"foo{pre_arch:b,pre_deps:d,post_deps:f}",
+
+		// After rename_bottom_up.
+		"foo_renamed1{pre_arch:a,pre_deps:c,post_deps:e}",
+		"foo_renamed1{pre_arch:a,pre_deps:c,post_deps:f}",
+		"foo_renamed1{pre_arch:a,pre_deps:d,post_deps:e}",
+		"foo_renamed1{pre_arch:a,pre_deps:d,post_deps:f}",
+		"foo_renamed1{pre_arch:b,pre_deps:c,post_deps:e}",
+		"foo_renamed1{pre_arch:b,pre_deps:c,post_deps:f}",
+		"foo_renamed1{pre_arch:b,pre_deps:d,post_deps:e}",
+		"foo_renamed1{pre_arch:b,pre_deps:d,post_deps:f}",
+	}
+
+	AssertDeepEquals(t, "module String() values", want, moduleStrings)
+}
+
+func TestTransitionMutatorInFinalDeps(t *testing.T) {
+	GroupFixturePreparers(
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
+				ctx.Transition("vars", &testTransitionMutator{
+					split: func(ctx BaseModuleContext) []string {
+						return []string{"a", "b"}
+					},
+				})
+			})
+
+			ctx.RegisterModuleType("test", mutatorTestModuleFactory)
+		}),
+		FixtureWithRootAndroidBp(`test {name: "foo"}`),
+	).
+		ExtendWithErrorHandler(FixtureExpectsOneErrorPattern("not allowed in FinalDepsMutators")).
+		RunTest(t)
+}
diff --git a/android/util.go b/android/util.go
index 3fc4608..4520f40 100644
--- a/android/util.go
+++ b/android/util.go
@@ -23,7 +23,6 @@
 	"runtime"
 	"sort"
 	"strings"
-	"sync"
 
 	"github.com/google/blueprint/proptools"
 )
@@ -102,13 +101,6 @@
 	return buf.String()
 }
 
-// SortedStringKeys returns the keys of the given map in the ascending order.
-//
-// Deprecated: Use SortedKeys instead.
-func SortedStringKeys[V any](m map[string]V) []string {
-	return SortedKeys(m)
-}
-
 // SortedKeys returns the keys of the given map in the ascending order.
 func SortedKeys[T cmp.Ordered, V any](m map[T]V) []T {
 	if len(m) == 0 {
@@ -213,21 +205,23 @@
 }
 
 // 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
+// a boolean which is true if there is a difference, and then returns lists of unique elements
 // that are in l1 but not l2, and l2 but not l1.
 func ListSetDifference[T comparable](l1, l2 []T) (bool, []T, []T) {
 	listsDiffer := false
+	l1 = firstUnique(l1)
+	l2 = firstUnique(l2)
 	diff1 := []T{}
 	diff2 := []T{}
 	m1 := setFromList(l1)
 	m2 := setFromList(l2)
-	for t := range m1 {
+	for _, t := range l1 {
 		if _, ok := m2[t]; !ok {
 			diff1 = append(diff1, t)
 			listsDiffer = true
 		}
 	}
-	for t := range m2 {
+	for _, t := range l2 {
 		if _, ok := m1[t]; !ok {
 			diff2 = append(diff2, t)
 			listsDiffer = true
@@ -238,8 +232,13 @@
 
 // Returns true if the two lists have common elements.
 func HasIntersection[T comparable](l1, l2 []T) bool {
-	_, a, b := ListSetDifference(l1, l2)
-	return len(a)+len(b) < len(setFromList(l1))+len(setFromList(l2))
+	m1 := setFromList(l1)
+	for _, x := range l2 {
+		if _, ok := m1[x]; ok {
+			return true
+		}
+	}
+	return false
 }
 
 // Returns true if the given string s is prefixed with any string in the given prefix list.
@@ -308,6 +307,20 @@
 	return
 }
 
+// FilterListByPrefixes performs the same splitting as FilterList does, but treats the passed
+// filters as prefixes
+func FilterListByPrefix(list []string, filter []string) (remainder []string, filtered []string) {
+	for _, l := range list {
+		if HasAnyPrefix(l, filter) {
+			filtered = append(filtered, l)
+		} else {
+			remainder = append(remainder, l)
+		}
+	}
+
+	return
+}
+
 // FilterListPred returns the elements of the given list for which the predicate
 // returns true. Order is kept.
 func FilterListPred(list []string, pred func(s string) bool) (filtered []string) {
@@ -632,35 +645,6 @@
 	}
 }
 
-// SyncMap is a wrapper around sync.Map that provides type safety via generics.
-type SyncMap[K comparable, V any] struct {
-	sync.Map
-}
-
-// Load returns the value stored in the map for a key, or the zero value if no
-// value is present.
-// The ok result indicates whether value was found in the map.
-func (m *SyncMap[K, V]) Load(key K) (value V, ok bool) {
-	v, ok := m.Map.Load(key)
-	if !ok {
-		return *new(V), false
-	}
-	return v.(V), true
-}
-
-// Store sets the value for a key.
-func (m *SyncMap[K, V]) Store(key K, value V) {
-	m.Map.Store(key, value)
-}
-
-// LoadOrStore returns the existing value for the key if present.
-// Otherwise, it stores and returns the given value.
-// The loaded result is true if the value was loaded, false if stored.
-func (m *SyncMap[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
-	v, loaded := m.Map.LoadOrStore(key, value)
-	return v.(V), loaded
-}
-
 // AppendIfNotZero append the given value to the slice if it is not the zero value
 // for its type.
 func AppendIfNotZero[T comparable](slice []T, value T) []T {
diff --git a/android/variable.go b/android/variable.go
index e012103..f00dd13 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -65,12 +65,6 @@
 			Enabled proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"`
 		} `android:"arch_variant"`
 
-		// similar to `Unbundled_build`, but `Always_use_prebuilt_sdks` means that it uses prebuilt
-		// sdk specifically.
-		Always_use_prebuilt_sdks struct {
-			Enabled proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"`
-		} `android:"arch_variant"`
-
 		Malloc_low_memory struct {
 			Cflags              []string `android:"arch_variant"`
 			Shared_libs         []string `android:"arch_variant"`
@@ -162,6 +156,7 @@
 			Optimize struct {
 				Enabled *bool
 			}
+			Aaptflags []string
 		}
 
 		Uml struct {
@@ -215,7 +210,7 @@
 	Platform_display_version_name          *string  `json:",omitempty"`
 	Platform_version_name                  *string  `json:",omitempty"`
 	Platform_sdk_version                   *int     `json:",omitempty"`
-	Platform_sdk_minor_version             *int     `json:",omitempty"`
+	Platform_sdk_version_full              *string  `json:",omitempty"`
 	Platform_sdk_codename                  *string  `json:",omitempty"`
 	Platform_sdk_version_or_codename       *string  `json:",omitempty"`
 	Platform_sdk_final                     *bool    `json:",omitempty"`
@@ -230,8 +225,8 @@
 	Platform_version_last_stable           *string  `json:",omitempty"`
 	Platform_version_known_codenames       *string  `json:",omitempty"`
 
-	DeviceName                            *string  `json:",omitempty"`
-	DeviceProduct                         *string  `json:",omitempty"`
+	DeviceName                            *string  `json:",omitempty" generic:"generic"`
+	DeviceProduct                         *string  `json:",omitempty" generic:"generic"`
 	DeviceArch                            *string  `json:",omitempty"`
 	DeviceArchVariant                     *string  `json:",omitempty"`
 	DeviceCpuVariant                      *string  `json:",omitempty"`
@@ -533,7 +528,8 @@
 	OdmPropFiles       []string `json:",omitempty"`
 	VendorPropFiles    []string `json:",omitempty"`
 
-	EnableUffdGc *string `json:",omitempty"`
+	EnableUffdGc       *string `json:",omitempty"`
+	BoardKernelVersion *string `json:",omitempty"`
 
 	BoardAvbEnable                         *bool    `json:",omitempty"`
 	BoardAvbSystemAddHashtreeFooterArgs    []string `json:",omitempty"`
@@ -542,8 +538,6 @@
 
 	PartitionVarsForSoongMigrationOnlyDoNotUse PartitionVariables
 
-	ExtraAllowedDepsTxt *string `json:",omitempty"`
-
 	AdbKeys *string `json:",omitempty"`
 
 	DeviceMatrixFile       []string `json:",omitempty"`
@@ -552,10 +546,15 @@
 	SystemExtManifestFiles []string `json:",omitempty"`
 	DeviceManifestFiles    []string `json:",omitempty"`
 	OdmManifestFiles       []string `json:",omitempty"`
+
+	UseSoongNoticeXML *bool `json:",omitempty"`
+
+	StripByDefault *bool `json:",omitempty"`
 }
 
 type PartitionQualifiedVariablesType struct {
 	BuildingImage               bool   `json:",omitempty"`
+	PrebuiltImage               bool   `json:",omitempty"`
 	BoardErofsCompressor        string `json:",omitempty"`
 	BoardErofsCompressHints     string `json:",omitempty"`
 	BoardErofsPclusterSize      string `json:",omitempty"`
@@ -618,39 +617,55 @@
 	ProductUseDynamicPartitionSize bool   `json:",omitempty"`
 	CopyImagesForTargetFilesZip    bool   `json:",omitempty"`
 
-	VendorSecurityPatch string `json:",omitempty"`
+	VendorSecurityPatch     string `json:",omitempty"`
+	OdmSecurityPatch        string `json:",omitempty"`
+	SystemDlkmSecurityPatch string `json:",omitempty"`
+	VendorDlkmSecurityPatch string `json:",omitempty"`
+	OdmDlkmSecurityPatch    string `json:",omitempty"`
+
+	BuildingSystemOtherImage bool `json:",omitempty"`
 
 	// Boot image stuff
-	BuildingRamdiskImage            bool     `json:",omitempty"`
-	ProductBuildBootImage           bool     `json:",omitempty"`
-	ProductBuildVendorBootImage     string   `json:",omitempty"`
-	ProductBuildInitBootImage       bool     `json:",omitempty"`
-	BoardUsesRecoveryAsBoot         bool     `json:",omitempty"`
-	BoardPrebuiltBootimage          string   `json:",omitempty"`
-	BoardPrebuiltInitBootimage      string   `json:",omitempty"`
-	BoardBootimagePartitionSize     string   `json:",omitempty"`
-	BoardInitBootimagePartitionSize string   `json:",omitempty"`
-	BoardBootHeaderVersion          string   `json:",omitempty"`
-	TargetKernelPath                string   `json:",omitempty"`
-	BoardUsesGenericKernelImage     bool     `json:",omitempty"`
-	BootSecurityPatch               string   `json:",omitempty"`
-	InitBootSecurityPatch           string   `json:",omitempty"`
-	BoardIncludeDtbInBootimg        bool     `json:",omitempty"`
-	InternalKernelCmdline           []string `json:",omitempty"`
-	InternalBootconfig              []string `json:",omitempty"`
-	InternalBootconfigFile          string   `json:",omitempty"`
+	BuildingRamdiskImage              bool     `json:",omitempty"`
+	ProductBuildBootImage             bool     `json:",omitempty"`
+	ProductBuildVendorBootImage       string   `json:",omitempty"`
+	ProductBuildInitBootImage         bool     `json:",omitempty"`
+	BoardUsesRecoveryAsBoot           bool     `json:",omitempty"`
+	BoardPrebuiltBootimage            string   `json:",omitempty"`
+	BoardPrebuiltInitBootimage        string   `json:",omitempty"`
+	BoardBootimagePartitionSize       string   `json:",omitempty"`
+	BoardVendorBootimagePartitionSize string   `json:",omitempty"`
+	BoardInitBootimagePartitionSize   string   `json:",omitempty"`
+	BoardBootHeaderVersion            string   `json:",omitempty"`
+	TargetKernelPath                  string   `json:",omitempty"`
+	BoardUsesGenericKernelImage       bool     `json:",omitempty"`
+	BootSecurityPatch                 string   `json:",omitempty"`
+	InitBootSecurityPatch             string   `json:",omitempty"`
+	BoardIncludeDtbInBootimg          bool     `json:",omitempty"`
+	InternalKernelCmdline             []string `json:",omitempty"`
+	InternalBootconfig                []string `json:",omitempty"`
+	InternalBootconfigFile            string   `json:",omitempty"`
 
 	// Super image stuff
 	ProductUseDynamicPartitions       bool                                     `json:",omitempty"`
 	ProductRetrofitDynamicPartitions  bool                                     `json:",omitempty"`
 	ProductBuildSuperPartition        bool                                     `json:",omitempty"`
+	BuildingSuperEmptyImage           bool                                     `json:",omitempty"`
 	BoardSuperPartitionSize           string                                   `json:",omitempty"`
 	BoardSuperPartitionMetadataDevice string                                   `json:",omitempty"`
 	BoardSuperPartitionBlockDevices   []string                                 `json:",omitempty"`
 	BoardSuperPartitionGroups         map[string]BoardSuperPartitionGroupProps `json:",omitempty"`
 	ProductVirtualAbOta               bool                                     `json:",omitempty"`
 	ProductVirtualAbOtaRetrofit       bool                                     `json:",omitempty"`
+	ProductVirtualAbCompression       bool                                     `json:",omitempty"`
+	ProductVirtualAbCompressionMethod string                                   `json:",omitempty"`
+	ProductVirtualAbCompressionFactor string                                   `json:",omitempty"`
+	ProductVirtualAbCowVersion        string                                   `json:",omitempty"`
 	AbOtaUpdater                      bool                                     `json:",omitempty"`
+	AbOtaPartitions                   []string                                 `json:",omitempty"`
+	AbOtaKeys                         []string                                 `json:",omitempty"`
+	AbOtaPostInstallConfig            []string                                 `json:",omitempty"`
+	BoardSuperImageInUpdatePackage    bool                                     `json:",omitempty"`
 
 	// Avb (android verified boot) stuff
 	BoardAvbEnable          bool                                `json:",omitempty"`
@@ -689,6 +704,22 @@
 	ProductFsverityGenerateMetadata bool `json:",omitempty"`
 
 	TargetScreenDensity string `json:",omitempty"`
+
+	PrivateRecoveryUiProperties map[string]string `json:",omitempty"`
+
+	PrebuiltBootloader string `json:",omitempty"`
+
+	ProductFsCasefold    string `json:",omitempty"`
+	ProductQuotaProjid   string `json:",omitempty"`
+	ProductFsCompression string `json:",omitempty"`
+
+	ReleaseToolsExtensionDir string `json:",omitempty"`
+
+	BoardPartialOtaUpdatePartitionsList []string `json:",omitempty"`
+	BoardFlashBlockSize                 string   `json:",omitempty"`
+	BootloaderInUpdatePackage           bool     `json:",omitempty"`
+
+	BoardFastbootInfoFile string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/android/variable_test.go b/android/variable_test.go
index 73dc052..1d928f2 100644
--- a/android/variable_test.go
+++ b/android/variable_test.go
@@ -299,7 +299,7 @@
 		FixtureWithRootAndroidBp(bp),
 	).RunTest(t)
 
-	foo := result.ModuleForTests("foo", "").Module().(*productVariablesDefaultsTestModule)
+	foo := result.ModuleForTests(t, "foo", "").Module().(*productVariablesDefaultsTestModule)
 
 	want := []string{"defaults", "module", "product_variable_defaults", "product_variable_module"}
 	AssertDeepEquals(t, "foo", want, foo.properties.Foo)
@@ -360,7 +360,7 @@
 		FixtureWithRootAndroidBp(bp),
 	).RunTest(t)
 
-	foo := result.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*productVariablesDefaultsTestModule)
+	foo := result.ModuleForTests(t, "foo", "android_arm64_armv8-a").Module().(*productVariablesDefaultsTestModule)
 
 	want := []string{"module", "arm64"}
 	AssertDeepEquals(t, "foo", want, foo.properties.Foo)
diff --git a/android/vendor_api_levels.go b/android/vendor_api_levels.go
index 4d364fd..d32bc56 100644
--- a/android/vendor_api_levels.go
+++ b/android/vendor_api_levels.go
@@ -27,6 +27,8 @@
 		sdkVersion = 35
 	case 202504:
 		sdkVersion = 36
+	case 202604:
+		sdkVersion = 37
 	default:
 		ok = false
 	}
diff --git a/android/vintf_data.go b/android/vintf_data.go
index 401f4d2..2909817 100644
--- a/android/vintf_data.go
+++ b/android/vintf_data.go
@@ -140,6 +140,7 @@
 
 	// Process vintf fragment source file with assemble_vintf tool
 	builder.Command().
+		Implicits(inputPaths).
 		Flags(assembleVintfEnvs).
 		BuiltTool("assemble_vintf").
 		FlagWithArg("-i ", strings.Join(inputPaths.Strings(), ":")).
diff --git a/android/vintf_fragment.go b/android/vintf_fragment.go
index a3343fd..4a29fee 100644
--- a/android/vintf_fragment.go
+++ b/android/vintf_fragment.go
@@ -14,13 +14,16 @@
 
 package android
 
+import "github.com/google/blueprint"
+
 type vintfFragmentProperties struct {
 	// Vintf fragment XML file.
 	Src string `android:"path"`
 }
 
-type vintfFragmentModule struct {
+type VintfFragmentModule struct {
 	ModuleBase
+	ApexModuleBase
 
 	properties vintfFragmentProperties
 
@@ -36,11 +39,17 @@
 	ctx.RegisterModuleType("vintf_fragment", vintfLibraryFactory)
 }
 
+type VintfFragmentInfo struct {
+	OutputFile Path
+}
+
+var VintfFragmentInfoProvider = blueprint.NewProvider[VintfFragmentInfo]()
+
 // vintf_fragment module processes vintf fragment file and installs under etc/vintf/manifest.
 // Vintf fragment files formerly listed in vintf_fragment property would be transformed into
 // this module type.
 func vintfLibraryFactory() Module {
-	m := &vintfFragmentModule{}
+	m := &VintfFragmentModule{}
 	m.AddProperties(
 		&m.properties,
 	)
@@ -49,7 +58,7 @@
 	return m
 }
 
-func (m *vintfFragmentModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+func (m *VintfFragmentModule) GenerateAndroidBuildActions(ctx ModuleContext) {
 	builder := NewRuleBuilder(pctx, ctx)
 	srcVintfFragment := PathForModuleSrc(ctx, m.properties.Src)
 	processedVintfFragment := PathForModuleOut(ctx, srcVintfFragment.Base())
@@ -67,10 +76,18 @@
 	m.outputFilePath = processedVintfFragment
 
 	ctx.InstallFile(m.installDirPath, processedVintfFragment.Base(), processedVintfFragment)
+
+	SetProvider(ctx, VintfFragmentInfoProvider, VintfFragmentInfo{
+		OutputFile: m.OutputFile(),
+	})
+}
+
+func (m *VintfFragmentModule) OutputFile() Path {
+	return m.outputFilePath
 }
 
 // Make this module visible to AndroidMK so it can be referenced from modules defined from Android.mk files
-func (m *vintfFragmentModule) AndroidMkEntries() []AndroidMkEntries {
+func (m *VintfFragmentModule) AndroidMkEntries() []AndroidMkEntries {
 	return []AndroidMkEntries{{
 		Class:      "ETC",
 		OutputFile: OptionalPathForPath(m.outputFilePath),
@@ -82,3 +99,10 @@
 		},
 	}}
 }
+
+var _ ApexModule = (*VintfFragmentModule)(nil)
+
+// Implements android.ApexModule
+func (m *VintfFragmentModule) MinSdkVersionSupported(ctx BaseModuleContext) ApiLevel {
+	return MinApiLevel
+}
diff --git a/android/vintf_fragment_test.go b/android/vintf_fragment_test.go
index cd90b98..7f0078c 100644
--- a/android/vintf_fragment_test.go
+++ b/android/vintf_fragment_test.go
@@ -29,8 +29,8 @@
 
 	testResult := PrepareForTestWithAndroidBuildComponents.RunTestWithBp(t, bp)
 
-	vintfFragmentBuild := testResult.TestContext.ModuleForTests("test_vintf_fragment", "android_common").Rule("assemble_vintf")
+	vintfFragmentBuild := testResult.TestContext.ModuleForTests(t, "test_vintf_fragment", "android_common").Rule("assemble_vintf")
 	if !strings.Contains(vintfFragmentBuild.RuleParams.Command, "assemble_vintf") {
-		t.Errorf("Vintf_manifest build command does not process with assemble_vintf : " + vintfFragmentBuild.RuleParams.Command)
+		t.Error("Vintf_manifest build command does not process with assemble_vintf : " + vintfFragmentBuild.RuleParams.Command)
 	}
 }
diff --git a/android/visibility.go b/android/visibility.go
index cee465e..9153687 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -58,15 +58,29 @@
 var visibilityRuleRegexp = regexp.MustCompile(visibilityRulePattern)
 
 type visibilityModuleReference struct {
-	name   qualifiedModuleName
-	module Module
+	name          qualifiedModuleName
+	partitionType *string
 }
 
 func createVisibilityModuleReference(name, dir string, module Module) visibilityModuleReference {
-	return visibilityModuleReference{
-		name:   createQualifiedModuleName(name, dir),
-		module: module,
+	vis := visibilityModuleReference{
+		name: createQualifiedModuleName(name, dir),
 	}
+	if m, ok := module.(PartitionTypeInterface); ok {
+		pt := m.PartitionType()
+		vis.partitionType = &pt
+	}
+	return vis
+}
+
+func createVisibilityModuleProxyReference(ctx OtherModuleProviderContext, name, dir string, module ModuleProxy) visibilityModuleReference {
+	vis := visibilityModuleReference{
+		name: createQualifiedModuleName(name, dir),
+	}
+	if m, ok := OtherModuleProvider(ctx, module, PartitionTypeInfoProvider); ok {
+		vis.partitionType = &m.PartitionType
+	}
+	return vis
 }
 
 // A visibility rule is associated with a module and determines which other modules it is visible
@@ -222,9 +236,17 @@
 	PartitionType() string
 }
 
+type PartitionTypeInfo struct {
+	// Identifies which partition this is for //visibility:any_system_image (and others) visibility
+	// checks, and will be used in the future for API surface checks.
+	PartitionType string
+}
+
+var PartitionTypeInfoProvider = blueprint.NewProvider[PartitionTypeInfo]()
+
 func (r anyPartitionRule) matches(m visibilityModuleReference) bool {
-	if m2, ok := m.module.(PartitionTypeInterface); ok {
-		return m2.PartitionType() == r.partitionType
+	if m.partitionType != nil {
+		return *m.partitionType == r.partitionType
 	}
 	return false
 }
@@ -647,42 +669,6 @@
 	return v.rules
 }
 
-// Get the effective visibility rules, i.e. the actual rules that affect the visibility of the
-// property irrespective of where they are defined.
-//
-// Includes visibility rules specified by package default_visibility and/or on defaults.
-// Short hand forms, e.g. //:__subpackages__ are replaced with their full form, e.g.
-// //package/containing/rule:__subpackages__.
-func EffectiveVisibilityRules(ctx BaseModuleContext, module Module) VisibilityRuleSet {
-	moduleName := ctx.OtherModuleName(module)
-	dir := ctx.OtherModuleDir(module)
-	qualified := qualifiedModuleName{dir, moduleName}
-
-	rule := effectiveVisibilityRules(ctx.Config(), qualified)
-
-	currentModule := createVisibilityModuleReference(moduleName, dir, module)
-
-	// Modules are implicitly visible to other modules in the same package,
-	// without checking the visibility rules. Here we need to add that visibility
-	// explicitly.
-	if !rule.matches(currentModule) {
-		if len(rule) == 1 {
-			if _, ok := rule[0].(privateRule); ok {
-				// If the rule is //visibility:private we can't append another
-				// visibility to it. Semantically we need to convert it to a package
-				// visibility rule for the location where the result is used, but since
-				// modules are implicitly visible within the package we get the same
-				// result without any rule at all, so just make it an empty list to be
-				// appended below.
-				rule = nil
-			}
-		}
-		rule = append(rule, packageRule{dir})
-	}
-
-	return &visibilityRuleSet{rule.Strings()}
-}
-
 // Clear the default visibility properties so they can be replaced.
 func clearVisibilityProperties(module Module) {
 	module.base().visibilityPropertyInfo = nil
diff --git a/android/visibility_test.go b/android/visibility_test.go
index 277be0f..4acaa02 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -2112,7 +2112,10 @@
 	ctx.AddVariationDependencies(nil, dependencyTag{name: "mockdeps"}, j.properties.Deps...)
 }
 
-func (p *mockFilesystemModule) GenerateAndroidBuildActions(ModuleContext) {
+func (p *mockFilesystemModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	SetProvider(ctx, PartitionTypeInfoProvider, PartitionTypeInfo{
+		PartitionType: p.PartitionType(),
+	})
 }
 
 func (p *mockFilesystemModule) PartitionType() string {
diff --git a/android_sdk/sdk_repo_host_test.go b/android_sdk/sdk_repo_host_test.go
index 0688921..ce84204 100644
--- a/android_sdk/sdk_repo_host_test.go
+++ b/android_sdk/sdk_repo_host_test.go
@@ -44,7 +44,7 @@
 	`)
 
 	// produces "sdk-repo-{OS}-platform-tools.zip"
-	result.ModuleForTests("platform-tools", "linux_glibc_common").Output("sdk-repo-linux-platform-tools.zip")
+	result.ModuleForTests(t, "platform-tools", "linux_glibc_common").Output("sdk-repo-linux-platform-tools.zip")
 }
 
 func TestRemapPackageSpecs(t *testing.T) {
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 18c97b5..e723abd 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -103,25 +103,26 @@
 func init() {
 	addStandardProperties(bpparser.StringType,
 		map[string]string{
-			"LOCAL_MODULE":                  "name",
-			"LOCAL_CXX_STL":                 "stl",
-			"LOCAL_MULTILIB":                "compile_multilib",
-			"LOCAL_ARM_MODE_HACK":           "instruction_set",
-			"LOCAL_SDK_VERSION":             "sdk_version",
-			"LOCAL_MIN_SDK_VERSION":         "min_sdk_version",
-			"LOCAL_TARGET_SDK_VERSION":      "target_sdk_version",
-			"LOCAL_NDK_STL_VARIANT":         "stl",
-			"LOCAL_JAR_MANIFEST":            "manifest",
-			"LOCAL_CERTIFICATE":             "certificate",
-			"LOCAL_CERTIFICATE_LINEAGE":     "lineage",
-			"LOCAL_PACKAGE_NAME":            "name",
-			"LOCAL_MODULE_RELATIVE_PATH":    "relative_install_path",
-			"LOCAL_PROTOC_OPTIMIZE_TYPE":    "proto.type",
-			"LOCAL_MODULE_OWNER":            "owner",
-			"LOCAL_RENDERSCRIPT_TARGET_API": "renderscript.target_api",
-			"LOCAL_JAVA_LANGUAGE_VERSION":   "java_version",
-			"LOCAL_INSTRUMENTATION_FOR":     "instrumentation_for",
-			"LOCAL_MANIFEST_FILE":           "manifest",
+			"LOCAL_MODULE":                   "name",
+			"LOCAL_CXX_STL":                  "stl",
+			"LOCAL_MULTILIB":                 "compile_multilib",
+			"LOCAL_ARM_MODE_HACK":            "instruction_set",
+			"LOCAL_SDK_VERSION":              "sdk_version",
+			"LOCAL_MIN_SDK_VERSION":          "min_sdk_version",
+			"LOCAL_ROTATION_MIN_SDK_VERSION": "rotationMinSdkVersion",
+			"LOCAL_TARGET_SDK_VERSION":       "target_sdk_version",
+			"LOCAL_NDK_STL_VARIANT":          "stl",
+			"LOCAL_JAR_MANIFEST":             "manifest",
+			"LOCAL_CERTIFICATE":              "certificate",
+			"LOCAL_CERTIFICATE_LINEAGE":      "lineage",
+			"LOCAL_PACKAGE_NAME":             "name",
+			"LOCAL_MODULE_RELATIVE_PATH":     "relative_install_path",
+			"LOCAL_PROTOC_OPTIMIZE_TYPE":     "proto.type",
+			"LOCAL_MODULE_OWNER":             "owner",
+			"LOCAL_RENDERSCRIPT_TARGET_API":  "renderscript.target_api",
+			"LOCAL_JAVA_LANGUAGE_VERSION":    "java_version",
+			"LOCAL_INSTRUMENTATION_FOR":      "instrumentation_for",
+			"LOCAL_MANIFEST_FILE":            "manifest",
 
 			"LOCAL_DEX_PREOPT_PROFILE_CLASS_LISTING": "dex_preopt.profile",
 			"LOCAL_TEST_CONFIG":                      "test_config",
diff --git a/apex/androidmk.go b/apex/androidmk.go
index ec5ca15..0a5644a 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -75,7 +75,7 @@
 // populated by Soong for unconverted APEXes, or Bazel in mixed mode. Use
 // apexFile#isBazelPrebuilt to differentiate.
 func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, moduleDir string,
-	apexAndroidMkData android.AndroidMkData) []string {
+	apexAndroidMkData android.AndroidMkData) (archSpecificModuleNames []string, moduleNames []string) {
 
 	// apexBundleName comes from the 'name' property or soong module.
 	// apexName comes from 'name' property of apex_manifest.
@@ -84,11 +84,12 @@
 	// However, symbol files for apex files are installed under /apex/<apexBundleName> to avoid
 	// conflicts between two apexes with the same apexName.
 
-	moduleNames := []string{}
-
 	for _, fi := range a.filesInfo {
 		linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform()
 		moduleName := a.fullModuleName(apexBundleName, linkToSystemLib, &fi)
+		if !android.InList(moduleName, moduleNames) {
+			moduleNames = append(moduleNames, moduleName)
+		}
 
 		// This name will be added to LOCAL_REQUIRED_MODULES of the APEX. We need to be
 		// arch-specific otherwise we will end up installing both ABIs even when only
@@ -100,8 +101,8 @@
 		case "lib64":
 			aName = aName + ":64"
 		}
-		if !android.InList(aName, moduleNames) {
-			moduleNames = append(moduleNames, aName)
+		if !android.InList(aName, archSpecificModuleNames) {
+			archSpecificModuleNames = append(archSpecificModuleNames, aName)
 		}
 
 		if linkToSystemLib {
@@ -216,7 +217,7 @@
 			fmt.Fprintf(w, "%s: %s\n", fi.androidMkModuleName, moduleName)
 		}
 	}
-	return moduleNames
+	return
 }
 
 func (a *apexBundle) writeRequiredModules(w io.Writer, moduleNames []string) {
@@ -226,11 +227,6 @@
 	required = append(required, a.required...)
 	targetRequired = append(targetRequired, a.TargetRequiredModuleNames()...)
 	hostRequired = append(hostRequired, a.HostRequiredModuleNames()...)
-	for _, fi := range a.filesInfo {
-		required = append(required, fi.requiredModuleNames...)
-		targetRequired = append(targetRequired, fi.targetRequiredModuleNames...)
-		hostRequired = append(hostRequired, fi.hostRequiredModuleNames...)
-	}
 	android.AndroidMkEmitAssignList(w, "LOCAL_REQUIRED_MODULES", moduleNames, a.makeModulesToInstall, required)
 	android.AndroidMkEmitAssignList(w, "LOCAL_TARGET_REQUIRED_MODULES", targetRequired)
 	android.AndroidMkEmitAssignList(w, "LOCAL_HOST_REQUIRED_MODULES", hostRequired)
@@ -240,9 +236,10 @@
 	return android.AndroidMkData{
 		// While we do not provide a value for `Extra`, AconfigUpdateAndroidMkData may add some, which we must honor.
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+			archSpecificModuleNames := []string{}
 			moduleNames := []string{}
 			if a.installable() {
-				moduleNames = a.androidMkForFiles(w, name, moduleDir, data)
+				archSpecificModuleNames, moduleNames = a.androidMkForFiles(w, name, moduleDir, data)
 			}
 
 			fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)  # apex.apexBundle")
@@ -261,6 +258,7 @@
 				fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", a.installedFile.String())
 				fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", a.outputFile.String()+":"+a.installedFile.String())
 				fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_SYMLINKS := ", strings.Join(a.compatSymlinks.Strings(), " "))
+				fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS +=", a.extraInstalledPairs.String())
 			}
 			fmt.Fprintln(w, "LOCAL_APEX_KEY_PATH := ", a.apexKeysPath.String())
 
@@ -278,7 +276,7 @@
 			}
 
 			android.AndroidMkEmitAssignList(w, "LOCAL_OVERRIDES_MODULES", a.overridableProperties.Overrides)
-			a.writeRequiredModules(w, moduleNames)
+			a.writeRequiredModules(w, archSpecificModuleNames)
 			// AconfigUpdateAndroidMkData may have added elements to Extra.  Process them here.
 			for _, extra := range data.Extra {
 				extra(w, a.outputFile)
@@ -300,6 +298,9 @@
 				fmt.Fprintln(w, dist)
 			}
 
+			fmt.Fprintf(w, "ALL_MODULES.$(my_register_name).SYMBOLIC_OUTPUT_PATH := $(foreach m,%s,$(ALL_MODULES.$(m).SYMBOLIC_OUTPUT_PATH))\n", strings.Join(moduleNames, " "))
+			fmt.Fprintf(w, "ALL_MODULES.$(my_register_name).ELF_SYMBOL_MAPPING_PATH := $(foreach m,%s,$(ALL_MODULES.$(m).ELF_SYMBOL_MAPPING_PATH))\n", strings.Join(moduleNames, " "))
+
 			distCoverageFiles(w, "ndk_apis_usedby_apex", a.nativeApisUsedByModuleFile.String())
 			distCoverageFiles(w, "ndk_apis_backedby_apex", a.nativeApisBackedByModuleFile.String())
 			distCoverageFiles(w, "java_apis_used_by_apex", a.javaApisUsedByModuleFile.String())
diff --git a/apex/apex.go b/apex/apex.go
index 9fdb2a2..a726098 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -18,6 +18,7 @@
 
 import (
 	"fmt"
+	"path"
 	"path/filepath"
 	"regexp"
 	"slices"
@@ -61,12 +62,11 @@
 }
 
 func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
-	ctx.TopDown("apex_info", apexInfoMutator)
 	ctx.BottomUp("apex_unique", apexUniqueVariationsMutator)
 	// Run mark_platform_availability before the apexMutator as the apexMutator needs to know whether
 	// it should create a platform variant.
 	ctx.BottomUp("mark_platform_availability", markPlatformAvailability)
-	ctx.Transition("apex", &apexTransitionMutator{})
+	ctx.InfoBasedTransition("apex", android.NewGenericTransitionMutatorAdapter(&apexTransitionMutator{}))
 }
 
 type apexBundleProperties struct {
@@ -83,10 +83,6 @@
 	// /system/sepolicy/apex/<module_name>_file_contexts.
 	File_contexts *string `android:"path"`
 
-	// By default, file_contexts is amended by force-labelling / and /apex_manifest.pb as system_file
-	// to avoid mistakes. When set as true, no force-labelling.
-	Use_file_contexts_as_is *bool
-
 	// Path to the canned fs config file for customizing file's
 	// uid/gid/mod/capabilities. The content of this file is appended to the
 	// default config, so that the custom entries are preferred. The format is
@@ -415,6 +411,30 @@
 	Min_sdk_version *string
 }
 
+// installPair stores a path to a built object and its install location.  It is used for holding
+// the installation location of the dexpreopt artifacts for system server jars in apexes that need
+// to be installed when the apex is installed.
+type installPair struct {
+	from android.Path
+	to   android.InstallPath
+}
+
+type installPairs []installPair
+
+// String converts a list of installPair structs to the form accepted by LOCAL_SOONG_INSTALL_PAIRS.
+func (p installPairs) String() string {
+	sb := &strings.Builder{}
+	for i, pair := range p {
+		if i != 0 {
+			sb.WriteByte(' ')
+		}
+		sb.WriteString(pair.from.String())
+		sb.WriteByte(':')
+		sb.WriteString(pair.to.String())
+	}
+	return sb.String()
+}
+
 type apexBundle struct {
 	// Inherited structs
 	android.ModuleBase
@@ -496,6 +516,12 @@
 	// Path where this APEX was installed.
 	installedFile android.InstallPath
 
+	// Extra files that are installed alongside this APEX.
+	extraInstalledFiles android.InstallPaths
+
+	// The source and install locations for extraInstalledFiles for use in LOCAL_SOONG_INSTALL_PAIRS.
+	extraInstalledPairs installPairs
+
 	// fragment for this apex for apexkeys.txt
 	apexKeysPath android.WritablePath
 
@@ -524,6 +550,9 @@
 
 	// Required modules, filled out during GenerateAndroidBuildActions and used in AndroidMk
 	required []string
+
+	// appinfo of the apk-in-apex of this module
+	appInfos java.AppInfos
 }
 
 // apexFileClass represents a type of file that can be included in APEX.
@@ -540,19 +569,6 @@
 	shBinary
 )
 
-var (
-	classes = map[string]apexFileClass{
-		"app":              app,
-		"appSet":           appSet,
-		"etc":              etc,
-		"javaSharedLib":    javaSharedLib,
-		"nativeExecutable": nativeExecutable,
-		"nativeSharedLib":  nativeSharedLib,
-		"nativeTest":       nativeTest,
-		"shBinary":         shBinary,
-	}
-)
-
 // apexFile represents a file in an APEX bundle. This is created during the first half of
 // GenerateAndroidBuildActions by traversing the dependencies of the APEX. Then in the second half
 // of the function, this is used to create commands that copies the files into a staging directory,
@@ -570,13 +586,15 @@
 	// Info for Android.mk Module name of `module` in AndroidMk. Note the generated AndroidMk
 	// module for apexFile is named something like <AndroidMk module name>.<apex name>[<apex
 	// suffix>]
-	androidMkModuleName       string             // becomes LOCAL_MODULE
-	class                     apexFileClass      // becomes LOCAL_MODULE_CLASS
-	moduleDir                 string             // becomes LOCAL_PATH
-	requiredModuleNames       []string           // becomes LOCAL_REQUIRED_MODULES
-	targetRequiredModuleNames []string           // becomes LOCAL_TARGET_REQUIRED_MODULES
-	hostRequiredModuleNames   []string           // becomes LOCAL_HOST_REQUIRED_MODULES
-	dataPaths                 []android.DataPath // becomes LOCAL_TEST_DATA
+	androidMkModuleName string             // becomes LOCAL_MODULE
+	class               apexFileClass      // becomes LOCAL_MODULE_CLASS
+	moduleDir           string             // becomes LOCAL_PATH
+	dataPaths           []android.DataPath // becomes LOCAL_TEST_DATA
+
+	// systemServerDexpreoptInstalls stores the list of dexpreopt artifacts for a system server jar.
+	systemServerDexpreoptInstalls []java.DexpreopterInstall
+	// systemServerDexJars stores the list of dexjars for a system server jar.
+	systemServerDexJars android.Paths
 
 	jacocoReportClassesFile android.Path     // only for javalibs and apps
 	lintInfo                *java.LintInfo   // only for javalibs and apps
@@ -593,7 +611,8 @@
 }
 
 // TODO(jiyong): shorten the arglist using an option struct
-func newApexFile(ctx android.BaseModuleContext, builtFile android.Path, androidMkModuleName string, installDir string, class apexFileClass, module android.Module) apexFile {
+func newApexFile(ctx android.BaseModuleContext, builtFile android.Path, androidMkModuleName string,
+	installDir string, class apexFileClass, module android.Module) apexFile {
 	ret := apexFile{
 		builtFile:           builtFile,
 		installDir:          installDir,
@@ -736,6 +755,17 @@
 	shBinaryTag     = &dependencyTag{name: "shBinary", payload: true}
 )
 
+type fragmentInApexDepTag struct {
+	blueprint.BaseDependencyTag
+	android.FragmentInApexTag
+}
+
+func (fragmentInApexDepTag) ExcludeFromVisibilityEnforcement() {}
+
+// fragmentInApexTag is used by apex modules to depend on their fragments.  Java bootclasspath
+// modules can traverse from the apex to the fragment using android.IsFragmentInApexTag.
+var fragmentInApexTag = fragmentInApexDepTag{}
+
 // TODO(jiyong): shorten this function signature
 func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeModules ResolvedApexNativeDependencies, target android.Target, imageVariation string) {
 	binVariations := target.Variations()
@@ -884,12 +914,31 @@
 	commonVariation := ctx.Config().AndroidCommonTarget.Variations()
 	ctx.AddFarVariationDependencies(commonVariation, rroTag, a.properties.Rros...)
 	ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.properties.Bootclasspath_fragments.GetOrDefault(ctx, nil)...)
+	ctx.AddFarVariationDependencies(commonVariation, fragmentInApexTag, a.properties.Bootclasspath_fragments.GetOrDefault(ctx, nil)...)
 	ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.properties.Systemserverclasspath_fragments.GetOrDefault(ctx, nil)...)
 	ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
 	ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
 	ctx.AddFarVariationDependencies(commonVariation, compatConfigTag, a.properties.Compat_configs...)
+
+	// Add a reverse dependency to all_apex_certs singleton module.
+	// all_apex_certs will use this dependency to collect the certificate of this apex.
+	ctx.AddReverseDependency(ctx.Module(), allApexCertsDepTag, "all_apex_certs")
+
+	// TODO: When all branches contain this singleton module, make this strict
+	// TODO: Add this dependency only for mainline prebuilts and not every prebuilt module
+	if ctx.OtherModuleExists("all_apex_contributions") {
+		ctx.AddDependency(ctx.Module(), android.AcDepTag, "all_apex_contributions")
+	}
 }
 
+type allApexCertsDependencyTag struct {
+	blueprint.DependencyTag
+}
+
+func (_ allApexCertsDependencyTag) ExcludeFromVisibilityEnforcement() {}
+
+var allApexCertsDepTag = allApexCertsDependencyTag{}
+
 // DepsMutator for the overridden properties.
 func (a *apexBundle) OverridablePropertiesDepsMutator(ctx android.BottomUpMutatorContext) {
 	if a.overridableProperties.Allowed_files != nil {
@@ -934,58 +983,29 @@
 	}
 }
 
-var _ ApexInfoMutator = (*apexBundle)(nil)
+var _ ApexTransitionMutator = (*apexBundle)(nil)
 
 func (a *apexBundle) ApexVariationName() string {
 	return a.properties.ApexVariationName
 }
 
-// ApexInfoMutator is responsible for collecting modules that need to have apex variants. They are
-// identified by doing a graph walk starting from an apexBundle. Basically, all the (direct and
-// indirect) dependencies are collected. But a few types of modules that shouldn't be included in
-// the apexBundle (e.g. stub libraries) are not collected. Note that a single module can be depended
-// on by multiple apexBundles. In that case, the module is collected for all of the apexBundles.
-//
-// For each dependency between an apex and an ApexModule an ApexInfo object describing the apex
-// is passed to that module's BuildForApex(ApexInfo) method which collates them all in a list.
-// The apexMutator uses that list to create module variants for the apexes to which it belongs.
-// The relationship between module variants and apexes is not one-to-one as variants will be
-// shared between compatible apexes.
-func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) {
+type generateApexInfoContext interface {
+	android.MinSdkVersionFromValueContext
+	Module() android.Module
+	ModuleName() string
+}
 
+// generateApexInfo returns an android.ApexInfo configuration that should be used for dependencies of this apex.
+func (a *apexBundle) generateApexInfo(ctx generateApexInfoContext) android.ApexInfo {
 	// The VNDK APEX is special. For the APEX, the membership is described in a very different
 	// way. There is no dependency from the VNDK APEX to the VNDK libraries. Instead, VNDK
 	// libraries are self-identified by their vndk.enabled properties. There is no need to run
-	// this mutator for the APEX as nothing will be collected. So, let's return fast.
+	// this mutator for the APEX as nothing will be collected so return an empty ApexInfo.
 	if a.vndkApex {
-		return
+		return android.ApexInfo{}
 	}
 
-	continueApexDepsWalk := func(child, parent android.Module) bool {
-		am, ok := child.(android.ApexModule)
-		if !ok || !am.CanHaveApexVariants() {
-			return false
-		}
-		depTag := mctx.OtherModuleDependencyTag(child)
-
-		// Check to see if the tag always requires that the child module has an apex variant for every
-		// apex variant of the parent module. If it does not then it is still possible for something
-		// else, e.g. the DepIsInSameApex(...) method to decide that a variant is required.
-		if required, ok := depTag.(android.AlwaysRequireApexVariantTag); ok && required.AlwaysRequireApexVariant() {
-			return true
-		}
-		if !android.IsDepInSameApex(mctx, parent, child) {
-			return false
-		}
-
-		// By default, all the transitive dependencies are collected, unless filtered out
-		// above.
-		return true
-	}
-
-	android.SetProvider(mctx, android.ApexBundleInfoProvider, android.ApexBundleInfo{})
-
-	minSdkVersion := a.minSdkVersion(mctx)
+	minSdkVersion := a.minSdkVersion(ctx)
 	// When min_sdk_version is not set, the apex is built against FutureApiLevel.
 	if minSdkVersion.IsNone() {
 		minSdkVersion = android.FutureApiLevel
@@ -994,61 +1014,45 @@
 	// This is the main part of this mutator. Mark the collected dependencies that they need to
 	// be built for this apexBundle.
 
-	apexVariationName := mctx.ModuleName() // could be com.android.foo
-	if overridable, ok := mctx.Module().(android.OverridableModule); ok && overridable.GetOverriddenBy() != "" {
+	apexVariationName := ctx.ModuleName() // could be com.android.foo
+	if a.GetOverriddenBy() != "" {
 		// use the overridden name com.mycompany.android.foo
-		apexVariationName = overridable.GetOverriddenBy()
+		apexVariationName = a.GetOverriddenBy()
 	}
 
-	a.properties.ApexVariationName = apexVariationName
-	testApexes := []string{}
-	if a.testApex {
-		testApexes = []string{apexVariationName}
-	}
 	apexInfo := android.ApexInfo{
 		ApexVariationName: apexVariationName,
 		MinSdkVersion:     minSdkVersion,
 		Updatable:         a.Updatable(),
 		UsePlatformApis:   a.UsePlatformApis(),
-		InApexVariants:    []string{apexVariationName},
-		TestApexes:        testApexes,
-		BaseApexName:      mctx.ModuleName(),
+		BaseApexName:      ctx.ModuleName(),
 		ApexAvailableName: proptools.String(a.properties.Apex_available_name),
 	}
-	mctx.WalkDeps(func(child, parent android.Module) bool {
-		if !continueApexDepsWalk(child, parent) {
-			return false
-		}
-		child.(android.ApexModule).BuildForApex(apexInfo) // leave a mark!
-		return true
-	})
+	return apexInfo
 }
 
-type ApexInfoMutator interface {
-	// ApexVariationName returns the name of the APEX variation to use in the apex
-	// mutator etc. It is the same name as ApexInfo.ApexVariationName.
-	ApexVariationName() string
-
-	// ApexInfoMutator implementations must call BuildForApex(ApexInfo) on any modules that are
-	// depended upon by an apex and which require an apex specific variant.
-	ApexInfoMutator(android.TopDownMutatorContext)
+func (a *apexBundle) ApexTransitionMutatorSplit(ctx android.BaseModuleContext) []android.ApexInfo {
+	return []android.ApexInfo{a.generateApexInfo(ctx)}
 }
 
-// apexInfoMutator delegates the work of identifying which modules need an ApexInfo and apex
-// specific variant to modules that support the ApexInfoMutator.
-// It also propagates updatable=true to apps of updatable apexes
-func apexInfoMutator(mctx android.TopDownMutatorContext) {
-	if !mctx.Module().Enabled(mctx) {
-		return
-	}
+func (a *apexBundle) ApexTransitionMutatorOutgoing(ctx android.OutgoingTransitionContext, sourceInfo android.ApexInfo) android.ApexInfo {
+	return sourceInfo
+}
 
-	if a, ok := mctx.Module().(ApexInfoMutator); ok {
-		a.ApexInfoMutator(mctx)
-	}
+func (a *apexBundle) ApexTransitionMutatorIncoming(ctx android.IncomingTransitionContext, outgoingInfo android.ApexInfo) android.ApexInfo {
+	return a.generateApexInfo(ctx)
+}
 
-	if am, ok := mctx.Module().(android.ApexModule); ok {
-		android.ApexInfoMutator(mctx, am)
-	}
+func (a *apexBundle) ApexTransitionMutatorMutate(ctx android.BottomUpMutatorContext, info android.ApexInfo) {
+	android.SetProvider(ctx, android.ApexBundleInfoProvider, android.ApexBundleInfo{})
+	a.properties.ApexVariationName = info.ApexVariationName
+}
+
+type ApexTransitionMutator interface {
+	ApexTransitionMutatorSplit(ctx android.BaseModuleContext) []android.ApexInfo
+	ApexTransitionMutatorOutgoing(ctx android.OutgoingTransitionContext, sourceInfo android.ApexInfo) android.ApexInfo
+	ApexTransitionMutatorIncoming(ctx android.IncomingTransitionContext, outgoingInfo android.ApexInfo) android.ApexInfo
+	ApexTransitionMutatorMutate(ctx android.BottomUpMutatorContext, info android.ApexInfo)
 }
 
 // TODO: b/215736885 Whittle the denylist
@@ -1062,7 +1066,7 @@
 		"com.android.appsearch",
 		"com.android.art",
 		"com.android.art.debug",
-		"com.android.btservices",
+		"com.android.bt",
 		"com.android.cellbroadcast",
 		"com.android.configinfrastructure",
 		"com.android.conscrypt",
@@ -1111,6 +1115,9 @@
 	}
 	if am, ok := mctx.Module().(android.ApexModule); ok {
 		android.UpdateUniqueApexVariationsForDeps(mctx, am)
+		android.SetProvider(mctx, android.DepInSameApexInfoProvider, android.DepInSameApexInfo{
+			Checker: am.GetDepInSameApexChecker(),
+		})
 	}
 }
 
@@ -1163,53 +1170,35 @@
 
 type apexTransitionMutator struct{}
 
-func (a *apexTransitionMutator) Split(ctx android.BaseModuleContext) []string {
-	// apexBundle itself is mutated so that it and its dependencies have the same apex variant.
-	if ai, ok := ctx.Module().(ApexInfoMutator); ok && apexModuleTypeRequiresVariant(ai) {
-		if overridable, ok := ctx.Module().(android.OverridableModule); ok && overridable.GetOverriddenBy() != "" {
-			return []string{overridable.GetOverriddenBy()}
-		}
-		return []string{ai.ApexVariationName()}
-	} else if _, ok := ctx.Module().(*OverrideApex); ok {
-		return []string{ctx.ModuleName()}
+func (a *apexTransitionMutator) Split(ctx android.BaseModuleContext) []android.ApexInfo {
+	if ai, ok := ctx.Module().(ApexTransitionMutator); ok {
+		return ai.ApexTransitionMutatorSplit(ctx)
 	}
-	return []string{""}
+	return []android.ApexInfo{{}}
 }
 
-func (a *apexTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
-	return sourceVariation
-}
-
-func (a *apexTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
-	if am, ok := ctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() {
-		return android.IncomingApexTransition(ctx, incomingVariation)
-	} else if ai, ok := ctx.Module().(ApexInfoMutator); ok {
-		if overridable, ok := ctx.Module().(android.OverridableModule); ok && overridable.GetOverriddenBy() != "" {
-			return overridable.GetOverriddenBy()
-		}
-		return ai.ApexVariationName()
-	} else if _, ok := ctx.Module().(*OverrideApex); ok {
-		return ctx.Module().Name()
+func (a *apexTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceInfo android.ApexInfo) android.ApexInfo {
+	if ai, ok := ctx.Module().(ApexTransitionMutator); ok {
+		return ai.ApexTransitionMutatorOutgoing(ctx, sourceInfo)
 	}
-
-	return ""
+	return android.ApexInfo{}
 }
 
-func (a *apexTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
-	if am, ok := ctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() {
-		android.MutateApexTransition(ctx, variation)
+func (a *apexTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, outgoingInfo android.ApexInfo) android.ApexInfo {
+	if ai, ok := ctx.Module().(ApexTransitionMutator); ok {
+		return ai.ApexTransitionMutatorIncoming(ctx, outgoingInfo)
+	}
+	return android.ApexInfo{}
+}
+
+func (a *apexTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, info android.ApexInfo) {
+	if ai, ok := ctx.Module().(ApexTransitionMutator); ok {
+		ai.ApexTransitionMutatorMutate(ctx, info)
 	}
 }
 
-// apexModuleTypeRequiresVariant determines whether the module supplied requires an apex specific
-// variant.
-func apexModuleTypeRequiresVariant(module ApexInfoMutator) bool {
-	if a, ok := module.(*apexBundle); ok {
-		// TODO(jiyong): document the reason why the VNDK APEX is an exception here.
-		return !a.vndkApex
-	}
-
-	return true
+func (a *apexTransitionMutator) TransitionInfoFromVariation(variation string) android.ApexInfo {
+	panic(fmt.Errorf("adding dependencies on explicit apex variations is not supported"))
 }
 
 const (
@@ -1225,15 +1214,6 @@
 	erofsFsType = "erofs"
 )
 
-var _ android.DepIsInSameApex = (*apexBundle)(nil)
-
-// Implements android.DepInInSameApex
-func (a *apexBundle) DepIsInSameApex(_ android.BaseModuleContext, _ android.Module) bool {
-	// direct deps of an APEX bundle are all part of the APEX bundle
-	// TODO(jiyong): shouldn't we look into the payload field of the dependencyTag?
-	return true
-}
-
 func (a *apexBundle) Exportable() bool {
 	return true
 }
@@ -1383,24 +1363,29 @@
 	}
 }
 
+func setDirInApexForNativeBridge(commonInfo *android.CommonModuleInfo, dir *string) {
+	if commonInfo.Target.NativeBridge == android.NativeBridgeEnabled {
+		*dir = filepath.Join(*dir, commonInfo.Target.NativeBridgeRelativePath)
+	}
+}
+
 // apexFileFor<Type> functions below create an apexFile struct for a given Soong module. The
 // returned apexFile saves information about the Soong module that will be used for creating the
 // build rules.
-func apexFileForNativeLibrary(ctx android.BaseModuleContext, ccMod *cc.Module, handleSpecialLibs bool) apexFile {
+func apexFileForNativeLibrary(ctx android.BaseModuleContext, module android.Module,
+	commonInfo *android.CommonModuleInfo, ccMod *cc.LinkableInfo, handleSpecialLibs bool) apexFile {
 	// Decide the APEX-local directory by the multilib of the library In the future, we may
 	// query this to the module.
 	// TODO(jiyong): use the new PackagingSpec
 	var dirInApex string
-	switch ccMod.Arch().ArchType.Multilib {
+	switch ccMod.Multilib {
 	case "lib32":
 		dirInApex = "lib"
 	case "lib64":
 		dirInApex = "lib64"
 	}
-	if ccMod.Target().NativeBridge == android.NativeBridgeEnabled {
-		dirInApex = filepath.Join(dirInApex, ccMod.Target().NativeBridgeRelativePath)
-	}
-	if handleSpecialLibs && cc.InstallToBootstrap(ccMod.BaseModuleName(), ctx.Config()) {
+	setDirInApexForNativeBridge(commonInfo, &dirInApex)
+	if handleSpecialLibs && cc.InstallToBootstrap(commonInfo.BaseModuleName, ctx.Config()) {
 		// Special case for Bionic libs and other libs installed with them. This is to
 		// prevent those libs from being included in the search path
 		// /apex/com.android.runtime/${LIB}. This exclusion is required because those libs
@@ -1415,79 +1400,68 @@
 	// This needs to go after the runtime APEX handling because otherwise we would get
 	// weird paths like lib64/rel_install_path/bionic rather than
 	// lib64/bionic/rel_install_path.
-	dirInApex = filepath.Join(dirInApex, ccMod.RelativeInstallPath())
+	dirInApex = filepath.Join(dirInApex, ccMod.RelativeInstallPath)
 
-	fileToCopy := android.OutputFileForModule(ctx, ccMod, "")
-	androidMkModuleName := ccMod.BaseModuleName() + ccMod.Properties.SubName
-	return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, ccMod)
+	fileToCopy := android.OutputFileForModule(ctx, module, "")
+	androidMkModuleName := commonInfo.BaseModuleName + ccMod.SubName
+	return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, module)
 }
 
-func apexFileForExecutable(ctx android.BaseModuleContext, cc *cc.Module) apexFile {
+func apexFileForExecutable(ctx android.BaseModuleContext, module android.Module,
+	commonInfo *android.CommonModuleInfo, ccInfo *cc.CcInfo) apexFile {
+	linkableInfo := android.OtherModuleProviderOrDefault(ctx, module, cc.LinkableInfoProvider)
 	dirInApex := "bin"
-	if cc.Target().NativeBridge == android.NativeBridgeEnabled {
-		dirInApex = filepath.Join(dirInApex, cc.Target().NativeBridgeRelativePath)
-	}
-	dirInApex = filepath.Join(dirInApex, cc.RelativeInstallPath())
-	fileToCopy := android.OutputFileForModule(ctx, cc, "")
-	androidMkModuleName := cc.BaseModuleName() + cc.Properties.SubName
-	af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, cc)
-	af.symlinks = cc.Symlinks()
-	af.dataPaths = cc.DataPaths()
+	setDirInApexForNativeBridge(commonInfo, &dirInApex)
+	dirInApex = filepath.Join(dirInApex, linkableInfo.RelativeInstallPath)
+	fileToCopy := android.OutputFileForModule(ctx, module, "")
+	androidMkModuleName := commonInfo.BaseModuleName + linkableInfo.SubName
+	af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, module)
+	af.symlinks = linkableInfo.Symlinks
+	af.dataPaths = ccInfo.DataPaths
 	return af
 }
 
-func apexFileForRustExecutable(ctx android.BaseModuleContext, rustm *rust.Module) apexFile {
+func apexFileForRustExecutable(ctx android.BaseModuleContext, module android.Module,
+	commonInfo *android.CommonModuleInfo) apexFile {
+	linkableInfo := android.OtherModuleProviderOrDefault(ctx, module, cc.LinkableInfoProvider)
 	dirInApex := "bin"
-	if rustm.Target().NativeBridge == android.NativeBridgeEnabled {
-		dirInApex = filepath.Join(dirInApex, rustm.Target().NativeBridgeRelativePath)
-	}
-	dirInApex = filepath.Join(dirInApex, rustm.RelativeInstallPath())
-	fileToCopy := android.OutputFileForModule(ctx, rustm, "")
-	androidMkModuleName := rustm.BaseModuleName() + rustm.Properties.SubName
-	af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, rustm)
+	setDirInApexForNativeBridge(commonInfo, &dirInApex)
+	dirInApex = filepath.Join(dirInApex, linkableInfo.RelativeInstallPath)
+	fileToCopy := android.OutputFileForModule(ctx, module, "")
+	androidMkModuleName := commonInfo.BaseModuleName + linkableInfo.SubName
+	af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, module)
 	return af
 }
 
-func apexFileForRustLibrary(ctx android.BaseModuleContext, rustm *rust.Module) apexFile {
-	// Decide the APEX-local directory by the multilib of the library
-	// In the future, we may query this to the module.
-	var dirInApex string
-	switch rustm.Arch().ArchType.Multilib {
-	case "lib32":
-		dirInApex = "lib"
-	case "lib64":
-		dirInApex = "lib64"
-	}
-	if rustm.Target().NativeBridge == android.NativeBridgeEnabled {
-		dirInApex = filepath.Join(dirInApex, rustm.Target().NativeBridgeRelativePath)
-	}
-	dirInApex = filepath.Join(dirInApex, rustm.RelativeInstallPath())
-	fileToCopy := android.OutputFileForModule(ctx, rustm, "")
-	androidMkModuleName := rustm.BaseModuleName() + rustm.Properties.SubName
-	return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, rustm)
-}
-
-func apexFileForShBinary(ctx android.BaseModuleContext, sh *sh.ShBinary) apexFile {
-	dirInApex := filepath.Join("bin", sh.SubDir())
-	if sh.Target().NativeBridge == android.NativeBridgeEnabled {
-		dirInApex = filepath.Join(dirInApex, sh.Target().NativeBridgeRelativePath)
-	}
-	fileToCopy := sh.OutputFile()
-	af := newApexFile(ctx, fileToCopy, sh.BaseModuleName(), dirInApex, shBinary, sh)
-	af.symlinks = sh.Symlinks()
+func apexFileForShBinary(ctx android.BaseModuleContext, module android.Module,
+	commonInfo *android.CommonModuleInfo, sh *sh.ShBinaryInfo) apexFile {
+	dirInApex := filepath.Join("bin", sh.SubDir)
+	setDirInApexForNativeBridge(commonInfo, &dirInApex)
+	fileToCopy := sh.OutputFile
+	af := newApexFile(ctx, fileToCopy, commonInfo.BaseModuleName, dirInApex, shBinary, module)
+	af.symlinks = sh.Symlinks
 	return af
 }
 
-func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, prebuilt prebuilt_etc.PrebuiltEtcModule, outputFile android.Path) apexFile {
-	dirInApex := filepath.Join(prebuilt.BaseDir(), prebuilt.SubDir())
+func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, module android.Module,
+	prebuilt *prebuilt_etc.PrebuiltEtcInfo, outputFile android.Path) apexFile {
+	dirInApex := filepath.Join(prebuilt.BaseDir, prebuilt.SubDir)
 	makeModuleName := strings.ReplaceAll(filepath.Join(dirInApex, outputFile.Base()), "/", "_")
-	return newApexFile(ctx, outputFile, makeModuleName, dirInApex, etc, prebuilt)
+	return newApexFile(ctx, outputFile, makeModuleName, dirInApex, etc, module)
 }
 
-func apexFileForCompatConfig(ctx android.BaseModuleContext, config java.PlatformCompatConfigIntf, depName string) apexFile {
-	dirInApex := filepath.Join("etc", config.SubDir())
-	fileToCopy := config.CompatConfig()
-	return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, config)
+func apexFileForCompatConfig(ctx android.BaseModuleContext, module android.Module,
+	config *java.PlatformCompatConfigInfo, depName string) apexFile {
+	dirInApex := filepath.Join("etc", config.SubDir)
+	fileToCopy := config.CompatConfig
+	return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, module)
+}
+
+func apexFileForVintfFragment(ctx android.BaseModuleContext, module android.Module,
+	commonInfo *android.CommonModuleInfo, vf *android.VintfFragmentInfo) apexFile {
+	dirInApex := filepath.Join("etc", "vintf")
+
+	return newApexFile(ctx, vf.OutputFile, commonInfo.BaseModuleName, dirInApex, etc, module)
 }
 
 // javaModule is an interface to handle all Java modules (java_library, dex_import, etc) in the same
@@ -1507,61 +1481,42 @@
 var _ javaModule = (*java.SdkLibraryImport)(nil)
 
 // apexFileForJavaModule creates an apexFile for a java module's dex implementation jar.
-func apexFileForJavaModule(ctx android.ModuleContext, module javaModule) apexFile {
-	return apexFileForJavaModuleWithFile(ctx, module, module.DexJarBuildPath(ctx).PathOrNil())
+func apexFileForJavaModule(ctx android.ModuleContext, module android.Module, javaInfo *java.JavaInfo) apexFile {
+	return apexFileForJavaModuleWithFile(ctx, module, javaInfo, javaInfo.DexJarBuildPath.PathOrNil())
 }
 
 // apexFileForJavaModuleWithFile creates an apexFile for a java module with the supplied file.
-func apexFileForJavaModuleWithFile(ctx android.ModuleContext, module javaModule, dexImplementationJar android.Path) apexFile {
+func apexFileForJavaModuleWithFile(ctx android.ModuleContext, module android.Module,
+	javaInfo *java.JavaInfo, dexImplementationJar android.Path) apexFile {
 	dirInApex := "javalib"
-	af := newApexFile(ctx, dexImplementationJar, module.BaseModuleName(), dirInApex, javaSharedLib, module)
-	af.jacocoReportClassesFile = module.JacocoReportClassesFile()
+	commonInfo := android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider)
+	af := newApexFile(ctx, dexImplementationJar, commonInfo.BaseModuleName, dirInApex, javaSharedLib, module)
+	af.jacocoReportClassesFile = javaInfo.JacocoReportClassesFile
 	if lintInfo, ok := android.OtherModuleProvider(ctx, module, java.LintProvider); ok {
 		af.lintInfo = lintInfo
 	}
-	af.customStem = module.Stem() + ".jar"
+	af.customStem = javaInfo.Stem + ".jar"
+	// Collect any system server dex jars and dexpreopt artifacts for installation alongside the apex.
 	// TODO: b/338641779 - Remove special casing of sdkLibrary once bcpf and sscpf depends
 	// on the implementation library
-	if sdkLib, ok := module.(*java.SdkLibrary); ok {
-		for _, install := range sdkLib.BuiltInstalledForApex() {
-			af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName())
-		}
-	} else if dexpreopter, ok := module.(java.DexpreopterInterface); ok {
-		for _, install := range dexpreopter.DexpreoptBuiltInstalledForApex() {
-			af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName())
-		}
+	if javaInfo.DexpreopterInfo != nil {
+		af.systemServerDexpreoptInstalls = append(af.systemServerDexpreoptInstalls, javaInfo.DexpreopterInfo.ApexSystemServerDexpreoptInstalls...)
+		af.systemServerDexJars = append(af.systemServerDexJars, javaInfo.DexpreopterInfo.ApexSystemServerDexJars...)
 	}
 	return af
 }
 
-func apexFileForJavaModuleProfile(ctx android.BaseModuleContext, module javaModule) *apexFile {
-	if dexpreopter, ok := module.(java.DexpreopterInterface); ok {
-		if profilePathOnHost := dexpreopter.OutputProfilePathOnHost(); profilePathOnHost != nil {
-			dirInApex := "javalib"
-			af := newApexFile(ctx, profilePathOnHost, module.BaseModuleName()+"-profile", dirInApex, etc, nil)
-			af.customStem = module.Stem() + ".jar.prof"
-			return &af
-		}
+func apexFileForJavaModuleProfile(ctx android.BaseModuleContext, commonInfo *android.CommonModuleInfo,
+	javaInfo *java.JavaInfo) *apexFile {
+	if profilePathOnHost := javaInfo.DexpreopterInfo.OutputProfilePathOnHost; profilePathOnHost != nil {
+		dirInApex := "javalib"
+		af := newApexFile(ctx, profilePathOnHost, commonInfo.BaseModuleName+"-profile", dirInApex, etc, nil)
+		af.customStem = javaInfo.Stem + ".jar.prof"
+		return &af
 	}
 	return nil
 }
 
-// androidApp is an interface to handle all app modules (android_app, android_app_import, etc.) in
-// the same way.
-type androidApp interface {
-	android.Module
-	Privileged() bool
-	InstallApkName() string
-	OutputFile() android.Path
-	JacocoReportClassesFile() android.Path
-	Certificate() java.Certificate
-	BaseModuleName() string
-	PrivAppAllowlist() android.OptionalPath
-}
-
-var _ androidApp = (*java.AndroidApp)(nil)
-var _ androidApp = (*java.AndroidAppImport)(nil)
-
 func sanitizedBuildIdForPath(ctx android.BaseModuleContext) string {
 	buildId := ctx.Config().BuildId()
 
@@ -1577,36 +1532,35 @@
 	return buildId
 }
 
-func apexFilesForAndroidApp(ctx android.BaseModuleContext, aapp androidApp) []apexFile {
+func apexFilesForAndroidApp(ctx android.BaseModuleContext, module android.Module,
+	commonInfo *android.CommonModuleInfo, aapp *java.AppInfo) []apexFile {
 	appDir := "app"
-	if aapp.Privileged() {
+	if aapp.Privileged {
 		appDir = "priv-app"
 	}
 
 	// TODO(b/224589412, b/226559955): Ensure that the subdirname is suffixed
 	// so that PackageManager correctly invalidates the existing installed apk
 	// in favour of the new APK-in-APEX.  See bugs for more information.
-	dirInApex := filepath.Join(appDir, aapp.InstallApkName()+"@"+sanitizedBuildIdForPath(ctx))
-	fileToCopy := aapp.OutputFile()
+	dirInApex := filepath.Join(appDir, aapp.InstallApkName+"@"+sanitizedBuildIdForPath(ctx))
+	fileToCopy := aapp.OutputFile
 
-	af := newApexFile(ctx, fileToCopy, aapp.BaseModuleName(), dirInApex, app, aapp)
-	af.jacocoReportClassesFile = aapp.JacocoReportClassesFile()
-	if lintInfo, ok := android.OtherModuleProvider(ctx, aapp, java.LintProvider); ok {
+	af := newApexFile(ctx, fileToCopy, commonInfo.BaseModuleName, dirInApex, app, module)
+	af.jacocoReportClassesFile = aapp.JacocoReportClassesFile
+	if lintInfo, ok := android.OtherModuleProvider(ctx, module, java.LintProvider); ok {
 		af.lintInfo = lintInfo
 	}
-	af.certificate = aapp.Certificate()
+	af.certificate = aapp.Certificate
 
-	if app, ok := aapp.(interface {
-		OverriddenManifestPackageName() string
-	}); ok {
-		af.overriddenPackageName = app.OverriddenManifestPackageName()
+	if aapp.OverriddenManifestPackageName != nil {
+		af.overriddenPackageName = *aapp.OverriddenManifestPackageName
 	}
 
 	apexFiles := []apexFile{}
 
-	if allowlist := aapp.PrivAppAllowlist(); allowlist.Valid() {
+	if allowlist := aapp.PrivAppAllowlist; allowlist.Valid() {
 		dirInApex := filepath.Join("etc", "permissions")
-		privAppAllowlist := newApexFile(ctx, allowlist.Path(), aapp.BaseModuleName()+"_privapp", dirInApex, etc, aapp)
+		privAppAllowlist := newApexFile(ctx, allowlist.Path(), commonInfo.BaseModuleName+"_privapp", dirInApex, etc, module)
 		apexFiles = append(apexFiles, privAppAllowlist)
 	}
 
@@ -1615,29 +1569,24 @@
 	return apexFiles
 }
 
-func apexFileForRuntimeResourceOverlay(ctx android.BaseModuleContext, rro java.RuntimeResourceOverlayModule) apexFile {
+func apexFileForRuntimeResourceOverlay(ctx android.BaseModuleContext, module android.Module, rro java.RuntimeResourceOverlayInfo) apexFile {
 	rroDir := "overlay"
-	dirInApex := filepath.Join(rroDir, rro.Theme())
-	fileToCopy := rro.OutputFile()
-	af := newApexFile(ctx, fileToCopy, rro.Name(), dirInApex, app, rro)
-	af.certificate = rro.Certificate()
+	dirInApex := filepath.Join(rroDir, rro.Theme)
+	fileToCopy := rro.OutputFile
+	af := newApexFile(ctx, fileToCopy, module.Name(), dirInApex, app, module)
+	af.certificate = rro.Certificate
 
-	if a, ok := rro.(interface {
-		OverriddenManifestPackageName() string
-	}); ok {
-		af.overriddenPackageName = a.OverriddenManifestPackageName()
-	}
 	return af
 }
 
-func apexFileForBpfProgram(ctx android.BaseModuleContext, builtFile android.Path, apex_sub_dir string, bpfProgram bpf.BpfModule) apexFile {
+func apexFileForBpfProgram(ctx android.BaseModuleContext, builtFile android.Path, apex_sub_dir string, bpfProgram android.Module) apexFile {
 	dirInApex := filepath.Join("etc", "bpf", apex_sub_dir)
 	return newApexFile(ctx, builtFile, builtFile.Base(), dirInApex, etc, bpfProgram)
 }
 
-func apexFileForFilesystem(ctx android.BaseModuleContext, buildFile android.Path, fs filesystem.Filesystem) apexFile {
+func apexFileForFilesystem(ctx android.BaseModuleContext, buildFile android.Path, module android.Module) apexFile {
 	dirInApex := filepath.Join("etc", "fs")
-	return newApexFile(ctx, buildFile, buildFile.Base(), dirInApex, etc, fs)
+	return newApexFile(ctx, buildFile, buildFile.Base(), dirInApex, etc, module)
 }
 
 // WalkPayloadDeps visits dependencies that contributes to the payload of this APEX. For each of the
@@ -1645,36 +1594,8 @@
 // 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.BaseModuleContext, do android.PayloadDepsCallback) {
-	ctx.WalkDeps(func(child, parent android.Module) bool {
-		am, ok := child.(android.ApexModule)
-		if !ok || !am.CanHaveApexVariants() {
-			return false
-		}
-
-		// Filter-out unwanted depedendencies
-		depTag := ctx.OtherModuleDependencyTag(child)
-		if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
-			return false
-		}
-		if dt, ok := depTag.(*dependencyTag); ok && !dt.payload {
-			return false
-		}
-		if depTag == android.RequiredDepTag {
-			return false
-		}
-
-		ai, _ := android.OtherModuleProvider(ctx, child, android.ApexInfoProvider)
-		externalDep := !android.InList(ctx.ModuleName(), ai.InApexVariants)
-
-		// Visit actually
-		return do(ctx, parent, am, externalDep)
-	})
-}
-
-func (a *apexBundle) WalkPayloadDepsProxy(ctx android.BaseModuleContext,
-	do func(ctx android.BaseModuleContext, from, to android.ModuleProxy, externalDep bool) bool) {
 	ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
-		if !android.OtherModuleProviderOrDefault(ctx, child, android.CommonModuleInfoKey).CanHaveApexVariants {
+		if !android.OtherModulePointerProviderOrDefault(ctx, child, android.CommonModuleInfoProvider).CanHaveApexVariants {
 			return false
 		}
 		// Filter-out unwanted depedendencies
@@ -1689,8 +1610,7 @@
 			return false
 		}
 
-		ai, _ := android.OtherModuleProvider(ctx, child, android.ApexInfoProvider)
-		externalDep := !android.InList(ctx.ModuleName(), ai.InApexVariants)
+		externalDep := !android.IsDepInSameApex(ctx, parent, child)
 
 		// Visit actually
 		return do(ctx, parent, child, externalDep)
@@ -1888,11 +1808,12 @@
 	if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
 		return false
 	}
-	if !child.Enabled(ctx) {
+	commonInfo := android.OtherModulePointerProviderOrDefault(ctx, child, android.CommonModuleInfoProvider)
+	if !commonInfo.Enabled {
 		return false
 	}
 	depName := ctx.OtherModuleName(child)
-	if _, isDirectDep := parent.(*apexBundle); isDirectDep {
+	if android.EqualModules(parent, ctx.Module()) {
 		switch depTag {
 		case sharedLibTag, jniLibTag:
 			isJniLib := depTag == jniLibTag
@@ -1900,58 +1821,54 @@
 			if isJniLib {
 				propertyName = "jni_libs"
 			}
-			switch ch := child.(type) {
-			case *cc.Module:
-				if ch.IsStubs() {
+
+			if ch, ok := android.OtherModuleProvider(ctx, child, cc.LinkableInfoProvider); ok {
+				if ch.IsStubs {
 					ctx.PropertyErrorf(propertyName, "%q is a stub. Remove it from the list.", depName)
 				}
-				fi := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs)
+				fi := apexFileForNativeLibrary(ctx, child, commonInfo, ch, vctx.handleSpecialLibs)
 				fi.isJniLib = isJniLib
 				vctx.filesInfo = append(vctx.filesInfo, fi)
 				// Collect the list of stub-providing libs except:
 				// - VNDK libs are only for vendors
 				// - bootstrap bionic libs are treated as provided by system
-				if ch.HasStubsVariants() && !a.vndkApex && !cc.InstallToBootstrap(ch.BaseModuleName(), ctx.Config()) {
+				if ch.HasStubsVariants && !a.vndkApex && !cc.InstallToBootstrap(commonInfo.BaseModuleName, ctx.Config()) {
 					vctx.provideNativeLibs = append(vctx.provideNativeLibs, fi.stem())
 				}
 				return true // track transitive dependencies
-			case *rust.Module:
-				fi := apexFileForRustLibrary(ctx, ch)
-				fi.isJniLib = isJniLib
-				vctx.filesInfo = append(vctx.filesInfo, fi)
-				return true // track transitive dependencies
-			default:
-				ctx.PropertyErrorf(propertyName, "%q is not a cc_library or cc_library_shared module", depName)
+			} else {
+				ctx.PropertyErrorf(propertyName,
+					"%q is not a VersionLinkableInterface (e.g. cc_library or rust_ffi module)", depName)
 			}
+
 		case executableTag:
-			switch ch := child.(type) {
-			case *cc.Module:
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForExecutable(ctx, ch))
+			if ccInfo, ok := android.OtherModuleProvider(ctx, child, cc.CcInfoProvider); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForExecutable(ctx, child, commonInfo, ccInfo))
 				return true // track transitive dependencies
-			case *rust.Module:
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForRustExecutable(ctx, ch))
+			}
+			if _, ok := android.OtherModuleProvider(ctx, child, rust.RustInfoProvider); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForRustExecutable(ctx, child, commonInfo))
 				return true // track transitive dependencies
-			default:
+			} else {
 				ctx.PropertyErrorf("binaries",
 					"%q is neither cc_binary, rust_binary, (embedded) py_binary, (host) blueprint_go_binary, nor (host) bootstrap_go_binary", depName)
 			}
 		case shBinaryTag:
-			if csh, ok := child.(*sh.ShBinary); ok {
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForShBinary(ctx, csh))
+			if csh, ok := android.OtherModuleProvider(ctx, child, sh.ShBinaryInfoProvider); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForShBinary(ctx, child, commonInfo, &csh))
 			} else {
 				ctx.PropertyErrorf("sh_binaries", "%q is not a sh_binary module", depName)
 			}
 		case bcpfTag:
-			_, ok := child.(*java.BootclasspathFragmentModule)
+			_, ok := android.OtherModuleProvider(ctx, child, java.BootclasspathFragmentInfoProvider)
 			if !ok {
 				ctx.PropertyErrorf("bootclasspath_fragments", "%q is not a bootclasspath_fragment module", depName)
 				return false
 			}
-
 			vctx.filesInfo = append(vctx.filesInfo, apexBootclasspathFragmentFiles(ctx, child)...)
 			return true
 		case sscpfTag:
-			if _, ok := child.(*java.SystemServerClasspathModule); !ok {
+			if _, ok := android.OtherModuleProvider(ctx, child, java.LibraryNameToPartitionInfoProvider); !ok {
 				ctx.PropertyErrorf("systemserverclasspath_fragments",
 					"%q is not a systemserverclasspath_fragment module", depName)
 				return false
@@ -1961,98 +1878,104 @@
 			}
 			return true
 		case javaLibTag:
-			switch child.(type) {
-			case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport, *java.Import:
-				af := apexFileForJavaModule(ctx, child.(javaModule))
+			if ctx.OtherModuleHasProvider(child, java.JavaLibraryInfoProvider) ||
+				ctx.OtherModuleHasProvider(child, java.JavaDexImportInfoProvider) ||
+				ctx.OtherModuleHasProvider(child, java.SdkLibraryInfoProvider) {
+				javaInfo := android.OtherModuleProviderOrDefault(ctx, child, java.JavaInfoProvider)
+				af := apexFileForJavaModule(ctx, child, javaInfo)
 				if !af.ok() {
 					ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
 					return false
 				}
 				vctx.filesInfo = append(vctx.filesInfo, af)
 				return true // track transitive dependencies
-			default:
+			} else {
 				ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
 			}
 		case androidAppTag:
-			switch ap := child.(type) {
-			case *java.AndroidApp:
-				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
-				return true // track transitive dependencies
-			case *java.AndroidAppImport:
-				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
-			case *java.AndroidTestHelperApp:
-				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
-			case *java.AndroidAppSet:
-				appDir := "app"
-				if ap.Privileged() {
-					appDir = "priv-app"
+			if appInfo, ok := android.OtherModuleProvider(ctx, child, java.AppInfoProvider); ok {
+				a.appInfos = append(a.appInfos, *appInfo)
+				if appInfo.AppSet {
+					appDir := "app"
+					if appInfo.Privileged {
+						appDir = "priv-app"
+					}
+					// TODO(b/224589412, b/226559955): Ensure that the dirname is
+					// suffixed so that PackageManager correctly invalidates the
+					// existing installed apk in favour of the new APK-in-APEX.
+					// See bugs for more information.
+					appDirName := filepath.Join(appDir, commonInfo.BaseModuleName+"@"+sanitizedBuildIdForPath(ctx))
+					af := newApexFile(ctx, appInfo.OutputFile, commonInfo.BaseModuleName, appDirName, appSet, child)
+					af.certificate = java.PresignedCertificate
+					vctx.filesInfo = append(vctx.filesInfo, af)
+				} else {
+					vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, child, commonInfo, appInfo)...)
+					if !appInfo.Prebuilt && !appInfo.TestHelperApp {
+						return true // track transitive dependencies
+					}
 				}
-				// TODO(b/224589412, b/226559955): Ensure that the dirname is
-				// suffixed so that PackageManager correctly invalidates the
-				// existing installed apk in favour of the new APK-in-APEX.
-				// See bugs for more information.
-				appDirName := filepath.Join(appDir, ap.BaseModuleName()+"@"+sanitizedBuildIdForPath(ctx))
-				af := newApexFile(ctx, ap.OutputFile(), ap.BaseModuleName(), appDirName, appSet, ap)
-				af.certificate = java.PresignedCertificate
-				vctx.filesInfo = append(vctx.filesInfo, af)
-			default:
+			} else {
 				ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
 			}
 		case rroTag:
-			if rro, ok := child.(java.RuntimeResourceOverlayModule); ok {
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForRuntimeResourceOverlay(ctx, rro))
+			if rro, ok := android.OtherModuleProvider(ctx, child, java.RuntimeResourceOverlayInfoProvider); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForRuntimeResourceOverlay(ctx, child, rro))
 			} else {
 				ctx.PropertyErrorf("rros", "%q is not an runtime_resource_overlay module", depName)
 			}
 		case bpfTag:
-			if bpfProgram, ok := child.(bpf.BpfModule); ok {
-				filesToCopy := android.OutputFilesForModule(ctx, bpfProgram, "")
-				apex_sub_dir := bpfProgram.SubDir()
+			if bpfProgram, ok := android.OtherModuleProvider(ctx, child, bpf.BpfInfoProvider); ok {
+				filesToCopy := android.OutputFilesForModule(ctx, child, "")
+				apex_sub_dir := bpfProgram.SubDir
 				for _, bpfFile := range filesToCopy {
-					vctx.filesInfo = append(vctx.filesInfo, apexFileForBpfProgram(ctx, bpfFile, apex_sub_dir, bpfProgram))
+					vctx.filesInfo = append(vctx.filesInfo, apexFileForBpfProgram(ctx, bpfFile, apex_sub_dir, child))
 				}
 			} else {
 				ctx.PropertyErrorf("bpfs", "%q is not a bpf module", depName)
 			}
 		case fsTag:
-			if fs, ok := child.(filesystem.Filesystem); ok {
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForFilesystem(ctx, fs.OutputPath(), fs))
+			if fs, ok := android.OtherModuleProvider(ctx, child, filesystem.FilesystemProvider); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForFilesystem(ctx, fs.Output, child))
 			} else {
 				ctx.PropertyErrorf("filesystems", "%q is not a filesystem module", depName)
 			}
 		case prebuiltTag:
-			if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
-				filesToCopy := android.OutputFilesForModule(ctx, prebuilt, "")
+			if prebuilt, ok := android.OtherModuleProvider(ctx, child, prebuilt_etc.PrebuiltEtcInfoProvider); ok {
+				filesToCopy := android.OutputFilesForModule(ctx, child, "")
 				for _, etcFile := range filesToCopy {
-					vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, etcFile))
+					vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, child, &prebuilt, etcFile))
 				}
 			} else {
 				ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
 			}
 		case compatConfigTag:
-			if compatConfig, ok := child.(java.PlatformCompatConfigIntf); ok {
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForCompatConfig(ctx, compatConfig, depName))
+			if compatConfig, ok := android.OtherModuleProvider(ctx, child, java.PlatformCompatConfigInfoProvider); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForCompatConfig(ctx, child, &compatConfig, depName))
 			} else {
 				ctx.PropertyErrorf("compat_configs", "%q is not a platform_compat_config module", depName)
 			}
 		case testTag:
-			if ccTest, ok := child.(*cc.Module); ok {
-				af := apexFileForExecutable(ctx, ccTest)
-				af.class = nativeTest
+			if ccInfo, ok := android.OtherModuleProvider(ctx, child, cc.CcInfoProvider); ok {
+				af := apexFileForExecutable(ctx, child, commonInfo, ccInfo)
+				// We make this a nativeExecutable instead of a nativeTest because we don't want
+				// the androidmk modules generated in AndroidMkForFiles to be treated as real
+				// tests that are then packaged into suites. Our AndroidMkForFiles does not
+				// implement enough functionality to support real tests.
+				af.class = nativeExecutable
 				vctx.filesInfo = append(vctx.filesInfo, af)
 				return true // track transitive dependencies
 			} else {
 				ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
 			}
 		case keyTag:
-			if key, ok := child.(*apexKey); ok {
-				a.privateKeyFile = key.privateKeyFile
-				a.publicKeyFile = key.publicKeyFile
+			if key, ok := android.OtherModuleProvider(ctx, child, ApexKeyInfoProvider); ok {
+				a.privateKeyFile = key.PrivateKeyFile
+				a.publicKeyFile = key.PublicKeyFile
 			} else {
 				ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
 			}
 		case certificateTag:
-			if dep, ok := child.(*java.AndroidAppCertificate); ok {
+			if dep, ok := android.OtherModuleProvider(ctx, child, java.AndroidAppCertificateInfoProvider); ok {
 				a.containerCertificateFile = dep.Certificate.Pem
 				a.containerPrivateKeyFile = dep.Certificate.Key
 			} else {
@@ -2067,18 +1990,17 @@
 	}
 
 	// indirect dependencies
-	am, ok := child.(android.ApexModule)
-	if !ok {
+	if !commonInfo.IsApexModule {
 		return false
 	}
 	// We cannot use a switch statement on `depTag` here as the checked
 	// tags used below are private (e.g. `cc.sharedDepTag`).
 	if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) {
-		if ch, ok := child.(*cc.Module); ok {
-			af := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs)
+		if ch, ok := android.OtherModuleProvider(ctx, child, cc.LinkableInfoProvider); ok {
+			af := apexFileForNativeLibrary(ctx, child, commonInfo, ch, vctx.handleSpecialLibs)
 			af.transitiveDep = true
 
-			if ch.IsStubs() || ch.HasStubsVariants() {
+			if ch.IsStubs || ch.HasStubsVariants {
 				// If the dependency is a stubs lib, don't include it in this APEX,
 				// but make sure that the lib is installed on the device.
 				// In case no APEX is having the lib, the lib is installed to the system
@@ -2089,9 +2011,10 @@
 				//
 				// Skip the dependency in unbundled builds where the device image is not
 				// being built.
-				if ch.IsStubsImplementationRequired() && !am.NotInPlatform() && !ctx.Config().UnbundledBuild() {
+				if ch.IsStubsImplementationRequired &&
+					!commonInfo.NotInPlatform && !ctx.Config().UnbundledBuild() {
 					// we need a module name for Make
-					name := ch.ImplementationModuleNameForMake(ctx) + ch.Properties.SubName
+					name := ch.ImplementationModuleNameForMake + ch.SubName
 					if !android.InList(name, a.makeModulesToInstall) {
 						a.makeModulesToInstall = append(a.makeModulesToInstall, name)
 					}
@@ -2110,43 +2033,33 @@
 			// like to record requiredNativeLibs even when
 			// DepIsInSameAPex is false. We also shouldn't do
 			// this for host.
-			//
-			// TODO(jiyong): explain why the same module is passed in twice.
-			// Switching the first am to parent breaks lots of tests.
-			if !android.IsDepInSameApex(ctx, am, am) {
+			if !android.IsDepInSameApex(ctx, parent, child) {
 				return false
 			}
 
 			vctx.filesInfo = append(vctx.filesInfo, af)
 			return true // track transitive dependencies
-		} else if rm, ok := child.(*rust.Module); ok {
-			if !android.IsDepInSameApex(ctx, am, am) {
-				return false
-			}
-
-			af := apexFileForRustLibrary(ctx, rm)
-			af.transitiveDep = true
-			vctx.filesInfo = append(vctx.filesInfo, af)
-			return true // track transitive dependencies
 		}
 	} else if cc.IsHeaderDepTag(depTag) {
 		// nothing
 	} else if java.IsJniDepTag(depTag) {
 		// Because APK-in-APEX embeds jni_libs transitively, we don't need to track transitive deps
 	} else if java.IsXmlPermissionsFileDepTag(depTag) {
-		if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
-			filesToCopy := android.OutputFilesForModule(ctx, prebuilt, "")
+		if prebuilt, ok := android.OtherModuleProvider(ctx, child, prebuilt_etc.PrebuiltEtcInfoProvider); ok {
+			filesToCopy := android.OutputFilesForModule(ctx, child, "")
 			for _, etcFile := range filesToCopy {
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, etcFile))
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, child, &prebuilt, etcFile))
 			}
 		}
 	} else if rust.IsDylibDepTag(depTag) {
-		if rustm, ok := child.(*rust.Module); ok && rustm.IsInstallableToApex() {
-			if !android.IsDepInSameApex(ctx, am, am) {
+		if _, ok := android.OtherModuleProvider(ctx, child, rust.RustInfoProvider); ok &&
+			commonInfo.IsInstallableToApex {
+			if !android.IsDepInSameApex(ctx, parent, child) {
 				return false
 			}
 
-			af := apexFileForRustLibrary(ctx, rustm)
+			linkableInfo := android.OtherModuleProviderOrDefault(ctx, child, cc.LinkableInfoProvider)
+			af := apexFileForNativeLibrary(ctx, child, commonInfo, linkableInfo, vctx.handleSpecialLibs)
 			af.transitiveDep = true
 			vctx.filesInfo = append(vctx.filesInfo, af)
 			return true // track transitive dependencies
@@ -2157,10 +2070,9 @@
 		return true
 	} else if java.IsBootclasspathFragmentContentDepTag(depTag) {
 		// Add the contents of the bootclasspath fragment to the apex.
-		switch child.(type) {
-		case *java.Library, *java.SdkLibrary:
-			javaModule := child.(javaModule)
-			af := apexFileForBootclasspathFragmentContentModule(ctx, parent, javaModule)
+		if ctx.OtherModuleHasProvider(child, java.JavaLibraryInfoProvider) ||
+			ctx.OtherModuleHasProvider(child, java.SdkLibraryInfoProvider) {
+			af := apexFileForBootclasspathFragmentContentModule(ctx, parent, child)
 			if !af.ok() {
 				ctx.PropertyErrorf("bootclasspath_fragments",
 					"bootclasspath_fragment content %q is not configured to be compiled into dex", depName)
@@ -2168,21 +2080,22 @@
 			}
 			vctx.filesInfo = append(vctx.filesInfo, af)
 			return true // track transitive dependencies
-		default:
+		} else {
 			ctx.PropertyErrorf("bootclasspath_fragments",
 				"bootclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
 		}
 	} else if java.IsSystemServerClasspathFragmentContentDepTag(depTag) {
 		// Add the contents of the systemserverclasspath fragment to the apex.
-		switch child.(type) {
-		case *java.Library, *java.SdkLibrary:
-			af := apexFileForJavaModule(ctx, child.(javaModule))
+		if ctx.OtherModuleHasProvider(child, java.JavaLibraryInfoProvider) ||
+			ctx.OtherModuleHasProvider(child, java.SdkLibraryInfoProvider) {
+			javaInfo := android.OtherModuleProviderOrDefault(ctx, child, java.JavaInfoProvider)
+			af := apexFileForJavaModule(ctx, child, javaInfo)
 			vctx.filesInfo = append(vctx.filesInfo, af)
-			if profileAf := apexFileForJavaModuleProfile(ctx, child.(javaModule)); profileAf != nil {
+			if profileAf := apexFileForJavaModuleProfile(ctx, commonInfo, javaInfo); profileAf != nil {
 				vctx.filesInfo = append(vctx.filesInfo, *profileAf)
 			}
 			return true // track transitive dependencies
-		default:
+		} else {
 			ctx.PropertyErrorf("systemserverclasspath_fragments",
 				"systemserverclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
 		}
@@ -2190,9 +2103,15 @@
 		// nothing
 	} else if depTag == android.RequiredDepTag {
 		// nothing
-	} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
+	} else if commonInfo.IsInstallableToApex {
 		ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
+	} else if android.IsVintfDepTag(depTag) {
+		if vf, ok := android.OtherModuleProvider(ctx, child, android.VintfFragmentInfoProvider); ok {
+			apexFile := apexFileForVintfFragment(ctx, child, commonInfo, &vf)
+			vctx.filesInfo = append(vctx.filesInfo, apexFile)
+		}
 	}
+
 	return false
 }
 
@@ -2291,6 +2210,7 @@
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 4) generate the build rules to create the APEX. This is done in builder.go.
 	a.buildManifest(ctx, vctx.provideNativeLibs, vctx.requireNativeLibs)
+	a.installApexSystemServerFiles(ctx)
 	a.buildApex(ctx)
 	a.buildApexDependencyInfo(ctx)
 	a.buildLintReports(ctx)
@@ -2307,6 +2227,16 @@
 	a.enforcePartitionTagOnApexSystemServerJar(ctx)
 
 	a.verifyNativeImplementationLibs(ctx)
+	a.enforceNoVintfInUpdatable(ctx)
+
+	android.SetProvider(ctx, android.ApexBundleDepsDataProvider, android.ApexBundleDepsData{
+		FlatListPath: a.FlatListPath(),
+		Updatable:    a.Updatable(),
+	})
+
+	android.SetProvider(ctx, filesystem.ApexKeyPathInfoProvider, filesystem.ApexKeyPathInfo{a.apexKeysPath})
+
+	android.SetProvider(ctx, java.AppInfosProvider, a.appInfos)
 }
 
 // Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file
@@ -2356,8 +2286,8 @@
 		// checking direct deps is sufficient since apex->apk is a direct edge, even when inherited via apex_defaults
 		mctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 			if appInfo, ok := android.OtherModuleProvider(mctx, module, java.AppInfoProvider); ok {
-				// ignore android_test_app
-				if !appInfo.TestHelperApp && !appInfo.Updatable {
+				// ignore android_test_app and android_app_import
+				if !appInfo.TestHelperApp && !appInfo.Prebuilt && !appInfo.Updatable {
 					mctx.ModuleErrorf("app dependency %s must have updatable: true", mctx.OtherModuleName(module))
 				}
 			}
@@ -2367,7 +2297,7 @@
 
 // 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 {
+func apexBootclasspathFragmentFiles(ctx android.ModuleContext, module android.Module) []apexFile {
 	bootclasspathFragmentInfo, _ := android.OtherModuleProvider(ctx, module, java.BootclasspathFragmentApexContentInfoProvider)
 	var filesToAdd []apexFile
 
@@ -2416,7 +2346,7 @@
 
 // apexClasspathFragmentProtoFile returns *apexFile structure defining the classpath.proto config that
 // the module contributes to the apex; or nil if the proto config was not generated.
-func apexClasspathFragmentProtoFile(ctx android.ModuleContext, module blueprint.Module) *apexFile {
+func apexClasspathFragmentProtoFile(ctx android.ModuleContext, module android.Module) *apexFile {
 	info, _ := android.OtherModuleProvider(ctx, module, java.ClasspathFragmentProtoContentInfoProvider)
 	if !info.ClasspathFragmentProtoGenerated {
 		return nil
@@ -2428,7 +2358,7 @@
 
 // apexFileForBootclasspathFragmentContentModule creates an apexFile for a bootclasspath_fragment
 // content module, i.e. a library that is part of the bootclasspath.
-func apexFileForBootclasspathFragmentContentModule(ctx android.ModuleContext, fragmentModule blueprint.Module, javaModule javaModule) apexFile {
+func apexFileForBootclasspathFragmentContentModule(ctx android.ModuleContext, fragmentModule, javaModule android.Module) apexFile {
 	bootclasspathFragmentInfo, _ := android.OtherModuleProvider(ctx, fragmentModule, java.BootclasspathFragmentApexContentInfoProvider)
 
 	// Get the dexBootJar from the bootclasspath_fragment as that is responsible for performing the
@@ -2440,7 +2370,8 @@
 
 	// Create an apexFile as for a normal java module but with the dex boot jar provided by the
 	// bootclasspath_fragment.
-	af := apexFileForJavaModuleWithFile(ctx, javaModule, dexBootJar)
+	javaInfo := android.OtherModuleProviderOrDefault(ctx, javaModule, java.JavaInfoProvider)
+	af := apexFileForJavaModuleWithFile(ctx, javaModule, javaInfo, dexBootJar)
 	return af
 }
 
@@ -2550,7 +2481,7 @@
 }
 
 // Returns apex's min_sdk_version string value, honoring overrides
-func (a *apexBundle) minSdkVersionValue(ctx android.EarlyModuleContext) string {
+func (a *apexBundle) minSdkVersionValue(ctx android.MinSdkVersionFromValueContext) string {
 	// Only override the minSdkVersion value on Apexes which already specify
 	// a min_sdk_version (it's optional for non-updatable apexes), and that its
 	// min_sdk_version value is lower than the one to override with.
@@ -2574,7 +2505,7 @@
 }
 
 // Returns apex's min_sdk_version ApiLevel, honoring overrides
-func (a *apexBundle) minSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+func (a *apexBundle) minSdkVersion(ctx android.MinSdkVersionFromValueContext) android.ApiLevel {
 	return android.MinSdkVersionFromValue(ctx, a.minSdkVersionValue(ctx))
 }
 
@@ -2590,8 +2521,8 @@
 		librariesDirectlyInApex[ctx.OtherModuleName(dep)] = true
 	})
 
-	a.WalkPayloadDepsProxy(ctx, func(ctx android.BaseModuleContext, from, to android.ModuleProxy, externalDep bool) bool {
-		if ccInfo, ok := android.OtherModuleProvider(ctx, to, cc.CcInfoProvider); ok {
+	a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from, to android.ModuleProxy, externalDep bool) bool {
+		if info, ok := android.OtherModuleProvider(ctx, to, cc.LinkableInfoProvider); ok {
 			// If `to` is not actually in the same APEX as `from` then it does not need
 			// apex_available and neither do any of its dependencies.
 			if externalDep {
@@ -2603,7 +2534,7 @@
 			fromName := ctx.OtherModuleName(from)
 			toName := ctx.OtherModuleName(to)
 
-			// The dynamic linker and crash_dump tool in the runtime APEX is the only
+			// The dynamic linker and crash_dump tool in the runtime APEX is an
 			// exception to this rule. It can't make the static dependencies dynamic
 			// because it can't do the dynamic linking for itself.
 			// Same rule should be applied to linkerconfig, because it should be executed
@@ -2612,7 +2543,16 @@
 				return false
 			}
 
-			isStubLibraryFromOtherApex := ccInfo.HasStubsVariants && !librariesDirectlyInApex[toName]
+			// b/389067742 adds libz as an exception to this check. Although libz is
+			// a part of NDK and thus provides a stable interface, it never was the
+			// intention because the upstream zlib provides neither ABI- nor behavior-
+			// stability. Therefore, we want to allow portable components like APEXes to
+			// bundle libz by statically linking to it.
+			if toName == "libz" {
+				return false
+			}
+
+			isStubLibraryFromOtherApex := info.HasStubsVariants && !librariesDirectlyInApex[toName]
 			if isStubLibraryFromOtherApex && !externalDep {
 				ctx.ModuleErrorf("%q required by %q is a native library providing stub. "+
 					"It shouldn't be included in this APEX via static linking. Dependency path: %s", to.String(), fromName, ctx.GetPathString(false))
@@ -2695,7 +2635,7 @@
 		return
 	}
 
-	a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+	a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from, to android.ModuleProxy, externalDep bool) bool {
 		// As soon as the dependency graph crosses the APEX boundary, don't go further.
 		if externalDep {
 			return false
@@ -2712,17 +2652,8 @@
 		fromName := ctx.OtherModuleName(from)
 		toName := ctx.OtherModuleName(to)
 
-		// If `to` is not actually in the same APEX as `from` then it does not need
-		// apex_available and neither do any of its dependencies.
-		//
-		// It is ok to call DepIsInSameApex() directly from within WalkPayloadDeps().
-		if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
-			// As soon as the dependency graph crosses the APEX boundary, don't go
-			// further.
-			return false
-		}
-
-		if to.AvailableFor(apexName) {
+		if android.CheckAvailableForApex(apexName,
+			android.OtherModuleProviderOrDefault(ctx, to, android.ApexAvailableInfoProvider).ApexAvailableFor) {
 			return true
 		}
 
@@ -2752,7 +2683,7 @@
 			return
 		}
 
-		if android.OtherModuleProviderOrDefault(ctx, module, cc.LinkableInfoKey).StaticExecutable {
+		if android.OtherModuleProviderOrDefault(ctx, module, cc.LinkableInfoProvider).StaticExecutable {
 			apex := a.ApexVariationName()
 			exec := ctx.OtherModuleName(module)
 			if isStaticExecutableAllowed(apex, exec) {
@@ -2927,14 +2858,14 @@
 		if !inApex && !inApkInApex {
 			ctx.ModuleErrorf("library in apex transitively linked against implementation library %q not in apex", lib)
 			var depPath []android.Module
-			ctx.WalkDeps(func(child, parent android.Module) bool {
+			ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
 				if depPath != nil {
 					return false
 				}
 
 				tag := ctx.OtherModuleDependencyTag(child)
 
-				if parent == ctx.Module() {
+				if android.EqualModules(parent, ctx.Module()) {
 					if !checkApexTag(tag) {
 						return false
 					}
@@ -2959,3 +2890,16 @@
 		}
 	}
 }
+
+// TODO(b/399527905) libvintf is not forward compatible.
+func (a *apexBundle) enforceNoVintfInUpdatable(ctx android.ModuleContext) {
+	if !a.Updatable() {
+		return
+	}
+	for _, fi := range a.filesInfo {
+		if match, _ := path.Match("etc/vintf/*", fi.path()); match {
+			ctx.ModuleErrorf("VINTF fragment (%s) is not supported in updatable APEX.", fi.path())
+			break
+		}
+	}
+}
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index d46104e..797f47b 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -59,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_list} and ${new_allowed_deps}",
+		Description: "Diff ${allowed_deps} and ${new_allowed_deps}",
 		Command: `
-			if grep -v -h '^#' ${allowed_deps_list} | sort -u -f| diff -B -u - ${new_allowed_deps}; then
+			if grep -v '^#' ${allowed_deps} | diff -B - ${new_allowed_deps}; then
 			   touch ${out};
 			else
 				echo;
@@ -85,62 +85,54 @@
 				exit 1;
 			fi;
 		`,
-	}, "allowed_deps_list", "new_allowed_deps")
+	}, "allowed_deps", "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 {
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+		if binaryInfo, ok := android.OtherModuleProvider(ctx, module, android.ApexBundleDepsDataProvider); ok {
 			apexInfo, _ := android.OtherModuleProvider(ctx, module, android.ApexInfoProvider)
-			if path := binaryInfo.FlatListPath(); path != nil {
-				if binaryInfo.Updatable() || apexInfo.Updatable {
-					updatableFlatLists = append(updatableFlatLists, path)
+			if path := binaryInfo.FlatListPath; path != nil {
+				if binaryInfo.Updatable || apexInfo.Updatable {
+					if strings.HasPrefix(module.String(), "com.android.") {
+						updatableFlatLists = append(updatableFlatLists, path)
+					}
 				}
 			}
 		}
 	})
+
+	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")
-	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 {
+
+	if !allowedDepsSource.Valid() {
 		// 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: updatableFlatLists,
+			Inputs: append(updatableFlatLists, allowedDeps),
 			Output: newAllowedDeps,
 		})
+
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   diffAllowedApexDepsInfoRule,
 			Input:  newAllowedDeps,
 			Output: s.allowedApexDepsInfoCheckResult,
 			Args: map[string]string{
-				"allowed_deps_list": allowedDepsListString,
-				"new_allowed_deps":  newAllowedDeps.String(),
+				"allowed_deps":     allowedDeps.String(),
+				"new_allowed_deps": newAllowedDeps.String(),
 			},
 		})
 	}
+
 	ctx.Phony("apex-allowed-deps-check", s.allowedApexDepsInfoCheckResult)
 }
 
@@ -168,11 +160,11 @@
 func (a *apexPrebuiltInfo) GenerateBuildActions(ctx android.SingletonContext) {
 	prebuiltInfos := []android.PrebuiltInfo{}
 
-	ctx.VisitAllModules(func(m android.Module) {
+	ctx.VisitAllModuleProxies(func(m android.ModuleProxy) {
 		prebuiltInfo, exists := android.OtherModuleProvider(ctx, m, android.PrebuiltInfoProvider)
 		// Use prebuiltInfoProvider to filter out non apex soong modules.
 		// Use HideFromMake to filter out the unselected variants of a specific apex.
-		if exists && !m.IsHideFromMake() {
+		if exists && !android.OtherModulePointerProviderOrDefault(ctx, m, android.CommonModuleInfoProvider).HideFromMake {
 			prebuiltInfos = append(prebuiltInfos, prebuiltInfo)
 		}
 	})
@@ -183,8 +175,5 @@
 	}
 	a.out = android.PathForOutput(ctx, "prebuilt_info.json")
 	android.WriteFileRule(ctx, a.out, string(j))
-}
-
-func (a *apexPrebuiltInfo) MakeVars(ctx android.MakeVarsContext) {
 	ctx.DistForGoal("droidcore", a.out)
 }
diff --git a/apex/apex_test.go b/apex/apex_test.go
index dd55152..327e018 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -20,7 +20,6 @@
 	"path/filepath"
 	"reflect"
 	"regexp"
-	"slices"
 	"sort"
 	"strconv"
 	"strings"
@@ -29,7 +28,6 @@
 	"android/soong/aconfig/codegen"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/bpmodify"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -507,10 +505,10 @@
 		}
 	`)
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+	apexRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexRule")
 
 	// Make sure that Android.mk is created
-	ab := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
+	ab := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Module().(*apexBundle)
 	data := android.AndroidMkDataForTest(t, ctx, ab)
 	var builder strings.Builder
 	data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data)
@@ -587,14 +585,14 @@
 	}
 
 	fullDepsInfo := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
-		ctx.ModuleForTests("myapex", "android_common_myapex").Output("depsinfo/fulllist.txt")), "\n")
+		ctx.ModuleForTests(t, "myapex", "android_common_myapex").Output("depsinfo/fulllist.txt")), "\n")
 	ensureListContains(t, fullDepsInfo, "  myjar(minSdkVersion:(no version)) <- myapex")
 	ensureListContains(t, fullDepsInfo, "  mylib2(minSdkVersion:(no version)) <- mylib")
 	ensureListContains(t, fullDepsInfo, "  myotherjar(minSdkVersion:(no version)) <- myjar")
 	ensureListContains(t, fullDepsInfo, "  mysharedjar(minSdkVersion:(no version)) (external) <- myjar")
 
 	flatDepsInfo := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
-		ctx.ModuleForTests("myapex", "android_common_myapex").Output("depsinfo/flatlist.txt")), "\n")
+		ctx.ModuleForTests(t, "myapex", "android_common_myapex").Output("depsinfo/flatlist.txt")), "\n")
 	ensureListContains(t, flatDepsInfo, "myjar(minSdkVersion:(no version))")
 	ensureListContains(t, flatDepsInfo, "mylib2(minSdkVersion:(no version))")
 	ensureListContains(t, flatDepsInfo, "myotherjar(minSdkVersion:(no version))")
@@ -701,7 +699,7 @@
 		}
 	`)
 
-	module := ctx.ModuleForTests("myapex", "android_common_myapex")
+	module := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	args := module.Rule("apexRule").Args
 	if manifest := args["manifest"]; manifest != module.Output("apex_manifest.pb").Output.String() {
 		t.Error("manifest should be apex_manifest.pb, but " + manifest)
@@ -773,7 +771,7 @@
 		},
 	}
 	for _, tc := range testCases {
-		module := ctx.ModuleForTests(tc.module, "android_common_"+tc.module)
+		module := ctx.ModuleForTests(t, tc.module, "android_common_"+tc.module)
 		args := module.Rule("apexRule").Args
 		optFlags := args["opt_flags"]
 		if !strings.Contains(optFlags, "--min_sdk_version "+tc.minSdkVersion) {
@@ -847,7 +845,7 @@
 		},
 	}
 	for _, tc := range testCases {
-		module := ctx.ModuleForTests(tc.module, "android_common_"+tc.module)
+		module := ctx.ModuleForTests(t, tc.module, "android_common_"+tc.module)
 		args := module.Rule("apexRule").Args
 		optFlags := args["opt_flags"]
 		if !strings.Contains(optFlags, "--min_sdk_version "+tc.minSdkVersion) {
@@ -878,7 +876,7 @@
 			}
 		`)
 
-		rule := ctx.ModuleForTests("myapex", "android_common_myapex").Output("file_contexts")
+		rule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Output("file_contexts")
 		if vendor {
 			android.AssertStringDoesContain(t, "should force-label as vendor_apex_metadata_file",
 				rule.RuleParams.Command,
@@ -897,7 +895,11 @@
 		apex {
 			name: "myapex",
 			key: "myapex.key",
-			native_shared_libs: ["mylib", "mylib3"],
+			native_shared_libs: [
+				"mylib",
+				"mylib3",
+				"libmylib3_rs",
+			],
 			binaries: ["foo.rust"],
 			updatable: false,
 		}
@@ -911,7 +913,14 @@
 		cc_library {
 			name: "mylib",
 			srcs: ["mylib.cpp"],
-			shared_libs: ["mylib2", "mylib3#impl", "my_prebuilt_platform_lib", "my_prebuilt_platform_stub_only_lib"],
+			shared_libs: [
+				"mylib2",
+				"mylib3#impl",
+				"libmylib2_rs",
+				"libmylib3_rs#impl",
+				"my_prebuilt_platform_lib",
+				"my_prebuilt_platform_stub_only_lib",
+			],
 			system_shared_libs: [],
 			stl: "none",
 			apex_available: [ "myapex" ],
@@ -929,6 +938,16 @@
 			},
 		}
 
+		rust_ffi {
+			name: "libmylib2_rs",
+			crate_name: "mylib2",
+			srcs: ["mylib.rs"],
+			stubs: {
+				symbol_file: "mylib2.map.txt",
+				versions: ["1", "2", "3"],
+			},
+		}
+
 		cc_library {
 			name: "mylib3",
 			srcs: ["mylib.cpp"],
@@ -942,6 +961,18 @@
 			apex_available: [ "myapex" ],
 		}
 
+		rust_ffi {
+			name: "libmylib3_rs",
+			crate_name: "mylib3",
+			srcs: ["mylib.rs"],
+			shared_libs: ["mylib4.from_rust"],
+			stubs: {
+				symbol_file: "mylib3.map.txt",
+				versions: ["10", "11", "12"],
+			},
+			apex_available: [ "myapex" ],
+		}
+
 		cc_library {
 			name: "mylib4",
 			srcs: ["mylib.cpp"],
@@ -950,6 +981,14 @@
 			apex_available: [ "myapex" ],
 		}
 
+		cc_library {
+			name: "mylib4.from_rust",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+		}
+
 		cc_prebuilt_library_shared {
 			name: "my_prebuilt_platform_lib",
 			stubs: {
@@ -971,7 +1010,10 @@
 		rust_binary {
 			name: "foo.rust",
 			srcs: ["foo.rs"],
-			shared_libs: ["libfoo.shared_from_rust"],
+			shared_libs: [
+				"libfoo.shared_from_rust",
+				"libfoo_rs.shared_from_rust",
+			],
 			prefer_rlib: true,
 			apex_available: ["myapex"],
 		}
@@ -986,9 +1028,18 @@
 			},
 		}
 
+		rust_ffi {
+			name: "libfoo_rs.shared_from_rust",
+			crate_name: "foo_rs",
+			srcs: ["mylib.rs"],
+			stubs: {
+				versions: ["10", "11", "12"],
+			},
+		}
+
 	`)
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+	apexRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
 	// Ensure that direct non-stubs dep is always included
@@ -996,21 +1047,27 @@
 
 	// Ensure that indirect stubs dep is not included
 	ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so")
+	ensureNotContains(t, copyCmds, "image.apex/lib64/libmylib2_rs.so")
 
 	// Ensure that direct stubs dep is included
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib3.so")
+	ensureContains(t, copyCmds, "image.apex/lib64/libmylib3_rs.so")
 
-	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
+	mylibLdFlags := ctx.ModuleForTests(t, "mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
 
 	// Ensure that mylib is linking with the latest version of stubs for mylib2
 	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so")
+	ensureContains(t, mylibLdFlags, "libmylib2_rs/android_arm64_armv8-a_shared_current/unstripped/libmylib2_rs.so")
 	// ... and not linking to the non-stub (impl) variant of mylib2
 	ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
+	ensureNotContains(t, mylibLdFlags, "libmylib2_rs/android_arm64_armv8-a_shared/unstripped/libmylib2_rs.so")
 
 	// Ensure that mylib is linking with the non-stub (impl) of mylib3 (because the dependency is added with mylib3#impl)
 	ensureContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_apex10000/mylib3.so")
+	ensureContains(t, mylibLdFlags, "libmylib3_rs/android_arm64_armv8-a_shared_apex10000/unstripped/libmylib3_rs.so")
 	// .. and not linking to the stubs variant of mylib3
 	ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_12/mylib3.so")
+	ensureNotContains(t, mylibLdFlags, "libmylib3_rs/android_arm64_armv8-a_shared_12/unstripped/mylib3.so")
 
 	// Comment out this test. Now it fails after the optimization of sharing "cflags" in cc/cc.go
 	// is replaced by sharing of "cFlags" in cc/builder.go.
@@ -1021,46 +1078,54 @@
 	// including the original cflags's "-include mylib.h".
 	//
 	// Ensure that stubs libs are built without -include flags
-	// mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+	// mylib2Cflags := ctx.ModuleForTests(t, "mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	// ensureNotContains(t, mylib2Cflags, "-include ")
 
 	// Ensure that genstub for platform-provided lib is invoked with --systemapi
-	ensureContains(t, ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"], "--systemapi")
+	ensureContains(t, ctx.ModuleForTests(t, "mylib2", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"], "--systemapi")
+	ensureContains(t, ctx.ModuleForTests(t, "libmylib2_rs", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"], "--systemapi")
 	// Ensure that genstub for apex-provided lib is invoked with --apex
-	ensureContains(t, ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_shared_12").Rule("genStubSrc").Args["flags"], "--apex")
+	ensureContains(t, ctx.ModuleForTests(t, "mylib3", "android_arm64_armv8-a_shared_12").Rule("genStubSrc").Args["flags"], "--apex")
+	ensureContains(t, ctx.ModuleForTests(t, "libmylib3_rs", "android_arm64_armv8-a_shared_12").Rule("genStubSrc").Args["flags"], "--apex")
 
 	ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{
 		"lib64/mylib.so",
 		"lib64/mylib3.so",
+		"lib64/libmylib3_rs.so",
 		"lib64/mylib4.so",
+		"lib64/mylib4.from_rust.so",
 		"bin/foo.rust",
-		"lib64/libc++.so", // by the implicit dependency from foo.rust
-		"lib64/liblog.so", // by the implicit dependency from foo.rust
+
+		"lib64/libstd.dylib.so", // implicit rust ffi dep
 	})
 
 	// Ensure that stub dependency from a rust module is not included
 	ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so")
+	ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo_rs.shared_from_rust.so")
 	// The rust module is linked to the stub cc library
-	rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"]
+	rustDeps := ctx.ModuleForTests(t, "foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"]
 	ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so")
+	ensureContains(t, rustDeps, "libfoo_rs.shared_from_rust/android_arm64_armv8-a_shared_current/unstripped/libfoo_rs.shared_from_rust.so")
 	ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so")
+	ensureNotContains(t, rustDeps, "libfoo_rs.shared_from_rust/android_arm64_armv8-a_shared/unstripped/libfoo_rs.shared_from_rust.so")
 
-	apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule")
+	apexManifestRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexManifestRule")
 	ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.shared_from_rust.so")
+	ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo_rs.shared_from_rust.so")
 
 	// Ensure that mylib is linking with the latest version of stubs for my_prebuilt_platform_lib
 	ensureContains(t, mylibLdFlags, "my_prebuilt_platform_lib/android_arm64_armv8-a_shared_current/my_prebuilt_platform_lib.so")
 	// ... and not linking to the non-stub (impl) variant of my_prebuilt_platform_lib
 	ensureNotContains(t, mylibLdFlags, "my_prebuilt_platform_lib/android_arm64_armv8-a_shared/my_prebuilt_platform_lib.so")
 	// Ensure that genstub for platform-provided lib is invoked with --systemapi
-	ensureContains(t, ctx.ModuleForTests("my_prebuilt_platform_lib", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"], "--systemapi")
+	ensureContains(t, ctx.ModuleForTests(t, "my_prebuilt_platform_lib", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"], "--systemapi")
 
 	// Ensure that mylib is linking with the latest version of stubs for my_prebuilt_platform_lib
 	ensureContains(t, mylibLdFlags, "my_prebuilt_platform_stub_only_lib/android_arm64_armv8-a_shared_current/my_prebuilt_platform_stub_only_lib.so")
 	// ... and not linking to the non-stub (impl) variant of my_prebuilt_platform_lib
 	ensureNotContains(t, mylibLdFlags, "my_prebuilt_platform_stub_only_lib/android_arm64_armv8-a_shared/my_prebuilt_platform_stub_only_lib.so")
 	// Ensure that genstub for platform-provided lib is invoked with --systemapi
-	ensureContains(t, ctx.ModuleForTests("my_prebuilt_platform_stub_only_lib", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"], "--systemapi")
+	ensureContains(t, ctx.ModuleForTests(t, "my_prebuilt_platform_stub_only_lib", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"], "--systemapi")
 }
 
 func TestApexShouldNotEmbedStubVariant(t *testing.T) {
@@ -1111,7 +1176,10 @@
 		cc_library {
 			name: "mylib",
 			srcs: ["mylib.cpp"],
-			shared_libs: ["mylib2"],
+			shared_libs: [
+				"mylib2",
+				"libmylib2_rust"
+			],
 			system_shared_libs: [],
 			stl: "none",
 			apex_available: [ "myapex" ],
@@ -1128,10 +1196,22 @@
 			},
 		}
 
+		rust_ffi {
+			name: "libmylib2_rust",
+			crate_name: "mylib2_rust",
+			srcs: ["mylib.rs"],
+			stubs: {
+				versions: ["1", "2", "3"],
+			},
+		}
+
 		rust_binary {
 			name: "foo.rust",
 			srcs: ["foo.rs"],
-			shared_libs: ["libfoo.shared_from_rust"],
+			shared_libs: [
+				"libfoo.shared_from_rust",
+				"libmylib_rust.shared_from_rust"
+			],
 			prefer_rlib: true,
 			apex_available: ["myapex"],
 		}
@@ -1145,23 +1225,38 @@
 				versions: ["10", "11", "12"],
 			},
 		}
+		rust_ffi {
+			name: "libmylib_rust.shared_from_rust",
+			crate_name: "mylib_rust",
+			srcs: ["mylib.rs"],
+			stubs: {
+				versions: ["1", "2", "3"],
+			},
+		}
+
 	`)
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+	apexRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
 	// Ensure that indirect stubs dep is not included
 	ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so")
+	ensureNotContains(t, copyCmds, "image.apex/lib64/libmylib_rust.so")
+	ensureNotContains(t, copyCmds, "image.apex/lib64/libmylib_rust.shared_from_rust.so")
 	ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so")
 
 	// Ensure that we are using non-stub variants of mylib2 and libfoo.shared_from_rust (because
 	// of the platform_apis: true)
-	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
+	mylibLdFlags := ctx.ModuleForTests(t, "mylib", "android_arm64_armv8-a_shared_apex10000_p").Rule("ld").Args["libFlags"]
 	ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so")
 	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
-	rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"]
+	ensureNotContains(t, mylibLdFlags, "libmylib2_rust/android_arm64_armv8-a_shared_current/unstripped/libmylib2_rust.so")
+	ensureContains(t, mylibLdFlags, "libmylib2_rust/android_arm64_armv8-a_shared/unstripped/libmylib2_rust.so")
+	rustDeps := ctx.ModuleForTests(t, "foo.rust", "android_arm64_armv8-a_apex10000_p").Rule("rustc").Args["linkFlags"]
 	ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so")
 	ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so")
+	ensureNotContains(t, rustDeps, "libmylib_rust.shared_from_rust/android_arm64_armv8-a_shared_current/unstripped/libmylib_rust.shared_from_rust.so")
+	ensureContains(t, rustDeps, "libmylib_rust.shared_from_rust/android_arm64_armv8-a_shared/unstripped/libmylib_rust.shared_from_rust.so")
 }
 
 func TestApexWithStubsWithMinSdkVersion(t *testing.T) {
@@ -1170,7 +1265,11 @@
 		apex {
 			name: "myapex",
 			key: "myapex.key",
-			native_shared_libs: ["mylib", "mylib3"],
+			native_shared_libs: [
+				"mylib",
+				"mylib3",
+				"libmylib3_rust",
+			],
 			min_sdk_version: "29",
 		}
 
@@ -1183,7 +1282,12 @@
 		cc_library {
 			name: "mylib",
 			srcs: ["mylib.cpp"],
-			shared_libs: ["mylib2", "mylib3#impl"],
+			shared_libs: [
+				"mylib2",
+				"mylib3#impl",
+				"libmylib2_rust",
+				"libmylib3_rust#impl",
+			],
 			system_shared_libs: [],
 			stl: "none",
 			apex_available: [ "myapex" ],
@@ -1203,6 +1307,17 @@
 			min_sdk_version: "28",
 		}
 
+		rust_ffi {
+			name: "libmylib2_rust",
+			crate_name: "mylib2_rust",
+			srcs: ["mylib.rs"],
+			stubs: {
+				symbol_file: "mylib2.map.txt",
+				versions: ["28", "29", "30", "current"],
+			},
+			min_sdk_version: "28",
+		}
+
 		cc_library {
 			name: "mylib3",
 			srcs: ["mylib.cpp"],
@@ -1217,6 +1332,19 @@
 			min_sdk_version: "28",
 		}
 
+		rust_ffi {
+			name: "libmylib3_rust",
+			crate_name: "mylib3_rust",
+			srcs: ["mylib.rs"],
+			shared_libs: ["libmylib4.from_rust"],
+			stubs: {
+				symbol_file: "mylib3.map.txt",
+				versions: ["28", "29", "30", "current"],
+			},
+			apex_available: [ "myapex" ],
+			min_sdk_version: "28",
+		}
+
 		cc_library {
 			name: "mylib4",
 			srcs: ["mylib.cpp"],
@@ -1225,43 +1353,63 @@
 			apex_available: [ "myapex" ],
 			min_sdk_version: "28",
 		}
+
+		rust_ffi {
+			name: "libmylib4.from_rust",
+			crate_name: "mylib4",
+			srcs: ["mylib.rs"],
+			apex_available: [ "myapex" ],
+			min_sdk_version: "28",
+		}
 	`)
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+	apexRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
 	// Ensure that direct non-stubs dep is always included
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
+	ensureContains(t, copyCmds, "image.apex/lib64/mylib3.so")
+	ensureContains(t, copyCmds, "image.apex/lib64/libmylib3_rust.so")
 
 	// Ensure that indirect stubs dep is not included
 	ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so")
+	ensureNotContains(t, copyCmds, "image.apex/lib64/libmylib2_rust.so")
 
 	// Ensure that direct stubs dep is included
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib3.so")
 
-	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex29").Rule("ld").Args["libFlags"]
+	mylibLdFlags := ctx.ModuleForTests(t, "mylib", "android_arm64_armv8-a_shared_apex29").Rule("ld").Args["libFlags"]
 
 	// Ensure that mylib is linking with the latest version of stub for mylib2
 	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so")
+	ensureContains(t, mylibLdFlags, "libmylib2_rust/android_arm64_armv8-a_shared_current/unstripped/libmylib2_rust.so")
 	// ... and not linking to the non-stub (impl) variant of mylib2
 	ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
+	ensureNotContains(t, mylibLdFlags, "libmylib2_rust/android_arm64_armv8-a_shared/unstripped/libmylib2_rust.so")
 
 	// Ensure that mylib is linking with the non-stub (impl) of mylib3 (because the dependency is added with mylib3#impl)
 	ensureContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_apex29/mylib3.so")
+	ensureContains(t, mylibLdFlags, "libmylib3_rust/android_arm64_armv8-a_shared_apex29/unstripped/libmylib3_rust.so")
 	// .. and not linking to the stubs variant of mylib3
 	ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_29/mylib3.so")
+	ensureNotContains(t, mylibLdFlags, "libmylib3_rust/android_arm64_armv8-a_shared_29/unstripped/libmylib3_rust.so")
 
 	// Ensure that stubs libs are built without -include flags
-	mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_29").Rule("cc").Args["cFlags"]
+	mylib2Cflags := ctx.ModuleForTests(t, "mylib2", "android_arm64_armv8-a_shared_29").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylib2Cflags, "-include ")
 
 	// Ensure that genstub is invoked with --systemapi
-	ensureContains(t, ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_29").Rule("genStubSrc").Args["flags"], "--systemapi")
+	ensureContains(t, ctx.ModuleForTests(t, "mylib2", "android_arm64_armv8-a_shared_29").Rule("genStubSrc").Args["flags"], "--systemapi")
+	ensureContains(t, ctx.ModuleForTests(t, "libmylib2_rust", "android_arm64_armv8-a_shared_29").Rule("cc.genStubSrc").Args["flags"], "--systemapi")
 
 	ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{
 		"lib64/mylib.so",
 		"lib64/mylib3.so",
+		"lib64/libmylib3_rust.so",
 		"lib64/mylib4.so",
+		"lib64/libmylib4.from_rust.so",
+
+		"lib64/libstd.dylib.so", // by the implicit dependency from foo.rust
 	})
 }
 
@@ -1286,7 +1434,10 @@
 		cc_library {
 			name: "mylib",
 			srcs: ["mylib.cpp"],
-			shared_libs: ["libstub"],
+			shared_libs: [
+				"libstub",
+				"libstub_rust",
+			],
 			apex_available: ["myapex"],
 			min_sdk_version: "Z",
 		}
@@ -1300,7 +1451,10 @@
 		apex {
 			name: "otherapex",
 			key: "myapex.key",
-			native_shared_libs: ["libstub"],
+			native_shared_libs: [
+				"libstub",
+				"libstub_rust",
+			],
 			min_sdk_version: "29",
 		}
 
@@ -1314,11 +1468,25 @@
 			min_sdk_version: "29",
 		}
 
+		rust_ffi {
+			name: "libstub_rust",
+			crate_name: "stub_rust",
+			srcs: ["mylib.rs"],
+			stubs: {
+				versions: ["29", "Z", "current"],
+			},
+			apex_available: ["otherapex"],
+			min_sdk_version: "29",
+		}
+
 		// platform module depending on libstub from otherapex should use the latest stub("current")
 		cc_library {
 			name: "libplatform",
 			srcs: ["mylib.cpp"],
-			shared_libs: ["libstub"],
+			shared_libs: [
+				"libstub",
+				"libstub_rust",
+			],
 		}
 	`,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -1329,16 +1497,22 @@
 	)
 
 	// Ensure that mylib from myapex is built against the latest stub (current)
-	mylibCflags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"]
+	mylibCflags := ctx.ModuleForTests(t, "mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"]
 	ensureContains(t, mylibCflags, "-D__LIBSTUB_API__=10000 ")
-	mylibLdflags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
+	// rust stubs do not emit -D__LIBFOO_API__ flags as this is deprecated behavior for cc stubs
+
+	mylibLdflags := ctx.ModuleForTests(t, "mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
 	ensureContains(t, mylibLdflags, "libstub/android_arm64_armv8-a_shared_current/libstub.so ")
+	ensureContains(t, mylibLdflags, "libstub_rust/android_arm64_armv8-a_shared_current/unstripped/libstub_rust.so ")
 
 	// Ensure that libplatform is built against latest stub ("current") of mylib3 from the apex
-	libplatformCflags := ctx.ModuleForTests("libplatform", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+	libplatformCflags := ctx.ModuleForTests(t, "libplatform", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	ensureContains(t, libplatformCflags, "-D__LIBSTUB_API__=10000 ") // "current" maps to 10000
-	libplatformLdflags := ctx.ModuleForTests("libplatform", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"]
+	// rust stubs do not emit -D__LIBFOO_API__ flags as this is deprecated behavior for cc stubs
+
+	libplatformLdflags := ctx.ModuleForTests(t, "libplatform", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"]
 	ensureContains(t, libplatformLdflags, "libstub/android_arm64_armv8-a_shared_current/libstub.so ")
+	ensureContains(t, libplatformLdflags, "libstub_rust/android_arm64_armv8-a_shared_current/unstripped/libstub_rust.so ")
 }
 
 func TestApexWithExplicitStubsDependency(t *testing.T) {
@@ -1360,7 +1534,10 @@
 		cc_library {
 			name: "mylib",
 			srcs: ["mylib.cpp"],
-			shared_libs: ["libfoo#10"],
+			shared_libs: [
+				"libfoo#10",
+				"libfoo_rust#10"
+			],
 			static_libs: ["libbaz"],
 			system_shared_libs: [],
 			stl: "none",
@@ -1378,6 +1555,16 @@
 			},
 		}
 
+		rust_ffi {
+			name: "libfoo_rust",
+			crate_name: "foo_rust",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["libbar.from_rust"],
+			stubs: {
+				versions: ["10", "20", "30"],
+			},
+		}
+
 		cc_library {
 			name: "libbar",
 			srcs: ["mylib.cpp"],
@@ -1385,6 +1572,13 @@
 			stl: "none",
 		}
 
+		cc_library {
+			name: "libbar.from_rust",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
 		cc_library_static {
 			name: "libbaz",
 			srcs: ["mylib.cpp"],
@@ -1395,7 +1589,7 @@
 
 	`)
 
-	apexRule := ctx.ModuleForTests("myapex2", "android_common_myapex2").Rule("apexRule")
+	apexRule := ctx.ModuleForTests(t, "myapex2", "android_common_myapex2").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
 	// Ensure that direct non-stubs dep is always included
@@ -1403,28 +1597,34 @@
 
 	// Ensure that indirect stubs dep is not included
 	ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.so")
+	ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo_rust.so")
 
 	// Ensure that dependency of stubs is not included
 	ensureNotContains(t, copyCmds, "image.apex/lib64/libbar.so")
+	ensureNotContains(t, copyCmds, "image.apex/lib64/libbar.from_rust.so")
 
-	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
+	mylibLdFlags := ctx.ModuleForTests(t, "mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
 
 	// Ensure that mylib is linking with version 10 of libfoo
 	ensureContains(t, mylibLdFlags, "libfoo/android_arm64_armv8-a_shared_10/libfoo.so")
+	ensureContains(t, mylibLdFlags, "libfoo_rust/android_arm64_armv8-a_shared_10/unstripped/libfoo_rust.so")
 	// ... and not linking to the non-stub (impl) variant of libfoo
 	ensureNotContains(t, mylibLdFlags, "libfoo/android_arm64_armv8-a_shared/libfoo.so")
+	ensureNotContains(t, mylibLdFlags, "libfoo_rust/android_arm64_armv8-a_shared/unstripped/libfoo_rust.so")
 
-	libFooStubsLdFlags := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared_10").Rule("ld").Args["libFlags"]
+	libFooStubsLdFlags := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared_10").Rule("ld").Args["libFlags"]
+	libFooRustStubsLdFlags := ctx.ModuleForTests(t, "libfoo_rust", "android_arm64_armv8-a_shared_10").Rule("ld").Args["libFlags"]
 
 	// Ensure that libfoo stubs is not linking to libbar (since it is a stubs)
 	ensureNotContains(t, libFooStubsLdFlags, "libbar.so")
+	ensureNotContains(t, libFooRustStubsLdFlags, "libbar.from_rust.so")
 
 	fullDepsInfo := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
-		ctx.ModuleForTests("myapex2", "android_common_myapex2").Output("depsinfo/fulllist.txt")), "\n")
+		ctx.ModuleForTests(t, "myapex2", "android_common_myapex2").Output("depsinfo/fulllist.txt")), "\n")
 	ensureListContains(t, fullDepsInfo, "  libfoo(minSdkVersion:(no version)) (external) <- mylib")
 
 	flatDepsInfo := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
-		ctx.ModuleForTests("myapex2", "android_common_myapex2").Output("depsinfo/flatlist.txt")), "\n")
+		ctx.ModuleForTests(t, "myapex2", "android_common_myapex2").Output("depsinfo/flatlist.txt")), "\n")
 	ensureListContains(t, flatDepsInfo, "libfoo(minSdkVersion:(no version)) (external)")
 }
 
@@ -1457,7 +1657,11 @@
 			srcs: ["mylib.cpp"],
 			static_libs: ["libstatic"],
 			shared_libs: ["libshared"],
-			runtime_libs: ["libfoo", "libbar"],
+			runtime_libs: [
+				"libfoo",
+				"libbar",
+				"libfoo_rs",
+			],
 			system_shared_libs: [],
 			stl: "none",
 			apex_available: [ "myapex" ],
@@ -1473,6 +1677,15 @@
 			},
 		}
 
+		rust_ffi {
+			name: "libfoo_rs",
+			crate_name: "foo_rs",
+			srcs: ["mylib.rs"],
+			stubs: {
+				versions: ["10", "20", "30"],
+			},
+		}
+
 		cc_library {
 			name: "libbar",
 			srcs: ["mylib.cpp"],
@@ -1516,7 +1729,7 @@
 		}
 	`)
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+	apexRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
 	// Ensure that direct non-stubs dep is always included
@@ -1524,6 +1737,7 @@
 
 	// Ensure that indirect stubs dep is not included
 	ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.so")
+	ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo_rs.so")
 
 	// Ensure that runtime_libs dep in included
 	ensureContains(t, copyCmds, "image.apex/lib64/libbar.so")
@@ -1532,9 +1746,10 @@
 
 	ensureNotContains(t, copyCmds, "image.apex/lib64/libstatic_to_runtime.so")
 
-	apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule")
+	apexManifestRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexManifestRule")
 	ensureListEmpty(t, names(apexManifestRule.Args["provideNativeLibs"]))
 	ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.so")
+	ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo_rs.so")
 }
 
 var prepareForTestOfRuntimeApexWithHwasan = android.GroupFixturePreparers(
@@ -1601,7 +1816,7 @@
 		"lib64/bionic/libclang_rt.hwasan-aarch64-android.so",
 	})
 
-	hwasan := ctx.ModuleForTests("libclang_rt.hwasan", "android_arm64_armv8-a_shared")
+	hwasan := ctx.ModuleForTests(t, "libclang_rt.hwasan", "android_arm64_armv8-a_shared")
 
 	installed := hwasan.Description("install libclang_rt.hwasan")
 	ensureContains(t, installed.Output.String(), "/system/lib64/bootstrap/libclang_rt.hwasan-aarch64-android.so")
@@ -1657,7 +1872,7 @@
 		"lib64/bionic/libclang_rt.hwasan-aarch64-android.so",
 	})
 
-	hwasan := ctx.ModuleForTests("libclang_rt.hwasan", "android_arm64_armv8-a_shared")
+	hwasan := ctx.ModuleForTests(t, "libclang_rt.hwasan", "android_arm64_armv8-a_shared")
 
 	installed := hwasan.Description("install libclang_rt.hwasan")
 	ensureContains(t, installed.Output.String(), "/system/lib64/bootstrap/libclang_rt.hwasan-aarch64-android.so")
@@ -1740,17 +1955,17 @@
 			})
 
 			// Ensure that LLNDK dep is required
-			apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule")
+			apexManifestRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexManifestRule")
 			ensureListEmpty(t, names(apexManifestRule.Args["provideNativeLibs"]))
 			ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libbar.so")
 
-			mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_"+tc.apexVariant).Rule("ld").Args["libFlags"]
+			mylibLdFlags := ctx.ModuleForTests(t, "mylib", "android_arm64_armv8-a_shared_"+tc.apexVariant).Rule("ld").Args["libFlags"]
 			ensureContains(t, mylibLdFlags, "libbar/android_arm64_armv8-a_shared_"+tc.shouldLink+"/libbar.so")
 			for _, ver := range tc.shouldNotLink {
 				ensureNotContains(t, mylibLdFlags, "libbar/android_arm64_armv8-a_shared_"+ver+"/libbar.so")
 			}
 
-			mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_"+tc.apexVariant).Rule("cc").Args["cFlags"]
+			mylibCFlags := ctx.ModuleForTests(t, "mylib", "android_arm64_armv8-a_static_"+tc.apexVariant).Rule("cc").Args["cFlags"]
 			ver := tc.shouldLink
 			if tc.shouldLink == "current" {
 				ver = strconv.Itoa(android.FutureApiLevelInt)
@@ -1766,7 +1981,7 @@
 		apex {
 			name: "myapex",
 			key: "myapex.key",
-			native_shared_libs: ["mylib", "mylib_shared", "libdl", "libm"],
+			native_shared_libs: ["mylib", "mylib_shared", "libdl", "libm", "libmylib_rs"],
 			updatable: false,
 		}
 
@@ -1785,6 +2000,14 @@
 			apex_available: [ "myapex" ],
 		}
 
+		rust_ffi {
+			name: "libmylib_rs",
+			crate_name: "mylib_rs",
+			shared_libs: ["libvers#27", "libm#impl"],
+			srcs: ["mylib.rs"],
+			apex_available: [ "myapex" ],
+		}
+
 		cc_library_shared {
 			name: "mylib_shared",
 			srcs: ["mylib.cpp"],
@@ -1799,22 +2022,40 @@
 			stl: "none",
 			bootstrap: true,
 		}
+
+		rust_ffi {
+			name: "libbootstrap_rs",
+			srcs: ["mylib.cpp"],
+			crate_name: "bootstrap_rs",
+			bootstrap: true,
+		}
+
+		cc_library {
+			name: "libvers",
+			srcs: ["mylib.cpp"],
+			stl: "none",
+			stubs: { versions: ["27","30"] },
+		}
 	`)
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+	apexRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
-	// Ensure that mylib, libm, libdl are included.
+	// Ensure that mylib, libmylib_rs, libm, libdl, libstd.dylib.so (from Rust) are included.
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
+	ensureContains(t, copyCmds, "image.apex/lib64/libmylib_rs.so")
 	ensureContains(t, copyCmds, "image.apex/lib64/bionic/libm.so")
 	ensureContains(t, copyCmds, "image.apex/lib64/bionic/libdl.so")
+	ensureContains(t, copyCmds, "image.apex/lib64/libstd.dylib.so")
 
-	// Ensure that libc is not included (since it has stubs and not listed in native_shared_libs)
+	// Ensure that libc and liblog (from Rust) is not included (since it has stubs and not listed in native_shared_libs)
 	ensureNotContains(t, copyCmds, "image.apex/lib64/bionic/libc.so")
+	ensureNotContains(t, copyCmds, "image.apex/lib64/liblog.so")
 
-	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
-	mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"]
-	mylibSharedCFlags := ctx.ModuleForTests("mylib_shared", "android_arm64_armv8-a_shared_apex10000").Rule("cc").Args["cFlags"]
+	mylibLdFlags := ctx.ModuleForTests(t, "mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
+	mylibRsFlags := ctx.ModuleForTests(t, "libmylib_rs", "android_arm64_armv8-a_shared_apex10000").Rule("rustc").Args["linkFlags"]
+	mylibCFlags := ctx.ModuleForTests(t, "mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"]
+	mylibSharedCFlags := ctx.ModuleForTests(t, "mylib_shared", "android_arm64_armv8-a_shared_apex10000").Rule("cc").Args["cFlags"]
 
 	// For dependency to libc
 	// Ensure that mylib is linking with the latest version of stubs
@@ -1846,11 +2087,42 @@
 	ensureContains(t, mylibCFlags, "__LIBDL_API__=27")
 	ensureContains(t, mylibSharedCFlags, "__LIBDL_API__=27")
 
+	// Rust checks
+	// For dependency to libc, liblog
+	// Ensure that libmylib_rs is linking with the latest versions of stubs
+	ensureContains(t, mylibRsFlags, "libc/android_arm64_armv8-a_shared_current/libc.so")
+	ensureContains(t, mylibRsFlags, "liblog/android_arm64_armv8-a_shared_current/liblog.so")
+	// ... and not linking to the non-stub (impl) variants
+	ensureNotContains(t, mylibRsFlags, "libc/android_arm64_armv8-a_shared/libc.so")
+	ensureNotContains(t, mylibRsFlags, "liblog/android_arm64_armv8-a_shared/liblog.so")
+
+	// For libm dependency (explicit)
+	// Ensure that mylib is linking with the non-stub (impl) variant
+	ensureContains(t, mylibRsFlags, "libm/android_arm64_armv8-a_shared_apex10000/libm.so")
+	// ... and not linking to the stub variant
+	ensureNotContains(t, mylibRsFlags, "libm/android_arm64_armv8-a_shared_29/libm.so")
+
+	// For dependency to libvers
+	// (We do not use libdl#27 as Rust links the system libs implicitly and does
+	// not currently have a system_shared_libs equivalent to prevent this)
+	// Ensure that mylib is linking with the specified version of stubs
+	ensureContains(t, mylibRsFlags, "libvers/android_arm64_armv8-a_shared_27/libvers.so")
+	// ... and not linking to the other versions of stubs
+	ensureNotContains(t, mylibRsFlags, "libvers/android_arm64_armv8-a_shared_30/libvers.so")
+	// ... and not linking to the non-stub (impl) variant
+	ensureNotContains(t, mylibRsFlags, "libvers/android_arm64_armv8-a_shared_apex10000/libvers.so")
+
 	// Ensure that libBootstrap is depending on the platform variant of bionic libs
-	libFlags := ctx.ModuleForTests("libBootstrap", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"]
+	libFlags := ctx.ModuleForTests(t, "libBootstrap", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"]
 	ensureContains(t, libFlags, "libc/android_arm64_armv8-a_shared/libc.so")
 	ensureContains(t, libFlags, "libm/android_arm64_armv8-a_shared/libm.so")
 	ensureContains(t, libFlags, "libdl/android_arm64_armv8-a_shared/libdl.so")
+
+	// Ensure that libbootstrap_rs is depending on the platform variant of bionic libs
+	libRsFlags := ctx.ModuleForTests(t, "libbootstrap_rs", "android_arm64_armv8-a_shared").Rule("rustc").Args["linkFlags"]
+	ensureContains(t, libRsFlags, "libc/android_arm64_armv8-a_shared/libc.so")
+	ensureContains(t, libRsFlags, "libm/android_arm64_armv8-a_shared/libm.so")
+	ensureContains(t, libRsFlags, "libdl/android_arm64_armv8-a_shared/libdl.so")
 }
 
 func TestApexMinSdkVersion_NativeModulesShouldBeBuiltAgainstStubs(t *testing.T) {
@@ -1900,7 +2172,7 @@
 
 		cc_library {
 			name: "liba",
-			shared_libs: ["libz"],
+			shared_libs: ["libz", "libz_rs"],
 			system_shared_libs: [],
 			stl: "none",
 			apex_available: [
@@ -1918,28 +2190,46 @@
 				versions: ["28", "30"],
 			},
 		}
+
+		rust_ffi {
+			name: "libz_rs",
+			crate_name: "z_rs",
+			srcs: ["foo.rs"],
+			stubs: {
+				versions: ["28", "30"],
+			},
+		}
 	`)
 
 	expectLink := func(from, from_variant, to, to_variant string) {
-		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
+		ldArgs := ctx.ModuleForTests(t, from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
 		ensureContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
 	expectNoLink := func(from, from_variant, to, to_variant string) {
-		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
+		ldArgs := ctx.ModuleForTests(t, from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
 		ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
 	// platform liba is linked to non-stub version
 	expectLink("liba", "shared", "libz", "shared")
+	expectLink("liba", "shared", "unstripped/libz_rs", "shared")
 	// liba in myapex is linked to current
 	expectLink("liba", "shared_apex29", "libz", "shared_current")
 	expectNoLink("liba", "shared_apex29", "libz", "shared_30")
 	expectNoLink("liba", "shared_apex29", "libz", "shared_28")
 	expectNoLink("liba", "shared_apex29", "libz", "shared")
+	expectLink("liba", "shared_apex29", "unstripped/libz_rs", "shared_current")
+	expectNoLink("liba", "shared_apex29", "unstripped/libz_rs", "shared_30")
+	expectNoLink("liba", "shared_apex29", "unstripped/libz_rs", "shared_28")
+	expectNoLink("liba", "shared_apex29", "unstripped/libz_rs", "shared")
 	// liba in otherapex is linked to current
 	expectLink("liba", "shared_apex30", "libz", "shared_current")
 	expectNoLink("liba", "shared_apex30", "libz", "shared_30")
 	expectNoLink("liba", "shared_apex30", "libz", "shared_28")
 	expectNoLink("liba", "shared_apex30", "libz", "shared")
+	expectLink("liba", "shared_apex30", "unstripped/libz_rs", "shared_current")
+	expectNoLink("liba", "shared_apex30", "unstripped/libz_rs", "shared_30")
+	expectNoLink("liba", "shared_apex30", "unstripped/libz_rs", "shared_28")
+	expectNoLink("liba", "shared_apex30", "unstripped/libz_rs", "shared")
 }
 
 func TestApexMinSdkVersion_SupportsCodeNames(t *testing.T) {
@@ -1982,11 +2272,11 @@
 	)
 
 	expectLink := func(from, from_variant, to, to_variant string) {
-		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
+		ldArgs := ctx.ModuleForTests(t, from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
 		ensureContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
 	expectNoLink := func(from, from_variant, to, to_variant string) {
-		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
+		ldArgs := ctx.ModuleForTests(t, from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
 		ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
 	expectLink("libx", "shared_apex10000", "libz", "shared_current")
@@ -2062,11 +2352,11 @@
 	`)
 
 	expectLink := func(from, from_variant, to, to_variant string) {
-		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
+		ldArgs := ctx.ModuleForTests(t, from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
 		ensureContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
 	expectNoLink := func(from, from_variant, to, to_variant string) {
-		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
+		ldArgs := ctx.ModuleForTests(t, from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
 		ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
 	expectLink("libx", "shared_apex10000", "libz", "shared_current")
@@ -2110,14 +2400,14 @@
 
 	vendorVariant := "android_vendor_arm64_armv8-a"
 
-	mylib := ctx.ModuleForTests("mylib", vendorVariant+"_shared_apex29")
+	mylib := ctx.ModuleForTests(t, "mylib", vendorVariant+"_shared_apex29")
 
 	// Ensure that mylib links with "current" LLNDK
 	libFlags := names(mylib.Rule("ld").Args["libFlags"])
 	ensureListContains(t, libFlags, "out/soong/.intermediates/libbar/"+vendorVariant+"_shared/libbar.so")
 
 	// Ensure that mylib is targeting 29
-	ccRule := ctx.ModuleForTests("mylib", vendorVariant+"_static_apex29").Output("obj/mylib.o")
+	ccRule := ctx.ModuleForTests(t, "mylib", vendorVariant+"_static_apex29").Output("obj/mylib.o")
 	ensureContains(t, ccRule.Args["cFlags"], "-target aarch64-linux-android29")
 
 	// Ensure that the correct variant of crtbegin_so is used.
@@ -2125,11 +2415,91 @@
 	ensureContains(t, crtBegin, "out/soong/.intermediates/"+cc.DefaultCcCommonTestModulesDir+"crtbegin_so/"+vendorVariant+"_apex29/crtbegin_so.o")
 
 	// Ensure that the crtbegin_so used by the APEX is targeting 29
-	cflags := ctx.ModuleForTests("crtbegin_so", vendorVariant+"_apex29").Rule("cc").Args["cFlags"]
+	cflags := ctx.ModuleForTests(t, "crtbegin_so", vendorVariant+"_apex29").Rule("cc").Args["cFlags"]
 	android.AssertStringDoesContain(t, "cflags", cflags, "-target aarch64-linux-android29")
 }
 
-func TestTrackAllowedDeps(t *testing.T) {
+func TestTrackAllowedDepsForAndroidApex(t *testing.T) {
+	t.Parallel()
+	ctx := testApex(t, `
+		apex {
+			name: "com.android.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", "libbar_rs"],
+			min_sdk_version: "29",
+			apex_available: ["com.android.myapex"],
+		}
+
+		cc_library {
+			name: "libbar",
+			stubs: { versions: ["29", "30"] },
+		}
+
+		rust_ffi {
+			name: "libbar_rs",
+			crate_name: "bar_rs",
+			srcs: ["bar.rs"],
+			stubs: { versions: ["29", "30"] },
+		}
+
+		cc_library {
+			name: "yourlib",
+			srcs: ["mylib.cpp"],
+			min_sdk_version: "29",
+			apex_available: ["com.android.myapex", "myapex2", "//apex_available:platform"],
+		}
+	`, withFiles(android.MockFS{
+		"packages/modules/common/build/allowed_deps.txt": nil,
+	}),
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/com.android.myapex-file_contexts": nil,
+		}))
+
+	depsinfo := ctx.SingletonForTests(t, "apex_depsinfo_singleton")
+	inputs := depsinfo.Rule("generateApexDepsInfoFilesRule").BuildParams.Inputs.Strings()
+	android.AssertStringListContains(t, "updatable com.android.myapex should generate depsinfo file", inputs,
+		"out/soong/.intermediates/com.android.myapex/android_common_com.android.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(t, "com.android.myapex", "android_common_com.android.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.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep",
+		flatlist, "libbar_rs(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 TestNotTrackAllowedDepsForNonAndroidApex(t *testing.T) {
 	t.Parallel()
 	ctx := testApex(t, `
 		apex {
@@ -2179,176 +2549,19 @@
 		"packages/modules/common/build/allowed_deps.txt": nil,
 	}))
 
-	depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton")
+	depsinfo := ctx.SingletonForTests(t, "apex_depsinfo_singleton")
 	inputs := depsinfo.Rule("generateApexDepsInfoFilesRule").BuildParams.Inputs.Strings()
-	android.AssertStringListContains(t, "updatable myapex should generate depsinfo file", inputs,
+	android.AssertStringListDoesNotContain(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 TestTrackCustomAllowedDepsInvalidDefaultTxt(t *testing.T) {
-	t.Parallel()
-	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) {
-	t.Parallel()
-	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) {
 	t.Parallel()
 	ctx := testApex(t, `
 		apex {
-			name: "myapex",
+			name: "com.android.myapex",
 			key: "myapex.key",
 			updatable: true,
 			min_sdk_version: "29",
@@ -2359,8 +2572,11 @@
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
 		}
-	`)
-	depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton")
+	`,
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/com.android.myapex-file_contexts": nil,
+		}))
+	depsinfo := ctx.SingletonForTests(t, "apex_depsinfo_singleton")
 	if nil != depsinfo.MaybeRule("generateApexDepsInfoFilesRule").Output {
 		t.Error("apex_depsinfo_singleton shouldn't run when allowed_deps.txt doesn't exist")
 	}
@@ -2372,7 +2588,7 @@
 		apex {
 			name: "myapex",
 			key: "myapex.key",
-			native_shared_libs: ["libx"],
+			native_shared_libs: ["libx", "libx_rs"],
 			updatable: false,
 		}
 
@@ -2392,9 +2608,19 @@
 			},
 		}
 
+		rust_ffi {
+			name: "libx_rs",
+			crate_name: "x_rs",
+			srcs: ["x.rs"],
+			apex_available: [ "myapex" ],
+			stubs: {
+				versions: ["1", "2"],
+			},
+		}
+
 		cc_library {
 			name: "libz",
-			shared_libs: ["libx"],
+			shared_libs: ["libx", "libx_rs",],
 			system_shared_libs: [],
 			stl: "none",
 		}
@@ -2402,16 +2628,18 @@
 
 	expectLink := func(from, from_variant, to, to_variant string) {
 		t.Helper()
-		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
+		ldArgs := ctx.ModuleForTests(t, from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
 		ensureContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
 	expectNoLink := func(from, from_variant, to, to_variant string) {
 		t.Helper()
-		ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
+		ldArgs := ctx.ModuleForTests(t, from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"]
 		ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
 	expectLink("libz", "shared", "libx", "shared_current")
 	expectNoLink("libz", "shared", "libx", "shared_2")
+	expectLink("libz", "shared", "unstripped/libx_rs", "shared_current")
+	expectNoLink("libz", "shared", "unstripped/libx_rs", "shared_2")
 	expectNoLink("libz", "shared", "libz", "shared_1")
 	expectNoLink("libz", "shared", "libz", "shared")
 }
@@ -2440,11 +2668,18 @@
 
 		cc_library {
 			name: "libx",
-			shared_libs: ["libbar"],
+			shared_libs: ["libbar", "libbar_rs"],
 			apex_available: [ "myapex" ],
 			min_sdk_version: "29",
 		}
 
+		rust_ffi {
+			name: "libbar_rs",
+			crate_name: "bar_rs",
+			srcs: ["bar.rs"],
+			stubs: { versions: ["29", "30"] },
+		}
+
 		cc_library {
 			name: "libbar",
 			stubs: {
@@ -2455,11 +2690,12 @@
 		prepareForTestWithSantitizeHwaddress,
 	)
 	expectLink := func(from, from_variant, to, to_variant string) {
-		ld := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld")
+		ld := ctx.ModuleForTests(t, from, "android_arm64_armv8-a_"+from_variant).Rule("ld")
 		libFlags := ld.Args["libFlags"]
 		ensureContains(t, libFlags, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
 	expectLink("libx", "shared_hwasan_apex29", "libbar", "shared_current")
+	expectLink("libx", "shared_hwasan_apex29", "unstripped/libbar_rs", "shared_current")
 }
 
 func TestQTargetApexUsesStaticUnwinder(t *testing.T) {
@@ -2486,10 +2722,10 @@
 	`)
 
 	// ensure apex variant of c++ is linked with static unwinder
-	cm := ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared_apex29").Module().(*cc.Module)
+	cm := ctx.ModuleForTests(t, "libc++", "android_arm64_armv8-a_shared_apex29").Module().(*cc.Module)
 	ensureListContains(t, cm.Properties.AndroidMkStaticLibs, "libunwind")
 	// note that platform variant is not.
-	cm = ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared").Module().(*cc.Module)
+	cm = ctx.ModuleForTests(t, "libc++", "android_arm64_armv8-a_shared").Module().(*cc.Module)
 	ensureListNotContains(t, cm.Properties.AndroidMkStaticLibs, "libunwind")
 }
 
@@ -2982,7 +3218,7 @@
 		}
 	`)
 	expectLink := func(from, from_variant, to, to_variant string) {
-		ld := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld")
+		ld := ctx.ModuleForTests(t, from, "android_arm64_armv8-a_"+from_variant).Rule("ld")
 		libFlags := ld.Args["libFlags"]
 		ensureContains(t, libFlags, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
@@ -3057,7 +3293,7 @@
 	`, withSAsActiveCodeNames)
 
 	// ensure libfoo is linked with current version of libbar stub
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared_apex10000")
+	libfoo := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared_apex10000")
 	libFlags := libfoo.Rule("ld").Args["libFlags"]
 	ensureContains(t, libFlags, "android_arm64_armv8-a_shared_current/libbar.so")
 }
@@ -3113,7 +3349,7 @@
 		}
 	`)
 
-	generateFsRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("generateFsConfig")
+	generateFsRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("generateFsConfig")
 	cmd := generateFsRule.RuleParams.Command
 
 	// Ensure that the subdirectories are all listed
@@ -3227,7 +3463,7 @@
 		"lib64/libc++.so",
 	})
 
-	apexBundle := result.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
+	apexBundle := result.ModuleForTests(t, "myapex", "android_common_myapex").Module().(*apexBundle)
 	data := android.AndroidMkDataForTest(t, result.TestContext, apexBundle)
 	name := apexBundle.BaseModuleName()
 	prefix := "TARGET_"
@@ -3237,7 +3473,7 @@
 	installPath := "out/target/product/test_device/vendor/apex"
 	ensureContains(t, androidMk, "LOCAL_MODULE_PATH := "+installPath)
 
-	apexManifestRule := result.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule")
+	apexManifestRule := result.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexManifestRule")
 	requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"])
 	ensureListNotContains(t, requireNativeLibs, ":vndk")
 }
@@ -3268,7 +3504,7 @@
 	`)
 
 	cflags := strings.Fields(
-		ctx.ModuleForTests("foo", "android_product_arm64_armv8-a_apex10000").Rule("cc").Args["cFlags"])
+		ctx.ModuleForTests(t, "foo", "android_product_arm64_armv8-a_apex10000").Rule("cc").Args["cFlags"])
 	ensureListContains(t, cflags, "-D__ANDROID_VNDK__")
 	ensureListContains(t, cflags, "-D__ANDROID_APEX__")
 	ensureListContains(t, cflags, "-D__ANDROID_PRODUCT__")
@@ -3337,7 +3573,7 @@
 		}
 	`)
 
-	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
+	apexBundle := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Module().(*apexBundle)
 	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
 	name := apexBundle.BaseModuleName()
 	prefix := "TARGET_"
@@ -3366,7 +3602,7 @@
 		}
 	`)
 
-	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
+	apexBundle := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Module().(*apexBundle)
 	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
 	name := apexBundle.BaseModuleName()
 	prefix := "TARGET_"
@@ -3404,20 +3640,31 @@
 			apex_available: ["myapex"],
 		}
 
+		rust_ffi {
+			name: "libmylib_rs",
+			crate_name: "mylib_rs",
+			srcs: ["mylib.rs"],
+			stubs: {
+				versions: ["1", "2", "3"],
+			},
+			apex_available: ["myapex"],
+		}
+
 		cc_binary {
 			name: "not_in_apex",
 			srcs: ["mylib.cpp"],
-			static_libs: ["mylib"],
+			static_libs: ["mylib", "libmylib_rs"],
 			static_executable: true,
 			system_shared_libs: [],
 			stl: "none",
 		}
 	`)
 
-	ldFlags := ctx.ModuleForTests("not_in_apex", "android_arm64_armv8-a").Rule("ld").Args["libFlags"]
+	ldFlags := ctx.ModuleForTests(t, "not_in_apex", "android_arm64_armv8-a").Rule("ld").Args["libFlags"]
 
 	// Ensure that not_in_apex is linking with the static variant of mylib
 	ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_static/mylib.a")
+	ensureContains(t, ldFlags, "generated_rust_staticlib/librustlibs.a")
 }
 
 func TestKeys(t *testing.T) {
@@ -3459,7 +3706,7 @@
 	`)
 
 	// check the APEX keys
-	keys := ctx.ModuleForTests("myapex.key", "android_common").Module().(*apexKey)
+	keys := ctx.ModuleForTests(t, "myapex.key", "android_common").Module().(*apexKey)
 
 	if keys.publicKeyFile.String() != "vendor/foo/devkeys/testkey.avbpubkey" {
 		t.Errorf("public key %q is not %q", keys.publicKeyFile.String(),
@@ -3471,7 +3718,7 @@
 	}
 
 	// check the APK certs. It should be overridden to myapex.certificate.override
-	certs := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest").Rule("signapk").Args["certificates"]
+	certs := ctx.ModuleForTests(t, "myapex_keytest", "android_common_myapex_keytest").Rule("signapk").Args["certificates"]
 	if certs != "testkey.override.x509.pem testkey.override.pk8" {
 		t.Errorf("cert and private key %q are not %q", certs,
 			"testkey.override.509.pem testkey.override.pk8")
@@ -3493,7 +3740,7 @@
 				public_key: "testkey.avbpubkey",
 				private_key: "testkey.pem",
 			}`)
-		rule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("signapk")
+		rule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("signapk")
 		expected := "vendor/foo/devkeys/test.x509.pem vendor/foo/devkeys/test.pk8"
 		if actual := rule.Args["certificates"]; actual != expected {
 			t.Errorf("certificates should be %q, not %q", expected, actual)
@@ -3517,7 +3764,7 @@
 				name: "myapex.certificate.override",
 				certificate: "testkey.override",
 			}`)
-		rule := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest").Rule("signapk")
+		rule := ctx.ModuleForTests(t, "myapex_keytest", "android_common_myapex_keytest").Rule("signapk")
 		expected := "testkey.override.x509.pem testkey.override.pk8"
 		if actual := rule.Args["certificates"]; actual != expected {
 			t.Errorf("certificates should be %q, not %q", expected, actual)
@@ -3541,7 +3788,7 @@
 				name: "myapex.certificate",
 				certificate: "testkey",
 			}`)
-		rule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("signapk")
+		rule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("signapk")
 		expected := "testkey.x509.pem testkey.pk8"
 		if actual := rule.Args["certificates"]; actual != expected {
 			t.Errorf("certificates should be %q, not %q", expected, actual)
@@ -3566,7 +3813,7 @@
 				name: "myapex.certificate.override",
 				certificate: "testkey.override",
 			}`)
-		rule := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest").Rule("signapk")
+		rule := ctx.ModuleForTests(t, "myapex_keytest", "android_common_myapex_keytest").Rule("signapk")
 		expected := "testkey.override.x509.pem testkey.override.pk8"
 		if actual := rule.Args["certificates"]; actual != expected {
 			t.Errorf("certificates should be %q, not %q", expected, actual)
@@ -3585,8 +3832,12 @@
 				name: "myapex.key",
 				public_key: "testkey.avbpubkey",
 				private_key: "testkey.pem",
-			}`)
-		rule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("signapk")
+			}`,
+			android.MockFS{
+				"vendor/foo/devkeys/testkey.x509.pem": nil,
+			}.AddToFixture(),
+		)
+		rule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("signapk")
 		expected := "vendor/foo/devkeys/testkey.x509.pem vendor/foo/devkeys/testkey.pk8"
 		if actual := rule.Args["certificates"]; actual != expected {
 			t.Errorf("certificates should be %q, not %q", expected, actual)
@@ -3611,7 +3862,7 @@
 				name: "myapex.certificate.override",
 				certificate: "testkey.override",
 			}`)
-		rule := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest").Rule("signapk")
+		rule := ctx.ModuleForTests(t, "myapex_keytest", "android_common_myapex_keytest").Rule("signapk")
 		expected := "testkey.override.x509.pem testkey.override.pk8"
 		if actual := rule.Args["certificates"]; actual != expected {
 			t.Errorf("certificates should be %q, not %q", expected, actual)
@@ -3682,34 +3933,34 @@
 	`)
 
 	// non-APEX variant does not have __ANDROID_APEX__ defined
-	mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+	mylibCFlags := ctx.ModuleForTests(t, "mylib", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 
 	// APEX variant has __ANDROID_APEX__ and __ANDROID_APEX__ defined
-	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"]
+	mylibCFlags = ctx.ModuleForTests(t, "mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"]
 	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 
 	// APEX variant has __ANDROID_APEX__ and __ANDROID_APEX__ defined
-	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex29").Rule("cc").Args["cFlags"]
+	mylibCFlags = ctx.ModuleForTests(t, "mylib", "android_arm64_armv8-a_static_apex29").Rule("cc").Args["cFlags"]
 	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 
 	// When a cc_library sets use_apex_name_macro: true each apex gets a unique variant and
 	// each variant defines additional macros to distinguish which apex variant it is built for
 
 	// non-APEX variant does not have __ANDROID_APEX__ defined
-	mylibCFlags = ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+	mylibCFlags = ctx.ModuleForTests(t, "mylib3", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 
 	// recovery variant does not set __ANDROID_APEX__
-	mylibCFlags = ctx.ModuleForTests("mylib3", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+	mylibCFlags = ctx.ModuleForTests(t, "mylib3", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 
 	// non-APEX variant does not have __ANDROID_APEX__ defined
-	mylibCFlags = ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+	mylibCFlags = ctx.ModuleForTests(t, "mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 
 	// recovery variant does not set __ANDROID_APEX__
-	mylibCFlags = ctx.ModuleForTests("mylib2", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+	mylibCFlags = ctx.ModuleForTests(t, "mylib2", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 }
 
@@ -3759,7 +4010,7 @@
 		}
 	`)
 
-	cFlags := ctx.ModuleForTests("otherlib", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+	cFlags := ctx.ModuleForTests(t, "otherlib", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 
 	// Ensure that the include path of the header lib is exported to 'otherlib'
 	ensureContains(t, cFlags, "-Imy_include")
@@ -3791,7 +4042,7 @@
 
 func getFiles(t *testing.T, ctx *android.TestContext, moduleName, variant string) []fileInApex {
 	t.Helper()
-	module := ctx.ModuleForTests(moduleName, variant)
+	module := ctx.ModuleForTests(t, moduleName, variant)
 	apexRule := module.MaybeRule("apexRule")
 	apexDir := "/image.apex/"
 	copyCmds := apexRule.Args["copy_commands"]
@@ -3882,7 +4133,7 @@
 }
 
 func ensureExactDeapexedContents(t *testing.T, ctx *android.TestContext, moduleName string, variant string, files []string) {
-	deapexer := ctx.ModuleForTests(moduleName, variant).Description("deapex")
+	deapexer := ctx.ModuleForTests(t, moduleName, variant).Description("deapex")
 	outputs := make([]string, 0, len(deapexer.ImplicitOutputs)+1)
 	if deapexer.Output != nil {
 		outputs = append(outputs, deapexer.Output.String())
@@ -4011,7 +4262,7 @@
 		}`+vndkLibrariesTxtFiles("28", "29"))
 
 	assertApexName := func(expected, moduleName string) {
-		module := ctx.ModuleForTests(moduleName, "android_common")
+		module := ctx.ModuleForTests(t, moduleName, "android_common")
 		apexManifestRule := module.Rule("apexManifestRule")
 		ensureContains(t, apexManifestRule.Args["opt"], "-v name "+expected)
 	}
@@ -4239,25 +4490,25 @@
 	var apexManifestRule android.TestingBuildParams
 	var provideNativeLibs, requireNativeLibs []string
 
-	apexManifestRule = ctx.ModuleForTests("myapex_nodep", "android_common_myapex_nodep").Rule("apexManifestRule")
+	apexManifestRule = ctx.ModuleForTests(t, "myapex_nodep", "android_common_myapex_nodep").Rule("apexManifestRule")
 	provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"])
 	requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"])
 	ensureListEmpty(t, provideNativeLibs)
 	ensureListEmpty(t, requireNativeLibs)
 
-	apexManifestRule = ctx.ModuleForTests("myapex_dep", "android_common_myapex_dep").Rule("apexManifestRule")
+	apexManifestRule = ctx.ModuleForTests(t, "myapex_dep", "android_common_myapex_dep").Rule("apexManifestRule")
 	provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"])
 	requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"])
 	ensureListEmpty(t, provideNativeLibs)
 	ensureListContains(t, requireNativeLibs, "libfoo.so")
 
-	apexManifestRule = ctx.ModuleForTests("myapex_provider", "android_common_myapex_provider").Rule("apexManifestRule")
+	apexManifestRule = ctx.ModuleForTests(t, "myapex_provider", "android_common_myapex_provider").Rule("apexManifestRule")
 	provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"])
 	requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"])
 	ensureListContains(t, provideNativeLibs, "libfoo.so")
 	ensureListEmpty(t, requireNativeLibs)
 
-	apexManifestRule = ctx.ModuleForTests("myapex_selfcontained", "android_common_myapex_selfcontained").Rule("apexManifestRule")
+	apexManifestRule = ctx.ModuleForTests(t, "myapex_selfcontained", "android_common_myapex_selfcontained").Rule("apexManifestRule")
 	provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"])
 	requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"])
 	ensureListContains(t, provideNativeLibs, "libbar.so")
@@ -4294,7 +4545,7 @@
 		"OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION": "1234",
 	}))
 
-	module := ctx.ModuleForTests("myapex", "android_common_myapex")
+	module := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	apexManifestRule := module.Rule("apexManifestRule")
 	ensureContains(t, apexManifestRule.Args["default_version"], "1234")
 }
@@ -4358,7 +4609,7 @@
 			}
 		`, testCase.compileMultiLibProp),
 		)
-		module := ctx.ModuleForTests("myapex", "android_common_myapex")
+		module := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 		apexRule := module.Rule("apexRule")
 		copyCmds := apexRule.Args["copy_commands"]
 		for _, containedLib := range testCase.containedLibs {
@@ -4398,7 +4649,7 @@
 		}
 	`)
 
-	module := ctx.ModuleForTests("myapex", "android_common_myapex")
+	module := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	apexRule := module.Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
@@ -4418,7 +4669,7 @@
 	// Ensure that the platform variant ends with _shared
 	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_shared")
 
-	if !ctx.ModuleForTests("mylib_common", "android_arm64_armv8-a_shared_apex10000").Module().(*cc.Module).InAnyApex() {
+	if !ctx.ModuleForTests(t, "mylib_common", "android_arm64_armv8-a_shared_apex10000").Module().(*cc.Module).InAnyApex() {
 		t.Log("Found mylib_common not in any apex!")
 		t.Fail()
 	}
@@ -4453,7 +4704,7 @@
 		}
 	`)
 
-	module := ctx.ModuleForTests("myapex", "android_common_myapex")
+	module := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	apexRule := module.Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
@@ -4527,7 +4778,7 @@
 		ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{
 			"bin/mybin",
 		})
-		apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule")
+		apexManifestRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexManifestRule")
 		android.AssertStringEquals(t, "should require libz", apexManifestRule.Args["requireNativeLibs"], "libz.so")
 	}
 	// libz doesn't provide stubs for vendor variant.
@@ -4536,7 +4787,7 @@
 			"bin/mybin",
 			"lib64/libz.so",
 		})
-		apexManifestRule := ctx.ModuleForTests("myvendorapex", "android_common_myvendorapex").Rule("apexManifestRule")
+		apexManifestRule := ctx.ModuleForTests(t, "myvendorapex", "android_common_myvendorapex").Rule("apexManifestRule")
 		android.AssertStringEquals(t, "should not require libz", apexManifestRule.Args["requireNativeLibs"], "")
 	}
 }
@@ -4611,7 +4862,7 @@
 		}
 	`)
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+	apexRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
 	// Ensure that main rule creates an output
@@ -4696,7 +4947,7 @@
 		}
 	`)
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+	apexRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
 	// Ensure that apex variant is created for the direct dep
@@ -4734,7 +4985,7 @@
 		}
 	`)
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+	apexRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
 	ensureContains(t, copyCmds, "image.apex/bin/script/myscript.sh")
@@ -4770,8 +5021,8 @@
 				}
 			`)
 
-			apex := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
-			expected := "out/soong/target/product/test_device/" + tc.partition + "/apex"
+			apex := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Module().(*apexBundle)
+			expected := "out/target/product/test_device/" + tc.partition + "/apex"
 			actual := apex.installDir.RelativeToTop().String()
 			if actual != expected {
 				t.Errorf("wrong install path. expected %q. actual %q", expected, actual)
@@ -4795,7 +5046,7 @@
 			private_key: "testkey.pem",
 		}
 	`)
-	module := ctx.ModuleForTests("myapex", "android_common_myapex")
+	module := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	rule := module.Output("file_contexts")
 	ensureContains(t, rule.RuleParams.Command, "cat system/sepolicy/apex/myapex-file_contexts")
 }
@@ -4855,7 +5106,7 @@
 	`, withFiles(map[string][]byte{
 		"product_specific_file_contexts": nil,
 	}))
-	module := ctx.ModuleForTests("myapex", "android_common_myapex")
+	module := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	rule := module.Output("file_contexts")
 	ensureContains(t, rule.RuleParams.Command, "cat product_specific_file_contexts")
 }
@@ -4884,7 +5135,7 @@
 	`, withFiles(map[string][]byte{
 		"product_specific_file_contexts": nil,
 	}))
-	module := ctx.ModuleForTests("myapex", "android_common_myapex")
+	module := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	rule := module.Output("file_contexts")
 	ensureContains(t, rule.RuleParams.Command, "cat product_specific_file_contexts")
 }
@@ -4910,7 +5161,7 @@
 		}
 	`)
 
-	apex_key := ctx.ModuleForTests("myapex.key", "android_common").Module().(*apexKey)
+	apex_key := ctx.ModuleForTests(t, "myapex.key", "android_common").Module().(*apexKey)
 	expected_pubkey := "testkey2.avbpubkey"
 	actual_pubkey := apex_key.publicKeyFile.String()
 	if actual_pubkey != expected_pubkey {
@@ -4939,7 +5190,7 @@
 		}
 	`)
 
-	testingModule := ctx.ModuleForTests("myapex", "android_common_myapex")
+	testingModule := ctx.ModuleForTests(t, "myapex", "android_common_prebuilt_myapex")
 	prebuilt := testingModule.Module().(*Prebuilt)
 
 	expectedInput := "myapex-arm64.apex"
@@ -4960,7 +5211,7 @@
 
 func TestPrebuiltMissingSrc(t *testing.T) {
 	t.Parallel()
-	testApexError(t, `module "myapex" variant "android_common_myapex".*: prebuilt_apex does not support "arm64_armv8-a"`, `
+	testApexError(t, `module "myapex" variant "android_common_prebuilt_myapex".*: prebuilt_apex does not support "arm64_armv8-a"`, `
 		prebuilt_apex {
 			name: "myapex",
 		}
@@ -4977,7 +5228,7 @@
 		}
 	`)
 
-	testingModule := ctx.ModuleForTests("myapex", "android_common_myapex")
+	testingModule := ctx.ModuleForTests(t, "myapex", "android_common_prebuilt_myapex")
 	p := testingModule.Module().(*Prebuilt)
 
 	expected := "notmyapex.apex"
@@ -5000,7 +5251,7 @@
 			set: "company-myapex.apks",
       filename: "com.company.android.myapex.apex"
 		}
-	`).ModuleForTests("com.company.android.myapex", "android_common_com.android.myapex")
+	`).ModuleForTests(t, "com.company.android.myapex", "android_common_prebuilt_com.android.myapex")
 
 	testApex(t, `
 		apex_set {
@@ -5009,7 +5260,7 @@
 			set: "company-myapex.apks",
       filename: "com.company.android.myapex.capex"
 		}
-	`).ModuleForTests("com.company.android.myapex", "android_common_com.android.myapex")
+	`).ModuleForTests(t, "com.company.android.myapex", "android_common_prebuilt_com.android.myapex")
 
 	testApexError(t, `filename should end in .apex or .capex for apex_set`, `
 		apex_set {
@@ -5033,7 +5284,7 @@
 		}
 	`)
 
-	testingModule := ctx.ModuleForTests("myapex.prebuilt", "android_common_myapex.prebuilt")
+	testingModule := ctx.ModuleForTests(t, "myapex.prebuilt", "android_common_prebuilt_myapex.prebuilt")
 	p := testingModule.Module().(*Prebuilt)
 
 	expected := []string{"myapex"}
@@ -5056,7 +5307,7 @@
 			apex_name: "com.android.myapex",
 			src: "company-myapex-arm.apex",
 		}
-	`).ModuleForTests("com.company.android.myapex", "android_common_com.android.myapex")
+	`).ModuleForTests(t, "com.company.android.myapex", "android_common_prebuilt_com.android.myapex")
 
 	testApex(t, `
 		apex_set {
@@ -5064,7 +5315,7 @@
 			apex_name: "com.android.myapex",
 			set: "company-myapex.apks",
 		}
-	`).ModuleForTests("com.company.android.myapex", "android_common_com.android.myapex")
+	`).ModuleForTests(t, "com.company.android.myapex", "android_common_prebuilt_com.android.myapex")
 }
 
 func TestPrebuiltApexNameWithPlatformBootclasspath(t *testing.T) {
@@ -5090,6 +5341,12 @@
 				exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
 			}
 
+			prebuilt_apex {
+				name: "com.android.art",
+				src: "com.android.art-arm.apex",
+				exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
+			}
+
 			prebuilt_bootclasspath_fragment {
 				name: "art-bootclasspath-fragment",
 				image_name: "art",
@@ -5143,7 +5400,7 @@
 
 	checkHiddenAPIIndexFromClassesInputs := func(t *testing.T, ctx *android.TestContext, expectedIntermediateInputs string) {
 		t.Helper()
-		platformBootclasspath := ctx.ModuleForTests("platform-bootclasspath", "android_common")
+		platformBootclasspath := ctx.ModuleForTests(t, "platform-bootclasspath", "android_common")
 		var rule android.TestingBuildParams
 
 		rule = platformBootclasspath.Output("hiddenapi-monolithic/index-from-classes.csv")
@@ -5152,7 +5409,7 @@
 
 	checkHiddenAPIIndexFromFlagsInputs := func(t *testing.T, ctx *android.TestContext, expectedIntermediateInputs string) {
 		t.Helper()
-		platformBootclasspath := ctx.ModuleForTests("platform-bootclasspath", "android_common")
+		platformBootclasspath := ctx.ModuleForTests(t, "platform-bootclasspath", "android_common")
 		var rule android.TestingBuildParams
 
 		rule = platformBootclasspath.Output("hiddenapi-index.csv")
@@ -5222,7 +5479,7 @@
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			my-bootclasspath-fragment/index.csv
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
-			out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv
+			out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_com.android.art/modular-hiddenapi/index.csv
 		`)
 	})
 
@@ -5295,10 +5552,10 @@
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			my-bootclasspath-fragment/index.csv
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
-			out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv
+			out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_com.android.art/modular-hiddenapi/index.csv
 		`)
 
-		myApex := ctx.ModuleForTests("myapex", "android_common_myapex").Module()
+		myApex := ctx.ModuleForTests(t, "myapex", "android_common_prebuilt_myapex").Module()
 
 		overrideNames := []string{
 			"",
@@ -5382,7 +5639,7 @@
 		// prebuilt_apex module always depends on the prebuilt, and so it doesn't
 		// find the dex boot jar in it. We either need to disable the source libfoo
 		// or make the prebuilt libfoo preferred.
-		testDexpreoptWithApexes(t, bp, "module libfoo does not provide a dex boot jar", preparer, fragment)
+		testDexpreoptWithApexes(t, bp, `module "platform-bootclasspath" variant ".*": module libfoo{.*} does not provide a dex jar`, preparer, fragment)
 		// dexbootjar check is skipped if AllowMissingDependencies is true
 		preparerAllowMissingDeps := android.GroupFixturePreparers(
 			preparer,
@@ -5418,6 +5675,7 @@
 
 		prebuilt_apex {
 			name: "myapex",
+			prefer: true,
 			arch: {
 				arm64: {
 					src: "myapex-arm64.apex",
@@ -5490,7 +5748,7 @@
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			my-bootclasspath-fragment/index.csv
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
-			out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv
+			out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_com.android.art/modular-hiddenapi/index.csv
 		`)
 	})
 
@@ -5589,7 +5847,7 @@
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
 			out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/modular-hiddenapi/index.csv
-			out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv
+			out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_com.android.art/modular-hiddenapi/index.csv
 		`)
 	})
 
@@ -5701,7 +5959,7 @@
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			my-bootclasspath-fragment/index.csv
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
-			out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv
+			out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_com.android.art/modular-hiddenapi/index.csv
 		`)
 	})
 
@@ -5818,7 +6076,6 @@
 			relative_install_path: "test",
 			shared_libs: ["mylib"],
 			system_shared_libs: [],
-			static_executable: true,
 			stl: "none",
 			data: [":fg"],
 		}
@@ -5838,7 +6095,7 @@
 		}
 	`)
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+	apexRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
 	// Ensure that test dep (and their transitive dependencies) are copied into apex.
@@ -5850,7 +6107,7 @@
 	ensureContains(t, copyCmds, "image.apex/bin/test/bar/baz")
 
 	// Ensure the module is correctly translated.
-	bundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
+	bundle := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Module().(*apexBundle)
 	data := android.AndroidMkDataForTest(t, ctx, bundle)
 	name := bundle.BaseModuleName()
 	prefix := "TARGET_"
@@ -5932,7 +6189,7 @@
 		}
 	`)
 
-	module := ctx.ModuleForTests("myapex", "android_common_myapex")
+	module := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	apexRule := module.Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 	ensureContains(t, copyCmds, "image.apex/javalib/myjavaimport.jar")
@@ -5998,7 +6255,7 @@
 		}
 	`)
 
-	module := ctx.ModuleForTests("myapex", "android_common_myapex")
+	module := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	apexRule := module.Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
@@ -6006,14 +6263,14 @@
 	ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPriv@TEST.BUILD_ID/AppFooPriv.apk")
 	ensureContains(t, copyCmds, "image.apex/etc/permissions/privapp_allowlist_com.android.AppFooPriv.xml")
 
-	appZipRule := ctx.ModuleForTests("AppFoo", "android_common_apex10000").Description("zip jni libs")
+	appZipRule := ctx.ModuleForTests(t, "AppFoo", "android_common_apex10000").Description("zip jni libs")
 	// JNI libraries are uncompressed
 	if args := appZipRule.Args["jarArgs"]; !strings.Contains(args, "-L 0") {
 		t.Errorf("jni libs are not uncompressed for AppFoo")
 	}
 	// JNI libraries including transitive deps are
 	for _, jni := range []string{"libjni", "libfoo"} {
-		jniOutput := ctx.ModuleForTests(jni, "android_arm64_armv8-a_sdk_shared_apex10000").Module().(*cc.Module).OutputFile().RelativeToTop()
+		jniOutput := ctx.ModuleForTests(t, jni, "android_arm64_armv8-a_sdk_shared_apex10000").Module().(*cc.Module).OutputFile().RelativeToTop()
 		// ... embedded inside APK (jnilibs.zip)
 		ensureListContains(t, appZipRule.Implicits.Strings(), jniOutput.String())
 		// ... and not directly inside the APEX
@@ -6106,7 +6363,7 @@
 		}
 	`)
 
-	module := ctx.ModuleForTests("myapex", "android_common_myapex")
+	module := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	apexRule := module.Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
@@ -6184,7 +6441,7 @@
 
 	`)
 
-	module := ctx.ModuleForTests("myapex", "android_common_myapex")
+	module := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	apexRule := module.Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
@@ -6682,14 +6939,14 @@
 		}
 	`)
 
-	fooManifestRule := result.ModuleForTests("foo", "android_common_foo").Rule("apexManifestRule")
+	fooManifestRule := result.ModuleForTests(t, "foo", "android_common_foo").Rule("apexManifestRule")
 	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")
+	barManifestRule := result.ModuleForTests(t, "bar", "android_common_bar").Rule("apexManifestRule")
 	defaultVersionInt, _ := strconv.Atoi(testDefaultUpdatableModuleVersion)
 	barExpectedDefaultVersion := fmt.Sprint(defaultVersionInt + 3)
 	barActualDefaultVersion := barManifestRule.Args["default_version"]
@@ -6697,7 +6954,7 @@
 		t.Errorf("expected to find defaultVersion %q; got %q", barExpectedDefaultVersion, barActualDefaultVersion)
 	}
 
-	overrideBarManifestRule := result.ModuleForTests("bar", "android_common_myoverrideapex_myoverrideapex").Rule("apexManifestRule")
+	overrideBarManifestRule := result.ModuleForTests(t, "bar", "android_common_myoverrideapex_myoverrideapex").Rule("apexManifestRule")
 	overrideBarActualDefaultVersion := overrideBarManifestRule.Args["default_version"]
 	if overrideBarActualDefaultVersion != barExpectedDefaultVersion {
 		t.Errorf("expected to find defaultVersion %q; got %q", barExpectedDefaultVersion, barActualDefaultVersion)
@@ -6937,14 +7194,14 @@
 
 	// libfoo shouldn't be available to platform even though it has "//apex_available:platform",
 	// because it depends on libbar which isn't available to platform
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module().(*cc.Module)
+	libfoo := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared").Module().(*cc.Module)
 	if libfoo.NotAvailableForPlatform() != true {
 		t.Errorf("%q shouldn't be available to platform", libfoo.String())
 	}
 
 	// libfoo2 however can be available to platform because it depends on libbaz which provides
 	// stubs
-	libfoo2 := ctx.ModuleForTests("libfoo2", "android_arm64_armv8-a_shared").Module().(*cc.Module)
+	libfoo2 := ctx.ModuleForTests(t, "libfoo2", "android_arm64_armv8-a_shared").Module().(*cc.Module)
 	if libfoo2.NotAvailableForPlatform() == true {
 		t.Errorf("%q should be available to platform", libfoo2.String())
 	}
@@ -6976,11 +7233,11 @@
 		},
 	}`)
 
-	libfooShared := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module().(*cc.Module)
+	libfooShared := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared").Module().(*cc.Module)
 	if libfooShared.NotAvailableForPlatform() != true {
 		t.Errorf("%q shouldn't be available to platform", libfooShared.String())
 	}
-	libfooStatic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Module().(*cc.Module)
+	libfooStatic := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_static").Module().(*cc.Module)
 	if libfooStatic.NotAvailableForPlatform() != false {
 		t.Errorf("%q should be available to platform", libfooStatic.String())
 	}
@@ -7081,6 +7338,34 @@
 	`)
 }
 
+func TestApexValidation_UsesProperPartitionTag(t *testing.T) {
+	t.Parallel()
+	ctx := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			updatable: false,
+			vendor: true,
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`, android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+		// vendor path should not affect "partition tag"
+		variables.VendorPath = proptools.StringPtr("system/vendor")
+	}))
+
+	module := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
+	android.AssertStringEquals(t, "partition tag for host_apex_verifier",
+		"vendor",
+		module.Output("host_apex_verifier.timestamp").Args["partition_tag"])
+	android.AssertStringEquals(t, "partition tag for apex_sepolicy_tests",
+		"vendor",
+		module.Output("apex_sepolicy_tests.timestamp").Args["partition_tag"])
+}
+
 func TestApexValidation_TestApexCanSkipInitRcCheck(t *testing.T) {
 	t.Parallel()
 	ctx := testApex(t, `
@@ -7099,7 +7384,7 @@
 		}
 	`)
 
-	validations := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("signapk").Validations.Strings()
+	validations := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("signapk").Validations.Strings()
 	if android.SuffixInList(validations, "host_apex_verifier.timestamp") {
 		t.Error("should not run host_apex_verifier")
 	}
@@ -7120,7 +7405,7 @@
 		}
 	`)
 
-	validations := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("signapk").Validations.Strings()
+	validations := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("signapk").Validations.Strings()
 	if !android.SuffixInList(validations, "host_apex_verifier.timestamp") {
 		t.Error("should run host_apex_verifier")
 	}
@@ -7224,8 +7509,8 @@
 		}
 	`, withManifestPackageNameOverrides([]string{"myapex:com.android.myapex"}))
 
-	originalVariant := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(android.OverridableModule)
-	overriddenVariant := ctx.ModuleForTests("myapex", "android_common_override_myapex_override_myapex").Module().(android.OverridableModule)
+	originalVariant := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Module().(android.OverridableModule)
+	overriddenVariant := ctx.ModuleForTests(t, "myapex", "android_common_override_myapex_override_myapex").Module().(android.OverridableModule)
 	if originalVariant.GetOverriddenBy() != "" {
 		t.Errorf("GetOverriddenBy should be empty, but was %q", originalVariant.GetOverriddenBy())
 	}
@@ -7233,7 +7518,7 @@
 		t.Errorf("GetOverriddenBy should be \"override_myapex\", but was %q", overriddenVariant.GetOverriddenBy())
 	}
 
-	module := ctx.ModuleForTests("myapex", "android_common_override_myapex_override_myapex")
+	module := ctx.ModuleForTests(t, "myapex", "android_common_override_myapex_override_myapex")
 	apexRule := module.Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
@@ -7324,7 +7609,7 @@
 
 	`, withApexGlobalMinSdkVersionOverride(&minSdkOverride31))
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+	apexRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
 	// Ensure that direct non-stubs dep is always included
@@ -7384,7 +7669,7 @@
 
 	`, withApexGlobalMinSdkVersionOverride(&minSdkOverride29))
 
-	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+	apexRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
 	// Ensure that direct non-stubs dep is always included
@@ -7423,7 +7708,7 @@
 		}
 	`, withUnbundledBuild)
 
-	module := ctx.ModuleForTests("myapex", "android_common_myapex")
+	module := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	args := module.Rule("apexRule").Args
 	ensureContains(t, args["opt_flags"], "--manifest_json "+module.Output("apex_manifest.json").Output.String())
 
@@ -7432,8 +7717,8 @@
 	// the dependency names directly here but for some reason the names are blank in
 	// this test.
 	for _, lib := range []string{"libc++", "mylib"} {
-		apexImplicits := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared_apex29").Rule("ld").Implicits
-		nonApexImplicits := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared").Rule("ld").Implicits
+		apexImplicits := ctx.ModuleForTests(t, lib, "android_arm64_armv8-a_shared_apex29").Rule("ld").Implicits
+		nonApexImplicits := ctx.ModuleForTests(t, lib, "android_arm64_armv8-a_shared").Rule("ld").Implicits
 		if len(apexImplicits) != len(nonApexImplicits)+1 {
 			t.Errorf("%q missing unwinder dep", lib)
 		}
@@ -7492,7 +7777,7 @@
 		"etc/permissions/foo.xml",
 	})
 	// Permission XML should point to the activated path of impl jar of java_sdk_library
-	sdkLibrary := ctx.ModuleForTests("foo.xml", "android_common_myapex").Output("foo.xml")
+	sdkLibrary := ctx.ModuleForTests(t, "foo.xml", "android_common_myapex").Output("foo.xml")
 	contents := android.ContentFromFileRuleForTests(t, ctx, sdkLibrary)
 	ensureMatches(t, contents, "<library\\n\\s+name=\\\"foo\\\"\\n\\s+file=\\\"/apex/myapex/javalib/foo.jar\\\"")
 }
@@ -7533,7 +7818,7 @@
 	// Permission XML should point to the activated path of impl jar of java_sdk_library.
 	// Since override variants (com.mycompany.android.foo) are installed in the same package as the overridden variant
 	// (com.android.foo), the filepath should not contain override apex name.
-	sdkLibrary := ctx.ModuleForTests("foo.xml", "android_common_mycompanyapex").Output("foo.xml")
+	sdkLibrary := ctx.ModuleForTests(t, "foo.xml", "android_common_mycompanyapex").Output("foo.xml")
 	contents := android.ContentFromFileRuleForTests(t, ctx, sdkLibrary)
 	ensureMatches(t, contents, "<library\\n\\s+name=\\\"foo\\\"\\n\\s+file=\\\"/apex/myapex/javalib/foo.jar\\\"")
 }
@@ -7588,8 +7873,8 @@
 	})
 
 	// The bar library should depend on the implementation jar.
-	barLibrary := ctx.ModuleForTests("bar", "android_common_myapex").Rule("javac")
-	if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	barLibrary := ctx.ModuleForTests(t, "bar", "android_common_apex10000").Rule("javac")
+	if expected, actual := `^-classpath [^:]*/turbine/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
@@ -7640,8 +7925,8 @@
 	})
 
 	// The bar library should depend on the stubs jar.
-	barLibrary := ctx.ModuleForTests("bar", "android_common").Rule("javac")
-	if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	barLibrary := ctx.ModuleForTests(t, "bar", "android_common").Rule("javac")
+	if expected, actual := `^-classpath [^:]*/foo\.stubs\.from-text/foo\.stubs\.from-text\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
@@ -7734,8 +8019,8 @@
 	})
 
 	// The bar library should depend on the implementation jar.
-	barLibrary := ctx.ModuleForTests("bar", "android_common_myapex").Rule("javac")
-	if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	barLibrary := ctx.ModuleForTests(t, "bar", "android_common_apex10000").Rule("javac")
+	if expected, actual := `^-classpath [^:]*/turbine/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
@@ -8113,7 +8398,7 @@
 		}
 	`)
 
-	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
+	apexBundle := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Module().(*apexBundle)
 	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
 	var builder strings.Builder
 	data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data)
@@ -8199,7 +8484,7 @@
 
 	`)
 
-	rule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule")
+	rule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexManifestRule")
 	// Notice mylib2.so (transitive dep) is not added as a jni_lib
 	ensureEquals(t, rule.Args["opt"], "-a jniLibs libfoo.rust.so mylib.so mylib3.so")
 	ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{
@@ -8208,39 +8493,12 @@
 		"lib64/mylib2.so",
 		"lib64/mylib3.so",
 		"lib64/libfoo.rust.so",
-		"lib64/libc++.so", // auto-added to libfoo.rust by Soong
-		"lib64/liblog.so", // auto-added to libfoo.rust by Soong
 	})
 
 	// b/220397949
 	ensureListContains(t, names(rule.Args["requireNativeLibs"]), "libfoo.shared_from_rust.so")
 }
 
-func TestApexMutatorsDontRunIfDisabled(t *testing.T) {
-	t.Parallel()
-	ctx := testApex(t, `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			updatable: false,
-		}
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-	`,
-		android.FixtureModifyConfig(func(config android.Config) {
-			delete(config.Targets, android.Android)
-			config.AndroidCommonTarget = android.Target{}
-		}),
-	)
-
-	if expected, got := []string{""}, ctx.ModuleVariantsForTests("myapex"); !reflect.DeepEqual(expected, got) {
-		t.Errorf("Expected variants: %v, but got: %v", expected, got)
-	}
-}
-
 func TestAppBundle(t *testing.T) {
 	t.Parallel()
 	ctx := testApex(t, `
@@ -8266,7 +8524,7 @@
 		}
 		`, withManifestPackageNameOverrides([]string{"AppFoo:com.android.foo"}))
 
-	bundleConfigRule := ctx.ModuleForTests("myapex", "android_common_myapex").Output("bundle_config.json")
+	bundleConfigRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Output("bundle_config.json")
 	content := android.ContentFromFileRuleForTests(t, ctx, bundleConfigRule)
 
 	ensureContains(t, content, `"compression":{"uncompressed_glob":["apex_payload.img","apex_manifest.*"]}`)
@@ -8293,7 +8551,7 @@
 			name: "AppSet",
 			set: "AppSet.apks",
 		}`)
-	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
+	mod := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	bundleConfigRule := mod.Output("bundle_config.json")
 	content := android.ContentFromFileRuleForTests(t, ctx, bundleConfigRule)
 	ensureContains(t, content, `"compression":{"uncompressed_glob":["apex_payload.img","apex_manifest.*"]}`)
@@ -8327,16 +8585,16 @@
 	ctx := testApex(t, bp, prepareForTestWithSantitizeHwaddress)
 
 	// Check that the extractor produces the correct output file from the correct input file.
-	extractorOutput := "out/soong/.intermediates/myapex/android_common_myapex/extracted/myapex.hwasan.apks"
+	extractorOutput := "out/soong/.intermediates/myapex/android_common_prebuilt_myapex/extracted/myapex.hwasan.apks"
 
-	m := ctx.ModuleForTests("myapex", "android_common_myapex")
+	m := ctx.ModuleForTests(t, "myapex", "android_common_prebuilt_myapex")
 	extractedApex := m.Output(extractorOutput)
 
 	android.AssertArrayString(t, "extractor input", []string{"myapex.hwasan.apks"}, extractedApex.Inputs.Strings())
 
 	// Ditto for the apex.
-	m = ctx.ModuleForTests("myapex", "android_common_myapex")
-	copiedApex := m.Output("out/soong/.intermediates/myapex/android_common_myapex/foo_v2.apex")
+	m = ctx.ModuleForTests(t, "myapex", "android_common_prebuilt_myapex")
+	copiedApex := m.Output("out/soong/.intermediates/myapex/android_common_prebuilt_myapex/foo_v2.apex")
 
 	android.AssertStringEquals(t, "myapex input", extractorOutput, copiedApex.Input.String())
 }
@@ -8355,10 +8613,10 @@
 		}
 	`)
 
-	m := ctx.ModuleForTests("myapex", "android_common_myapex")
+	m := ctx.ModuleForTests(t, "myapex", "android_common_prebuilt_myapex")
 
 	// Check that the extractor produces the correct apks file from the input module
-	extractorOutput := "out/soong/.intermediates/myapex/android_common_myapex/extracted/myapex.apks"
+	extractorOutput := "out/soong/.intermediates/myapex/android_common_prebuilt_myapex/extracted/myapex.apks"
 	extractedApex := m.Output(extractorOutput)
 
 	android.AssertArrayString(t, "extractor input", []string{"myapex.apks"}, extractedApex.Inputs.Strings())
@@ -8747,7 +9005,7 @@
 		}),
 	)
 
-	m := ctx.ModuleForTests("myapex", "android_common_myapex")
+	m := ctx.ModuleForTests(t, "myapex", "android_common_prebuilt_myapex")
 
 	// Check extract_apks tool parameters.
 	extractedApex := m.Output("extracted/myapex.apks")
@@ -8762,7 +9020,7 @@
 		t.Errorf("Unexpected abis parameter - expected %q vs actual %q", expected, actual)
 	}
 
-	m = ctx.ModuleForTests("myapex", "android_common_myapex")
+	m = ctx.ModuleForTests(t, "myapex", "android_common_prebuilt_myapex")
 	a := m.Module().(*ApexSet)
 	expectedOverrides := []string{"foo"}
 	actualOverrides := android.AndroidMkEntriesForTest(t, ctx, a)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
@@ -8789,7 +9047,7 @@
 		}),
 	)
 
-	m := ctx.ModuleForTests("myapex", "android_common_myapex")
+	m := ctx.ModuleForTests(t, "myapex", "android_common_prebuilt_myapex")
 
 	// Check extract_apks tool parameters. No native bridge arch expected
 	extractedApex := m.Output("extracted/myapex.apks")
@@ -8851,7 +9109,7 @@
 		}
 	`)
 
-	myapex := ctx.ModuleForTests("myapex", "android_common_myapex")
+	myapex := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	content := android.ContentFromFileRuleForTests(t, ctx, myapex.Output("apexkeys.txt"))
 	ensureContains(t, content, `name="myapex.apex" public_key="vendor/foo/devkeys/testkey.avbpubkey" private_key="vendor/foo/devkeys/testkey.pem" container_certificate="vendor/foo/devkeys/test.x509.pem" container_private_key="vendor/foo/devkeys/test.pk8" partition="system" sign_tool="sign_myapex"`)
 }
@@ -8894,10 +9152,10 @@
 	`)
 
 	content := android.ContentFromFileRuleForTests(t, ctx,
-		ctx.ModuleForTests("myapex", "android_common_myapex").Output("apexkeys.txt"))
+		ctx.ModuleForTests(t, "myapex", "android_common_myapex").Output("apexkeys.txt"))
 	ensureContains(t, content, `name="myapex.apex" public_key="vendor/foo/devkeys/testkey.avbpubkey" private_key="vendor/foo/devkeys/testkey.pem" container_certificate="vendor/foo/devkeys/test.x509.pem" container_private_key="vendor/foo/devkeys/test.pk8" partition="system" sign_tool="sign_myapex"`)
 	content = android.ContentFromFileRuleForTests(t, ctx,
-		ctx.ModuleForTests("myapex_set", "android_common_myapex_set").Output("apexkeys.txt"))
+		ctx.ModuleForTests(t, "myapex_set", "android_common_prebuilt_myapex_set").Output("apexkeys.txt"))
 	ensureContains(t, content, `name="myapex_set.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED" partition="system"`)
 }
 
@@ -8947,12 +9205,12 @@
 			`),
 	}))
 
-	rule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("diffApexContentRule")
+	rule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("diffApexContentRule")
 	if expected, actual := "allowed.txt", rule.Args["allowed_files_file"]; expected != actual {
 		t.Errorf("allowed_files_file: expected %q but got %q", expected, actual)
 	}
 
-	rule2 := ctx.ModuleForTests("myapex", "android_common_override_myapex_override_myapex").Rule("diffApexContentRule")
+	rule2 := ctx.ModuleForTests(t, "myapex", "android_common_override_myapex_override_myapex").Rule("diffApexContentRule")
 	if expected, actual := "sub/allowed.txt", rule2.Args["allowed_files_file"]; expected != actual {
 		t.Errorf("allowed_files_file: expected %q but got %q", expected, actual)
 	}
@@ -9015,14 +9273,14 @@
 		}),
 	)
 
-	compressRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("compressRule")
+	compressRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("compressRule")
 	ensureContains(t, compressRule.Output.String(), "myapex.capex.unsigned")
 
-	signApkRule := ctx.ModuleForTests("myapex", "android_common_myapex").Description("sign compressedApex")
+	signApkRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Description("sign compressedApex")
 	ensureEquals(t, signApkRule.Input.String(), compressRule.Output.String())
 
 	// Make sure output of bundle is .capex
-	ab := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
+	ab := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Module().(*apexBundle)
 	ensureContains(t, ab.outputFile.String(), "myapex.capex")
 
 	// Verify android.mk rules
@@ -9054,7 +9312,7 @@
 		}),
 	)
 
-	compressRule := ctx.ModuleForTests("myapex", "android_common_myapex").MaybeRule("compressRule")
+	compressRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").MaybeRule("compressRule")
 	if compressRule.Rule != nil {
 		t.Error("erofs apex should not be compressed")
 	}
@@ -9076,7 +9334,7 @@
 			}),
 			)
 
-			build := ctx.ModuleForTests("com.company.android.myapex", "android_common_com.android.myapex").Output("com.company.android.myapex.apex")
+			build := ctx.ModuleForTests(t, "com.company.android.myapex", "android_common_prebuilt_com.android.myapex").Output("com.company.android.myapex.apex")
 			if compressionEnabled {
 				ensureEquals(t, build.Rule.String(), "android/soong/android.Cp")
 			} else {
@@ -9128,7 +9386,7 @@
 		}
 	`)
 
-	ab := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
+	ab := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Module().(*apexBundle)
 	data := android.AndroidMkDataForTest(t, ctx, ab)
 	var builder strings.Builder
 	data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data)
@@ -9178,15 +9436,15 @@
 	`)
 
 	// Check if mylib is linked to mylib2 for the non-apex target
-	ldFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"]
+	ldFlags := ctx.ModuleForTests(t, "mylib", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"]
 	ensureContains(t, ldFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
 
 	// Make sure that the link doesn't occur for the apex target
-	ldFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
+	ldFlags = ctx.ModuleForTests(t, "mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
 	ensureNotContains(t, ldFlags, "mylib2/android_arm64_armv8-a_shared_apex10000/mylib2.so")
 
 	// It shouldn't appear in the copy cmd as well.
-	copyCmds := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule").Args["copy_commands"]
+	copyCmds := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("apexRule").Args["copy_commands"]
 	ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so")
 }
 
@@ -9302,7 +9560,7 @@
 							if !strings.HasPrefix(variant, "android_arm64_armv8-a_shared") {
 								continue
 							}
-							mod := ctx.ModuleForTests(modName, variant).Module().(*cc.Module)
+							mod := ctx.ModuleForTests(t, modName, variant).Module().(*cc.Module)
 							if !mod.Enabled(android.PanickingConfigAndErrorContext(ctx)) || mod.IsHideFromMake() {
 								continue
 							}
@@ -9425,13 +9683,13 @@
 	).RunTest(t)
 
 	// Make sure jacoco ran on both mylib and mybootclasspathlib
-	if result.ModuleForTests("mylib", "android_common_apex10000").MaybeRule("jacoco").Rule == nil {
+	if result.ModuleForTests(t, "mylib", "android_common_apex10000").MaybeRule("jacoco").Rule == nil {
 		t.Errorf("Failed to find jacoco rule for mylib")
 	}
-	if result.ModuleForTests("mybootclasspathlib", "android_common_apex10000").MaybeRule("jacoco").Rule == nil {
+	if result.ModuleForTests(t, "mybootclasspathlib", "android_common_apex10000").MaybeRule("jacoco").Rule == nil {
 		t.Errorf("Failed to find jacoco rule for mybootclasspathlib")
 	}
-	if result.ModuleForTests("mysystemserverclasspathlib", "android_common_apex10000").MaybeRule("jacoco").Rule == nil {
+	if result.ModuleForTests(t, "mysystemserverclasspathlib", "android_common_apex10000").MaybeRule("jacoco").Rule == nil {
 		t.Errorf("Failed to find jacoco rule for mysystemserverclasspathlib")
 	}
 }
@@ -9514,12 +9772,21 @@
 		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
 	)
 
-	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
+	apexBundle := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Module().(*apexBundle)
 	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
 	var builder strings.Builder
 	data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data)
 	androidMk := builder.String()
-	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")
+	out := ctx.Config().OutDir()
+	ensureContains(t, androidMk, "LOCAL_SOONG_INSTALL_PAIRS += "+
+		filepath.Join(out, "soong/.intermediates/foo/android_common_apex10000/dexpreopt/foo/oat/arm64/javalib.odex")+
+		":"+
+		filepath.Join(out, "target/product/test_device/system/framework/oat/arm64/apex@myapex@javalib@foo.jar@classes.odex")+
+		" "+
+		filepath.Join(out, "soong/.intermediates/foo/android_common_apex10000/dexpreopt/foo/oat/arm64/javalib.vdex")+
+		":"+
+		filepath.Join(out, "target/product/test_device/system/framework/oat/arm64/apex@myapex@javalib@foo.jar@classes.vdex")+
+		"\n")
 }
 
 func TestAndroidMk_RequiredModules(t *testing.T) {
@@ -9554,7 +9821,7 @@
 		}
 	`)
 
-	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
+	apexBundle := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Module().(*apexBundle)
 	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
 	var builder strings.Builder
 	data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data)
@@ -9578,7 +9845,7 @@
 		}
 	`)
 
-	bundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
+	bundle := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Module().(*apexBundle)
 	bundle.makeModulesToInstall = append(bundle.makeModulesToInstall, "foo")
 	data := android.AndroidMkDataForTest(t, ctx, bundle)
 	var builder strings.Builder
@@ -9630,7 +9897,7 @@
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 					variables.CompressedApex = proptools.BoolPtr(true)
 				}))
-			javaTest := ctx.ModuleForTests(tc.name, "android_common").Module().(*java.Test)
+			javaTest := ctx.ModuleForTests(t, tc.name, "android_common").Module().(*java.Test)
 			data := android.AndroidMkEntriesForTest(t, ctx, javaTest)[0].EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
 			android.AssertStringPathsRelativeToTopEquals(t, "data", ctx.Config(), tc.expected_data, data)
 		})
@@ -9833,7 +10100,7 @@
 
 // Verifies that the APEX depends on all the Make modules in the list.
 func ensureContainsRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
-	a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
+	a := ctx.ModuleForTests(t, moduleName, variant).Module().(*apexBundle)
 	for _, dep := range deps {
 		android.AssertStringListContains(t, "", a.makeModulesToInstall, dep)
 	}
@@ -9841,7 +10108,7 @@
 
 // Verifies that the APEX does not depend on any of the Make modules in the list.
 func ensureDoesNotContainRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
-	a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
+	a := ctx.ModuleForTests(t, moduleName, variant).Module().(*apexBundle)
 	for _, dep := range deps {
 		android.AssertStringListDoesNotContain(t, "", a.makeModulesToInstall, dep)
 	}
@@ -9949,8 +10216,8 @@
 				}
 			}
 
-			myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
-			apex := result.ModuleForTests("myapex", "android_common_myapex")
+			myjavalib := result.ModuleForTests(t, "myjavalib", "android_common_apex29")
+			apex := result.ModuleForTests(t, "myapex", "android_common_myapex")
 			apexStrictUpdatabilityCheck := apex.MaybeOutput("lint_strict_updatability_check.stamp")
 			javalibStrictUpdatabilityCheck := myjavalib.MaybeOutput("lint_strict_updatability_check.stamp")
 
@@ -9999,7 +10266,7 @@
 	}
 
 	result := testApex(t, bp, dexpreopt.FixtureSetApexBootJars("myapex:myjavalib"), fs.AddToFixture())
-	apex := result.ModuleForTests("myapex", "android_common_myapex")
+	apex := result.ModuleForTests(t, "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")
@@ -10051,7 +10318,7 @@
 		android.FixtureMergeMockFs(fs),
 	).RunTestWithBp(t, bp)
 
-	myapex := result.ModuleForTests("myapex", "android_common_myapex")
+	myapex := result.ModuleForTests(t, "myapex", "android_common_myapex")
 	lintReportInputs := strings.Join(myapex.Output("lint-report-xml.zip").Inputs.Strings(), " ")
 	android.AssertStringDoesContain(t,
 		"myapex lint report expected to contain that of the sdk library impl lib as an input",
@@ -10103,7 +10370,7 @@
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
 		}`)
-	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
+	mod := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	generateFsRule := mod.Rule("generateFsConfig")
 	cmd := generateFsRule.RuleParams.Command
 
@@ -10125,7 +10392,7 @@
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
 		}`)
-	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
+	mod := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	generateFsRule := mod.Rule("generateFsConfig")
 	cmd := generateFsRule.RuleParams.Command
 
@@ -10159,9 +10426,9 @@
 			expectedError: "Stub libraries should have a single apex_available.*myapex.*otherapex",
 		},
 		{
-			desc:          "stub library can be available to a core apex and a test apex",
+			desc:          "stub library can be available to a core apex and a test apex using apex_available_name",
 			hasStubs:      true,
-			apexAvailable: `["myapex", "test_myapex"]`,
+			apexAvailable: `["myapex"]`,
 		},
 	}
 	bpTemplate := `
@@ -10186,25 +10453,28 @@
 			key: "apex.key",
 			updatable: false,
 			native_shared_libs: ["libfoo"],
+			apex_available_name: "myapex",
 		}
 		apex_key {
 			name: "apex.key",
 		}
 	`
 	for _, tc := range testCases {
-		stubs := ""
-		if tc.hasStubs {
-			stubs = `stubs: {symbol_file: "libfoo.map.txt"},`
-		}
-		bp := fmt.Sprintf(bpTemplate, stubs, tc.apexAvailable)
-		mockFsFixturePreparer := android.FixtureModifyMockFS(func(fs android.MockFS) {
-			fs["system/sepolicy/apex/test_myapex-file_contexts"] = nil
+		t.Run(tc.desc, func(t *testing.T) {
+			stubs := ""
+			if tc.hasStubs {
+				stubs = `stubs: {symbol_file: "libfoo.map.txt"},`
+			}
+			bp := fmt.Sprintf(bpTemplate, stubs, tc.apexAvailable)
+			mockFsFixturePreparer := android.FixtureModifyMockFS(func(fs android.MockFS) {
+				fs["system/sepolicy/apex/test_myapex-file_contexts"] = nil
+			})
+			if tc.expectedError == "" {
+				testApex(t, bp, mockFsFixturePreparer)
+			} else {
+				testApexError(t, tc.expectedError, bp, mockFsFixturePreparer)
+			}
 		})
-		if tc.expectedError == "" {
-			testApex(t, bp, mockFsFixturePreparer)
-		} else {
-			testApexError(t, tc.expectedError, bp, mockFsFixturePreparer)
-		}
 	}
 }
 
@@ -10257,7 +10527,7 @@
 		}
 	`)
 
-	inputs := result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img").Implicits
+	inputs := result.ModuleForTests(t, "myfilesystem", "android_common").Output("myfilesystem.img").Implicits
 	android.AssertStringListDoesNotContain(t, "filesystem should not have libbar",
 		inputs.Strings(),
 		"out/soong/.intermediates/libbar/android_arm64_armv8-a_shared/libbar.so")
@@ -10351,7 +10621,7 @@
 		}
 	`)
 
-	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
+	mod := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	s := mod.Rule("apexRule").Args["copy_commands"]
 	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
 	if len(copyCmds) != 14 {
@@ -10490,7 +10760,7 @@
 		}
 	`)
 
-	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
+	mod := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	s := mod.Rule("apexRule").Args["copy_commands"]
 	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
 	if len(copyCmds) != 18 {
@@ -10536,15 +10806,6 @@
 		}
 
 		rust_library {
-			name: "libflags_rust", // test mock
-			crate_name: "flags_rust",
-			srcs: ["lib.rs"],
-			apex_available: [
-				"myapex",
-			],
-		}
-
-		rust_library {
 			name: "liblazy_static", // test mock
 			crate_name: "lazy_static",
 			srcs: ["src/lib.rs"],
@@ -10661,11 +10922,11 @@
 		}
 	`)
 
-	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
+	mod := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	s := mod.Rule("apexRule").Args["copy_commands"]
 	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
-	if len(copyCmds) != 38 {
-		t.Fatalf("Expected 38 commands, got %d in:\n%s", len(copyCmds), s)
+	if len(copyCmds) != 32 {
+		t.Fatalf("Expected 32 commands, got %d in:\n%s", len(copyCmds), s)
 	}
 
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/aconfig_flags.pb .*/image.apex/etc/aconfig_flags.pb")
@@ -10777,7 +11038,7 @@
 		}
 	`)
 
-	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
+	mod := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	combineAconfigRule := mod.Rule("All_aconfig_declarations_dump")
 	s := " " + combineAconfigRule.Args["cache_files"]
 	aconfigArgs := regexp.MustCompile(" --cache ").Split(s, -1)[1:]
@@ -10857,7 +11118,7 @@
 		}
 	`)
 
-	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
+	mod := ctx.ModuleForTests(t, "myapex", "android_common_myapex")
 	combineAconfigRule := mod.Rule("All_aconfig_declarations_dump")
 	s := " " + combineAconfigRule.Args["cache_files"]
 	aconfigArgs := regexp.MustCompile(" --cache ").Split(s, -1)[1:]
@@ -10880,7 +11141,7 @@
 	t.Parallel()
 	checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, stem string, bootDexJarPath string) {
 		t.Helper()
-		s := ctx.ModuleForTests("dex_bootjars", "android_common")
+		s := ctx.ModuleForTests(t, "dex_bootjars", "android_common")
 		foundLibfooJar := false
 		base := stem + ".jar"
 		for _, output := range s.AllOutputs() {
@@ -10898,14 +11159,14 @@
 	// Check that the boot jars of the selected apex are run through boot_jars_package_check
 	// This validates that the jars on the bootclasspath do not contain packages outside an allowlist
 	checkBootJarsPackageCheck := func(t *testing.T, ctx *android.TestContext, expectedBootJar string) {
-		platformBcp := ctx.ModuleForTests("platform-bootclasspath", "android_common")
+		platformBcp := ctx.ModuleForTests(t, "platform-bootclasspath", "android_common")
 		bootJarsCheckRule := platformBcp.Rule("boot_jars_package_check")
 		android.AssertStringMatches(t, "Could not find the correct boot dex jar in package check rule", bootJarsCheckRule.RuleParams.Command, "build/soong/scripts/check_boot_jars/package_allowed_list.txt.*"+expectedBootJar)
 	}
 
 	// Check that the boot jars used to generate the monolithic hiddenapi flags come from the selected apex
 	checkBootJarsForMonolithicHiddenapi := func(t *testing.T, ctx *android.TestContext, expectedBootJar string) {
-		monolithicHiddenapiFlagsCmd := ctx.ModuleForTests("platform-bootclasspath", "android_common").Output("out/soong/hiddenapi/hiddenapi-stub-flags.txt").RuleParams.Command
+		monolithicHiddenapiFlagsCmd := ctx.ModuleForTests(t, "platform-bootclasspath", "android_common").Output("out/soong/hiddenapi/hiddenapi-stub-flags.txt").RuleParams.Command
 		android.AssertStringMatches(t, "Could not find the correct boot dex jar in monolithic hiddenapi flags generation command", monolithicHiddenapiFlagsCmd, "--boot-dex="+expectedBootJar)
 	}
 
@@ -11015,17 +11276,17 @@
 		{
 			desc:                      "Source apex com.android.foo is selected, bootjar should come from source java library",
 			selectedApexContributions: "foo.source.contributions",
-			expectedBootJar:           "out/soong/.intermediates/foo-bootclasspath-fragment/android_common_apex10000/hiddenapi-modular/encoded/framework-foo.jar",
+			expectedBootJar:           "out/soong/.intermediates/foo-bootclasspath-fragment/android_common_com.android.foo/hiddenapi-modular/encoded/framework-foo.jar",
 		},
 		{
 			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/android_common_com.android.foo/deapexer/javalib/framework-foo.jar",
+			expectedBootJar:           "out/soong/.intermediates/prebuilt_com.android.foo/android_common_prebuilt_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/com.android.foo.v2/android_common_com.android.foo/deapexer/javalib/framework-foo.jar",
+			expectedBootJar:           "out/soong/.intermediates/com.android.foo.v2/android_common_prebuilt_com.android.foo/deapexer/javalib/framework-foo.jar",
 		},
 	}
 
@@ -11070,18 +11331,18 @@
 	// for a mainline module family, check that only the flagged soong module is visible to make
 	checkHideFromMake := func(t *testing.T, ctx *android.TestContext, visibleModuleName string, hiddenModuleNames []string) {
 		variation := func(moduleName string) string {
-			ret := "android_common_com.android.foo"
+			ret := "android_common_prebuilt_com.android.foo"
 			if moduleName == "com.google.android.foo" {
-				ret = "android_common_com.google.android.foo_com.google.android.foo"
+				ret = "android_common_com.google.android.foo"
 			}
 			return ret
 		}
 
-		visibleModule := ctx.ModuleForTests(visibleModuleName, variation(visibleModuleName)).Module()
+		visibleModule := ctx.ModuleForTests(t, visibleModuleName, variation(visibleModuleName)).Module()
 		android.AssertBoolEquals(t, "Apex "+visibleModuleName+" selected using apex_contributions should be visible to make", false, visibleModule.IsHideFromMake())
 
 		for _, hiddenModuleName := range hiddenModuleNames {
-			hiddenModule := ctx.ModuleForTests(hiddenModuleName, variation(hiddenModuleName)).Module()
+			hiddenModule := ctx.ModuleForTests(t, hiddenModuleName, variation(hiddenModuleName)).Module()
 			android.AssertBoolEquals(t, "Apex "+hiddenModuleName+" not selected using apex_contributions should be hidden from make", true, hiddenModule.IsHideFromMake())
 
 		}
@@ -11207,6 +11468,16 @@
 		// 1. The contents of the selected apex_contributions are visible to make
 		// 2. The rest of the apexes in the mainline module family (source or other prebuilt) is hidden from make
 		checkHideFromMake(t, ctx, tc.expectedVisibleModuleName, tc.expectedHiddenModuleNames)
+
+		// Check that source_apex_name is written as LOCAL_MODULE for make packaging
+		if tc.expectedVisibleModuleName == "prebuilt_com.google.android.foo.v2" {
+			apex := ctx.ModuleForTests(t, "prebuilt_com.google.android.foo.v2", "android_common_prebuilt_com.android.foo").Module()
+			entries := android.AndroidMkEntriesForTest(t, ctx, apex)[0]
+
+			expected := "com.google.android.foo"
+			actual := entries.EntryMap["LOCAL_MODULE"][0]
+			android.AssertStringEquals(t, "LOCAL_MODULE", expected, actual)
+		}
 	}
 }
 
@@ -11217,19 +11488,19 @@
 	checkHideFromMake := func(t *testing.T, ctx *android.TestContext, visibleModuleNames []string, hiddenModuleNames []string) {
 		variation := func(moduleName string) string {
 			ret := "android_common_com.android.adservices"
-			if moduleName == "com.google.android.foo" {
-				ret = "android_common_com.google.android.foo_com.google.android.foo"
+			if moduleName == "com.google.android.adservices" || moduleName == "com.google.android.adservices.v2" {
+				ret = "android_common_prebuilt_com.android.adservices"
 			}
 			return ret
 		}
 
 		for _, visibleModuleName := range visibleModuleNames {
-			visibleModule := ctx.ModuleForTests(visibleModuleName, variation(visibleModuleName)).Module()
+			visibleModule := ctx.ModuleForTests(t, visibleModuleName, variation(visibleModuleName)).Module()
 			android.AssertBoolEquals(t, "Apex "+visibleModuleName+" selected using apex_contributions should be visible to make", false, visibleModule.IsHideFromMake())
 		}
 
 		for _, hiddenModuleName := range hiddenModuleNames {
-			hiddenModule := ctx.ModuleForTests(hiddenModuleName, variation(hiddenModuleName)).Module()
+			hiddenModule := ctx.ModuleForTests(t, hiddenModuleName, variation(hiddenModuleName)).Module()
 			android.AssertBoolEquals(t, "Apex "+hiddenModuleName+" not selected using apex_contributions should be hidden from make", true, hiddenModule.IsHideFromMake())
 
 		}
@@ -11293,13 +11564,13 @@
 			expectedHiddenModuleNames:  []string{"com.google.android.adservices", "com.google.android.adservices.v2"},
 		},
 		{
-			desc:                       "Prebuilt apex prebuilt_com.android.foo is selected",
+			desc:                       "Prebuilt apex prebuilt_com.android.adservices is selected",
 			selectedApexContributions:  "adservices.prebuilt.contributions",
 			expectedVisibleModuleNames: []string{"com.android.adservices", "com.google.android.adservices"},
 			expectedHiddenModuleNames:  []string{"com.google.android.adservices.v2"},
 		},
 		{
-			desc:                       "Prebuilt apex prebuilt_com.android.foo.v2 is selected",
+			desc:                       "Prebuilt apex prebuilt_com.android.adservices.v2 is selected",
 			selectedApexContributions:  "adservices.prebuilt.v2.contributions",
 			expectedVisibleModuleNames: []string{"com.android.adservices", "com.google.android.adservices.v2"},
 			expectedHiddenModuleNames:  []string{"com.google.android.adservices"},
@@ -11401,12 +11672,12 @@
 		}
 	`+aconfigDeclarationLibraryString([]string{"bar", "baz", "qux", "quux"}))
 
-	m := result.ModuleForTests("foo.stubs.source", "android_common")
+	m := result.ModuleForTests(t, "foo.stubs.source", "android_common")
 	outDir := "out/soong/.intermediates"
 
 	// Arguments passed to aconfig to retrieve the state of the flags defined in the
 	// textproto files
-	aconfigFlagArgs := m.Output("released-flagged-apis-exportable.txt").Args["flags_path"]
+	aconfigFlagArgs := m.Output("released-flags-exportable.pb").Args["flags_path"]
 
 	// "bar-lib" is a static_lib of "foo" and is passed to metalava as classpath. Thus the
 	// cache file provided by the associated aconfig_declarations module "bar" should be passed
@@ -11466,7 +11737,7 @@
 		"packages/modules/common/build/allowed_deps.txt": nil,
 	}))
 
-	ab := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
+	ab := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Module().(*apexBundle)
 	data := android.AndroidMkDataForTest(t, ctx, ab)
 	var builder strings.Builder
 	data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data)
@@ -11538,18 +11809,18 @@
 	}),
 	)
 
-	baseModule := ctx.ModuleForTests("com.android.apex30", "android_common_com.android.apex30")
+	baseModule := ctx.ModuleForTests(t, "com.android.apex30", "android_common_com.android.apex30")
 	checkMinSdkVersion(t, baseModule, "30")
 
 	// Override module, but uses same min_sdk_version
-	overridingModuleSameMinSdkVersion := ctx.ModuleForTests("com.android.apex30", "android_common_com.mycompany.android.apex30_com.mycompany.android.apex30")
-	javalibApex30Variant := ctx.ModuleForTests("javalib", "android_common_apex30")
+	overridingModuleSameMinSdkVersion := ctx.ModuleForTests(t, "com.android.apex30", "android_common_com.mycompany.android.apex30_com.mycompany.android.apex30")
+	javalibApex30Variant := ctx.ModuleForTests(t, "javalib", "android_common_apex30")
 	checkMinSdkVersion(t, overridingModuleSameMinSdkVersion, "30")
 	checkHasDep(t, ctx, overridingModuleSameMinSdkVersion.Module(), javalibApex30Variant.Module())
 
 	// Override module, uses different min_sdk_version
-	overridingModuleDifferentMinSdkVersion := ctx.ModuleForTests("com.android.apex30", "android_common_com.mycompany.android.apex31_com.mycompany.android.apex31")
-	javalibApex31Variant := ctx.ModuleForTests("javalib", "android_common_apex31")
+	overridingModuleDifferentMinSdkVersion := ctx.ModuleForTests(t, "com.android.apex30", "android_common_com.mycompany.android.apex31_com.mycompany.android.apex31")
+	javalibApex31Variant := ctx.ModuleForTests(t, "javalib", "android_common_apex31")
 	checkMinSdkVersion(t, overridingModuleDifferentMinSdkVersion, "31")
 	checkHasDep(t, ctx, overridingModuleDifferentMinSdkVersion.Module(), javalibApex31Variant.Module())
 }
@@ -11587,7 +11858,7 @@
 		}
 	`)
 
-	java.CheckModuleHasDependency(t, res.TestContext, "myoverrideapex", "android_common_myoverrideapex_myoverrideapex", "foo")
+	java.CheckModuleHasDependency(t, res.TestContext, "myoverrideapex", "android_common_myoverrideapex", "foo")
 }
 
 func TestUpdatableApexMinSdkVersionCurrent(t *testing.T) {
@@ -11618,7 +11889,7 @@
 			android.FixtureMergeMockFs(fs),
 		).RunTest(t)
 
-		ldRule := result.ModuleForTests("installedlib", "android_arm64_armv8-a_shared").Rule("ld")
+		ldRule := result.ModuleForTests(t, "installedlib", "android_arm64_armv8-a_shared").Rule("ld")
 		android.AssertStringDoesContain(t, "", ldRule.Args["libFlags"], "android_arm64_armv8-a_shared_current/libfoo.so")
 
 		installRules := result.InstallMakeRulesForTesting(t)
@@ -11919,400 +12190,66 @@
 		}
 	`, filesystem.PrepareForTestWithFilesystemBuildComponents)
 
-	partition := result.ModuleForTests("myfilesystem", "android_common")
+	partition := result.ModuleForTests(t, "myfilesystem", "android_common")
 	fileList := android.ContentFromFileRuleForTests(t, result, partition.Output("fileList"))
 	android.AssertDeepEquals(t, "filesystem with apex", "apex/myapex.apex\n", fileList)
 }
 
-func TestApexVerifyNativeImplementationLibs(t *testing.T) {
+func TestVintfFragmentInApex(t *testing.T) {
 	t.Parallel()
-
-	extractDepenencyPathFromErrors := func(errs []error) []string {
-		i := slices.IndexFunc(errs, func(err error) bool {
-			return strings.Contains(err.Error(), "dependency path:")
-		})
-		if i < 0 {
-			return nil
+	ctx := testApex(t, apex_default_bp+`
+		apex {
+			name: "myapex",
+			manifest: ":myapex.manifest",
+			androidManifest: ":myapex.androidmanifest",
+			key: "myapex.key",
+			binaries: [ "mybin" ],
+			updatable: false,
 		}
-		var dependencyPath []string
-		for _, err := range errs[i+1:] {
-			s := err.Error()
-			lastSpace := strings.LastIndexByte(s, ' ')
-			if lastSpace >= 0 {
-				dependencyPath = append(dependencyPath, s[lastSpace+1:])
-			}
+
+		cc_binary {
+			name: "mybin",
+			srcs: ["mybin.cpp"],
+			vintf_fragment_modules: ["my_vintf_fragment.xml"],
+			apex_available: [ "myapex" ],
 		}
-		return dependencyPath
-	}
 
-	checkErrors := func(wantDependencyPath []string) func(t *testing.T, result *android.TestResult) {
-		return func(t *testing.T, result *android.TestResult) {
-			t.Helper()
-			if len(result.Errs) == 0 {
-				t.Fatalf("expected errors")
-			}
-			t.Log("found errors:")
-			for _, err := range result.Errs {
-				t.Log(err)
-			}
-			if g, w := result.Errs[0].Error(), "library in apex transitively linked against implementation library"; !strings.Contains(g, w) {
-				t.Fatalf("expected error %q, got %q", w, g)
-			}
-			dependencyPath := extractDepenencyPathFromErrors(result.Errs)
-			if g, w := dependencyPath, wantDependencyPath; !slices.Equal(g, w) {
-				t.Errorf("expected dependency path %q, got %q", w, g)
-			}
+		vintf_fragment {
+			name: "my_vintf_fragment.xml",
+			src: "my_vintf_fragment.xml",
 		}
-	}
+	`)
 
-	addToSharedLibs := func(module, lib string) func(bp *bpmodify.Blueprint) {
-		return func(bp *bpmodify.Blueprint) {
-			m := bp.ModulesByName(module)
-			props, err := m.GetOrCreateProperty(bpmodify.List, "shared_libs")
-			if err != nil {
-				panic(err)
-			}
-			props.AddStringToList(lib)
+	generateFsRule := ctx.ModuleForTests(t, "myapex", "android_common_myapex").Rule("generateFsConfig")
+	cmd := generateFsRule.RuleParams.Command
+
+	// Ensure that vintf fragment file is being installed
+	ensureContains(t, cmd, "/etc/vintf/my_vintf_fragment.xml ")
+}
+
+func TestNoVintfFragmentInUpdatableApex(t *testing.T) {
+	t.Parallel()
+	testApexError(t, `VINTF fragment .* is not supported in updatable APEX`, apex_default_bp+`
+		apex {
+			name: "myapex",
+			manifest: ":myapex.manifest",
+			key: "myapex.key",
+			binaries: [ "mybin" ],
+			updatable: true,
+			min_sdk_version: "29",
 		}
-	}
 
-	bpTemplate := `
-	apex {
-		name: "myapex",
-		key: "myapex.key",
-		native_shared_libs: ["mylib"],
-		rust_dyn_libs: ["libmyrust"],
-		binaries: ["mybin", "myrustbin"],
-		jni_libs: ["libjni"],
-		apps: ["myapp"],
-		updatable: false,
-	}
+		cc_binary {
+			name: "mybin",
+			srcs: ["mybin.cpp"],
+			vintf_fragment_modules: ["my_vintf_fragment.xml"],
+			apex_available: [ "myapex" ],
+			min_sdk_version: "29",
+		}
 
-	apex {
-		name: "otherapex",
-		key: "myapex.key",
-		native_shared_libs: ["libotherapex"],
-		updatable: false,
-	}
-
-	apex_key {
-		name: "myapex.key",
-		public_key: "testkey.avbpubkey",
-		private_key: "testkey.pem",
-	}
-
-	cc_library {
-		name: "mylib",
-		srcs: ["foo.cpp"],
-		apex_available: ["myapex"],
-	}
-
-	cc_binary {
-		name: "mybin",
-		srcs: ["foo.cpp"],
-		apex_available: ["myapex"],
-	}
-
-	rust_library {
-		name: "libmyrust",
-		crate_name: "myrust",
-		srcs: ["src/lib.rs"],
-		rustlibs: ["libmyrust_transitive_dylib"],
-		rlibs: ["libmyrust_transitive_rlib"],
-		apex_available: ["myapex"],
-	}
-
-	rust_library{
-		name: "libmyrust_transitive_dylib",
-		crate_name: "myrust_transitive_dylib",
-		srcs: ["src/lib.rs"],
-		apex_available: ["myapex"],
-	}
-
-	rust_library {
-		name: "libmyrust_transitive_rlib",
-		crate_name: "myrust_transitive_rlib",
-		srcs: ["src/lib.rs"],
-		apex_available: ["myapex"],
-	}
-
-	rust_binary {
-		name: "myrustbin",
-		srcs: ["src/main.rs"],
-		apex_available: ["myapex"],
-	}
-
-	cc_library {
-		name: "libbar",
-		sdk_version: "current",
-		srcs: ["bar.cpp"],
-		apex_available: ["myapex"],
-		stl: "none",
-	}
-
-	android_app {
-		name: "myapp",
-		jni_libs: ["libembeddedjni"],
-		use_embedded_native_libs: true,
-		sdk_version: "current",
-		apex_available: ["myapex"],
-	}
-
-	cc_library {
-		name: "libembeddedjni",
-		sdk_version: "current",
-		srcs: ["bar.cpp"],
-		apex_available: ["myapex"],
-		stl: "none",
-	}
-
-	cc_library {
-		name: "libjni",
-		sdk_version: "current",
-		srcs: ["bar.cpp"],
-		apex_available: ["myapex"],
-		stl: "none",
-	}
-
-	cc_library {
-		name: "libotherapex",
-		sdk_version: "current",
-		srcs: ["otherapex.cpp"],
-		apex_available: ["otherapex"],
-		stubs: {
-			symbol_file: "libotherapex.map.txt",
-			versions: ["1", "2", "3"],
-		},
-		stl: "none",
-	}
-
-	cc_library {
-		name: "libplatform",
-		sdk_version: "current",
-		srcs: ["libplatform.cpp"],
-		stubs: {
-			symbol_file: "libplatform.map.txt",
-			versions: ["1", "2", "3"],
-		},
-		stl: "none",
-		system_shared_libs: [],
-	}
-	`
-
-	testCases := []struct {
-		name           string
-		bpModifier     func(bp *bpmodify.Blueprint)
-		dependencyPath []string
-	}{
-		{
-			name:           "library dependency in other apex",
-			bpModifier:     addToSharedLibs("mylib", "libotherapex#impl"),
-			dependencyPath: []string{"myapex", "mylib", "libotherapex"},
-		},
-		{
-			name: "transitive library dependency in other apex",
-			bpModifier: func(bp *bpmodify.Blueprint) {
-				addToSharedLibs("mylib", "libbar")(bp)
-				addToSharedLibs("libbar", "libotherapex#impl")(bp)
-			},
-			dependencyPath: []string{"myapex", "mylib", "libbar", "libotherapex"},
-		},
-		{
-			name:           "library dependency in platform",
-			bpModifier:     addToSharedLibs("mylib", "libplatform#impl"),
-			dependencyPath: []string{"myapex", "mylib", "libplatform"},
-		},
-		{
-			name:           "jni library dependency in other apex",
-			bpModifier:     addToSharedLibs("libjni", "libotherapex#impl"),
-			dependencyPath: []string{"myapex", "libjni", "libotherapex"},
-		},
-		{
-			name: "transitive jni library dependency in other apex",
-			bpModifier: func(bp *bpmodify.Blueprint) {
-				addToSharedLibs("libjni", "libbar")(bp)
-				addToSharedLibs("libbar", "libotherapex#impl")(bp)
-			},
-			dependencyPath: []string{"myapex", "libjni", "libbar", "libotherapex"},
-		},
-		{
-			name:           "jni library dependency in platform",
-			bpModifier:     addToSharedLibs("libjni", "libplatform#impl"),
-			dependencyPath: []string{"myapex", "libjni", "libplatform"},
-		},
-		{
-			name: "transitive jni library dependency in platform",
-			bpModifier: func(bp *bpmodify.Blueprint) {
-				addToSharedLibs("libjni", "libbar")(bp)
-				addToSharedLibs("libbar", "libplatform#impl")(bp)
-			},
-			dependencyPath: []string{"myapex", "libjni", "libbar", "libplatform"},
-		},
-		{
-			name:           "app jni library dependency in other apex",
-			bpModifier:     addToSharedLibs("libembeddedjni", "libotherapex#impl"),
-			dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libotherapex"},
-		},
-		{
-			name: "transitive app jni library dependency in other apex",
-			bpModifier: func(bp *bpmodify.Blueprint) {
-				addToSharedLibs("libembeddedjni", "libbar")(bp)
-				addToSharedLibs("libbar", "libotherapex#impl")(bp)
-			},
-			dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libbar", "libotherapex"},
-		},
-		{
-			name:           "app jni library dependency in platform",
-			bpModifier:     addToSharedLibs("libembeddedjni", "libplatform#impl"),
-			dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libplatform"},
-		},
-		{
-			name: "transitive app jni library dependency in platform",
-			bpModifier: func(bp *bpmodify.Blueprint) {
-				addToSharedLibs("libembeddedjni", "libbar")(bp)
-				addToSharedLibs("libbar", "libplatform#impl")(bp)
-			},
-			dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libbar", "libplatform"},
-		},
-		{
-			name:           "binary dependency in other apex",
-			bpModifier:     addToSharedLibs("mybin", "libotherapex#impl"),
-			dependencyPath: []string{"myapex", "mybin", "libotherapex"},
-		},
-		{
-			name: "transitive binary dependency in other apex",
-			bpModifier: func(bp *bpmodify.Blueprint) {
-				addToSharedLibs("mybin", "libbar")(bp)
-				addToSharedLibs("libbar", "libotherapex#impl")(bp)
-			},
-			dependencyPath: []string{"myapex", "mybin", "libbar", "libotherapex"},
-		},
-		{
-			name:           "binary dependency in platform",
-			bpModifier:     addToSharedLibs("mybin", "libplatform#impl"),
-			dependencyPath: []string{"myapex", "mybin", "libplatform"},
-		},
-		{
-			name: "transitive binary dependency in platform",
-			bpModifier: func(bp *bpmodify.Blueprint) {
-				addToSharedLibs("mybin", "libbar")(bp)
-				addToSharedLibs("libbar", "libplatform#impl")(bp)
-			},
-			dependencyPath: []string{"myapex", "mybin", "libbar", "libplatform"},
-		},
-
-		{
-			name:           "rust library dependency in other apex",
-			bpModifier:     addToSharedLibs("libmyrust", "libotherapex#impl"),
-			dependencyPath: []string{"myapex", "libmyrust", "libotherapex"},
-		},
-		{
-			name: "transitive rust library dependency in other apex",
-			bpModifier: func(bp *bpmodify.Blueprint) {
-				addToSharedLibs("libmyrust", "libbar")(bp)
-				addToSharedLibs("libbar", "libotherapex#impl")(bp)
-			},
-			dependencyPath: []string{"myapex", "libmyrust", "libbar", "libotherapex"},
-		},
-		{
-			name:           "rust library dependency in platform",
-			bpModifier:     addToSharedLibs("libmyrust", "libplatform#impl"),
-			dependencyPath: []string{"myapex", "libmyrust", "libplatform"},
-		},
-		{
-			name: "transitive rust library dependency in platform",
-			bpModifier: func(bp *bpmodify.Blueprint) {
-				addToSharedLibs("libmyrust", "libbar")(bp)
-				addToSharedLibs("libbar", "libplatform#impl")(bp)
-			},
-			dependencyPath: []string{"myapex", "libmyrust", "libbar", "libplatform"},
-		},
-		{
-			name: "transitive rust library dylib dependency in other apex",
-			bpModifier: func(bp *bpmodify.Blueprint) {
-				addToSharedLibs("libmyrust_transitive_dylib", "libotherapex#impl")(bp)
-			},
-			dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_dylib", "libotherapex"},
-		},
-		{
-			name: "transitive rust library dylib dependency in platform",
-			bpModifier: func(bp *bpmodify.Blueprint) {
-				addToSharedLibs("libmyrust_transitive_dylib", "libplatform#impl")(bp)
-			},
-			dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_dylib", "libplatform"},
-		},
-		{
-			name: "transitive rust library rlib dependency in other apex",
-			bpModifier: func(bp *bpmodify.Blueprint) {
-				addToSharedLibs("libmyrust_transitive_rlib", "libotherapex#impl")(bp)
-			},
-			dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_rlib", "libotherapex"},
-		},
-		{
-			name: "transitive rust library rlib dependency in platform",
-			bpModifier: func(bp *bpmodify.Blueprint) {
-				addToSharedLibs("libmyrust_transitive_rlib", "libplatform#impl")(bp)
-			},
-			dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_rlib", "libplatform"},
-		},
-		{
-			name:           "rust binary dependency in other apex",
-			bpModifier:     addToSharedLibs("myrustbin", "libotherapex#impl"),
-			dependencyPath: []string{"myapex", "myrustbin", "libotherapex"},
-		},
-		{
-			name: "transitive rust binary dependency in other apex",
-			bpModifier: func(bp *bpmodify.Blueprint) {
-				addToSharedLibs("myrustbin", "libbar")(bp)
-				addToSharedLibs("libbar", "libotherapex#impl")(bp)
-			},
-			dependencyPath: []string{"myapex", "myrustbin", "libbar", "libotherapex"},
-		},
-		{
-			name:           "rust binary dependency in platform",
-			bpModifier:     addToSharedLibs("myrustbin", "libplatform#impl"),
-			dependencyPath: []string{"myapex", "myrustbin", "libplatform"},
-		},
-		{
-			name: "transitive rust binary dependency in platform",
-			bpModifier: func(bp *bpmodify.Blueprint) {
-				addToSharedLibs("myrustbin", "libbar")(bp)
-				addToSharedLibs("libbar", "libplatform#impl")(bp)
-			},
-			dependencyPath: []string{"myapex", "myrustbin", "libbar", "libplatform"},
-		},
-	}
-
-	for _, testCase := range testCases {
-		t.Run(testCase.name, func(t *testing.T) {
-			t.Parallel()
-			bp, err := bpmodify.NewBlueprint("", []byte(bpTemplate))
-			if err != nil {
-				t.Fatal(err)
-			}
-			if testCase.bpModifier != nil {
-				func() {
-					defer func() {
-						if r := recover(); r != nil {
-							t.Fatalf("panic in bpModifier: %v", r)
-						}
-					}()
-					testCase.bpModifier(bp)
-				}()
-			}
-			android.GroupFixturePreparers(
-				android.PrepareForTestWithAndroidBuildComponents,
-				cc.PrepareForTestWithCcBuildComponents,
-				java.PrepareForTestWithDexpreopt,
-				rust.PrepareForTestWithRustDefaultModules,
-				PrepareForTestWithApexBuildComponents,
-				prepareForTestWithMyapex,
-				prepareForTestWithOtherapex,
-				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-					variables.BuildId = proptools.StringPtr("TEST.BUILD_ID")
-				}),
-			).ExtendWithErrorHandler(android.FixtureCustomErrorHandler(checkErrors(testCase.dependencyPath))).
-				RunTestWithBp(t, bp.String())
-		})
-	}
+		vintf_fragment {
+			name: "my_vintf_fragment.xml",
+			src: "my_vintf_fragment.xml",
+		}
+	`)
 }
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 71a8246..97644e6 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -242,6 +242,7 @@
 					apex_available: [
 						"com.android.art",
 					],
+					min_sdk_version: "33",
 				}
 			`, content)
 		}
@@ -291,6 +292,7 @@
 					apex_available: [
 						"com.android.art",
 					],
+					min_sdk_version: "33",
 					compile_dex: true,
 				}
 			`, content, prefer)
@@ -320,6 +322,7 @@
 		})
 
 		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
+			`all_apex_contributions`,
 			`art-bootclasspath-fragment`,
 			`com.android.art.key`,
 			`dex2oatd`,
@@ -327,7 +330,7 @@
 
 		// Make sure that the source bootclasspath_fragment copies its dex files to the predefined
 		// locations for the art image.
-		module := result.ModuleForTests("dex_bootjars", "android_common")
+		module := result.ModuleForTests(t, "dex_bootjars", "android_common")
 		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 	})
 
@@ -418,22 +421,24 @@
 			java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
 		).RunTest(t)
 
-		ensureExactDeapexedContents(t, result.TestContext, "prebuilt_com.android.art", "android_common_com.android.art", []string{
+		ensureExactDeapexedContents(t, result.TestContext, "prebuilt_com.android.art", "android_common_prebuilt_com.android.art", []string{
 			"etc/boot-image.prof",
 			"javalib/bar.jar",
 			"javalib/foo.jar",
 		})
 
 		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
+			`all_apex_contributions`,
 			`art-bootclasspath-fragment`,
 			`com.android.art.key`,
 			`dex2oatd`,
+			`prebuilt_art-bootclasspath-fragment`,
 			`prebuilt_com.android.art`,
 		})
 
 		// Make sure that the prebuilt bootclasspath_fragment copies its dex files to the predefined
 		// locations for the art image.
-		module := result.ModuleForTests("dex_bootjars", "android_common")
+		module := result.ModuleForTests(t, "dex_bootjars", "android_common")
 		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 	})
 
@@ -586,20 +591,20 @@
 		t.Parallel()
 		result := preparers.RunTestWithBp(t, fmt.Sprintf(bp, "enabled: false,"))
 
-		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
+		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_prebuilt_com.android.art", []string{
 			`all_apex_contributions`,
 			`dex2oatd`,
 			`prebuilt_art-bootclasspath-fragment`,
 		})
 
-		java.CheckModuleDependencies(t, result.TestContext, "art-bootclasspath-fragment", "android_common_com.android.art", []string{
+		java.CheckModuleDependencies(t, result.TestContext, "art-bootclasspath-fragment", "android_common_prebuilt_com.android.art", []string{
 			`all_apex_contributions`,
 			`dex2oatd`,
 			`prebuilt_bar`,
 			`prebuilt_foo`,
 		})
 
-		module := result.ModuleForTests("dex_bootjars", "android_common")
+		module := result.ModuleForTests(t, "dex_bootjars", "android_common")
 		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 	})
 }
@@ -698,17 +703,18 @@
 	})
 
 	java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex", []string{
+		`all_apex_contributions`,
 		`dex2oatd`,
 		`myapex.key`,
 		`mybootclasspathfragment`,
 	})
 
-	apex := result.ModuleForTests("myapex", "android_common_myapex")
+	apex := result.ModuleForTests(t, "myapex", "android_common_myapex")
 	apexRule := apex.Rule("apexRule")
 	copyCommands := apexRule.Args["copy_commands"]
 
 	// Make sure that the fragment provides the hidden API encoded dex jars to the APEX.
-	fragment := result.Module("mybootclasspathfragment", "android_common_apex10000")
+	fragment := result.Module("mybootclasspathfragment", "android_common_myapex")
 
 	info, _ := android.OtherModuleProvider(result, fragment, java.BootclasspathFragmentApexContentInfoProvider)
 
@@ -724,8 +730,8 @@
 		android.AssertStringDoesContain(t, name+" apex copy command", copyCommands, expectedCopyCommand)
 	}
 
-	checkFragmentExportedDexJar("foo", "out/soong/.intermediates/mybootclasspathfragment/android_common_apex10000/hiddenapi-modular/encoded/foo.jar")
-	checkFragmentExportedDexJar("bar", "out/soong/.intermediates/mybootclasspathfragment/android_common_apex10000/hiddenapi-modular/encoded/bar.jar")
+	checkFragmentExportedDexJar("foo", "out/soong/.intermediates/mybootclasspathfragment/android_common_myapex/hiddenapi-modular/encoded/foo.jar")
+	checkFragmentExportedDexJar("bar", "out/soong/.intermediates/mybootclasspathfragment/android_common_myapex/hiddenapi-modular/encoded/bar.jar")
 }
 
 func getDexJarPath(result *android.TestResult, name string) string {
@@ -856,10 +862,10 @@
 		}
 	`)
 
-	java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{
+	java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_myapex", []string{
 		"all_apex_contributions",
-		"art-bootclasspath-fragment",
 		"bar",
+		"com.android.art",
 		"dex2oatd",
 		"foo",
 	})
@@ -871,7 +877,7 @@
 	quuzModuleLibStubs := getDexJarPath(result, "quuz.stubs.exportable.module_lib")
 
 	// Make sure that the fragment uses the quuz stub dex jars when generating the hidden API flags.
-	fragment := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
+	fragment := result.ModuleForTests(t, "mybootclasspathfragment", "android_common_myapex")
 
 	rule := fragment.Rule("modularHiddenAPIStubFlagsFile")
 	command := rule.RuleParams.Command
@@ -1029,14 +1035,14 @@
 		}
 	`)
 
-	java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{
+	java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_myapex", []string{
 		"all_apex_contributions",
 		"android-non-updatable.stubs",
 		"android-non-updatable.stubs.module_lib",
 		"android-non-updatable.stubs.system",
 		"android-non-updatable.stubs.test",
-		"art-bootclasspath-fragment",
 		"bar",
+		"com.android.art",
 		"dex2oatd",
 		"foo",
 	})
@@ -1048,7 +1054,7 @@
 
 	// Make sure that the fragment uses the android-non-updatable modules when generating the hidden
 	// API flags.
-	fragment := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
+	fragment := result.ModuleForTests(t, "mybootclasspathfragment", "android_common_myapex")
 
 	rule := fragment.Rule("modularHiddenAPIStubFlagsFile")
 	command := rule.RuleParams.Command
@@ -1203,14 +1209,14 @@
 		}
 	`)
 
-	java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{
+	java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_myapex", []string{
 		"all_apex_contributions",
 		"android-non-updatable.stubs",
 		"android-non-updatable.stubs.system",
 		"android-non-updatable.stubs.test",
 		"android-non-updatable.stubs.test_module_lib",
-		"art-bootclasspath-fragment",
 		"bar",
+		"com.android.art",
 		"dex2oatd",
 		"foo",
 	})
@@ -1219,7 +1225,7 @@
 
 	// Make sure that the fragment uses the android-non-updatable modules when generating the hidden
 	// API flags.
-	fragment := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
+	fragment := result.ModuleForTests(t, "mybootclasspathfragment", "android_common_myapex")
 
 	rule := fragment.Rule("modularHiddenAPIStubFlagsFile")
 	command := rule.RuleParams.Command
@@ -1358,10 +1364,10 @@
 		}
 	`)
 
-	java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{
+	java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_myapex", []string{
 		"all_apex_contributions",
-		"art-bootclasspath-fragment",
 		"bar",
+		"com.android.art",
 		"dex2oatd",
 		"foo",
 		"prebuilt_sdk_module-lib_current_android-non-updatable",
@@ -1377,7 +1383,7 @@
 
 	// Make sure that the fragment uses the android-non-updatable modules when generating the hidden
 	// API flags.
-	fragment := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
+	fragment := result.ModuleForTests(t, "mybootclasspathfragment", "android_common_myapex")
 
 	rule := fragment.Rule("modularHiddenAPIStubFlagsFile")
 	command := rule.RuleParams.Command
@@ -1460,7 +1466,7 @@
 		}
 	`)
 
-	fragment := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
+	fragment := result.ModuleForTests(t, "mybootclasspathfragment", "android_common_myapex")
 	classPathProtoContent := android.ContentFromFileRuleForTests(t, result.TestContext, fragment.Output("bootclasspath.pb.textproto"))
 	// foo
 	ensureContains(t, classPathProtoContent, `jars {
diff --git a/apex/builder.go b/apex/builder.go
index 41e2511..23c2ed8 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -20,12 +20,14 @@
 	"path"
 	"path/filepath"
 	"runtime"
+	"slices"
 	"sort"
 	"strconv"
 	"strings"
 
 	"android/soong/aconfig"
 	"android/soong/android"
+	"android/soong/dexpreopt"
 	"android/soong/java"
 
 	"github.com/google/blueprint"
@@ -70,9 +72,10 @@
 	pctx.HostBinToolVariable("extract_apks", "extract_apks")
 	pctx.HostBinToolVariable("make_f2fs", "make_f2fs")
 	pctx.HostBinToolVariable("sload_f2fs", "sload_f2fs")
-	pctx.HostBinToolVariable("make_erofs", "make_erofs")
+	pctx.HostBinToolVariable("make_erofs", "mkfs.erofs")
 	pctx.HostBinToolVariable("apex_compression_tool", "apex_compression_tool")
 	pctx.HostBinToolVariable("dexdeps", "dexdeps")
+	pctx.HostBinToolVariable("apex_ls", "apex-ls")
 	pctx.HostBinToolVariable("apex_sepolicy_tests", "apex_sepolicy_tests")
 	pctx.HostBinToolVariable("deapexer", "deapexer")
 	pctx.HostBinToolVariable("debugfs_static", "debugfs_static")
@@ -210,11 +213,11 @@
 	}, "image_dir", "readelf")
 
 	apexSepolicyTestsRule = pctx.StaticRule("apexSepolicyTestsRule", blueprint.RuleParams{
-		Command: `${deapexer} --debugfs_path ${debugfs_static} list -Z ${in} > ${out}.fc` +
-			` && ${apex_sepolicy_tests} -f ${out}.fc && touch ${out}`,
-		CommandDeps: []string{"${apex_sepolicy_tests}", "${deapexer}", "${debugfs_static}"},
+		Command: `${apex_ls} -Z ${in} > ${out}.fc` +
+			` && ${apex_sepolicy_tests} -f ${out}.fc --partition ${partition_tag} && touch ${out}`,
+		CommandDeps: []string{"${apex_sepolicy_tests}", "${apex_ls}"},
 		Description: "run apex_sepolicy_tests",
-	})
+	}, "partition_tag")
 
 	apexLinkerconfigValidationRule = pctx.StaticRule("apexLinkerconfigValidationRule", blueprint.RuleParams{
 		Command:     `${conv_linker_config} validate --type apex ${image_dir} && touch ${out}`,
@@ -275,6 +278,12 @@
 		})
 		files = append(files, newApexFile(ctx, apexAconfigFile, "aconfig_flags", "etc", etc, nil))
 
+		// To enable fingerprint, we need to have v2 storage files. The default version is 1.
+		storageFilesVersion := 1
+		if ctx.Config().ReleaseFingerprintAconfigPackages() {
+			storageFilesVersion = 2
+		}
+
 		for _, info := range createStorageInfo {
 			outputFile := android.PathForModuleOut(ctx, info.Output_file)
 			ctx.Build(pctx, android.BuildParams{
@@ -286,6 +295,7 @@
 					"container":   ctx.ModuleName(),
 					"file_type":   info.File_type,
 					"cache_files": android.JoinPathsWithPrefix(aconfigFiles, "--cache "),
+					"version":     strconv.Itoa(storageFilesVersion),
 				},
 			})
 			files = append(files, newApexFile(ctx, outputFile, info.File_type, "etc", etc, nil))
@@ -337,8 +347,8 @@
 		if err != nil {
 			ctx.ModuleErrorf("expected RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION to be an int, but got %s", defaultVersion)
 		}
-		if defaultVersionInt%10 != 0 {
-			ctx.ModuleErrorf("expected RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION to end in a zero, but got %s", defaultVersion)
+		if defaultVersionInt%10 != 0 && defaultVersionInt%10 != 9 {
+			ctx.ModuleErrorf("expected RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION to end in a zero or a nine, but got %s", defaultVersion)
 		}
 		variantVersion := []rune(*a.properties.Variant_version)
 		if len(variantVersion) != 1 || variantVersion[0] < '0' || variantVersion[0] > '9' {
@@ -418,8 +428,6 @@
 		ctx.PropertyErrorf("file_contexts", "cannot find file_contexts file: %q", fileContexts.String())
 	}
 
-	useFileContextsAsIs := proptools.Bool(a.properties.Use_file_contexts_as_is)
-
 	output := android.PathForModuleOut(ctx, "file_contexts")
 	rule := android.NewRuleBuilder(pctx, ctx)
 
@@ -436,11 +444,9 @@
 	rule.Command().Text("cat").Input(fileContexts).Text(">>").Output(output)
 	// new line
 	rule.Command().Text("echo").Text(">>").Output(output)
-	if !useFileContextsAsIs {
-		// force-label /apex_manifest.pb and /
-		rule.Command().Text("echo").Text("/apex_manifest\\\\.pb").Text(labelForManifest).Text(">>").Output(output)
-		rule.Command().Text("echo").Text("/").Text(labelForRoot).Text(">>").Output(output)
-	}
+	// force-label /apex_manifest.pb and /
+	rule.Command().Text("echo").Text("/apex_manifest\\\\.pb").Text(labelForManifest).Text(">>").Output(output)
+	rule.Command().Text("echo").Text("/").Text(labelForRoot).Text(">>").Output(output)
 
 	rule.Build("file_contexts."+a.Name(), "Generate file_contexts")
 	return output
@@ -518,9 +524,10 @@
 	})
 }
 
-func isVintfFragment(fi apexFile) bool {
+func shouldApplyAssembleVintf(fi apexFile) bool {
 	isVintfFragment, _ := path.Match("etc/vintf/*", fi.path())
-	return isVintfFragment
+	_, fromVintfFragmentModule := fi.module.(*android.VintfFragmentModule)
+	return isVintfFragment && !fromVintfFragmentModule
 }
 
 func runAssembleVintf(ctx android.ModuleContext, vintfFragment android.Path) android.Path {
@@ -534,6 +541,65 @@
 	return processed
 }
 
+// installApexSystemServerFiles installs dexpreopt and dexjar files for system server classpath entries
+// provided by the apex.  They are installed here instead of in library module because there may be multiple
+// variants of the library, generally one for the "main" apex and another with a different min_sdk_version
+// for the Android Go version of the apex.  Both variants would attempt to install to the same locations,
+// and the library variants cannot determine which one should.  The apex module is better equipped to determine
+// if it is "selected".
+// This assumes that the jars produced by different min_sdk_version values are identical, which is currently
+// true but may not be true if the min_sdk_version difference between the variants spans version that changed
+// the dex format.
+func (a *apexBundle) installApexSystemServerFiles(ctx android.ModuleContext) {
+	// If performInstalls is set this module is responsible for creating the install rules.
+	performInstalls := a.GetOverriddenBy() == "" && !a.testApex && a.installable()
+	// TODO(b/234351700): Remove once ART does not have separated debug APEX, or make the selection
+	// explicit in the ART Android.bp files.
+	if ctx.Config().UseDebugArt() {
+		if ctx.ModuleName() == "com.android.art" {
+			performInstalls = false
+		}
+	} else {
+		if ctx.ModuleName() == "com.android.art.debug" {
+			performInstalls = false
+		}
+	}
+
+	psi := android.PrebuiltSelectionInfoMap{}
+	ctx.VisitDirectDeps(func(am android.Module) {
+		if info, exists := android.OtherModuleProvider(ctx, am, android.PrebuiltSelectionInfoProvider); exists {
+			psi = info
+		}
+	})
+
+	if len(psi.GetSelectedModulesForApiDomain(ctx.ModuleName())) > 0 {
+		performInstalls = false
+	}
+
+	for _, fi := range a.filesInfo {
+		for _, install := range fi.systemServerDexpreoptInstalls {
+			var installedFile android.InstallPath
+			if performInstalls {
+				installedFile = ctx.InstallFile(install.InstallDirOnDevice, install.InstallFileOnDevice, install.OutputPathOnHost)
+			} else {
+				// Another module created the install rules, but this module should still depend on
+				// the installed locations.
+				installedFile = install.InstallDirOnDevice.Join(ctx, install.InstallFileOnDevice)
+			}
+			a.extraInstalledFiles = append(a.extraInstalledFiles, installedFile)
+			a.extraInstalledPairs = append(a.extraInstalledPairs, installPair{install.OutputPathOnHost, installedFile})
+			ctx.PackageFile(install.InstallDirOnDevice, install.InstallFileOnDevice, install.OutputPathOnHost)
+		}
+		if performInstalls {
+			for _, dexJar := range fi.systemServerDexJars {
+				// Copy the system server dex jar to a predefined location where dex2oat will find it.
+				android.CopyFileRule(ctx, dexJar,
+					android.PathForOutput(ctx, dexpreopt.SystemServerDexjarsDir, dexJar.Base()))
+			}
+		}
+	}
+}
+
 // buildApex creates build rules to build an APEX using apexer.
 func (a *apexBundle) buildApex(ctx android.ModuleContext) {
 	suffix := imageApexSuffix
@@ -544,7 +610,7 @@
 
 	imageDir := android.PathForModuleOut(ctx, "image"+suffix)
 
-	installSymbolFiles := (!ctx.Config().KatiEnabled() || a.ExportedToMake()) && a.installable()
+	installSymbolFiles := a.ExportedToMake() && a.installable()
 
 	// set of dependency module:location mappings
 	installMapSet := make(map[string]bool)
@@ -571,7 +637,7 @@
 			copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath)
 		} else {
 			// Copy the file into APEX
-			if !a.testApex && isVintfFragment(fi) {
+			if !a.testApex && shouldApplyAssembleVintf(fi) {
 				// copy the output of assemble_vintf instead of the original
 				vintfFragment := runAssembleVintf(ctx, fi.builtFile)
 				copyCommands = append(copyCommands, "cp -f "+vintfFragment.String()+" "+destPath)
@@ -595,7 +661,7 @@
 			} else {
 				if installSymbolFiles {
 					// store installedPath. symlinks might be created if required.
-					installedPath = apexDir.Join(ctx, fi.installDir, fi.stem())
+					installedPath = ctx.InstallFile(apexDir.Join(ctx, fi.installDir), fi.stem(), fi.builtFile)
 				}
 			}
 
@@ -911,9 +977,8 @@
 	}
 	var validations android.Paths
 	validations = append(validations, runApexLinkerconfigValidation(ctx, unsignedOutputFile, imageDir))
-	// TODO(b/279688635) deapexer supports [ext4]
-	if !a.skipValidation(apexSepolicyTests) && suffix == imageApexSuffix && ext4 == a.payloadFsType {
-		validations = append(validations, runApexSepolicyTests(ctx, unsignedOutputFile))
+	if !a.skipValidation(apexSepolicyTests) && android.InList(a.payloadFsType, []fsType{ext4, erofs}) {
+		validations = append(validations, runApexSepolicyTests(ctx, a, unsignedOutputFile))
 	}
 	if !a.testApex && len(a.properties.Unwanted_transitive_deps) > 0 {
 		validations = append(validations,
@@ -978,9 +1043,9 @@
 		a.SkipInstall()
 	}
 
+	installDeps := slices.Concat(a.compatSymlinks, a.extraInstalledFiles)
 	// Install to $OUT/soong/{target,host}/.../apex.
-	a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile,
-		a.compatSymlinks...)
+	a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile, installDeps...)
 
 	// installed-files.txt is dist'ed
 	a.installedFilesFile = a.buildInstalledFilesFile(ctx, a.outputFile, imageDir)
@@ -1037,7 +1102,7 @@
 	}
 
 	depInfos := android.DepNameToDepInfoMap{}
-	a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+	a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from, to android.ModuleProxy, 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.
@@ -1046,7 +1111,7 @@
 
 		// Skip dependencies that are only available to APEXes; they are developed with updatability
 		// in mind and don't need manual approval.
-		if to.(android.ApexModule).NotAvailableForPlatform() {
+		if android.OtherModulePointerProviderOrDefault(ctx, to, android.CommonModuleInfoProvider).NotAvailableForPlatform {
 			return !externalDep
 		}
 
@@ -1064,18 +1129,9 @@
 			depInfos[to.Name()] = info
 		} else {
 			toMinSdkVersion := "(no version)"
-			if m, ok := to.(interface {
-				MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel
-			}); ok {
-				if v := m.MinSdkVersion(ctx); !v.IsNone() {
-					toMinSdkVersion = v.String()
-				}
-			} else if m, ok := to.(interface{ MinSdkVersion() string }); ok {
-				// TODO(b/175678607) eliminate the use of MinSdkVersion returning
-				// string
-				if v := m.MinSdkVersion(); v != "" {
-					toMinSdkVersion = v
-				}
+			if info, ok := android.OtherModuleProvider(ctx, to, android.CommonModuleInfoProvider); ok &&
+				!info.MinSdkVersion.IsPlatform && info.MinSdkVersion.ApiLevel != nil {
+				toMinSdkVersion = info.MinSdkVersion.ApiLevel.String()
 			}
 			depInfos[to.Name()] = android.ApexModuleDepInfo{
 				To:            to.Name(),
@@ -1203,16 +1259,35 @@
 	return timestamp
 }
 
+// Can't use PartitionTag() because PartitionTag() returns the partition this module is actually
+// installed (e.g. PartitionTag() may return "system" for vendor apex when vendor is linked to /system/vendor)
+func (a *apexBundle) partition() string {
+	if a.SocSpecific() {
+		return "vendor"
+	} else if a.DeviceSpecific() {
+		return "odm"
+	} else if a.ProductSpecific() {
+		return "product"
+	} else if a.SystemExtSpecific() {
+		return "system_ext"
+	} else {
+		return "system"
+	}
+}
+
 // Runs apex_sepolicy_tests
 //
-// $ deapexer list -Z {apex_file} > {file_contexts}
+// $ apex-ls -Z {apex_file} > {file_contexts}
 // $ apex_sepolicy_tests -f {file_contexts}
-func runApexSepolicyTests(ctx android.ModuleContext, apexFile android.Path) android.Path {
+func runApexSepolicyTests(ctx android.ModuleContext, a *apexBundle, apexFile android.Path) android.Path {
 	timestamp := android.PathForModuleOut(ctx, "apex_sepolicy_tests.timestamp")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   apexSepolicyTestsRule,
 		Input:  apexFile,
 		Output: timestamp,
+		Args: map[string]string{
+			"partition_tag": a.partition(),
+		},
 	})
 	return timestamp
 }
@@ -1238,7 +1313,7 @@
 		Input:  apexFile,
 		Output: timestamp,
 		Args: map[string]string{
-			"partition_tag": a.PartitionTag(ctx.DeviceConfig()),
+			"partition_tag": a.partition(),
 		},
 	})
 	return timestamp
diff --git a/apex/classpath_element_test.go b/apex/classpath_element_test.go
index f367174..c2f2fc5 100644
--- a/apex/classpath_element_test.go
+++ b/apex/classpath_element_test.go
@@ -198,15 +198,13 @@
 
 	result := preparer.RunTest(t)
 
-	artFragment := result.Module("art-bootclasspath-fragment", "android_common_apex10000")
+	artFragment := result.Module("art-bootclasspath-fragment", "android_common_com.android.art")
 	artBaz := result.Module("baz", "android_common_apex10000")
 	artQuuz := result.Module("quuz", "android_common_apex10000")
 
-	myFragment := result.Module("mybootclasspath-fragment", "android_common_apex10000")
+	myFragment := result.Module("mybootclasspath-fragment", "android_common_myapex")
 	myBar := result.Module("bar", "android_common_apex10000")
 
-	other := result.Module("othersdklibrary", "android_common_apex10000")
-
 	otherApexLibrary := result.Module("otherapexlibrary", "android_common_apex10000")
 
 	platformFoo := result.Module("quuz", "android_common")
@@ -240,7 +238,11 @@
 	t.Run("art:baz, art:quuz, my:bar, foo", func(t *testing.T) {
 		t.Parallel()
 		ctx := newCtx()
-		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, artQuuz, myBar, platformFoo}, []android.Module{artFragment, myFragment})
+		elements := java.CreateClasspathElements(ctx,
+			[]android.Module{artBaz, artQuuz, myBar, platformFoo},
+			[]android.Module{artFragment, myFragment},
+			map[android.Module]string{artBaz: "com.android.art", artQuuz: "com.android.art", myBar: "myapex"},
+			map[string]android.Module{"com.android.art": artFragment, "myapex": myFragment})
 		expectedElements := java.ClasspathElements{
 			expectFragmentElement(artFragment, artBaz, artQuuz),
 			expectFragmentElement(myFragment, myBar),
@@ -249,32 +251,16 @@
 		assertElementsEquals(t, "elements", expectedElements, elements)
 	})
 
-	// Verify that CreateClasspathElements detects when an apex has multiple fragments.
-	t.Run("multiple fragments for same apex", func(t *testing.T) {
-		t.Parallel()
-		ctx := newCtx()
-		elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{artFragment, artFragment})
-		android.FailIfNoMatchingErrors(t, "apex com.android.art has multiple fragments, art-bootclasspath-fragment{.*} and art-bootclasspath-fragment{.*}", ctx.errs)
-		expectedElements := java.ClasspathElements{}
-		assertElementsEquals(t, "elements", expectedElements, elements)
-	})
-
-	// Verify that CreateClasspathElements detects when a library is in multiple fragments.
-	t.Run("library from multiple fragments", func(t *testing.T) {
-		t.Parallel()
-		ctx := newCtx()
-		elements := java.CreateClasspathElements(ctx, []android.Module{other}, []android.Module{artFragment, myFragment})
-		android.FailIfNoMatchingErrors(t, "library othersdklibrary{.*} is in two separate fragments, art-bootclasspath-fragment{.*} and mybootclasspath-fragment{.*}", ctx.errs)
-		expectedElements := java.ClasspathElements{}
-		assertElementsEquals(t, "elements", expectedElements, elements)
-	})
-
 	// Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and
 	// are separated by a library from another fragment.
 	t.Run("discontiguous separated by fragment", func(t *testing.T) {
 		t.Parallel()
 		ctx := newCtx()
-		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, myBar, artQuuz, platformFoo}, []android.Module{artFragment, myFragment})
+		elements := java.CreateClasspathElements(ctx,
+			[]android.Module{artBaz, myBar, artQuuz, platformFoo},
+			[]android.Module{artFragment, myFragment},
+			map[android.Module]string{artBaz: "com.android.art", artQuuz: "com.android.art", myBar: "myapex"},
+			map[string]android.Module{"com.android.art": artFragment, "myapex": myFragment})
 		expectedElements := java.ClasspathElements{
 			expectFragmentElement(artFragment, artBaz, artQuuz),
 			expectFragmentElement(myFragment, myBar),
@@ -289,7 +275,11 @@
 	t.Run("discontiguous separated by library", func(t *testing.T) {
 		t.Parallel()
 		ctx := newCtx()
-		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, platformFoo, artQuuz, myBar}, []android.Module{artFragment, myFragment})
+		elements := java.CreateClasspathElements(ctx,
+			[]android.Module{artBaz, platformFoo, artQuuz, myBar},
+			[]android.Module{artFragment, myFragment},
+			map[android.Module]string{artBaz: "com.android.art", artQuuz: "com.android.art", myBar: "myapex"},
+			map[string]android.Module{"com.android.art": artFragment, "myapex": myFragment})
 		expectedElements := java.ClasspathElements{
 			expectFragmentElement(artFragment, artBaz, artQuuz),
 			expectLibraryElement(platformFoo),
@@ -305,7 +295,11 @@
 	t.Run("no fragment for apex", func(t *testing.T) {
 		t.Parallel()
 		ctx := newCtx()
-		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, otherApexLibrary}, []android.Module{artFragment})
+		elements := java.CreateClasspathElements(ctx,
+			[]android.Module{artBaz, otherApexLibrary},
+			[]android.Module{artFragment},
+			map[android.Module]string{artBaz: "com.android.art", otherApexLibrary: "otherapex"},
+			map[string]android.Module{"com.android.art": artFragment})
 		expectedElements := java.ClasspathElements{
 			expectFragmentElement(artFragment, artBaz),
 		}
diff --git a/apex/container_test.go b/apex/container_test.go
index 395793f..b19e050 100644
--- a/apex/container_test.go
+++ b/apex/container_test.go
@@ -29,6 +29,7 @@
 
 func TestApexDepsContainers(t *testing.T) {
 	t.Parallel()
+	t.Skip("TODO(b/394955484): this probably has to be moved to a check by the apex")
 	result := android.GroupFixturePreparers(
 		prepareForApexTest,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
@@ -156,7 +157,7 @@
 	}
 
 	for _, c := range testcases {
-		m := result.ModuleForTests(c.moduleName, c.variant)
+		m := result.ModuleForTests(t, c.moduleName, c.variant)
 		containers, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), m.Module(), android.ContainersInfoProvider)
 		belongingContainers := containers.BelongingContainers()
 		checkContainerMatch(t, c.moduleName, "system", c.isSystemContainer, android.InList(android.SystemContainer, belongingContainers))
@@ -166,6 +167,7 @@
 
 func TestNonUpdatableApexDepsContainers(t *testing.T) {
 	t.Parallel()
+	t.Skip("TODO(b/394955484): this probably has to be moved to a check by the apex")
 	result := android.GroupFixturePreparers(
 		prepareForApexTest,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
@@ -271,7 +273,7 @@
 	}
 
 	for _, c := range testcases {
-		m := result.ModuleForTests(c.moduleName, c.variant)
+		m := result.ModuleForTests(t, c.moduleName, c.variant)
 		containers, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), m.Module(), android.ContainersInfoProvider)
 		belongingContainers := containers.BelongingContainers()
 		checkContainerMatch(t, c.moduleName, "system", c.isSystemContainer, android.InList(android.SystemContainer, belongingContainers))
@@ -281,6 +283,7 @@
 
 func TestUpdatableAndNonUpdatableApexesIdenticalMinSdkVersion(t *testing.T) {
 	t.Parallel()
+	t.Skip("TODO(b/394955484): this probably has to be moved to a check by the apex")
 	result := android.GroupFixturePreparers(
 		prepareForApexTest,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
@@ -334,7 +337,7 @@
 		}
 	`)
 
-	fooApexVariant := result.ModuleForTests("foo", "android_common_apex30")
+	fooApexVariant := result.ModuleForTests(t, "foo", "android_common_apex30")
 	containers, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), fooApexVariant.Module(), android.ContainersInfoProvider)
 	belongingContainers := containers.BelongingContainers()
 	checkContainerMatch(t, "foo", "system", true, android.InList(android.SystemContainer, belongingContainers))
diff --git a/apex/dexpreopt_bootjars_test.go b/apex/dexpreopt_bootjars_test.go
index b51bb36..2c7c459 100644
--- a/apex/dexpreopt_bootjars_test.go
+++ b/apex/dexpreopt_bootjars_test.go
@@ -151,7 +151,7 @@
 	}
 	result := fixture.RunTestWithBp(t, fmt.Sprintf(bp, preferPrebuilt))
 
-	dexBootJars := result.ModuleForTests("dex_bootjars", "android_common")
+	dexBootJars := result.ModuleForTests(t, "dex_bootjars", "android_common")
 	rule := dexBootJars.Output(ruleFile)
 
 	inputs := rule.Implicits.Strings()
@@ -176,7 +176,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/art-bootclasspath-fragment/android_common_apex10000/art-bootclasspath-fragment/boot.prof",
+		"out/soong/.intermediates/art-bootclasspath-fragment/android_common_com.android.art/art-bootclasspath-fragment/boot.prof",
 		"out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof",
 		"out/soong/dexpreopt/uffd_gc_flag.txt",
 	}
@@ -215,7 +215,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/android_common_com.android.art/deapexer/etc/boot-image.prof",
+		"out/soong/.intermediates/prebuilt_com.android.art/android_common_prebuilt_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",
 	}
@@ -396,17 +396,17 @@
 		{
 			desc:                         "Source apex com.android.art is selected, profile should come from source java library",
 			selectedArtApexContributions: "art.source.contributions",
-			expectedProfile:              "out/soong/.intermediates/art-bootclasspath-fragment/android_common_apex10000/art-bootclasspath-fragment/boot.prof",
+			expectedProfile:              "out/soong/.intermediates/art-bootclasspath-fragment/android_common_com.android.art/art-bootclasspath-fragment/boot.prof",
 		},
 		{
 			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/android_common_com.android.art/deapexer/etc/boot-image.prof",
+			expectedProfile:              "out/soong/.intermediates/prebuilt_com.android.art/android_common_prebuilt_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/com.android.art.v2/android_common_com.android.art/deapexer/etc/boot-image.prof",
+			expectedProfile:              "out/soong/.intermediates/com.android.art.v2/android_common_prebuilt_com.android.art/deapexer/etc/boot-image.prof",
 		},
 	}
 	for _, tc := range testCases {
@@ -419,7 +419,7 @@
 			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", tc.selectedArtApexContributions),
 		).RunTestWithBp(t, bp)
 
-		dexBootJars := result.ModuleForTests("dex_bootjars", "android_common")
+		dexBootJars := result.ModuleForTests(t, "dex_bootjars", "android_common")
 		rule := dexBootJars.Output(ruleFile)
 
 		inputs := rule.Implicits.Strings()
diff --git a/apex/key.go b/apex/key.go
index e4214f0..cc66a13 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -18,6 +18,7 @@
 	"fmt"
 
 	"android/soong/android"
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -29,8 +30,16 @@
 
 func registerApexKeyBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
+	ctx.RegisterParallelSingletonModuleType("all_apex_certs", allApexCertsFactory)
 }
 
+type ApexKeyInfo struct {
+	PublicKeyFile  android.Path
+	PrivateKeyFile android.Path
+}
+
+var ApexKeyInfoProvider = blueprint.NewProvider[ApexKeyInfo]()
+
 type apexKey struct {
 	android.ModuleBase
 
@@ -93,6 +102,11 @@
 			m.publicKeyFile.String(), pubKeyName, m.privateKeyFile, privKeyName)
 		return
 	}
+
+	android.SetProvider(ctx, ApexKeyInfoProvider, ApexKeyInfo{
+		PublicKeyFile:  m.publicKeyFile,
+		PrivateKeyFile: m.privateKeyFile,
+	})
 }
 
 type apexKeyEntry struct {
@@ -155,3 +169,64 @@
 	android.WriteFileRuleVerbatim(ctx, path, entry.String())
 	return path
 }
+
+var (
+	pemToDer = pctx.AndroidStaticRule("pem_to_der",
+		blueprint.RuleParams{
+			Command:     `openssl x509 -inform PEM -outform DER -in $in -out $out`,
+			Description: "Convert certificate from PEM to DER format",
+		},
+	)
+)
+
+// all_apex_certs is a singleton module that collects the certs of all apexes in the tree.
+// It provides two types of output files
+// 1. .pem: This is usually the checked-in x509 certificate in PEM format
+// 2. .der: This is DER format of the certificate, and is generated from the PEM certificate using `openssl x509`
+func allApexCertsFactory() android.SingletonModule {
+	m := &allApexCerts{}
+	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
+	return m
+}
+
+type allApexCerts struct {
+	android.SingletonModuleBase
+}
+
+func (_ *allApexCerts) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	var avbpubkeys android.Paths
+	var certificatesPem android.Paths
+	ctx.VisitDirectDeps(func(m android.Module) {
+		if apex, ok := m.(*apexBundle); ok {
+			pem, _ := apex.getCertificateAndPrivateKey(ctx)
+			if !android.ExistentPathForSource(ctx, pem.String()).Valid() {
+				if ctx.Config().AllowMissingDependencies() {
+					return
+				} else {
+					ctx.ModuleErrorf("Path %s is not valid\n", pem.String())
+				}
+			}
+			certificatesPem = append(certificatesPem, pem)
+			// avbpubkey for signing the apex payload
+			avbpubkeys = append(avbpubkeys, apex.publicKeyFile)
+		}
+	})
+	certificatesPem = android.SortedUniquePaths(certificatesPem) // For hermiticity
+	avbpubkeys = android.SortedUniquePaths(avbpubkeys)           // For hermiticity
+	var certificatesDer android.Paths
+	for index, certificatePem := range certificatesPem {
+		certificateDer := android.PathForModuleOut(ctx, fmt.Sprintf("x509.%v.der", index))
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   pemToDer,
+			Input:  certificatePem,
+			Output: certificateDer,
+		})
+		certificatesDer = append(certificatesDer, certificateDer)
+	}
+	ctx.SetOutputFiles(certificatesPem, ".pem")
+	ctx.SetOutputFiles(certificatesDer, ".der")
+	ctx.SetOutputFiles(avbpubkeys, ".avbpubkey")
+}
+
+func (_ *allApexCerts) GenerateSingletonBuildActions(ctx android.SingletonContext) {
+}
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 9f9b0b4..d79af86 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -23,7 +23,6 @@
 	"android/soong/dexpreopt"
 	"android/soong/java"
 
-	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -165,12 +164,12 @@
 		android.AssertPathsRelativeToTopEquals(t, message, expected, info.FlagsFilesByCategory[category])
 	}
 
-	android.AssertPathsRelativeToTopEquals(t, "annotation flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex30/modular-hiddenapi/annotation-flags.csv"}, info.AnnotationFlagsPaths)
-	android.AssertPathsRelativeToTopEquals(t, "metadata flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex30/modular-hiddenapi/metadata.csv"}, info.MetadataPaths)
-	android.AssertPathsRelativeToTopEquals(t, "index flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex30/modular-hiddenapi/index.csv"}, info.IndexPaths)
+	android.AssertPathsRelativeToTopEquals(t, "annotation flags", []string{"out/soong/.intermediates/bar-fragment/android_common_myapex/modular-hiddenapi/annotation-flags.csv"}, info.AnnotationFlagsPaths)
+	android.AssertPathsRelativeToTopEquals(t, "metadata flags", []string{"out/soong/.intermediates/bar-fragment/android_common_myapex/modular-hiddenapi/metadata.csv"}, info.MetadataPaths)
+	android.AssertPathsRelativeToTopEquals(t, "index flags", []string{"out/soong/.intermediates/bar-fragment/android_common_myapex/modular-hiddenapi/index.csv"}, info.IndexPaths)
 
-	android.AssertArrayString(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex30/modular-hiddenapi/filtered-stub-flags.csv:out/soong/.intermediates/bar-fragment/android_common_apex30/modular-hiddenapi/signature-patterns.csv"}, info.StubFlagSubsets.RelativeToTop())
-	android.AssertArrayString(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex30/modular-hiddenapi/filtered-flags.csv:out/soong/.intermediates/bar-fragment/android_common_apex30/modular-hiddenapi/signature-patterns.csv"}, info.FlagSubsets.RelativeToTop())
+	android.AssertArrayString(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common_myapex/modular-hiddenapi/filtered-stub-flags.csv:out/soong/.intermediates/bar-fragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv"}, info.StubFlagSubsets.RelativeToTop())
+	android.AssertArrayString(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common_myapex/modular-hiddenapi/filtered-flags.csv:out/soong/.intermediates/bar-fragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv"}, info.FlagSubsets.RelativeToTop())
 }
 
 // TestPlatformBootclasspath_LegacyPrebuiltFragment verifies that the
@@ -240,8 +239,8 @@
 	pbcp := result.Module("myplatform-bootclasspath", "android_common")
 	info, _ := android.OtherModuleProvider(result, pbcp, java.MonolithicHiddenAPIInfoProvider)
 
-	android.AssertArrayString(t, "stub flags", []string{"prebuilt-stub-flags.csv:out/soong/.intermediates/mybootclasspath-fragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv"}, info.StubFlagSubsets.RelativeToTop())
-	android.AssertArrayString(t, "all flags", []string{"prebuilt-all-flags.csv:out/soong/.intermediates/mybootclasspath-fragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv"}, info.FlagSubsets.RelativeToTop())
+	android.AssertArrayString(t, "stub flags", []string{"prebuilt-stub-flags.csv:out/soong/.intermediates/mybootclasspath-fragment/android_common_prebuilt_myapex/modular-hiddenapi/signature-patterns.csv"}, info.StubFlagSubsets.RelativeToTop())
+	android.AssertArrayString(t, "all flags", []string{"prebuilt-all-flags.csv:out/soong/.intermediates/mybootclasspath-fragment/android_common_prebuilt_myapex/modular-hiddenapi/signature-patterns.csv"}, info.FlagSubsets.RelativeToTop())
 }
 
 func TestPlatformBootclasspathDependencies(t *testing.T) {
@@ -388,7 +387,7 @@
 	})
 
 	// Make sure that the myplatform-bootclasspath has the correct dependencies.
-	CheckModuleDependencies(t, result.TestContext, "myplatform-bootclasspath", "android_common", []string{
+	java.CheckPlatformBootclasspathDependencies(t, result.TestContext, "myplatform-bootclasspath", "android_common", []string{
 		// source vs prebuilt selection metadata module
 		`platform:all_apex_contributions`,
 
@@ -401,17 +400,17 @@
 		// Needed for generating the boot image.
 		`platform:dex2oatd`,
 
-		// The configured contents of BootJars.
-		`com.android.art:baz`,
-		`com.android.art:quuz`,
+		// The configured contents of BootJars, via their apexes if necessary.
+		`platform:com.android.art`,
+		`platform:com.android.art`,
 		`platform:foo`,
 
-		// The configured contents of ApexBootJars.
-		`myapex:bar`,
+		// The configured contents of ApexBootJars, via their apex.
+		`platform:myapex`,
 
-		// The fragments.
-		`com.android.art:art-bootclasspath-fragment`,
-		`myapex:my-bootclasspath-fragment`,
+		// The fragments via their apexes.
+		`platform:com.android.art`,
+		`platform:myapex`,
 
 		// Impl lib of sdk_library for transitive srcjar generation
 		`platform:foo.impl`,
@@ -429,7 +428,7 @@
 		// of AlwaysUsePrebuiltsSdk(). The second is a normal library that is unaffected. The order
 		// matters, so that the dependencies resolved by the platform_bootclasspath matches the
 		// configured list.
-		java.FixtureConfigureApexBootJars("myapex:foo", "myapex:bar"),
+		java.FixtureConfigureApexBootJars("myapex:foo"),
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 			variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
@@ -479,6 +478,7 @@
 			name: "myapex",
 			src: "myapex.apex",
 			exported_bootclasspath_fragments: ["mybootclasspath-fragment"],
+			prefer: true,
 		}
 
 		// A prebuilt java_sdk_library_import that is not preferred by default but will be preferred
@@ -544,12 +544,11 @@
 
 	java.CheckPlatformBootclasspathModules(t, result, "myplatform-bootclasspath", []string{
 		// The configured contents of BootJars.
-		"myapex:prebuilt_foo",
-		"myapex:bar",
+		"prebuilt_myapex:prebuilt_foo",
 	})
 
 	// Make sure that the myplatform-bootclasspath has the correct dependencies.
-	CheckModuleDependencies(t, result.TestContext, "myplatform-bootclasspath", "android_common", []string{
+	java.CheckPlatformBootclasspathDependencies(t, result.TestContext, "myplatform-bootclasspath", "android_common", []string{
 		// source vs prebuilt selection metadata module
 		`platform:all_apex_contributions`,
 
@@ -561,39 +560,17 @@
 		// Not a prebuilt as no prebuilt existed when it was added.
 		"platform:legacy.core.platform.api.stubs.exportable",
 
-		// The platform_bootclasspath intentionally adds dependencies on both source and prebuilt
-		// modules when available as it does not know which one will be preferred.
-		"myapex:foo",
-		"myapex:prebuilt_foo",
+		// The prebuilt library via the apex.
+		"platform:prebuilt_myapex",
 
-		// Only a source module exists.
-		"myapex:bar",
-
-		// The fragments.
-		"myapex:mybootclasspath-fragment",
-		"myapex:prebuilt_mybootclasspath-fragment",
+		// The fragments via the apex.
+		"platform:prebuilt_myapex",
 
 		// Impl lib of sdk_library for transitive srcjar generation
 		"platform:foo.impl",
 	})
 }
 
-// CheckModuleDependencies checks the dependencies of the selected module against the expected list.
-//
-// The expected list must be a list of strings of the form "<apex>:<module>", where <apex> is the
-// name of the apex, or platform is it is not part of an apex and <module> is the module name.
-func CheckModuleDependencies(t *testing.T, ctx *android.TestContext, name, variant string, expected []string) {
-	t.Helper()
-	module := ctx.ModuleForTests(name, variant).Module()
-	modules := []android.Module{}
-	ctx.VisitDirectDeps(module, func(m blueprint.Module) {
-		modules = append(modules, m.(android.Module))
-	})
-
-	pairs := java.ApexNamePairsFromModules(ctx, modules)
-	android.AssertDeepEquals(t, "module dependencies", expected, pairs)
-}
-
 // TestPlatformBootclasspath_IncludesRemainingApexJars verifies that any apex boot jar is present in
 // platform_bootclasspath's classpaths.proto config, if the apex does not generate its own config
 // by setting generate_classpaths_proto property to false.
@@ -653,7 +630,7 @@
 		true,         // proto should be generated
 		"myapex:foo", // apex doesn't generate its own config, so must be in platform_bootclasspath
 		"bootclasspath.pb",
-		"out/soong/target/product/test_device/system/etc/classpaths",
+		"out/target/product/test_device/system/etc/classpaths",
 	)
 }
 
@@ -665,7 +642,7 @@
 		prepareForTestWithMyapex,
 		java.FixtureConfigureApexBootJars("myapex:foo"),
 	).ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
-		`dependency "foo" of "myplatform-bootclasspath" missing variant`)).
+		`module "myplatform-bootclasspath" variant ".*": failed to find module "foo" in apex "myapex"`)).
 		RunTestWithBp(t, `
 			apex {
 				name: "myapex",
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index f93eada..fdd9a75 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -15,11 +15,15 @@
 package apex
 
 import (
+	"fmt"
+	"slices"
+	"sort"
 	"strconv"
 	"strings"
 
 	"android/soong/android"
 	"android/soong/dexpreopt"
+	"android/soong/filesystem"
 	"android/soong/java"
 	"android/soong/provenance"
 
@@ -59,10 +63,12 @@
 	// Properties common to both prebuilt_apex and apex_set.
 	prebuiltCommonProperties *PrebuiltCommonProperties
 
-	installDir      android.InstallPath
-	installFilename string
-	installedFile   android.InstallPath
-	outputApex      android.WritablePath
+	installDir          android.InstallPath
+	installFilename     string
+	installedFile       android.InstallPath
+	extraInstalledFiles android.InstallPaths
+	extraInstalledPairs installPairs
+	outputApex          android.WritablePath
 
 	// fragment for this apex for apexkeys.txt
 	apexKeysPath android.WritablePath
@@ -70,8 +76,16 @@
 	// Installed locations of symlinks for backward compatibility.
 	compatSymlinks android.InstallPaths
 
-	hostRequired        []string
-	requiredModuleNames []string
+	// systemServerDexpreoptInstalls stores the list of dexpreopt artifacts for a system server jar.
+	systemServerDexpreoptInstalls []java.DexpreopterInstall
+
+	// systemServerDexJars stores the list of dexjars for system server jars in the prebuilt for use when
+	// dexpreopting system server jars that are later in the system server classpath.
+	systemServerDexJars android.Paths
+
+	// Certificate information of any apk packaged inside the prebuilt apex.
+	// This will be nil if the prebuilt apex does not contain any apk.
+	apkCertsFile android.WritablePath
 }
 
 type sanitizedPrebuilt interface {
@@ -188,9 +202,8 @@
 // initApexFilesForAndroidMk initializes the prebuiltCommon.requiredModuleNames field with the install only deps of the prebuilt apex
 func (p *prebuiltCommon) initApexFilesForAndroidMk(ctx android.ModuleContext) {
 	// If this apex contains a system server jar, then the dexpreopt artifacts should be added as required
-	for _, install := range p.Dexpreopter.DexpreoptBuiltInstalledForApex() {
-		p.requiredModuleNames = append(p.requiredModuleNames, install.FullModuleName())
-	}
+	p.systemServerDexpreoptInstalls = append(p.systemServerDexpreoptInstalls, p.Dexpreopter.ApexSystemServerDexpreoptInstalls()...)
+	p.systemServerDexJars = append(p.systemServerDexJars, p.Dexpreopter.ApexSystemServerDexJars()...)
 }
 
 // If this prebuilt has system server jar, create the rules to dexpreopt it and install it alongside the prebuilt apex
@@ -218,38 +231,63 @@
 	}
 }
 
-func (p *prebuiltCommon) addRequiredModules(entries *android.AndroidMkEntries) {
-	entries.AddStrings("LOCAL_REQUIRED_MODULES", p.requiredModuleNames...)
+// installApexSystemServerFiles installs dexpreopt files for system server classpath entries
+// provided by the apex.  They are installed here instead of in library module because there may be multiple
+// variants of the library, generally one for the "main" apex and another with a different min_sdk_version
+// for the Android Go version of the apex.  Both variants would attempt to install to the same locations,
+// and the library variants cannot determine which one should.  The apex module is better equipped to determine
+// if it is "selected".
+// This assumes that the jars produced by different min_sdk_version values are identical, which is currently
+// true but may not be true if the min_sdk_version difference between the variants spans version that changed
+// the dex format.
+func (p *prebuiltCommon) installApexSystemServerFiles(ctx android.ModuleContext) {
+	performInstalls := android.IsModulePreferred(ctx.Module())
+
+	for _, install := range p.systemServerDexpreoptInstalls {
+		var installedFile android.InstallPath
+		if performInstalls {
+			installedFile = ctx.InstallFile(install.InstallDirOnDevice, install.InstallFileOnDevice, install.OutputPathOnHost)
+		} else {
+			installedFile = install.InstallDirOnDevice.Join(ctx, install.InstallFileOnDevice)
+		}
+		p.extraInstalledFiles = append(p.extraInstalledFiles, installedFile)
+		p.extraInstalledPairs = append(p.extraInstalledPairs, installPair{install.OutputPathOnHost, installedFile})
+		ctx.PackageFile(install.InstallDirOnDevice, install.InstallFileOnDevice, install.OutputPathOnHost)
+	}
+
+	for _, dexJar := range p.systemServerDexJars {
+		// Copy the system server dex jar to a predefined location where dex2oat will find it.
+		android.CopyFileRule(ctx, dexJar,
+			android.PathForOutput(ctx, dexpreopt.SystemServerDexjarsDir, dexJar.Base()))
+	}
 }
 
 func (p *prebuiltCommon) AndroidMkEntries() []android.AndroidMkEntries {
 	entriesList := []android.AndroidMkEntries{
 		{
-			Class:         "ETC",
-			OutputFile:    android.OptionalPathForPath(p.outputApex),
-			Include:       "$(BUILD_PREBUILT)",
-			Host_required: p.hostRequired,
+			Class:      "ETC",
+			OutputFile: android.OptionalPathForPath(p.outputApex),
+			Include:    "$(BUILD_PREBUILT)",
 			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 					entries.SetString("LOCAL_MODULE_PATH", p.installDir.String())
 					entries.SetString("LOCAL_MODULE_STEM", p.installFilename)
 					entries.SetPath("LOCAL_SOONG_INSTALLED_MODULE", p.installedFile)
-					entries.SetString("LOCAL_SOONG_INSTALL_PAIRS", p.outputApex.String()+":"+p.installedFile.String())
+					installPairs := append(installPairs{{p.outputApex, p.installedFile}}, p.extraInstalledPairs...)
+					entries.SetString("LOCAL_SOONG_INSTALL_PAIRS", installPairs.String())
 					entries.AddStrings("LOCAL_SOONG_INSTALL_SYMLINKS", p.compatSymlinks.Strings()...)
 					entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
 					entries.AddStrings("LOCAL_OVERRIDES_MODULES", p.prebuiltCommonProperties.Overrides...)
 					entries.SetString("LOCAL_APEX_KEY_PATH", p.apexKeysPath.String())
-					p.addRequiredModules(entries)
+					if p.apkCertsFile != nil {
+						entries.SetString("LOCAL_APKCERTS_FILE", p.apkCertsFile.String())
+					}
+
 				},
 			},
 		},
 	}
 
-	// Add the dexpreopt artifacts to androidmk
-	for _, install := range p.Dexpreopter.DexpreoptBuiltInstalledForApex() {
-		entriesList = append(entriesList, install.ToMakeEntries())
-	}
-
 	return entriesList
 }
 
@@ -258,6 +296,14 @@
 		len(p.prebuiltCommonProperties.Exported_systemserverclasspath_fragments) > 0
 }
 
+type appInPrebuiltApexDepTag struct {
+	blueprint.BaseDependencyTag
+}
+
+func (appInPrebuiltApexDepTag) ExcludeFromVisibilityEnforcement() {}
+
+var appInPrebuiltApexTag = appInPrebuiltApexDepTag{}
+
 // prebuiltApexContentsDeps adds dependencies onto the prebuilt apex module's contents.
 func (p *prebuiltCommon) prebuiltApexContentsDeps(ctx android.BottomUpMutatorContext) {
 	module := ctx.Module()
@@ -265,6 +311,7 @@
 	for _, dep := range p.prebuiltCommonProperties.Exported_bootclasspath_fragments {
 		prebuiltDep := android.PrebuiltNameFromSource(dep)
 		ctx.AddDependency(module, exportedBootclasspathFragmentTag, prebuiltDep)
+		ctx.AddDependency(module, fragmentInApexTag, prebuiltDep)
 	}
 
 	for _, dep := range p.prebuiltCommonProperties.Exported_systemserverclasspath_fragments {
@@ -274,95 +321,47 @@
 }
 
 // Implements android.DepInInSameApex
-func (p *prebuiltCommon) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
-	tag := ctx.OtherModuleDependencyTag(dep)
+func (m *prebuiltCommon) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return ApexPrebuiltDepInSameApexChecker{}
+}
+
+type ApexPrebuiltDepInSameApexChecker struct {
+	android.BaseDepInSameApexChecker
+}
+
+func (m ApexPrebuiltDepInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool {
 	_, ok := tag.(exportedDependencyTag)
 	return ok
 }
 
-// apexInfoMutator marks any modules for which this apex exports a file as requiring an apex
-// specific variant and checks that they are supported.
-//
-// The apexMutator will ensure that the ApexInfo objects passed to BuildForApex(ApexInfo) are
-// associated with the apex specific variant using the ApexInfoProvider for later retrieval.
-//
-// Unlike the source apex module type the prebuilt_apex module type cannot share compatible variants
-// across prebuilt_apex modules. That is because there is no way to determine whether two
-// prebuilt_apex modules that export files for the same module are compatible. e.g. they could have
-// been built from different source at different times or they could have been built with different
-// build options that affect the libraries.
-//
-// While it may be possible to provide sufficient information to determine whether two prebuilt_apex
-// modules were compatible it would be a lot of work and would not provide much benefit for a couple
-// of reasons:
-//   - The number of prebuilt_apex modules that will be exporting files for the same module will be
-//     low as the prebuilt_apex only exports files for the direct dependencies that require it and
-//     very few modules are direct dependencies of multiple prebuilt_apex modules, e.g. there are a
-//     few com.android.art* apex files that contain the same contents and could export files for the
-//     same modules but only one of them needs to do so. Contrast that with source apex modules which
-//     need apex specific variants for every module that contributes code to the apex, whether direct
-//     or indirect.
-//   - The build cost of a prebuilt_apex variant is generally low as at worst it will involve some
-//     extra copying of files. Contrast that with source apex modules that has to build each variant
-//     from source.
-func (p *prebuiltCommon) apexInfoMutator(mctx android.TopDownMutatorContext) {
-	// Collect the list of dependencies.
-	var dependencies []android.ApexModule
-	mctx.WalkDeps(func(child, parent android.Module) bool {
-		// If the child is not in the same apex as the parent then exit immediately and do not visit
-		// any of the child's dependencies.
-		if !android.IsDepInSameApex(mctx, parent, child) {
-			return false
-		}
-
-		tag := mctx.OtherModuleDependencyTag(child)
-		depName := mctx.OtherModuleName(child)
+func (p *prebuiltCommon) checkExportedDependenciesArePrebuilts(ctx android.ModuleContext) {
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		tag := ctx.OtherModuleDependencyTag(dep)
+		depName := ctx.OtherModuleName(dep)
 		if exportedTag, ok := tag.(exportedDependencyTag); ok {
 			propertyName := exportedTag.name
 
 			// It is an error if the other module is not a prebuilt.
-			if !android.IsModulePrebuilt(child) {
-				mctx.PropertyErrorf(propertyName, "%q is not a prebuilt module", depName)
-				return false
+			if !android.IsModulePrebuilt(dep) {
+				ctx.PropertyErrorf(propertyName, "%q is not a prebuilt module", depName)
 			}
 
 			// It is an error if the other module is not an ApexModule.
-			if _, ok := child.(android.ApexModule); !ok {
-				mctx.PropertyErrorf(propertyName, "%q is not usable within an apex", depName)
-				return false
+			if _, ok := dep.(android.ApexModule); !ok {
+				ctx.PropertyErrorf(propertyName, "%q is not usable within an apex", depName)
 			}
 		}
 
-		// Ignore any modules that do not implement ApexModule as they cannot have an APEX specific
-		// variant.
-		if _, ok := child.(android.ApexModule); !ok {
-			return false
-		}
-
-		// Strip off the prebuilt_ prefix if present before storing content to ensure consistent
-		// behavior whether there is a corresponding source module present or not.
-		depName = android.RemoveOptionalPrebuiltPrefix(depName)
-
-		// Add the module to the list of dependencies that need to have an APEX variant.
-		dependencies = append(dependencies, child.(android.ApexModule))
-
-		return true
 	})
+}
 
-	android.SetProvider(mctx, android.ApexBundleInfoProvider, android.ApexBundleInfo{})
-
-	// Create an ApexInfo for the prebuilt_apex.
-	apexVariationName := p.ApexVariationName()
-	apexInfo := android.ApexInfo{
-		ApexVariationName: apexVariationName,
-		InApexVariants:    []string{apexVariationName},
+// generateApexInfo returns an android.ApexInfo configuration suitable for dependencies of this apex.
+func (p *prebuiltCommon) generateApexInfo(ctx generateApexInfoContext) android.ApexInfo {
+	return android.ApexInfo{
+		ApexVariationName: "prebuilt_" + p.ApexVariationName(),
+		BaseApexName:      p.ApexVariationName(),
 		ForPrebuiltApex:   true,
 	}
-
-	// Mark the dependencies of this module as requiring a variant for this module.
-	for _, am := range dependencies {
-		am.BuildForApex(apexInfo)
-	}
 }
 
 type Prebuilt struct {
@@ -451,6 +450,12 @@
 	ApexFileProperties
 
 	PrebuiltCommonProperties
+
+	// List of apps that are bundled inside this prebuilt apex.
+	// This will be used to create the certificate info of those apps for apkcerts.txt
+	// This dependency will only be used for apkcerts.txt processing.
+	// Notably, building the prebuilt apex will not build the source app.
+	Apps []string
 }
 
 func (a *Prebuilt) hasSanitizedSource(sanitizer string) bool {
@@ -564,12 +569,27 @@
 
 func (p *Prebuilt) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
 	p.prebuiltApexContentsDeps(ctx)
+	for _, app := range p.properties.Apps {
+		ctx.AddDependency(p, appInPrebuiltApexTag, app)
+	}
 }
 
-var _ ApexInfoMutator = (*Prebuilt)(nil)
+var _ ApexTransitionMutator = (*Prebuilt)(nil)
 
-func (p *Prebuilt) ApexInfoMutator(mctx android.TopDownMutatorContext) {
-	p.apexInfoMutator(mctx)
+func (p *Prebuilt) ApexTransitionMutatorSplit(ctx android.BaseModuleContext) []android.ApexInfo {
+	return []android.ApexInfo{p.generateApexInfo(ctx)}
+}
+
+func (p *Prebuilt) ApexTransitionMutatorOutgoing(ctx android.OutgoingTransitionContext, sourceInfo android.ApexInfo) android.ApexInfo {
+	return sourceInfo
+}
+
+func (p *Prebuilt) ApexTransitionMutatorIncoming(ctx android.IncomingTransitionContext, outgoingInfo android.ApexInfo) android.ApexInfo {
+	return p.generateApexInfo(ctx)
+}
+
+func (p *Prebuilt) ApexTransitionMutatorMutate(ctx android.BottomUpMutatorContext, info android.ApexInfo) {
+	android.SetProvider(ctx, android.ApexBundleInfoProvider, android.ApexBundleInfo{})
 }
 
 // creates the build rules to deapex the prebuilt, and returns a deapexerInfo
@@ -635,6 +655,8 @@
 		validateApexClasspathFragments(ctx)
 	}
 
+	p.checkExportedDependenciesArePrebuilts(ctx)
+
 	p.apexKeysPath = writeApexKeys(ctx, p)
 	// TODO(jungjw): Check the key validity.
 	p.inputApex = android.PathForModuleSrc(ctx, p.properties.prebuiltApexSelector(ctx, ctx.Module()))
@@ -676,27 +698,69 @@
 	}
 
 	if p.installable() {
-		p.installedFile = ctx.InstallFile(p.installDir, p.installFilename, p.inputApex, p.compatSymlinks...)
+		p.installApexSystemServerFiles(ctx)
+		installDeps := slices.Concat(p.compatSymlinks, p.extraInstalledFiles)
+		p.installedFile = ctx.InstallFile(p.installDir, p.installFilename, p.inputApex, installDeps...)
 		p.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, p.inputApex, p.installedFile)
 	}
 
+	p.addApkCertsInfo(ctx)
+
 	ctx.SetOutputFiles(android.Paths{p.outputApex}, "")
+
+	android.SetProvider(ctx, filesystem.ApexKeyPathInfoProvider, filesystem.ApexKeyPathInfo{p.apexKeysPath})
+}
+
+// `addApkCertsInfo` sets a provider that will be used to create apkcerts.txt
+func (p *Prebuilt) addApkCertsInfo(ctx android.ModuleContext) {
+	formatLine := func(cert java.Certificate, name, partition string) string {
+		pem := cert.AndroidMkString()
+		var key string
+		if cert.Key == nil {
+			key = ""
+		} else {
+			key = cert.Key.String()
+		}
+		return fmt.Sprintf(`name="%s" certificate="%s" private_key="%s" partition="%s"`, name, pem, key, partition)
+	}
+
+	// Determine if this prebuilt_apex contains any .apks
+	var appInfos java.AppInfos
+	ctx.VisitDirectDepsProxyWithTag(appInPrebuiltApexTag, func(app android.ModuleProxy) {
+		if appInfo, ok := android.OtherModuleProvider(ctx, app, java.AppInfoProvider); ok {
+			appInfos = append(appInfos, *appInfo)
+		} else {
+			ctx.ModuleErrorf("App %s does not set AppInfoProvider\n", app.Name())
+		}
+	})
+	sort.Slice(appInfos, func(i, j int) bool {
+		return appInfos[i].InstallApkName < appInfos[j].InstallApkName
+	})
+
+	if len(appInfos) == 0 {
+		return
+	}
+
+	// Set a provider for use by `android_device`.
+	// `android_device` will create an apkcerts.txt with the list of installed apps for that device.
+	android.SetProvider(ctx, java.AppInfosProvider, appInfos)
+
+	// Set a Make variable for legacy apkcerts.txt creation
+	// p.apkCertsFile will become `LOCAL_APKCERTS_FILE`
+	var lines []string
+	for _, appInfo := range appInfos {
+		lines = append(lines, formatLine(appInfo.Certificate, appInfo.InstallApkName+".apk", p.PartitionTag(ctx.DeviceConfig())))
+	}
+	if len(lines) > 0 {
+		p.apkCertsFile = android.PathForModuleOut(ctx, "apkcerts.txt")
+		android.WriteFileRule(ctx, p.apkCertsFile, strings.Join(lines, "\n"))
+	}
 }
 
 func (p *Prebuilt) ProvenanceMetaDataFile() android.Path {
 	return p.provenanceMetaDataFile
 }
 
-// prebuiltApexExtractorModule is a private module type that is only created by the prebuilt_apex
-// module. It extracts the correct apex to use and makes it available for use by apex_set.
-type prebuiltApexExtractorModule struct {
-	android.ModuleBase
-
-	properties ApexExtractorProperties
-
-	extractedApex android.WritablePath
-}
-
 // 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 {
@@ -803,10 +867,22 @@
 	a.prebuiltApexContentsDeps(ctx)
 }
 
-var _ ApexInfoMutator = (*ApexSet)(nil)
+var _ ApexTransitionMutator = (*ApexSet)(nil)
 
-func (a *ApexSet) ApexInfoMutator(mctx android.TopDownMutatorContext) {
-	a.apexInfoMutator(mctx)
+func (a *ApexSet) ApexTransitionMutatorSplit(ctx android.BaseModuleContext) []android.ApexInfo {
+	return []android.ApexInfo{a.generateApexInfo(ctx)}
+}
+
+func (a *ApexSet) ApexTransitionMutatorOutgoing(ctx android.OutgoingTransitionContext, sourceInfo android.ApexInfo) android.ApexInfo {
+	return sourceInfo
+}
+
+func (a *ApexSet) ApexTransitionMutatorIncoming(ctx android.IncomingTransitionContext, outgoingInfo android.ApexInfo) android.ApexInfo {
+	return a.generateApexInfo(ctx)
+}
+
+func (a *ApexSet) ApexTransitionMutatorMutate(ctx android.BottomUpMutatorContext, info android.ApexInfo) {
+	android.SetProvider(ctx, android.ApexBundleInfoProvider, android.ApexBundleInfo{})
 }
 
 func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -863,7 +939,8 @@
 
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	if a.installable() {
-		a.installedFile = ctx.InstallFile(a.installDir, a.installFilename, a.outputApex)
+		a.installApexSystemServerFiles(ctx)
+		a.installedFile = ctx.InstallFile(a.installDir, a.installFilename, a.outputApex, a.extraInstalledFiles...)
 	}
 
 	// in case that apex_set replaces source apex (using prefer: prop)
@@ -874,12 +951,6 @@
 	}
 
 	ctx.SetOutputFiles(android.Paths{a.outputApex}, "")
-}
 
-type systemExtContext struct {
-	android.ModuleContext
-}
-
-func (*systemExtContext) SystemExtSpecific() bool {
-	return true
+	android.SetProvider(ctx, filesystem.ApexKeyPathInfoProvider, filesystem.ApexKeyPathInfo{a.apexKeysPath})
 }
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index 7dbac5f..61f79d6 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -108,6 +108,7 @@
 	})
 
 	java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex", []string{
+		`all_apex_contributions`,
 		`dex2oatd`,
 		`myapex.key`,
 		`mysystemserverclasspathfragment`,
@@ -166,6 +167,7 @@
 	})
 
 	java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex", []string{
+		`all_apex_contributions`,
 		`dex2oatd`,
 		`myapex.key`,
 		`mysystemserverclasspathfragment`,
@@ -278,19 +280,19 @@
 
 	ctx := result.TestContext
 
-	java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex", []string{
+	java.CheckModuleDependencies(t, ctx, "myapex", "android_common_prebuilt_myapex", []string{
 		`all_apex_contributions`,
 		`dex2oatd`,
 		`prebuilt_mysystemserverclasspathfragment`,
 	})
 
-	java.CheckModuleDependencies(t, ctx, "mysystemserverclasspathfragment", "android_common_myapex", []string{
+	java.CheckModuleDependencies(t, ctx, "mysystemserverclasspathfragment", "android_common_prebuilt_myapex", []string{
 		`all_apex_contributions`,
 		`prebuilt_bar`,
 		`prebuilt_foo`,
 	})
 
-	ensureExactDeapexedContents(t, ctx, "myapex", "android_common_myapex", []string{
+	ensureExactDeapexedContents(t, ctx, "myapex", "android_common_prebuilt_myapex", []string{
 		"javalib/foo.jar",
 		"javalib/bar.jar",
 		"javalib/bar.jar.prof",
@@ -438,13 +440,13 @@
 
 	ctx := result.TestContext
 
-	java.CheckModuleDependencies(t, ctx, "mysystemserverclasspathfragment", "android_common_myapex", []string{
+	java.CheckModuleDependencies(t, ctx, "mysystemserverclasspathfragment", "android_common_prebuilt_myapex", []string{
 		`all_apex_contributions`,
 		`prebuilt_bar`,
 		`prebuilt_foo`,
 	})
 
-	ensureExactDeapexedContents(t, ctx, "myapex", "android_common_myapex", []string{
+	ensureExactDeapexedContents(t, ctx, "myapex", "android_common_prebuilt_myapex", []string{
 		"javalib/foo.jar",
 		"javalib/bar.jar",
 		"javalib/bar.jar.prof",
@@ -455,7 +457,7 @@
 }
 
 func assertProfileGuided(t *testing.T, ctx *android.TestContext, moduleName string, variant string, expected bool) {
-	dexpreopt := ctx.ModuleForTests(moduleName, variant).Rule("dexpreopt")
+	dexpreopt := ctx.ModuleForTests(t, moduleName, variant).Rule("dexpreopt")
 	actual := strings.Contains(dexpreopt.RuleParams.Command, "--profile-file=")
 	if expected != actual {
 		t.Fatalf("Expected profile-guided to be %v, got %v", expected, actual)
@@ -463,9 +465,126 @@
 }
 
 func assertProfileGuidedPrebuilt(t *testing.T, ctx *android.TestContext, apexName string, moduleName string, expected bool) {
-	dexpreopt := ctx.ModuleForTests(apexName, "android_common_"+apexName).Rule("dexpreopt." + moduleName)
+	dexpreopt := ctx.ModuleForTests(t, apexName, "android_common_prebuilt_"+apexName).Rule("dexpreopt." + moduleName)
 	actual := strings.Contains(dexpreopt.RuleParams.Command, "--profile-file=")
 	if expected != actual {
 		t.Fatalf("Expected profile-guided to be %v, got %v", expected, actual)
 	}
 }
+
+func TestCheckSystemServerOrderWithArtApex(t *testing.T) {
+	preparers := android.GroupFixturePreparers(
+		java.PrepareForTestWithDexpreopt,
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		PrepareForTestWithApexBuildComponents,
+		prepareForTestWithArtApex,
+		java.FixtureConfigureBootJars("com.android.art:framework-art"),
+		dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-apex1", "com.android.art:service-art"),
+		java.FixtureWithLastReleaseApis("baz"),
+	)
+
+	// Creates a com.android.art apex with a bootclasspath fragment and a systemserverclasspath fragment, and a
+	// com.android.apex1 prebuilt whose bootclasspath fragment depends on the com.android.art bootclasspath fragment.
+	// Verifies that the checkSystemServerOrder doesn't get confused by the bootclasspath dependencies and report
+	// that service-apex1 depends on service-art.
+	result := preparers.RunTestWithBp(t, `
+		apex {
+			name: "com.android.art",
+			key: "com.android.art.key",
+			bootclasspath_fragments: ["art-bootclasspath-fragment"],
+			systemserverclasspath_fragments: ["art-systemserverclasspath-fragment"],
+			updatable: false,
+		}
+
+		apex_key {
+			name: "com.android.art.key",
+			public_key: "com.android.art.avbpubkey",
+			private_key: "com.android.art.pem",
+		}
+
+		bootclasspath_fragment {
+			name: "art-bootclasspath-fragment",
+			image_name: "art",
+			contents: ["framework-art"],
+			apex_available: [
+				"com.android.art",
+			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+
+		java_library {
+			name: "framework-art",
+			apex_available: ["com.android.art"],
+			srcs: ["a.java"],
+			compile_dex: true,
+		}
+
+		systemserverclasspath_fragment {
+			name: "art-systemserverclasspath-fragment",
+			apex_available: ["com.android.art"],
+			contents: ["service-art"],
+		}
+
+		java_library {
+			name: "service-art",
+			srcs: ["a.java"],
+			apex_available: ["com.android.art"],
+			compile_dex: true,
+		}
+
+		prebuilt_apex {
+			name: "com.android.apex1",
+			arch: {
+				arm64: {
+					src: "myapex-arm64.apex",
+				},
+				arm: {
+					src: "myapex-arm.apex",
+				},
+			},
+			exported_bootclasspath_fragments: ["com.android.apex1-bootclasspath-fragment"],
+			exported_systemserverclasspath_fragments: ["com.android.apex1-systemserverclasspath-fragment"],
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "com.android.apex1-bootclasspath-fragment",
+			visibility: ["//visibility:public"],
+			apex_available: ["com.android.apex1"],
+			contents: ["framework-apex1"],
+			fragments: [
+				{
+					apex: "com.android.art",
+					module: "art-bootclasspath-fragment",
+				},
+			],
+			hidden_api: {
+				annotation_flags: "hiddenapi/annotation-flags.csv",
+				metadata: "hiddenapi/metadata.csv",
+				index: "hiddenapi/index.csv",
+				stub_flags: "hiddenapi/stub-flags.csv",
+				all_flags: "hiddenapi/all-flags.csv",
+			},
+		}
+
+		java_import {
+			name: "framework-apex1",
+			apex_available: ["com.android.apex1"],
+		}
+
+		prebuilt_systemserverclasspath_fragment {
+			name: "com.android.apex1-systemserverclasspath-fragment",
+			apex_available: ["com.android.apex1"],
+			contents: ["service-apex1"],
+		}
+
+		java_import {
+			name: "service-apex1",
+			installable: true,
+			apex_available: ["com.android.apex1"],
+			sdk_version: "current",
+		}`)
+
+	_ = result
+}
diff --git a/apex/testing.go b/apex/testing.go
index 63c5b69..a22f640 100644
--- a/apex/testing.go
+++ b/apex/testing.go
@@ -22,6 +22,9 @@
 	android.FixtureRegisterWithContext(registerApexBuildComponents),
 	android.FixtureRegisterWithContext(registerApexKeyBuildComponents),
 	android.FixtureRegisterWithContext(registerApexDepsInfoComponents),
+	android.FixtureAddTextFile("all_apex_certs/Android.bp", `
+		all_apex_certs { name: "all_apex_certs" }
+	`),
 	// Additional files needed in tests that disallow non-existent source files.
 	// This includes files that are needed by all, or at least most, instances of an apex module type.
 	android.MockFS{
@@ -30,6 +33,8 @@
 		"build/soong/scripts/gen_ndk_backedby_apex.sh":         nil,
 		// Needed by prebuilt_apex.
 		"build/soong/scripts/unpack-prebuilt-apex.sh": nil,
+		// Needed by all_apex_certs
+		"build/make/target/product/security/testkey.x509.pem": nil,
 	}.AddToFixture(),
 	android.PrepareForTestWithBuildFlag("RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION", testDefaultUpdatableModuleVersion),
 )
diff --git a/bin/get_build_vars b/bin/get_build_vars
new file mode 100755
index 0000000..aa887c7
--- /dev/null
+++ b/bin/get_build_vars
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+# 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.
+
+# Get the value of build variables.  The values are printed in a format suitable
+# for use in the import_build_vars function in build/make/shell_utils.sh
+#
+# For absolute variables, prefix the variable name with a '/'
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+$TOP/build/soong/soong_ui.bash --dumpvars-mode \
+    --vars="$(printf '%s\n' "$@" | grep -v '^/')" \
+    --abs-vars="$(printf '%s\n' "$@" | grep '^/' | sed 's:^/::')" \
+    --var-prefix= \
+    --abs-var-prefix=
+
+exit $?
diff --git a/bin/mm b/bin/mm
index 6461b1e..6f1c934 100755
--- a/bin/mm
+++ b/bin/mm
@@ -19,6 +19,6 @@
 
 require_top
 
-_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --modules-in-a-dir-no-deps --dir="$(pwd)" "$@"
+_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --modules-in-a-dir --dir="$(pwd)" "$@"
 
 exit $?
diff --git a/bin/mmm b/bin/mmm
index ab3a632..d9190e5 100755
--- a/bin/mmm
+++ b/bin/mmm
@@ -19,6 +19,6 @@
 
 require_top
 
-_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --modules-in-dirs-no-deps --dir="$(pwd)" "$@"
+_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --modules-in-dirs --dir="$(pwd)" "$@"
 
 exit $?
diff --git a/bin/soongdbg b/bin/soongdbg
index 0807291..dad5137 100755
--- a/bin/soongdbg
+++ b/bin/soongdbg
@@ -450,13 +450,17 @@
 
 
 def main():
+    global SOONG_DEBUG_DATA_FILENAME
     parser = argparse.ArgumentParser()
+    parser.add_argument("-f", "--debug-file", nargs=1, help="location of the debug info file",
+                        default=[SOONG_DEBUG_DATA_FILENAME])
     subparsers = parser.add_subparsers(required=True, dest="command")
     for name in sorted(COMMANDS.keys()):
         command = COMMANDS[name]
         subparser = subparsers.add_parser(name, help=command.help)
         command.args(subparser)
     args = parser.parse_args()
+    SOONG_DEBUG_DATA_FILENAME = args.debug_file[0]
     COMMANDS[args.command].run(args)
     sys.exit(0)
 
diff --git a/bloaty/bloaty.go b/bloaty/bloaty.go
index 8ecea98..d78a907 100644
--- a/bloaty/bloaty.go
+++ b/bloaty/bloaty.go
@@ -84,8 +84,8 @@
 
 func (singleton *sizesSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var deps android.Paths
-	ctx.VisitAllModules(func(m android.Module) {
-		if !m.ExportedToMake() {
+	ctx.VisitAllModuleProxies(func(m android.ModuleProxy) {
+		if !android.OtherModulePointerProviderOrDefault(ctx, m, android.CommonModuleInfoProvider).ExportedToMake {
 			return
 		}
 		filePaths, ok := android.OtherModuleProvider(ctx, m, fileSizeMeasurerKey)
@@ -105,13 +105,11 @@
 		}
 	})
 
+	protoFilenamePath := android.PathForOutput(ctx, protoFilename)
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   bloatyMerger,
 		Inputs: android.SortedUniquePaths(deps),
-		Output: android.PathForOutput(ctx, protoFilename),
+		Output: protoFilenamePath,
 	})
-}
-
-func (singleton *sizesSingleton) MakeVars(ctx android.MakeVarsContext) {
-	ctx.DistForGoalWithFilename("checkbuild", android.PathForOutput(ctx, protoFilename), protoFilename)
+	ctx.DistForGoalWithFilename("checkbuild", protoFilenamePath, protoFilename)
 }
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 3b7073e..deb465d 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -60,6 +60,12 @@
 	ctx.RegisterModuleType("bpf", BpfFactory)
 }
 
+type BpfInfo struct {
+	SubDir string
+}
+
+var BpfInfoProvider = blueprint.NewProvider[BpfInfo]()
+
 var PrepareForTestWithBpf = android.FixtureRegisterWithContext(registerBpfBuildComponents)
 
 // BpfModule interface is used by the apex package to gather information from a bpf module.
@@ -230,7 +236,9 @@
 		ctx.PackageFile(installDir, obj.Base(), obj)
 	}
 
-	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()})
+	android.SetProvider(ctx, BpfInfoProvider, BpfInfo{
+		SubDir: bpf.SubDir(),
+	})
 
 	ctx.SetOutputFiles(bpf.objs, "")
 }
diff --git a/bpf/libbpf/libbpf_prog.go b/bpf/libbpf/libbpf_prog.go
index 3b26d46..44013e5 100644
--- a/bpf/libbpf/libbpf_prog.go
+++ b/bpf/libbpf/libbpf_prog.go
@@ -239,8 +239,6 @@
 		ctx.PackageFile(installDir, obj.Base(), obj)
 	}
 
-	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()})
-
 	ctx.SetOutputFiles(libbpf.objs, "")
 }
 
diff --git a/cc/Android.bp b/cc/Android.bp
index 3b29ae8..1ac5a4a 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -36,6 +36,7 @@
         "linkable.go",
         "lto.go",
         "makevars.go",
+        "misc_disted_files.go",
         "orderfile.go",
         "prebuilt.go",
         "proto.go",
diff --git a/cc/afdo.go b/cc/afdo.go
index 14d105e..9be3918 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -65,8 +65,8 @@
 }
 
 func getFdoProfilePathFromDep(ctx ModuleContext) string {
-	fdoProfileDeps := ctx.GetDirectDepsWithTag(FdoProfileTag)
-	if len(fdoProfileDeps) > 0 && fdoProfileDeps[0] != nil {
+	fdoProfileDeps := ctx.GetDirectDepsProxyWithTag(FdoProfileTag)
+	if len(fdoProfileDeps) > 0 {
 		if info, ok := android.OtherModuleProvider(ctx, fdoProfileDeps[0], FdoProfileProvider); ok {
 			return info.Path.String()
 		}
@@ -96,13 +96,20 @@
 		flags.Local.CFlags = append([]string{"-funique-internal-linkage-names"}, flags.Local.CFlags...)
 		// Flags for Flow Sensitive AutoFDO
 		flags.Local.CFlags = append([]string{"-mllvm", "-enable-fs-discriminator=true"}, flags.Local.CFlags...)
+		flags.Local.LdFlags = append([]string{"-Wl,-mllvm,-enable-fs-discriminator=true"}, flags.Local.LdFlags...)
 		// TODO(b/266595187): Remove the following feature once it is enabled in LLVM by default.
 		flags.Local.CFlags = append([]string{"-mllvm", "-improved-fs-discriminator=true"}, flags.Local.CFlags...)
+		flags.Local.LdFlags = append([]string{"-Wl,-mllvm,-improved-fs-discriminator=true"}, flags.Local.LdFlags...)
 	}
 	if fdoProfilePath := getFdoProfilePathFromDep(ctx); fdoProfilePath != "" {
 		// The flags are prepended to allow overriding.
 		profileUseFlag := fmt.Sprintf(afdoFlagsFormat, fdoProfilePath)
 		flags.Local.CFlags = append([]string{profileUseFlag}, flags.Local.CFlags...)
+		// Salvage stale profile by fuzzy matching and use the remapped location for sample profile query.
+		flags.Local.CFlags = append([]string{"-mllvm", "--salvage-stale-profile=true"}, flags.Local.CFlags...)
+		flags.Local.CFlags = append([]string{"-mllvm", "--salvage-stale-profile-max-callsites=2000"}, flags.Local.CFlags...)
+		// Salvage stale profile by fuzzy matching renamed functions.
+		flags.Local.CFlags = append([]string{"-mllvm", "--salvage-unused-profile=true"}, flags.Local.CFlags...)
 		flags.Local.LdFlags = append([]string{profileUseFlag, "-Wl,-mllvm,-no-warn-sample-unused=true"}, flags.Local.LdFlags...)
 
 		// Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
@@ -163,7 +170,7 @@
 		}
 
 		// TODO(b/324141705): this is designed to prevent propagating AFDO from static libraries that have afdo: true set, but
-		//  it should be m.static() && !m.staticBinary() so that static binaries use AFDO variants of dependencies.
+		//  it should be m.staticLibrary() so that static binaries use AFDO variants of dependencies.
 		if m.static() {
 			return ""
 		}
@@ -188,7 +195,7 @@
 		if variation == "" {
 			// The empty variation is either a module that has enabled AFDO for itself, or the non-AFDO
 			// variant of a dependency.
-			if m.afdo.afdoEnabled() && !(m.static() && !m.staticBinary()) && !m.Host() {
+			if m.afdo.afdoEnabled() && !m.staticLibrary() && !m.Host() {
 				m.afdo.addDep(ctx, ctx.ModuleName())
 			}
 		} else {
diff --git a/cc/afdo_test.go b/cc/afdo_test.go
index 0679d13..d2d5584 100644
--- a/cc/afdo_test.go
+++ b/cc/afdo_test.go
@@ -94,9 +94,9 @@
 	afdoLtoLdFlag := "-Wl,-plugin-opt,-import-instr-limit=40"
 	noAfdoLtoLdFlag := "-Wl,-plugin-opt,-import-instr-limit=5"
 
-	libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared")
-	libFooAfdoVariant := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest")
-	libBarAfdoVariant := result.ModuleForTests("libBar", "android_arm64_armv8-a_static_afdo-libTest")
+	libTest := result.ModuleForTests(t, "libTest", "android_arm64_armv8-a_shared")
+	libFooAfdoVariant := result.ModuleForTests(t, "libFoo", "android_arm64_armv8-a_static_afdo-libTest")
+	libBarAfdoVariant := result.ModuleForTests(t, "libBar", "android_arm64_armv8-a_static_afdo-libTest")
 
 	// Check cFlags of afdo-enabled module and the afdo-variant of its static deps
 	cFlags := libTest.Rule("cc").Args["cFlags"]
@@ -138,8 +138,8 @@
 	}
 
 	// Verify non-afdo variant exists and doesn't contain afdo
-	libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static")
-	libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static")
+	libFoo := result.ModuleForTests(t, "libFoo", "android_arm64_armv8-a_static")
+	libBar := result.ModuleForTests(t, "libBar", "android_arm64_armv8-a_static")
 
 	cFlags = libFoo.Rule("cc").Args["cFlags"]
 	if strings.Contains(cFlags, profileSampleCFlag) {
@@ -166,9 +166,9 @@
 	}
 
 	// Verify that the arm variant does not have FDO since the fdo_profile module only has a profile for arm64
-	libTest32 := result.ModuleForTests("libTest", "android_arm_armv7-a-neon_shared")
-	libFooAfdoVariant32 := result.ModuleForTests("libFoo", "android_arm_armv7-a-neon_static_afdo-libTest_lto-thin")
-	libBarAfdoVariant32 := result.ModuleForTests("libBar", "android_arm_armv7-a-neon_static_afdo-libTest_lto-thin")
+	libTest32 := result.ModuleForTests(t, "libTest", "android_arm_armv7-a-neon_shared")
+	libFooAfdoVariant32 := result.ModuleForTests(t, "libFoo", "android_arm_armv7-a-neon_static_afdo-libTest_lto-thin")
+	libBarAfdoVariant32 := result.ModuleForTests(t, "libBar", "android_arm_armv7-a-neon_static_afdo-libTest_lto-thin")
 
 	cFlags = libTest32.Rule("cc").Args["cFlags"]
 	if strings.Contains(cFlags, profileSampleCFlag) {
@@ -215,9 +215,9 @@
 	}
 
 	// Verify that the host variants don't enable afdo
-	libTestHost := result.ModuleForTests("libTest", result.Config.BuildOSTarget.String()+"_shared")
-	libFooHost := result.ModuleForTests("libFoo", result.Config.BuildOSTarget.String()+"_static_lto-thin")
-	libBarHost := result.ModuleForTests("libBar", result.Config.BuildOSTarget.String()+"_static_lto-thin")
+	libTestHost := result.ModuleForTests(t, "libTest", result.Config.BuildOSTarget.String()+"_shared")
+	libFooHost := result.ModuleForTests(t, "libFoo", result.Config.BuildOSTarget.String()+"_static_lto-thin")
+	libBarHost := result.ModuleForTests(t, "libBar", result.Config.BuildOSTarget.String()+"_static_lto-thin")
 
 	cFlags = libTestHost.Rule("cc").Args["cFlags"]
 	if strings.Contains(cFlags, profileSampleCFlag) {
@@ -301,9 +301,9 @@
 		}.AddToFixture(),
 	).RunTestWithBp(t, bp)
 
-	libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared").Module()
-	libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static")
-	libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static").Module()
+	libTest := result.ModuleForTests(t, "libTest", "android_arm64_armv8-a_shared").Module()
+	libFoo := result.ModuleForTests(t, "libFoo", "android_arm64_armv8-a_static")
+	libBar := result.ModuleForTests(t, "libBar", "android_arm64_armv8-a_static").Module()
 
 	if !hasDirectDep(result, libTest, libFoo.Module()) {
 		t.Errorf("libTest missing dependency on non-afdo variant of libFoo")
@@ -412,13 +412,13 @@
 		}.AddToFixture(),
 	).RunTestWithBp(t, bp)
 
-	fooArm := result.ModuleForTests("foo", "android_arm_armv7-a-neon_shared")
+	fooArm := result.ModuleForTests(t, "foo", "android_arm_armv7-a-neon_shared")
 	fooArmCFlags := fooArm.Rule("cc").Args["cFlags"]
 	if w := "-fprofile-sample-use=afdo_profiles_package/foo_arm.afdo"; !strings.Contains(fooArmCFlags, w) {
 		t.Errorf("Expected 'foo' to enable afdo, but did not find %q in cflags %q", w, fooArmCFlags)
 	}
 
-	fooArm64 := result.ModuleForTests("foo", "android_arm64_armv8-a_shared")
+	fooArm64 := result.ModuleForTests(t, "foo", "android_arm64_armv8-a_shared")
 	fooArm64CFlags := fooArm64.Rule("cc").Args["cFlags"]
 	if w := "-fprofile-sample-use=afdo_profiles_package/foo_arm64.afdo"; !strings.Contains(fooArm64CFlags, w) {
 		t.Errorf("Expected 'foo' to enable afdo, but did not find %q in cflags %q", w, fooArm64CFlags)
@@ -476,11 +476,11 @@
 	expectedCFlagLibTest := "-fprofile-sample-use=afdo_profiles_package/libTest.afdo"
 	expectedCFlagLibBar := "-fprofile-sample-use=afdo_profiles_package/libBar.afdo"
 
-	libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared")
-	libFooAfdoVariantWithLibTest := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest")
+	libTest := result.ModuleForTests(t, "libTest", "android_arm64_armv8-a_shared")
+	libFooAfdoVariantWithLibTest := result.ModuleForTests(t, "libFoo", "android_arm64_armv8-a_static_afdo-libTest")
 
-	libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_shared")
-	libFooAfdoVariantWithLibBar := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libBar")
+	libBar := result.ModuleForTests(t, "libBar", "android_arm64_armv8-a_shared")
+	libFooAfdoVariantWithLibBar := result.ModuleForTests(t, "libFoo", "android_arm64_armv8-a_static_afdo-libBar")
 
 	// Check cFlags of afdo-enabled module and the afdo-variant of its static deps
 	cFlags := libTest.Rule("cc").Args["cFlags"]
@@ -543,9 +543,9 @@
 	// -funique-internal-linkage-names.
 	expectedCFlag := "-funique-internal-linkage-names"
 
-	libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared")
-	libFooAfdoVariant := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest")
-	libBarAfdoVariant := result.ModuleForTests("libBar", "android_arm64_armv8-a_static_afdo-libTest")
+	libTest := result.ModuleForTests(t, "libTest", "android_arm64_armv8-a_shared")
+	libFooAfdoVariant := result.ModuleForTests(t, "libFoo", "android_arm64_armv8-a_static_afdo-libTest")
+	libBarAfdoVariant := result.ModuleForTests(t, "libBar", "android_arm64_armv8-a_static_afdo-libTest")
 
 	// Check cFlags of afdo-enabled module and the afdo-variant of its static deps
 	cFlags := libTest.Rule("cc").Args["cFlags"]
@@ -572,8 +572,8 @@
 	}
 
 	// Verify non-afdo variant exists and doesn't contain afdo
-	libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static")
-	libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static")
+	libFoo := result.ModuleForTests(t, "libFoo", "android_arm64_armv8-a_static")
+	libBar := result.ModuleForTests(t, "libBar", "android_arm64_armv8-a_static")
 
 	cFlags = libFoo.Rule("cc").Args["cFlags"]
 	if strings.Contains(cFlags, expectedCFlag) {
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 8037272..b016788 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -231,7 +231,7 @@
 	} else if library.shared() {
 		entries.Class = "SHARED_LIBRARIES"
 		entries.SetString("LOCAL_SOONG_TOC", library.toc().String())
-		if !library.buildStubs() && library.unstrippedOutputFile != nil {
+		if !library.BuildStubs() && library.unstrippedOutputFile != nil {
 			entries.SetString("LOCAL_SOONG_UNSTRIPPED_BINARY", library.unstrippedOutputFile.String())
 		}
 		if len(library.Properties.Overrides) > 0 {
@@ -244,10 +244,6 @@
 		entries.Class = "HEADER_LIBRARIES"
 	}
 
-	if library.distFile != nil {
-		entries.DistFiles = android.MakeDefaultDistFiles(library.distFile)
-	}
-
 	library.androidMkWriteExportedFlags(entries)
 	library.androidMkEntriesWriteAdditionalDependenciesForSourceAbiDiff(entries)
 
@@ -260,16 +256,16 @@
 		entries.SetString("LOCAL_PREBUILT_COVERAGE_ARCHIVE", library.coverageOutputFile.String())
 	}
 
-	if library.shared() && !library.buildStubs() {
+	if library.shared() && !library.BuildStubs() {
 		ctx.subAndroidMk(config, entries, library.baseInstaller)
 	} else {
-		if library.buildStubs() && library.stubsVersion() != "" {
-			entries.SubName = "." + library.stubsVersion()
+		if library.BuildStubs() && library.StubsVersion() != "" {
+			entries.SubName = "." + library.StubsVersion()
 		}
 		// library.makeUninstallable() depends on this to bypass HideFromMake() for
 		// static libraries.
 		entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-		if library.buildStubs() {
+		if library.BuildStubs() {
 			entries.SetBool("LOCAL_NO_NOTICE_FILE", true)
 		}
 	}
@@ -281,10 +277,10 @@
 	// very early stage in the boot process).
 	if len(library.Properties.Stubs.Versions) > 0 && !ctx.Host() && ctx.NotInPlatform() &&
 		!ctx.InRamdisk() && !ctx.InVendorRamdisk() && !ctx.InRecovery() && !ctx.InVendorOrProduct() && !ctx.static() {
-		if library.buildStubs() && library.isLatestStubVersion() {
+		if library.BuildStubs() && library.isLatestStubVersion() {
 			entries.SubName = ""
 		}
-		if !library.buildStubs() {
+		if !library.BuildStubs() {
 			entries.SubName = ".bootstrap"
 		}
 	}
@@ -336,7 +332,6 @@
 	ctx.subAndroidMk(config, entries, binary.baseInstaller)
 
 	entries.Class = "EXECUTABLES"
-	entries.DistFiles = binary.distFiles
 	entries.SetString("LOCAL_SOONG_UNSTRIPPED_BINARY", binary.unstrippedOutputFile.String())
 	if len(binary.symlinks) > 0 {
 		entries.AddStrings("LOCAL_MODULE_SYMLINKS", binary.symlinks...)
@@ -423,7 +418,7 @@
 	entries.SubName = ndkLibrarySuffix + "." + c.apiLevel.String()
 	entries.Class = "SHARED_LIBRARIES"
 
-	if !c.buildStubs() {
+	if !c.BuildStubs() {
 		entries.Disabled = true
 		return
 	}
diff --git a/cc/api_level.go b/cc/api_level.go
index 7dc0213..fa8dfaf 100644
--- a/cc/api_level.go
+++ b/cc/api_level.go
@@ -51,7 +51,7 @@
 	return apiLevel
 }
 
-func nativeApiLevelFromUser(ctx android.BaseModuleContext,
+func NativeApiLevelFromUser(ctx android.BaseModuleContext,
 	raw string) (android.ApiLevel, error) {
 
 	if raw == "minimum" {
@@ -69,7 +69,7 @@
 func nativeApiLevelOrPanic(ctx android.BaseModuleContext,
 	raw string) android.ApiLevel {
 
-	value, err := nativeApiLevelFromUser(ctx, raw)
+	value, err := NativeApiLevelFromUser(ctx, raw)
 	if err != nil {
 		panic(err.Error())
 	}
diff --git a/cc/binary.go b/cc/binary.go
index 4b77bea..608251a 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -104,8 +104,8 @@
 	// Output archive of gcno coverage information
 	coverageOutputFile android.OptionalPath
 
-	// Location of the files that should be copied to dist dir when requested
-	distFiles android.TaggedDistFiles
+	// Location of the file that should be copied to dist dir when no explicit tag is requested
+	defaultDistFile android.Path
 
 	// Action command lines to run directly after the binary is installed. For example,
 	// may be used to symlink runtime dependencies (such as bionic) alongside installation.
@@ -385,11 +385,11 @@
 			// When dist'ing a library or binary that has use_version_lib set, always
 			// distribute the stamped version, even for the device.
 			versionedOutputFile := android.PathForModuleOut(ctx, "versioned", fileName)
-			binary.distFiles = android.MakeDefaultDistFiles(versionedOutputFile)
+			binary.defaultDistFile = versionedOutputFile
 
 			if binary.stripper.NeedsStrip(ctx) {
 				out := android.PathForModuleOut(ctx, "versioned-stripped", fileName)
-				binary.distFiles = android.MakeDefaultDistFiles(out)
+				binary.defaultDistFile = out
 				binary.stripper.StripExecutableOrSharedLib(ctx, versionedOutputFile, out, stripFlags)
 			}
 
@@ -426,7 +426,7 @@
 	validations = append(validations, objs.tidyDepFiles...)
 	linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
 
-	if generatedLib := generateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil {
+	if generatedLib := GenerateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil {
 		deps.StaticLibs = append(deps.StaticLibs, generatedLib)
 	}
 
@@ -551,6 +551,10 @@
 	binary.baseLinker.moduleInfoJSON(ctx, moduleInfoJSON)
 }
 
+func (binary *binaryDecorator) testSuiteInfo(ctx ModuleContext) {
+	// not a test
+}
+
 var _ overridable = (*binaryDecorator)(nil)
 
 func init() {
@@ -575,3 +579,10 @@
 		},
 	})
 }
+
+func (binary *binaryDecorator) defaultDistFiles() []android.Path {
+	if binary.defaultDistFile == nil {
+		return nil
+	}
+	return []android.Path{binary.defaultDistFile}
+}
diff --git a/cc/binary_test.go b/cc/binary_test.go
index 3e18940..4f001d7 100644
--- a/cc/binary_test.go
+++ b/cc/binary_test.go
@@ -29,7 +29,7 @@
 			linker_scripts: ["foo.ld", "bar.ld"],
 		}`)
 
-	binFoo := result.ModuleForTests("foo", "android_arm64_armv8-a").Rule("ld")
+	binFoo := result.ModuleForTests(t, "foo", "android_arm64_armv8-a").Rule("ld")
 
 	android.AssertStringListContains(t, "missing dependency on linker_scripts",
 		binFoo.Implicits.Strings(), "foo.ld")
diff --git a/cc/builder.go b/cc/builder.go
index b98bef9..f4f8596 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -472,15 +472,25 @@
 }
 
 // Generate rules for compiling multiple .c, .cpp, or .S files to individual .o files
-func transformSourceToObj(ctx ModuleContext, subdir string, srcFiles, noTidySrcs, timeoutTidySrcs android.Paths,
-	flags builderFlags, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
+func transformSourceToObj(ctx android.ModuleContext, subdir string, srcFiles, noTidySrcs, timeoutTidySrcs android.Paths,
+	flags builderFlags, pathDeps android.Paths, cFlagsDeps android.Paths, sharedFlags *SharedFlags) Objects {
+
+	// Not all source files produce a .o; a Rust source provider
+	// may provide both a .c and a .rs file (e.g. rust_bindgen).
+	srcObjFiles := android.Paths{}
+	for _, src := range srcFiles {
+		if src.Ext() != ".rs" {
+			srcObjFiles = append(srcObjFiles, src)
+		}
+	}
+
 	// Source files are one-to-one with tidy, coverage, or kythe files, if enabled.
-	objFiles := make(android.Paths, len(srcFiles))
+	objFiles := make(android.Paths, len(srcObjFiles))
 	var tidyFiles android.Paths
 	noTidySrcsMap := make(map[string]bool)
 	var tidyVars string
 	if flags.tidy {
-		tidyFiles = make(android.Paths, 0, len(srcFiles))
+		tidyFiles = make(android.Paths, 0, len(srcObjFiles))
 		for _, path := range noTidySrcs {
 			noTidySrcsMap[path.String()] = true
 		}
@@ -495,11 +505,11 @@
 	}
 	var coverageFiles android.Paths
 	if flags.gcovCoverage {
-		coverageFiles = make(android.Paths, 0, len(srcFiles))
+		coverageFiles = make(android.Paths, 0, len(srcObjFiles))
 	}
 	var kytheFiles android.Paths
 	if flags.emitXrefs && ctx.Module() == ctx.PrimaryModule() {
-		kytheFiles = make(android.Paths, 0, len(srcFiles))
+		kytheFiles = make(android.Paths, 0, len(srcObjFiles))
 	}
 
 	// Produce fully expanded flags for use by C tools, C compiles, C++ tools, C++ compiles, and asm compiles
@@ -548,16 +558,14 @@
 
 	var sAbiDumpFiles android.Paths
 	if flags.sAbiDump {
-		sAbiDumpFiles = make(android.Paths, 0, len(srcFiles))
+		sAbiDumpFiles = make(android.Paths, 0, len(srcObjFiles))
 	}
 
 	// Multiple source files have build rules usually share the same cFlags or tidyFlags.
-	// Define only one version in this module and share it in multiple build rules.
-	// To simplify the code, the shared variables are all named as $flags<nnn>.
-	shared := ctx.getSharedFlags()
-
+	// SharedFlags provides one version for this module and shares it in multiple build rules.
+	// To simplify the code, the SharedFlags variables are all named as $flags<nnn>.
 	// Share flags only when there are multiple files or tidy rules.
-	var hasMultipleRules = len(srcFiles) > 1 || flags.tidy
+	var hasMultipleRules = len(srcObjFiles) > 1 || flags.tidy
 
 	var shareFlags = func(kind string, flags string) string {
 		if !hasMultipleRules || len(flags) < 60 {
@@ -566,17 +574,17 @@
 			return flags
 		}
 		mapKey := kind + flags
-		n, ok := shared.flagsMap[mapKey]
+		n, ok := sharedFlags.FlagsMap[mapKey]
 		if !ok {
-			shared.numSharedFlags += 1
-			n = strconv.Itoa(shared.numSharedFlags)
-			shared.flagsMap[mapKey] = n
+			sharedFlags.NumSharedFlags += 1
+			n = strconv.Itoa(sharedFlags.NumSharedFlags)
+			sharedFlags.FlagsMap[mapKey] = n
 			ctx.Variable(pctx, kind+n, flags)
 		}
 		return "$" + kind + n
 	}
 
-	for i, srcFile := range srcFiles {
+	for i, srcFile := range srcObjFiles {
 		objFile := android.ObjPathWithExt(ctx, subdir, srcFile, "o")
 
 		objFiles[i] = objFile
@@ -809,7 +817,7 @@
 }
 
 // Generate a Rust staticlib from a list of rlibDeps. Returns nil if TransformRlibstoStaticlib is nil or rlibDeps is empty.
-func generateRustStaticlib(ctx android.ModuleContext, rlibDeps []RustRlibDep) android.Path {
+func GenerateRustStaticlib(ctx android.ModuleContext, rlibDeps []RustRlibDep) android.Path {
 	if TransformRlibstoStaticlib == nil && len(rlibDeps) > 0 {
 		// This should only be reachable if a module defines Rust deps in static_libs and
 		// soong-rust hasn't been loaded alongside soong-cc (e.g. in soong-cc tests).
@@ -853,6 +861,27 @@
 	return strings.Join(lines, "\n")
 }
 
+func BuildRustStubs(ctx android.ModuleContext, outputFile android.ModuleOutPath,
+	stubObjs Objects, ccFlags Flags) {
+
+	// Instantiate paths
+	sharedLibs := android.Paths{}
+	staticLibs := android.Paths{}
+	lateStaticLibs := android.Paths{}
+	wholeStaticLibs := android.Paths{}
+	deps := android.Paths{}
+	implicitOutputs := android.WritablePaths{}
+	validations := android.Paths{}
+	crtBegin := android.Paths{}
+	crtEnd := android.Paths{}
+	groupLate := false
+
+	builderFlags := flagsToBuilderFlags(ccFlags)
+	transformObjToDynamicBinary(ctx, stubObjs.objFiles, sharedLibs, staticLibs,
+		lateStaticLibs, wholeStaticLibs, deps, crtBegin, crtEnd,
+		groupLate, builderFlags, outputFile, implicitOutputs, validations)
+}
+
 // Generate a rule for compiling multiple .o files, plus static libraries, whole static libraries,
 // and shared libraries, to a shared library (.so) or dynamic executable
 func transformObjToDynamicBinary(ctx android.ModuleContext,
@@ -945,13 +974,18 @@
 func transformDumpToLinkedDump(ctx android.ModuleContext, sAbiDumps android.Paths, soFile android.Path,
 	baseName string, exportedIncludeDirs []string, symbolFile android.OptionalPath,
 	excludedSymbolVersions, excludedSymbolTags, includedSymbolTags []string,
-	api string) android.Path {
+	api string, commonGlobalIncludes bool) android.Path {
 
 	outputFile := android.PathForModuleOut(ctx, baseName+".lsdump")
 
 	implicits := android.Paths{soFile}
 	symbolFilterStr := "-so " + soFile.String()
 	exportedHeaderFlags := android.JoinWithPrefix(exportedIncludeDirs, "-I")
+	// If this library does not export any include directory, do not append the flags
+	// so that the ABI tool dumps everything without filtering by the include directories.
+	if commonGlobalIncludes && len(exportedIncludeDirs) > 0 {
+		exportedHeaderFlags += " ${config.CommonGlobalIncludes}"
+	}
 
 	if symbolFile.Valid() {
 		implicits = append(implicits, symbolFile.Path())
diff --git a/cc/cc.go b/cc/cc.go
index 6b4e0dc..3e0444d 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -34,7 +34,6 @@
 	"android/soong/android"
 	"android/soong/cc/config"
 	"android/soong/fuzz"
-	"android/soong/genrule"
 )
 
 type CcMakeVarsInfo struct {
@@ -46,26 +45,178 @@
 var CcMakeVarsInfoProvider = blueprint.NewProvider[*CcMakeVarsInfo]()
 
 type CcObjectInfo struct {
-	objFiles   android.Paths
-	tidyFiles  android.Paths
-	kytheFiles android.Paths
+	ObjFiles   android.Paths
+	TidyFiles  android.Paths
+	KytheFiles android.Paths
 }
 
 var CcObjectInfoProvider = blueprint.NewProvider[CcObjectInfo]()
 
-// Common info about the cc module.
-type CcInfo struct {
-	HasStubsVariants bool
+type AidlInterfaceInfo struct {
+	// list of aidl_interface sources
+	Sources []string
+	// root directory of AIDL sources
+	AidlRoot string
+	// AIDL backend language (e.g. "cpp", "ndk")
+	Lang string
+	// list of flags passed to AIDL generator
+	Flags []string
 }
 
-var CcInfoProvider = blueprint.NewProvider[CcInfo]()
+type CompilerInfo struct {
+	Srcs android.Paths
+	// list of module-specific flags that will be used for C and C++ compiles.
+	Cflags               []string
+	AidlInterfaceInfo    AidlInterfaceInfo
+	LibraryDecoratorInfo *LibraryDecoratorInfo
+}
+
+type LinkerInfo struct {
+	WholeStaticLibs []string
+	// list of modules that should be statically linked into this module.
+	StaticLibs []string
+	// list of modules that should be dynamically linked into this module.
+	SharedLibs []string
+	// list of modules that should only provide headers for this module.
+	HeaderLibs               []string
+	ImplementationModuleName *string
+
+	BinaryDecoratorInfo       *BinaryDecoratorInfo
+	LibraryDecoratorInfo      *LibraryDecoratorInfo
+	TestBinaryInfo            *TestBinaryInfo
+	BenchmarkDecoratorInfo    *BenchmarkDecoratorInfo
+	ObjectLinkerInfo          *ObjectLinkerInfo
+	StubDecoratorInfo         *StubDecoratorInfo
+	PrebuiltLibraryLinkerInfo *PrebuiltLibraryLinkerInfo
+}
+
+type BinaryDecoratorInfo struct{}
+type LibraryDecoratorInfo struct {
+	ExportIncludeDirs []string
+	InjectBsslHash    bool
+	// Location of the static library in the sysroot. Empty if the library is
+	// not included in the NDK.
+	NdkSysrootPath android.Path
+	VndkFileName   string
+}
+
+type SnapshotInfo struct {
+	SnapshotAndroidMkSuffix string
+}
+
+type TestBinaryInfo struct {
+	Gtest bool
+}
+type BenchmarkDecoratorInfo struct{}
+
+type StubDecoratorInfo struct {
+	AbiDumpPath  android.OutputPath
+	HasAbiDump   bool
+	AbiDiffPaths android.Paths
+	InstallPath  android.Path
+}
+
+type ObjectLinkerInfo struct {
+	// Location of the object in the sysroot. Empty if the object is not
+	// included in the NDK.
+	NdkSysrootPath android.Path
+}
+
+type PrebuiltLibraryLinkerInfo struct {
+	VndkFileName string
+}
+
+type LibraryInfo struct {
+	BuildStubs bool
+}
+
+type InstallerInfo struct {
+	StubDecoratorInfo *StubDecoratorInfo
+}
+
+type LocalOrGlobalFlagsInfo struct {
+	CommonFlags []string // Flags that apply to C, C++, and assembly source files
+	CFlags      []string // Flags that apply to C and C++ source files
+	ConlyFlags  []string // Flags that apply to C source files
+	CppFlags    []string // Flags that apply to C++ source files
+}
+
+// Common info about the cc module.
+type CcInfo struct {
+	IsPrebuilt             bool
+	CmakeSnapshotSupported bool
+	HasLlndkStubs          bool
+	DataPaths              []android.DataPath
+	CompilerInfo           *CompilerInfo
+	LinkerInfo             *LinkerInfo
+	SnapshotInfo           *SnapshotInfo
+	LibraryInfo            *LibraryInfo
+	InstallerInfo          *InstallerInfo
+}
+
+var CcInfoProvider = blueprint.NewProvider[*CcInfo]()
 
 type LinkableInfo struct {
 	// StaticExecutable returns true if this is a binary module with "static_executable: true".
-	StaticExecutable bool
+	StaticExecutable     bool
+	Static               bool
+	Shared               bool
+	Header               bool
+	HasStubsVariants     bool
+	StubsVersion         string
+	IsStubs              bool
+	UnstrippedOutputFile android.Path
+	OutputFile           android.OptionalPath
+	CoverageFiles        android.Paths
+	// CoverageOutputFile returns the output archive of gcno coverage information files.
+	CoverageOutputFile android.OptionalPath
+	SAbiDumpFiles      android.Paths
+	// Partition returns the partition string for this module.
+	Partition            string
+	CcLibrary            bool
+	CcLibraryInterface   bool
+	RustLibraryInterface bool
+	// CrateName returns the crateName for a Rust library
+	CrateName string
+	// DepFlags returns a slice of Rustc string flags
+	ExportedCrateLinkDirs []string
+	HasNonSystemVariants  bool
+	IsLlndk               bool
+	// True if the library is in the configs known NDK list.
+	IsNdk             bool
+	InVendorOrProduct bool
+	// SubName returns the modules SubName, used for image and NDK/SDK variations.
+	SubName             string
+	InRamdisk           bool
+	OnlyInRamdisk       bool
+	InVendorRamdisk     bool
+	OnlyInVendorRamdisk bool
+	InRecovery          bool
+	OnlyInRecovery      bool
+	InVendor            bool
+	Installable         *bool
+	// RelativeInstallPath returns the relative install path for this module.
+	RelativeInstallPath string
+	// TODO(b/362509506): remove this once all apex_exclude uses are switched to stubs.
+	RustApexExclude bool
+	// Bootstrap tests if this module is allowed to use non-APEX version of libraries.
+	Bootstrap                       bool
+	Multilib                        string
+	ImplementationModuleNameForMake string
+	IsStubsImplementationRequired   bool
+	// Symlinks returns a list of symlinks that should be created for this module.
+	Symlinks               []string
+	APIListCoverageXMLPath android.ModuleOutPath
+	// FuzzSharedLibraries returns the shared library dependencies for this module.
+	// Expects that IsFuzzModule returns true.
+	FuzzSharedLibraries      android.RuleBuilderInstalls
+	IsVndkPrebuiltLibrary    bool
+	HasLLNDKStubs            bool
+	IsLLNDKMovedToApex       bool
+	ImplementationModuleName string
 }
 
-var LinkableInfoKey = blueprint.NewProvider[LinkableInfo]()
+var LinkableInfoProvider = blueprint.NewProvider[*LinkableInfo]()
 
 func init() {
 	RegisterCCBuildComponents(android.InitRegistrationContext)
@@ -519,13 +670,13 @@
 type ModuleContextIntf interface {
 	static() bool
 	staticBinary() bool
+	staticLibrary() bool
 	testBinary() bool
 	testLibrary() bool
 	header() bool
 	binary() bool
 	object() bool
 	toolchain() config.Toolchain
-	canUseSdk() bool
 	useSdk() bool
 	sdkVersion() string
 	minSdkVersion() string
@@ -549,9 +700,7 @@
 	isFuzzer() bool
 	isNDKStubLibrary() bool
 	useClangLld(actx ModuleContext) bool
-	isForPlatform() bool
 	apexVariationName() string
-	apexSdkVersion() android.ApiLevel
 	bootstrap() bool
 	nativeCoverage() bool
 	isPreventInstall() bool
@@ -563,8 +712,8 @@
 }
 
 type SharedFlags struct {
-	numSharedFlags int
-	flagsMap       map[string]string
+	NumSharedFlags int
+	FlagsMap       map[string]string
 }
 
 type ModuleContext interface {
@@ -643,7 +792,13 @@
 	// Get the deps that have been explicitly specified in the properties.
 	linkerSpecifiedDeps(ctx android.ConfigurableEvaluatorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps
 
+	// Gets a list of files that will be disted when using the dist property without specifying
+	// an output file tag.
+	defaultDistFiles() []android.Path
+
 	moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON)
+
+	testSuiteInfo(ctx ModuleContext)
 }
 
 // specifiedDeps is a tuple struct representing dependencies of a linked binary owned by the linker.
@@ -734,6 +889,7 @@
 
 	reexportFlags       bool
 	explicitlyVersioned bool
+	explicitlyImpl      bool
 	dataLib             bool
 	ndk                 bool
 
@@ -822,13 +978,18 @@
 	dataLibDepTag         = dependencyTag{name: "data lib"}
 	dataBinDepTag         = dependencyTag{name: "data bin"}
 	runtimeDepTag         = installDependencyTag{name: "runtime lib"}
-	stubImplDepTag        = dependencyTag{name: "stub_impl"}
+	StubImplDepTag        = dependencyTag{name: "stub_impl"}
 	JniFuzzLibTag         = dependencyTag{name: "jni_fuzz_lib_tag"}
 	FdoProfileTag         = dependencyTag{name: "fdo_profile"}
 	aidlLibraryTag        = dependencyTag{name: "aidl_library"}
 	llndkHeaderLibTag     = dependencyTag{name: "llndk_header_lib"}
 )
 
+func IsExplicitImplSharedDepTag(depTag blueprint.DependencyTag) bool {
+	ccLibDepTag, ok := depTag.(libraryDependencyTag)
+	return ok && ccLibDepTag.shared() && ccLibDepTag.explicitlyImpl
+}
+
 func IsSharedDepTag(depTag blueprint.DependencyTag) bool {
 	ccLibDepTag, ok := depTag.(libraryDependencyTag)
 	return ok && ccLibDepTag.shared()
@@ -848,6 +1009,11 @@
 	return depTag == runtimeDepTag
 }
 
+func ExcludeInApexDepTag(depTag blueprint.DependencyTag) bool {
+	ccLibDepTag, ok := depTag.(libraryDependencyTag)
+	return ok && ccLibDepTag.excludeInApex
+}
+
 // Module contains the properties and members used by all C/C++ module types, and implements
 // the blueprint.Module interface.  It delegates to compiler, linker, and installer interfaces
 // to construct the output file.  Behavior can be customized with a Customizer, or "decorator",
@@ -1071,7 +1237,21 @@
 	return String(c.Properties.Min_sdk_version)
 }
 
-func (c *Module) isCrt() bool {
+func (c *Module) SetSdkVersion(s string) {
+	c.Properties.Sdk_version = StringPtr(s)
+}
+
+func (c *Module) SetMinSdkVersion(s string) {
+	c.Properties.Min_sdk_version = StringPtr(s)
+}
+
+func (c *Module) SetStl(s string) {
+	if c.stl != nil {
+		c.stl.Properties.Stl = StringPtr(s)
+	}
+}
+
+func (c *Module) IsCrt() bool {
 	if linker, ok := c.linker.(*objectLinker); ok {
 		return linker.isCrt()
 	}
@@ -1079,7 +1259,7 @@
 }
 
 func (c *Module) SplitPerApiLevel() bool {
-	return c.canUseSdk() && c.isCrt()
+	return CanUseSdk(c) && c.IsCrt()
 }
 
 func (c *Module) AlwaysSdk() bool {
@@ -1099,7 +1279,7 @@
 }
 
 func (c *Module) CcLibraryInterface() bool {
-	if _, ok := c.linker.(libraryInterface); ok {
+	if c.library != nil {
 		return true
 	}
 	return false
@@ -1193,11 +1373,6 @@
 	return false
 }
 
-func (c *Module) IsRustFFI() bool {
-	// cc modules are not Rust modules
-	return false
-}
-
 func (c *Module) Module() android.Module {
 	return c
 }
@@ -1217,6 +1392,13 @@
 
 var _ LinkableInterface = (*Module)(nil)
 
+func (c *Module) VersionedInterface() VersionedInterface {
+	if c.library != nil {
+		return c.library
+	}
+	return nil
+}
+
 func (c *Module) UnstrippedOutputFile() android.Path {
 	if c.linker != nil {
 		return c.linker.unstrippedOutputFilePath()
@@ -1301,13 +1483,13 @@
 	return c.Properties.VndkVersion != ""
 }
 
-func (c *Module) canUseSdk() bool {
-	return c.Os() == android.Android && c.Target().NativeBridge == android.NativeBridgeDisabled &&
+func CanUseSdk(c LinkableInterface) bool {
+	return c.Module().Target().Os == android.Android && c.Target().NativeBridge == android.NativeBridgeDisabled &&
 		!c.InVendorOrProduct() && !c.InRamdisk() && !c.InRecovery() && !c.InVendorRamdisk()
 }
 
 func (c *Module) UseSdk() bool {
-	if c.canUseSdk() {
+	if CanUseSdk(c) {
 		return String(c.Properties.Sdk_version) != ""
 	}
 	return false
@@ -1326,13 +1508,13 @@
 }
 
 func (m *Module) NeedsLlndkVariants() bool {
-	lib := moduleLibraryInterface(m)
-	return lib != nil && (lib.hasLLNDKStubs() || lib.hasLLNDKHeaders())
+	lib := moduleVersionedInterface(m)
+	return lib != nil && (lib.HasLLNDKStubs() || lib.HasLLNDKHeaders())
 }
 
 func (m *Module) NeedsVendorPublicLibraryVariants() bool {
-	lib := moduleLibraryInterface(m)
-	return lib != nil && (lib.hasVendorPublicLibrary())
+	lib := moduleVersionedInterface(m)
+	return lib != nil && (lib.HasVendorPublicLibrary())
 }
 
 // IsVendorPublicLibrary returns true for vendor public libraries.
@@ -1352,13 +1534,13 @@
 }
 
 func (c *Module) HasLlndkStubs() bool {
-	lib := moduleLibraryInterface(c)
-	return lib != nil && lib.hasLLNDKStubs()
+	lib := moduleVersionedInterface(c)
+	return lib != nil && lib.HasLLNDKStubs()
 }
 
 func (c *Module) StubsVersion() string {
-	if lib, ok := c.linker.(versionedInterface); ok {
-		return lib.stubsVersion()
+	if lib, ok := c.linker.(VersionedInterface); ok {
+		return lib.StubsVersion()
 	}
 	panic(fmt.Errorf("StubsVersion called on non-versioned module: %q", c.BaseModuleName()))
 }
@@ -1367,7 +1549,7 @@
 // and does not set llndk.vendor_available: false.
 func (c *Module) isImplementationForLLNDKPublic() bool {
 	library, _ := c.library.(*libraryDecorator)
-	return library != nil && library.hasLLNDKStubs() &&
+	return library != nil && library.HasLLNDKStubs() &&
 		!Bool(library.Properties.Llndk.Private)
 }
 
@@ -1406,21 +1588,25 @@
 
 func (c *Module) IsStubs() bool {
 	if lib := c.library; lib != nil {
-		return lib.buildStubs()
+		return lib.BuildStubs()
 	}
 	return false
 }
 
 func (c *Module) HasStubsVariants() bool {
 	if lib := c.library; lib != nil {
-		return lib.hasStubsVariants()
+		return lib.HasStubsVariants()
 	}
 	return false
 }
 
+func (c *Module) RustApexExclude() bool {
+	return false
+}
+
 func (c *Module) IsStubsImplementationRequired() bool {
 	if lib := c.library; lib != nil {
-		return lib.isStubsImplementationRequired()
+		return lib.IsStubsImplementationRequired()
 	}
 	return false
 }
@@ -1429,21 +1615,21 @@
 // the implementation.  If it is an implementation library it returns its own name.
 func (c *Module) ImplementationModuleName(ctx android.BaseModuleContext) string {
 	name := ctx.OtherModuleName(c)
-	if versioned, ok := c.linker.(versionedInterface); ok {
-		name = versioned.implementationModuleName(name)
+	if versioned, ok := c.linker.(VersionedInterface); ok {
+		name = versioned.ImplementationModuleName(name)
 	}
 	return name
 }
 
-// Similar to ImplementationModuleName, but uses the Make variant of the module
+// Similar to ImplementationModuleNameByCtx, but uses the Make variant of the module
 // name as base name, for use in AndroidMk output. E.g. for a prebuilt module
 // where the Soong name is prebuilt_foo, this returns foo (which works in Make
 // under the premise that the prebuilt module overrides its source counterpart
 // if it is exposed to Make).
-func (c *Module) ImplementationModuleNameForMake(ctx android.BaseModuleContext) string {
+func (c *Module) ImplementationModuleNameForMake() string {
 	name := c.BaseModuleName()
-	if versioned, ok := c.linker.(versionedInterface); ok {
-		name = versioned.implementationModuleName(name)
+	if versioned, ok := c.linker.(VersionedInterface); ok {
+		name = versioned.ImplementationModuleName(name)
 	}
 	return name
 }
@@ -1523,6 +1709,10 @@
 	return ctx.mod.staticBinary()
 }
 
+func (ctx *moduleContextImpl) staticLibrary() bool {
+	return ctx.mod.staticLibrary()
+}
+
 func (ctx *moduleContextImpl) testBinary() bool {
 	return ctx.mod.testBinary()
 }
@@ -1547,10 +1737,6 @@
 	return ctx.mod.OptimizeForSize()
 }
 
-func (ctx *moduleContextImpl) canUseSdk() bool {
-	return ctx.mod.canUseSdk()
-}
-
 func (ctx *moduleContextImpl) useSdk() bool {
 	return ctx.mod.UseSdk()
 }
@@ -1562,21 +1748,23 @@
 	return ""
 }
 
-func (ctx *moduleContextImpl) minSdkVersion() string {
-	ver := ctx.mod.MinSdkVersion()
-	if ver == "apex_inherit" && !ctx.isForPlatform() {
-		ver = ctx.apexSdkVersion().String()
+func MinSdkVersion(mod VersionedLinkableInterface, ctxIsForPlatform bool, device bool,
+	platformSdkVersion string) string {
+
+	ver := mod.MinSdkVersion()
+	if ver == "apex_inherit" && !ctxIsForPlatform {
+		ver = mod.ApexSdkVersion().String()
 	}
 	if ver == "apex_inherit" || ver == "" {
-		ver = ctx.sdkVersion()
+		ver = mod.SdkVersion()
 	}
 
-	if ctx.ctx.Device() {
+	if device {
 		// When building for vendor/product, use the latest _stable_ API as "current".
 		// This is passed to clang/aidl compilers so that compiled/generated code works
 		// with the system.
-		if (ctx.inVendor() || ctx.inProduct()) && (ver == "" || ver == "current") {
-			ver = ctx.ctx.Config().PlatformSdkVersion().String()
+		if (mod.InVendor() || mod.InProduct()) && (ver == "" || ver == "current") {
+			ver = platformSdkVersion
 		}
 	}
 
@@ -1590,19 +1778,19 @@
 	// support such an old version. The version is set to the later version in case when the
 	// non-sdk variant is for the platform, or the min_sdk_version of the containing APEX if
 	// it's for an APEX.
-	if ctx.mod.isCrt() && !ctx.isSdkVariant() {
-		if ctx.isForPlatform() {
+	if mod.IsCrt() && !mod.IsSdkVariant() {
+		if ctxIsForPlatform {
 			ver = strconv.Itoa(android.FutureApiLevelInt)
 		} else { // for apex
-			ver = ctx.apexSdkVersion().String()
+			ver = mod.ApexSdkVersion().String()
 			if ver == "" { // in case when min_sdk_version was not set by the APEX
-				ver = ctx.sdkVersion()
+				ver = mod.SdkVersion()
 			}
 		}
 	}
 
 	// Also make sure that minSdkVersion is not greater than sdkVersion, if they are both numbers
-	sdkVersionInt, err := strconv.Atoi(ctx.sdkVersion())
+	sdkVersionInt, err := strconv.Atoi(mod.SdkVersion())
 	minSdkVersionInt, err2 := strconv.Atoi(ver)
 	if err == nil && err2 == nil {
 		if sdkVersionInt < minSdkVersionInt {
@@ -1612,6 +1800,14 @@
 	return ver
 }
 
+func (ctx *moduleContextImpl) minSdkVersion() string {
+	platformSdkVersion := ""
+	if ctx.ctx.Device() {
+		platformSdkVersion = ctx.ctx.Config().PlatformSdkVersion().String()
+	}
+	return MinSdkVersion(ctx.mod, CtxIsForPlatform(ctx.ctx), ctx.ctx.Device(), platformSdkVersion)
+}
+
 func (ctx *moduleContextImpl) isSdkVariant() bool {
 	return ctx.mod.IsSdkVariant()
 }
@@ -1675,8 +1871,8 @@
 	return ctx.mod.BaseModuleName()
 }
 
-func (ctx *moduleContextImpl) isForPlatform() bool {
-	apexInfo, _ := android.ModuleProvider(ctx.ctx, android.ApexInfoProvider)
+func CtxIsForPlatform(ctx android.BaseModuleContext) bool {
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	return apexInfo.IsForPlatform()
 }
 
@@ -1685,10 +1881,6 @@
 	return apexInfo.ApexVariationName
 }
 
-func (ctx *moduleContextImpl) apexSdkVersion() android.ApiLevel {
-	return ctx.mod.apexSdkVersion
-}
-
 func (ctx *moduleContextImpl) bootstrap() bool {
 	return ctx.mod.Bootstrap()
 }
@@ -1703,9 +1895,9 @@
 
 func (ctx *moduleContextImpl) getSharedFlags() *SharedFlags {
 	shared := &ctx.mod.sharedFlags
-	if shared.flagsMap == nil {
-		shared.numSharedFlags = 0
-		shared.flagsMap = make(map[string]string)
+	if shared.FlagsMap == nil {
+		shared.NumSharedFlags = 0
+		shared.FlagsMap = make(map[string]string)
 	}
 	return shared
 }
@@ -1769,6 +1961,14 @@
 	return name
 }
 
+func (c *Module) Multilib() string {
+	return c.Arch().ArchType.Multilib
+}
+
+func (c *Module) ApexSdkVersion() android.ApiLevel {
+	return c.apexSdkVersion
+}
+
 func (c *Module) Symlinks() []string {
 	if p, ok := c.installer.(interface {
 		symlinkList() []string
@@ -1899,13 +2099,13 @@
 		return false
 	}
 
-	_, aaWithoutTestApexes, _ := android.ListSetDifference(c.ApexAvailable(), c.TestApexes())
 	// Stub libraries should not have more than one apex_available
-	if len(aaWithoutTestApexes) > 1 {
+	apexAvailable := android.FirstUniqueStrings(c.ApexAvailable())
+	if len(apexAvailable) > 1 {
 		return true
 	}
 	// Stub libraries should not use the wildcard
-	if aaWithoutTestApexes[0] == android.AvailableToAnyApex {
+	if apexAvailable[0] == android.AvailableToAnyApex {
 		return true
 	}
 	// Default: no violation
@@ -2064,8 +2264,6 @@
 		})
 	}
 
-	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: deps.GeneratedSources.Strings()})
-
 	if Bool(c.Properties.Cmake_snapshot_supported) {
 		android.SetProvider(ctx, cmakeSnapshotSourcesProvider, android.GlobFiles(ctx, ctx.ModuleDir()+"/**/*", nil))
 	}
@@ -2117,23 +2315,121 @@
 	}
 
 	ccObjectInfo := CcObjectInfo{
-		kytheFiles: objs.kytheFiles,
+		KytheFiles: objs.kytheFiles,
 	}
 	if !ctx.Config().KatiEnabled() || !android.ShouldSkipAndroidMkProcessing(ctx, c) {
-		ccObjectInfo.objFiles = objs.objFiles
-		ccObjectInfo.tidyFiles = objs.tidyFiles
+		ccObjectInfo.ObjFiles = objs.objFiles
+		ccObjectInfo.TidyFiles = objs.tidyFiles
 	}
-	if len(ccObjectInfo.kytheFiles)+len(ccObjectInfo.objFiles)+len(ccObjectInfo.tidyFiles) > 0 {
+	if len(ccObjectInfo.KytheFiles)+len(ccObjectInfo.ObjFiles)+len(ccObjectInfo.TidyFiles) > 0 {
 		android.SetProvider(ctx, CcObjectInfoProvider, ccObjectInfo)
 	}
 
-	android.SetProvider(ctx, LinkableInfoKey, LinkableInfo{
-		StaticExecutable: c.StaticExecutable(),
-	})
+	linkableInfo := CreateCommonLinkableInfo(ctx, c)
+	if lib, ok := c.linker.(VersionedInterface); ok {
+		linkableInfo.StubsVersion = lib.StubsVersion()
+	}
+	if c.linker != nil {
+		if library, ok := c.linker.(libraryInterface); ok {
+			linkableInfo.Static = library.static()
+			linkableInfo.Shared = library.shared()
+			linkableInfo.CoverageFiles = library.objs().coverageFiles
+			linkableInfo.SAbiDumpFiles = library.objs().sAbiDumpFiles
+		}
+	}
+	android.SetProvider(ctx, LinkableInfoProvider, linkableInfo)
 
-	android.SetProvider(ctx, CcInfoProvider, CcInfo{
-		HasStubsVariants: c.HasStubsVariants(),
-	})
+	ccInfo := CcInfo{
+		IsPrebuilt:             c.IsPrebuilt(),
+		CmakeSnapshotSupported: proptools.Bool(c.Properties.Cmake_snapshot_supported),
+		HasLlndkStubs:          c.HasLlndkStubs(),
+		DataPaths:              c.DataPaths(),
+	}
+	if c.compiler != nil {
+		cflags := c.compiler.baseCompilerProps().Cflags
+		ccInfo.CompilerInfo = &CompilerInfo{
+			Srcs:   c.compiler.(CompiledInterface).Srcs(),
+			Cflags: cflags.GetOrDefault(ctx, nil),
+			AidlInterfaceInfo: AidlInterfaceInfo{
+				Sources:  c.compiler.baseCompilerProps().AidlInterface.Sources,
+				AidlRoot: c.compiler.baseCompilerProps().AidlInterface.AidlRoot,
+				Lang:     c.compiler.baseCompilerProps().AidlInterface.Lang,
+				Flags:    c.compiler.baseCompilerProps().AidlInterface.Flags,
+			},
+		}
+		switch decorator := c.compiler.(type) {
+		case *libraryDecorator:
+			ccInfo.CompilerInfo.LibraryDecoratorInfo = &LibraryDecoratorInfo{
+				ExportIncludeDirs: decorator.flagExporter.Properties.Export_include_dirs.GetOrDefault(ctx, nil),
+			}
+		}
+	}
+	if c.linker != nil {
+		baseLinkerProps := c.linker.baseLinkerProps()
+		ccInfo.LinkerInfo = &LinkerInfo{
+			WholeStaticLibs: baseLinkerProps.Whole_static_libs.GetOrDefault(ctx, nil),
+			StaticLibs:      baseLinkerProps.Static_libs.GetOrDefault(ctx, nil),
+			SharedLibs:      baseLinkerProps.Shared_libs.GetOrDefault(ctx, nil),
+			HeaderLibs:      baseLinkerProps.Header_libs.GetOrDefault(ctx, nil),
+		}
+		switch decorator := c.linker.(type) {
+		case *binaryDecorator:
+			ccInfo.LinkerInfo.BinaryDecoratorInfo = &BinaryDecoratorInfo{}
+		case *libraryDecorator:
+			lk := c.linker.(*libraryDecorator)
+			ccInfo.LinkerInfo.LibraryDecoratorInfo = &LibraryDecoratorInfo{
+				InjectBsslHash: Bool(lk.Properties.Inject_bssl_hash),
+				NdkSysrootPath: lk.ndkSysrootPath,
+				VndkFileName:   lk.getLibNameHelper(c.BaseModuleName(), true, false) + ".so",
+			}
+		case *testBinary:
+			ccInfo.LinkerInfo.TestBinaryInfo = &TestBinaryInfo{
+				Gtest: decorator.testDecorator.gtest(),
+			}
+		case *benchmarkDecorator:
+			ccInfo.LinkerInfo.BenchmarkDecoratorInfo = &BenchmarkDecoratorInfo{}
+		case *objectLinker:
+			ccInfo.LinkerInfo.ObjectLinkerInfo = &ObjectLinkerInfo{
+				NdkSysrootPath: c.linker.(*objectLinker).ndkSysrootPath,
+			}
+		case *stubDecorator:
+			ccInfo.LinkerInfo.StubDecoratorInfo = &StubDecoratorInfo{}
+		case *prebuiltLibraryLinker:
+			ccInfo.LinkerInfo.PrebuiltLibraryLinkerInfo = &PrebuiltLibraryLinkerInfo{
+				VndkFileName: c.linker.(*prebuiltLibraryLinker).getLibNameHelper(
+					c.BaseModuleName(), true, false) + ".so",
+			}
+		}
+
+		if s, ok := c.linker.(SnapshotInterface); ok {
+			ccInfo.SnapshotInfo = &SnapshotInfo{
+				SnapshotAndroidMkSuffix: s.SnapshotAndroidMkSuffix(),
+			}
+		}
+		if v, ok := c.linker.(VersionedInterface); ok {
+			name := v.ImplementationModuleName(ctx.OtherModuleName(c))
+			ccInfo.LinkerInfo.ImplementationModuleName = &name
+		}
+
+		c.linker.testSuiteInfo(ctx)
+	}
+	if c.library != nil {
+		ccInfo.LibraryInfo = &LibraryInfo{
+			BuildStubs: c.library.BuildStubs(),
+		}
+	}
+	if c.installer != nil {
+		ccInfo.InstallerInfo = &InstallerInfo{}
+		if installer, ok := c.installer.(*stubDecorator); ok {
+			ccInfo.InstallerInfo.StubDecoratorInfo = &StubDecoratorInfo{
+				HasAbiDump:   installer.hasAbiDump,
+				AbiDumpPath:  installer.abiDumpPath,
+				AbiDiffPaths: installer.abiDiffPaths,
+				InstallPath:  installer.installPath,
+			}
+		}
+	}
+	android.SetProvider(ctx, CcInfoProvider, &ccInfo)
 
 	c.setOutputFiles(ctx)
 
@@ -2142,10 +2438,79 @@
 	}
 }
 
-func setOutputFilesIfNotEmpty(ctx ModuleContext, files android.Paths, tag string) {
-	if len(files) > 0 {
-		ctx.SetOutputFiles(files, tag)
+func (c *Module) CleanupAfterBuildActions() {
+	// Clear as much of Module as possible to reduce memory usage.
+	c.generators = nil
+	c.compiler = nil
+	c.installer = nil
+	c.features = nil
+	c.coverage = nil
+	c.fuzzer = nil
+	c.sabi = nil
+	c.lto = nil
+	c.afdo = nil
+	c.orderfile = nil
+
+	// TODO: these can be cleared after nativeBinaryInfoProperties and nativeLibInfoProperties are switched to
+	//  using providers.
+	// c.linker = nil
+	// c.stl = nil
+	// c.sanitize = nil
+	// c.library = nil
+}
+
+func CreateCommonLinkableInfo(ctx android.ModuleContext, mod VersionedLinkableInterface) *LinkableInfo {
+	info := &LinkableInfo{
+		StaticExecutable:     mod.StaticExecutable(),
+		HasStubsVariants:     mod.HasStubsVariants(),
+		OutputFile:           mod.OutputFile(),
+		UnstrippedOutputFile: mod.UnstrippedOutputFile(),
+		CoverageOutputFile:   mod.CoverageOutputFile(),
+		Partition:            mod.Partition(),
+		IsStubs:              mod.IsStubs(),
+		CcLibrary:            mod.CcLibrary(),
+		CcLibraryInterface:   mod.CcLibraryInterface(),
+		RustLibraryInterface: mod.RustLibraryInterface(),
+		IsLlndk:              mod.IsLlndk(),
+		IsNdk:                mod.IsNdk(ctx.Config()),
+		HasNonSystemVariants: mod.HasNonSystemVariants(),
+		SubName:              mod.SubName(),
+		InVendorOrProduct:    mod.InVendorOrProduct(),
+		InRamdisk:            mod.InRamdisk(),
+		OnlyInRamdisk:        mod.OnlyInRamdisk(),
+		InVendorRamdisk:      mod.InVendorRamdisk(),
+		OnlyInVendorRamdisk:  mod.OnlyInVendorRamdisk(),
+		InRecovery:           mod.InRecovery(),
+		OnlyInRecovery:       mod.OnlyInRecovery(),
+		InVendor:             mod.InVendor(),
+		Installable:          mod.Installable(),
+		RelativeInstallPath:  mod.RelativeInstallPath(),
+		// TODO(b/362509506): remove this once all apex_exclude uses are switched to stubs.
+		RustApexExclude:                 mod.RustApexExclude(),
+		Bootstrap:                       mod.Bootstrap(),
+		Multilib:                        mod.Multilib(),
+		ImplementationModuleNameForMake: mod.ImplementationModuleNameForMake(),
+		Symlinks:                        mod.Symlinks(),
+		Header:                          mod.Header(),
+		IsVndkPrebuiltLibrary:           mod.IsVndkPrebuiltLibrary(),
 	}
+
+	vi := mod.VersionedInterface()
+	if vi != nil {
+		info.IsStubsImplementationRequired = vi.IsStubsImplementationRequired()
+		info.APIListCoverageXMLPath = vi.GetAPIListCoverageXMLPath()
+		info.HasLLNDKStubs = vi.HasLLNDKStubs()
+		info.IsLLNDKMovedToApex = vi.IsLLNDKMovedToApex()
+		info.ImplementationModuleName = vi.ImplementationModuleName(mod.BaseModuleName())
+	}
+
+	if !mod.PreventInstall() && fuzz.IsValid(ctx, mod.FuzzModuleStruct()) && mod.IsFuzzModule() {
+		info.FuzzSharedLibraries = mod.FuzzSharedLibraries()
+		fm := mod.FuzzPackagedModule()
+		fuzz.SetFuzzPackagedModuleInfo(ctx, &fm)
+	}
+
+	return info
 }
 
 func (c *Module) setOutputFiles(ctx ModuleContext) {
@@ -2157,31 +2522,52 @@
 	if c.linker != nil {
 		ctx.SetOutputFiles(android.PathsIfNonNil(c.linker.unstrippedOutputFilePath()), "unstripped")
 		ctx.SetOutputFiles(android.PathsIfNonNil(c.linker.strippedAllOutputFilePath()), "stripped_all")
+		defaultDistFiles := c.linker.defaultDistFiles()
+		if len(defaultDistFiles) > 0 {
+			ctx.SetOutputFiles(defaultDistFiles, android.DefaultDistTag)
+		}
 	}
 }
 
 func buildComplianceMetadataInfo(ctx ModuleContext, c *Module, deps PathDeps) {
 	// Dump metadata that can not be done in android/compliance-metadata.go
 	complianceMetadataInfo := ctx.ComplianceMetadataInfo()
-	complianceMetadataInfo.SetStringValue(android.ComplianceMetadataProp.IS_STATIC_LIB, strconv.FormatBool(ctx.static()))
+	complianceMetadataInfo.SetStringValue(android.ComplianceMetadataProp.IS_STATIC_LIB, strconv.FormatBool(ctx.static() || ctx.ModuleType() == "cc_object"))
 	complianceMetadataInfo.SetStringValue(android.ComplianceMetadataProp.BUILT_FILES, c.outputFile.String())
 
 	// Static deps
-	staticDeps := ctx.GetDirectDepsWithTag(StaticDepTag(false))
+	staticDeps := ctx.GetDirectDepsProxyWithTag(StaticDepTag(false))
 	staticDepNames := make([]string, 0, len(staticDeps))
 	for _, dep := range staticDeps {
 		staticDepNames = append(staticDepNames, dep.Name())
 	}
+	// Process CrtBegin and CrtEnd as static libs
+	ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
+		depName := ctx.OtherModuleName(dep)
+		depTag := ctx.OtherModuleDependencyTag(dep)
+		switch depTag {
+		case CrtBeginDepTag:
+			staticDepNames = append(staticDepNames, depName)
+		case CrtEndDepTag:
+			staticDepNames = append(staticDepNames, depName)
+		}
+	})
 
-	staticDepPaths := make([]string, 0, len(deps.StaticLibs))
+	staticDepPaths := make([]string, 0, len(deps.StaticLibs)+len(deps.CrtBegin)+len(deps.CrtEnd))
 	for _, dep := range deps.StaticLibs {
 		staticDepPaths = append(staticDepPaths, dep.String())
 	}
+	for _, dep := range deps.CrtBegin {
+		staticDepPaths = append(staticDepPaths, dep.String())
+	}
+	for _, dep := range deps.CrtEnd {
+		staticDepPaths = append(staticDepPaths, dep.String())
+	}
 	complianceMetadataInfo.SetListValue(android.ComplianceMetadataProp.STATIC_DEPS, android.FirstUniqueStrings(staticDepNames))
 	complianceMetadataInfo.SetListValue(android.ComplianceMetadataProp.STATIC_DEP_FILES, android.FirstUniqueStrings(staticDepPaths))
 
 	// Whole static deps
-	wholeStaticDeps := ctx.GetDirectDepsWithTag(StaticDepTag(true))
+	wholeStaticDeps := ctx.GetDirectDepsProxyWithTag(StaticDepTag(true))
 	wholeStaticDepNames := make([]string, 0, len(wholeStaticDeps))
 	for _, dep := range wholeStaticDeps {
 		wholeStaticDepNames = append(wholeStaticDepNames, dep.Name())
@@ -2193,6 +2579,14 @@
 	}
 	complianceMetadataInfo.SetListValue(android.ComplianceMetadataProp.WHOLE_STATIC_DEPS, android.FirstUniqueStrings(wholeStaticDepNames))
 	complianceMetadataInfo.SetListValue(android.ComplianceMetadataProp.WHOLE_STATIC_DEP_FILES, android.FirstUniqueStrings(wholeStaticDepPaths))
+
+	// Header libs
+	headerLibDeps := ctx.GetDirectDepsProxyWithTag(HeaderDepTag())
+	headerLibDepNames := make([]string, 0, len(headerLibDeps))
+	for _, dep := range headerLibDeps {
+		headerLibDepNames = append(headerLibDepNames, dep.Name())
+	}
+	complianceMetadataInfo.SetListValue(android.ComplianceMetadataProp.HEADER_LIBS, android.FirstUniqueStrings(headerLibDepNames))
 }
 
 func (c *Module) maybeUnhideFromMake() {
@@ -2272,7 +2666,7 @@
 		c.orderfile.begin(ctx)
 	}
 	if ctx.useSdk() && c.IsSdkVariant() {
-		version, err := nativeApiLevelFromUser(ctx, ctx.sdkVersion())
+		version, err := NativeApiLevelFromUser(ctx, ctx.sdkVersion())
 		if err != nil {
 			ctx.PropertyErrorf("sdk_version", err.Error())
 			c.Properties.Sdk_version = nil
@@ -2428,6 +2822,9 @@
 		variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version})
 		if tag, ok := depTag.(libraryDependencyTag); ok {
 			tag.explicitlyVersioned = true
+			if version == "" {
+				tag.explicitlyImpl = true
+			}
 			// depTag is an interface that contains a concrete non-pointer struct.  That makes the local
 			// tag variable a copy of the contents of depTag, and updating it doesn't change depTag.  Reassign
 			// the modified copy to depTag.
@@ -2803,6 +3200,7 @@
 		// Recovery code is not NDK
 		return
 	}
+	// Change this to LinkableInterface if Rust gets NDK support, which stubDecorators are for
 	if c, ok := to.(*Module); ok {
 		if c.StubDecorator() {
 			// These aren't real libraries, but are the stub shared libraries that are included in
@@ -2909,7 +3307,7 @@
 		if depTag == staticVariantTag {
 			return false
 		}
-		if depTag == stubImplDepTag {
+		if depTag == StubImplDepTag {
 			return false
 		}
 		if depTag == android.RequiredDepTag {
@@ -2940,7 +3338,7 @@
 	}
 	if module, ok := ctx.Module().(*Module); ok {
 		if lib, ok := module.linker.(*libraryDecorator); ok && lib.shared() {
-			if lib.hasLLNDKStubs() {
+			if lib.HasLLNDKStubs() {
 				ctx.WalkDeps(check)
 			}
 		}
@@ -2984,7 +3382,7 @@
 
 	skipModuleList := map[string]bool{}
 
-	ctx.VisitDirectDeps(func(dep android.Module) {
+	ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
 		depName := ctx.OtherModuleName(dep)
 		depTag := ctx.OtherModuleDependencyTag(dep)
 
@@ -2993,8 +3391,17 @@
 			return
 		}
 
+		var ccInfo *CcInfo
+		v, hasCcInfo := android.OtherModuleProvider(ctx, dep, CcInfoProvider)
+		if hasCcInfo {
+			ccInfo = v
+		}
+		linkableInfo, hasLinkableInfo := android.OtherModuleProvider(ctx, dep, LinkableInfoProvider)
 		if depTag == android.DarwinUniversalVariantTag {
-			depPaths.DarwinSecondArchOutput = dep.(*Module).OutputFile()
+			if !hasCcInfo {
+				panic(fmt.Errorf("dep is not a cc module: %s", dep.String()))
+			}
+			depPaths.DarwinSecondArchOutput = linkableInfo.OutputFile
 			return
 		}
 
@@ -3007,34 +3414,32 @@
 			}
 		}
 
-		ccDep, ok := dep.(LinkableInterface)
-		if !ok {
-
+		if !hasLinkableInfo {
 			// handling for a few module types that aren't cc Module but that are also supported
+			genRule, ok := android.OtherModuleProvider(ctx, dep, android.GeneratedSourceInfoProvider)
 			switch depTag {
 			case genSourceDepTag:
-				if genRule, ok := dep.(genrule.SourceFileGenerator); ok {
+				if ok {
 					depPaths.GeneratedSources = append(depPaths.GeneratedSources,
-						genRule.GeneratedSourceFiles()...)
+						genRule.GeneratedSourceFiles...)
 				} else {
 					ctx.ModuleErrorf("module %q is not a gensrcs or genrule", depName)
 				}
 				// Support exported headers from a generated_sources dependency
 				fallthrough
 			case genHeaderDepTag, genHeaderExportDepTag:
-				if genRule, ok := dep.(genrule.SourceFileGenerator); ok {
+				if ok {
 					depPaths.GeneratedDeps = append(depPaths.GeneratedDeps,
-						genRule.GeneratedDeps()...)
-					dirs := genRule.GeneratedHeaderDirs()
+						genRule.GeneratedDeps...)
+					dirs := genRule.GeneratedHeaderDirs
 					depPaths.IncludeDirs = append(depPaths.IncludeDirs, dirs...)
 					if depTag == genHeaderExportDepTag {
 						depPaths.ReexportedDirs = append(depPaths.ReexportedDirs, dirs...)
 						depPaths.ReexportedGeneratedHeaders = append(depPaths.ReexportedGeneratedHeaders,
-							genRule.GeneratedSourceFiles()...)
-						depPaths.ReexportedDeps = append(depPaths.ReexportedDeps, genRule.GeneratedDeps()...)
+							genRule.GeneratedSourceFiles...)
+						depPaths.ReexportedDeps = append(depPaths.ReexportedDeps, genRule.GeneratedDeps...)
 						// Add these re-exported flags to help header-abi-dumper to infer the abi exported by a library.
 						c.sabi.Properties.ReexportedIncludes = append(c.sabi.Properties.ReexportedIncludes, dirs.Strings()...)
-
 					}
 				} else {
 					ctx.ModuleErrorf("module %q is not a genrule", depName)
@@ -3055,13 +3460,14 @@
 			return
 		}
 
-		if dep.Target().Os != ctx.Os() {
+		commonInfo := android.OtherModulePointerProviderOrDefault(ctx, dep, android.CommonModuleInfoProvider)
+		if commonInfo.Target.Os != ctx.Os() {
 			ctx.ModuleErrorf("OS mismatch between %q (%s) and %q (%s)", ctx.ModuleName(), ctx.Os().Name, depName, dep.Target().Os.Name)
 			return
 		}
-		if dep.Target().Arch.ArchType != ctx.Arch().ArchType {
+		if commonInfo.Target.Arch.ArchType != ctx.Arch().ArchType {
 			ctx.ModuleErrorf("Arch mismatch between %q(%v) and %q(%v)",
-				ctx.ModuleName(), ctx.Arch().ArchType, depName, dep.Target().Arch.ArchType)
+				ctx.ModuleName(), ctx.Arch().ArchType, depName, commonInfo.Target.Arch.ArchType)
 			return
 		}
 
@@ -3070,7 +3476,7 @@
 			// The reuseObjTag dependency still exists because the LinkageMutator runs before the
 			// version mutator, so the stubs variant is created from the shared variant that
 			// already has the reuseObjTag dependency on the static variant.
-			if !c.library.buildStubs() {
+			if !c.library.BuildStubs() {
 				staticAnalogue, _ := android.OtherModuleProvider(ctx, dep, StaticLibraryInfoProvider)
 				objs := staticAnalogue.ReuseObjects
 				depPaths.Objs = depPaths.Objs.Append(objs)
@@ -3086,7 +3492,7 @@
 			depPaths.LlndkSystemIncludeDirs = append(depPaths.LlndkSystemIncludeDirs, depExporterInfo.SystemIncludeDirs...)
 		}
 
-		linkFile := ccDep.OutputFile()
+		linkFile := linkableInfo.OutputFile
 
 		if libDepTag, ok := depTag.(libraryDependencyTag); ok {
 			// Only use static unwinder for legacy (min_sdk_version = 29) apexes (b/144430859)
@@ -3141,9 +3547,12 @@
 				depFile = sharedLibraryInfo.TableOfContents
 
 				if !sharedLibraryInfo.IsStubs {
-					depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, ""))
-					if info, ok := android.OtherModuleProvider(ctx, dep, ImplementationDepInfoProvider); ok {
-						depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
+					// TODO(b/362509506): remove this additional check once all apex_exclude uses are switched to stubs.
+					if !linkableInfo.RustApexExclude {
+						depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, ""))
+						if info, ok := android.OtherModuleProvider(ctx, dep, ImplementationDepInfoProvider); ok {
+							depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
+						}
 					}
 				}
 
@@ -3164,8 +3573,8 @@
 				}
 
 			case libDepTag.static():
-				if ccDep.RustLibraryInterface() {
-					rlibDep := RustRlibDep{LibPath: linkFile.Path(), CrateName: ccDep.CrateName(), LinkDirs: ccDep.ExportedCrateLinkDirs()}
+				if linkableInfo.RustLibraryInterface {
+					rlibDep := RustRlibDep{LibPath: linkFile.Path(), CrateName: linkableInfo.CrateName, LinkDirs: linkableInfo.ExportedCrateLinkDirs}
 					depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, rlibDep)
 					depPaths.IncludeDirs = append(depPaths.IncludeDirs, depExporterInfo.IncludeDirs...)
 					if libDepTag.wholeStatic {
@@ -3242,8 +3651,8 @@
 				}
 			}
 
-			if libDepTag.static() && !libDepTag.wholeStatic && !ccDep.RustLibraryInterface() {
-				if !ccDep.CcLibraryInterface() || !ccDep.Static() {
+			if libDepTag.static() && !libDepTag.wholeStatic && !linkableInfo.RustLibraryInterface {
+				if !linkableInfo.CcLibraryInterface || !linkableInfo.Static {
 					ctx.ModuleErrorf("module %q not a static library", depName)
 					return
 				}
@@ -3251,16 +3660,15 @@
 				// When combining coverage files for shared libraries and executables, coverage files
 				// in static libraries act as if they were whole static libraries. The same goes for
 				// source based Abi dump files.
-				if c, ok := ccDep.(*Module); ok {
-					staticLib := c.linker.(libraryInterface)
+				if hasCcInfo {
 					depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles,
-						staticLib.objs().coverageFiles...)
+						linkableInfo.CoverageFiles...)
 					depPaths.StaticLibObjs.sAbiDumpFiles = append(depPaths.StaticLibObjs.sAbiDumpFiles,
-						staticLib.objs().sAbiDumpFiles...)
+						linkableInfo.SAbiDumpFiles...)
 				} else {
 					// Handle non-CC modules here
 					depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles,
-						ccDep.CoverageFiles()...)
+						linkableInfo.CoverageFiles...)
 				}
 			}
 
@@ -3306,7 +3714,7 @@
 					c.sabi.Properties.ReexportedSystemIncludes, depExporterInfo.SystemIncludeDirs.Strings()...)
 			}
 
-			makeLibName := MakeLibName(ctx, c, ccDep, ccDep.BaseModuleName()) + libDepTag.makeSuffix
+			makeLibName := MakeLibName(ccInfo, linkableInfo, commonInfo, commonInfo.BaseModuleName) + libDepTag.makeSuffix
 			switch {
 			case libDepTag.header():
 				c.Properties.AndroidMkHeaderLibs = append(
@@ -3317,7 +3725,7 @@
 				c.Properties.AndroidMkSharedLibs = append(
 					c.Properties.AndroidMkSharedLibs, makeLibName)
 			case libDepTag.static():
-				if !ccDep.RustLibraryInterface() {
+				if !linkableInfo.RustLibraryInterface {
 					if libDepTag.wholeStatic {
 						c.Properties.AndroidMkWholeStaticLibs = append(
 							c.Properties.AndroidMkWholeStaticLibs, makeLibName)
@@ -3333,7 +3741,8 @@
 			switch depTag {
 			case runtimeDepTag:
 				c.Properties.AndroidMkRuntimeLibs = append(
-					c.Properties.AndroidMkRuntimeLibs, MakeLibName(ctx, c, ccDep, ccDep.BaseModuleName())+libDepTag.makeSuffix)
+					c.Properties.AndroidMkRuntimeLibs, MakeLibName(ccInfo, linkableInfo, commonInfo,
+						commonInfo.BaseModuleName)+libDepTag.makeSuffix)
 			case objDepTag:
 				depPaths.Objs.objFiles = append(depPaths.Objs.objFiles, linkFile.Path())
 			case CrtBeginDepTag:
@@ -3373,21 +3782,30 @@
 	return depPaths
 }
 
-func ShouldUseStubForApex(ctx android.ModuleContext, parent, dep android.Module) bool {
+func ShouldUseStubForApex(ctx android.ModuleContext, parent android.Module, dep android.ModuleProxy) bool {
 	inVendorOrProduct := false
 	bootstrap := false
-	if linkable, ok := parent.(LinkableInterface); !ok {
-		ctx.ModuleErrorf("Not a Linkable module: %q", ctx.ModuleName())
+	if android.EqualModules(ctx.Module(), parent) {
+		if linkable, ok := parent.(LinkableInterface); !ok {
+			ctx.ModuleErrorf("Not a Linkable module: %q", ctx.ModuleName())
+		} else {
+			inVendorOrProduct = linkable.InVendorOrProduct()
+			bootstrap = linkable.Bootstrap()
+		}
 	} else {
-		inVendorOrProduct = linkable.InVendorOrProduct()
-		bootstrap = linkable.Bootstrap()
+		if linkable, ok := android.OtherModuleProvider(ctx, parent, LinkableInfoProvider); !ok {
+			ctx.ModuleErrorf("Not a Linkable module: %q", ctx.ModuleName())
+		} else {
+			inVendorOrProduct = linkable.InVendorOrProduct
+			bootstrap = linkable.Bootstrap
+		}
 	}
 
 	apexInfo, _ := android.OtherModuleProvider(ctx, parent, android.ApexInfoProvider)
 
 	useStubs := false
 
-	if lib := moduleLibraryInterface(dep); lib.buildStubs() && inVendorOrProduct { // LLNDK
+	if android.OtherModuleProviderOrDefault(ctx, dep, LinkableInfoProvider).IsStubs && inVendorOrProduct { // LLNDK
 		if !apexInfo.IsForPlatform() {
 			// For platform libraries, use current version of LLNDK
 			// If this is for use_vendor apex we will apply the same rules
@@ -3399,7 +3817,7 @@
 		// platform APIs, use stubs only when it is from an APEX (and not from
 		// platform) However, for host, ramdisk, vendor_ramdisk, recovery or
 		// bootstrap modules, always link to non-stub variant
-		isNotInPlatform := dep.(android.ApexModule).NotInPlatform()
+		isNotInPlatform := android.OtherModulePointerProviderOrDefault(ctx, dep, android.CommonModuleInfoProvider).NotInPlatform
 
 		useStubs = isNotInPlatform && !bootstrap
 	} else {
@@ -3417,7 +3835,7 @@
 // library bar which provides stable interface and exists in the platform, foo uses the stub variant
 // of bar. If bar doesn't provide a stable interface (i.e. buildStubs() == false) or is in the
 // same APEX as foo, the non-stub variant of bar is used.
-func ChooseStubOrImpl(ctx android.ModuleContext, dep android.Module) (SharedLibraryInfo, FlagExporterInfo) {
+func ChooseStubOrImpl(ctx android.ModuleContext, dep android.ModuleProxy) (SharedLibraryInfo, FlagExporterInfo) {
 	depTag := ctx.OtherModuleDependencyTag(dep)
 	libDepTag, ok := depTag.(libraryDependencyTag)
 	if !ok || !libDepTag.shared() {
@@ -3478,32 +3896,29 @@
 	return libName
 }
 
-func MakeLibName(ctx android.ModuleContext, c LinkableInterface, ccDep LinkableInterface, depName string) string {
+func MakeLibName(ccInfo *CcInfo, linkableInfo *LinkableInfo, commonInfo *android.CommonModuleInfo, depName string) string {
 	libName := BaseLibName(depName)
-	ccDepModule, _ := ccDep.(*Module)
-	isLLndk := ccDepModule != nil && ccDepModule.IsLlndk()
-	nonSystemVariantsExist := ccDep.HasNonSystemVariants() || isLLndk
+	isLLndk := ccInfo != nil && linkableInfo.IsLlndk
+	nonSystemVariantsExist := linkableInfo.HasNonSystemVariants || isLLndk
 
-	if ccDepModule != nil {
+	if ccInfo != nil {
 		// Use base module name for snapshots when exporting to Makefile.
-		if snapshotPrebuilt, ok := ccDepModule.linker.(SnapshotInterface); ok {
-			baseName := ccDepModule.BaseModuleName()
-
-			return baseName + snapshotPrebuilt.SnapshotAndroidMkSuffix()
+		if ccInfo.SnapshotInfo != nil {
+			return commonInfo.BaseModuleName + ccInfo.SnapshotInfo.SnapshotAndroidMkSuffix
 		}
 	}
 
-	if ccDep.InVendorOrProduct() && nonSystemVariantsExist {
+	if linkableInfo.InVendorOrProduct && nonSystemVariantsExist {
 		// The vendor and product modules in Make will have been renamed to not conflict with the
 		// core module, so update the dependency name here accordingly.
-		return libName + ccDep.SubName()
-	} else if ccDep.InRamdisk() && !ccDep.OnlyInRamdisk() {
+		return libName + linkableInfo.SubName
+	} else if linkableInfo.InRamdisk && !linkableInfo.OnlyInRamdisk {
 		return libName + RamdiskSuffix
-	} else if ccDep.InVendorRamdisk() && !ccDep.OnlyInVendorRamdisk() {
+	} else if linkableInfo.InVendorRamdisk && !linkableInfo.OnlyInVendorRamdisk {
 		return libName + VendorRamdiskSuffix
-	} else if ccDep.InRecovery() && !ccDep.OnlyInRecovery() {
+	} else if linkableInfo.InRecovery && !linkableInfo.OnlyInRecovery {
 		return libName + RecoverySuffix
-	} else if ccDep.Target().NativeBridge == android.NativeBridgeEnabled {
+	} else if commonInfo.Target.NativeBridge == android.NativeBridgeEnabled {
 		return libName + NativeBridgeSuffix
 	} else {
 		return libName
@@ -3567,6 +3982,15 @@
 	return false
 }
 
+func (c *Module) staticLibrary() bool {
+	if static, ok := c.linker.(interface {
+		staticLibrary() bool
+	}); ok {
+		return static.staticLibrary()
+	}
+	return false
+}
+
 func (c *Module) staticBinary() bool {
 	if static, ok := c.linker.(interface {
 		staticBinary() bool
@@ -3631,6 +4055,10 @@
 	return false
 }
 
+func (c *Module) ForceDisableSanitizers() {
+	c.sanitize.Properties.ForceDisable = true
+}
+
 func (c *Module) StaticExecutable() bool {
 	if b, ok := c.linker.(*binaryDecorator); ok {
 		return b.static()
@@ -3686,19 +4114,24 @@
 	if lib := c.library; lib != nil {
 		// Stub libs and prebuilt libs in a versioned SDK are not
 		// installable to APEX even though they are shared libs.
-		return lib.shared() && !lib.buildStubs()
+		return lib.shared() && !lib.BuildStubs()
 	}
 	return false
 }
 
 func (c *Module) AvailableFor(what string) bool {
+	return android.CheckAvailableForApex(what, c.ApexAvailableFor())
+}
+
+func (c *Module) ApexAvailableFor() []string {
+	list := c.ApexModuleBase.ApexAvailable()
 	if linker, ok := c.linker.(interface {
-		availableFor(string) bool
+		apexAvailable() []string
 	}); ok {
-		return c.ApexModuleBase.AvailableFor(what) || linker.availableFor(what)
-	} else {
-		return c.ApexModuleBase.AvailableFor(what)
+		list = append(list, linker.apexAvailable()...)
 	}
+
+	return android.FirstUniqueStrings(list)
 }
 
 func (c *Module) EverInstallable() bool {
@@ -3733,15 +4166,6 @@
 		return ret
 	}
 
-	// Special case for modules that are configured to be installed to /data, which includes
-	// test modules. For these modules, both APEX and non-APEX variants are considered as
-	// installable. This is because even the APEX variants won't be included in the APEX, but
-	// will anyway be installed to /data/*.
-	// See b/146995717
-	if c.InstallInData() {
-		return ret
-	}
-
 	return false
 }
 
@@ -3756,36 +4180,24 @@
 var _ android.ApexModule = (*Module)(nil)
 
 // Implements android.ApexModule
-func (c *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
-	depTag := ctx.OtherModuleDependencyTag(dep)
-	libDepTag, isLibDepTag := depTag.(libraryDependencyTag)
-
-	if cc, ok := dep.(*Module); ok {
-		if cc.HasStubsVariants() {
-			if isLibDepTag && libDepTag.shared() {
-				// dynamic dep to a stubs lib crosses APEX boundary
-				return false
-			}
-			if IsRuntimeDepTag(depTag) {
-				// runtime dep to a stubs lib also crosses APEX boundary
-				return false
-			}
-		}
-		if cc.IsLlndk() {
-			return false
-		}
-		if isLibDepTag && c.static() && libDepTag.shared() {
-			// shared_lib dependency from a static lib is considered as crossing
-			// the APEX boundary because the dependency doesn't actually is
-			// linked; the dependency is used only during the compilation phase.
-			return false
-		}
-
-		if isLibDepTag && libDepTag.excludeInApex {
-			return false
-		}
+func (c *Module) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return CcDepInSameApexChecker{
+		Static:           c.static(),
+		HasStubsVariants: c.HasStubsVariants(),
+		IsLlndk:          c.IsLlndk(),
+		Host:             c.Host(),
 	}
-	if depTag == stubImplDepTag {
+}
+
+type CcDepInSameApexChecker struct {
+	Static           bool
+	HasStubsVariants bool
+	IsLlndk          bool
+	Host             bool
+}
+
+func (c CcDepInSameApexChecker) OutgoingDepIsInSameApex(depTag blueprint.DependencyTag) bool {
+	if depTag == StubImplDepTag {
 		// We don't track from an implementation library to its stubs.
 		return false
 	}
@@ -3795,24 +4207,60 @@
 		// APEX.
 		return false
 	}
+
+	libDepTag, isLibDepTag := depTag.(libraryDependencyTag)
+	if isLibDepTag && c.Static && libDepTag.shared() {
+		// shared_lib dependency from a static lib is considered as crossing
+		// the APEX boundary because the dependency doesn't actually is
+		// linked; the dependency is used only during the compilation phase.
+		return false
+	}
+
+	if isLibDepTag && libDepTag.excludeInApex {
+		return false
+	}
+
+	return true
+}
+
+func (c CcDepInSameApexChecker) IncomingDepIsInSameApex(depTag blueprint.DependencyTag) bool {
+	if c.Host {
+		return false
+	}
+	if c.HasStubsVariants {
+		if IsSharedDepTag(depTag) && !IsExplicitImplSharedDepTag(depTag) {
+			// dynamic dep to a stubs lib crosses APEX boundary
+			return false
+		}
+		if IsRuntimeDepTag(depTag) {
+			// runtime dep to a stubs lib also crosses APEX boundary
+			return false
+		}
+		if IsHeaderDepTag(depTag) {
+			return false
+		}
+	}
+	if c.IsLlndk {
+		return false
+	}
+
 	return true
 }
 
 // Implements android.ApexModule
-func (c *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
-	sdkVersion android.ApiLevel) error {
+func (c *Module) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
 	// We ignore libclang_rt.* prebuilt libs since they declare sdk_version: 14(b/121358700)
 	if strings.HasPrefix(ctx.OtherModuleName(c), "libclang_rt") {
-		return nil
+		return android.MinApiLevel
 	}
 	// We don't check for prebuilt modules
 	if _, ok := c.linker.(prebuiltLinkerInterface); ok {
-		return nil
+		return android.MinApiLevel
 	}
 
 	minSdkVersion := c.MinSdkVersion()
 	if minSdkVersion == "apex_inherit" {
-		return nil
+		return android.MinApiLevel
 	}
 	if minSdkVersion == "" {
 		// JNI libs within APK-in-APEX fall into here
@@ -3821,14 +4269,16 @@
 		// non-SDK variant resets sdk_version, which works too.
 		minSdkVersion = c.SdkVersion()
 	}
+
 	if minSdkVersion == "" {
-		return fmt.Errorf("neither min_sdk_version nor sdk_version specificed")
+		return android.NoneApiLevel
 	}
+
 	// Not using nativeApiLevelFromUser because the context here is not
 	// necessarily a native context.
-	ver, err := android.ApiLevelFromUser(ctx, minSdkVersion)
+	ver, err := android.ApiLevelFromUserWithConfig(ctx.Config(), minSdkVersion)
 	if err != nil {
-		return err
+		return android.NoneApiLevel
 	}
 
 	// A dependency only needs to support a min_sdk_version at least
@@ -3836,15 +4286,14 @@
 	// This allows introducing new architectures in the platform that
 	// need to be included in apexes that normally require an older
 	// min_sdk_version.
-	minApiForArch := MinApiForArch(ctx, c.Target().Arch.ArchType)
-	if sdkVersion.LessThan(minApiForArch) {
-		sdkVersion = minApiForArch
+	if c.Enabled(ctx) {
+		minApiForArch := MinApiForArch(ctx, c.Target().Arch.ArchType)
+		if ver.LessThanOrEqualTo(minApiForArch) {
+			ver = android.MinApiLevel
+		}
 	}
 
-	if ver.GreaterThan(sdkVersion) {
-		return fmt.Errorf("newer SDK(%v)", ver)
-	}
-	return nil
+	return ver
 }
 
 // Implements android.ApexModule
@@ -3918,7 +4367,6 @@
 type Defaults struct {
 	android.ModuleBase
 	android.DefaultsModuleBase
-	android.ApexModuleBase
 }
 
 // cc_defaults provides a set of properties that can be inherited by other cc
@@ -3985,7 +4433,7 @@
 func (ks *kytheExtractAllSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var xrefTargets android.Paths
 	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
-		files := android.OtherModuleProviderOrDefault(ctx, module, CcObjectInfoProvider).kytheFiles
+		files := android.OtherModuleProviderOrDefault(ctx, module, CcObjectInfoProvider).KytheFiles
 		if len(files) > 0 {
 			xrefTargets = append(xrefTargets, files...)
 		}
diff --git a/cc/cc_preprocess_no_configuration_test.go b/cc/cc_preprocess_no_configuration_test.go
index c6eae4c..f09c44a 100644
--- a/cc/cc_preprocess_no_configuration_test.go
+++ b/cc/cc_preprocess_no_configuration_test.go
@@ -36,7 +36,7 @@
 
 	result := fixture.RunTest(t)
 
-	foo := result.ModuleForTests("foo", "")
+	foo := result.ModuleForTests(t, "foo", "")
 	actual := foo.Rule("cc").Args["cFlags"]
 	expected := "-E -DANDROID -Ifoo/bar"
 	android.AssertStringEquals(t, "cflags should be correct", expected, actual)
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 98af7b6..7240ea5 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -160,7 +160,7 @@
 		}
 	`)
 
-	ld := ctx.ModuleForTests("libTest", vendorVariant).Rule("ld")
+	ld := ctx.ModuleForTests(t, "libTest", vendorVariant).Rule("ld")
 	var objs []string
 	for _, o := range ld.Inputs {
 		objs = append(objs, o.Base())
@@ -171,7 +171,7 @@
 }
 
 func checkInstallPartition(t *testing.T, ctx *android.TestContext, name, variant, expected string) {
-	mod := ctx.ModuleForTests(name, variant).Module().(*Module)
+	mod := ctx.ModuleForTests(t, name, variant).Module().(*Module)
 	partitionDefined := false
 	checkPartition := func(specific bool, partition string) {
 		if specific {
@@ -311,7 +311,7 @@
 	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 
 	ctx := testCcWithConfig(t, config)
-	testingModule := ctx.ModuleForTests("main_test", "android_arm_armv7-a-neon")
+	testingModule := ctx.ModuleForTests(t, "main_test", "android_arm_armv7-a-neon")
 	testBinary := testingModule.Module().(*Module).linker.(*testBinary)
 	outputFiles := testingModule.OutputFiles(ctx, t, "")
 	if len(outputFiles) != 1 {
@@ -363,7 +363,7 @@
 	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 
 	ctx := testCcWithConfig(t, config)
-	testingModule := ctx.ModuleForTests("main_test", "android_arm_armv7-a-neon")
+	testingModule := ctx.ModuleForTests(t, "main_test", "android_arm_armv7-a-neon")
 	module := testingModule.Module()
 	testBinary := module.(*Module).linker.(*testBinary)
 	outputFiles := testingModule.OutputFiles(ctx, t, "")
@@ -405,7 +405,7 @@
 	`
 
 	ctx := prepareForCcTest.RunTestWithBp(t, bp).TestContext
-	module := ctx.ModuleForTests("main_test", "android_arm_armv7-a-neon").Module()
+	module := ctx.ModuleForTests(t, "main_test", "android_arm_armv7-a-neon").Module()
 
 	entries := android.AndroidMkInfoForTest(t, ctx, module).PrimaryInfo
 	compatEntries := entries.EntryMap["LOCAL_COMPATIBILITY_SUITE"]
@@ -437,7 +437,7 @@
 	`
 
 	ctx := prepareForCcTest.RunTestWithBp(t, bp).TestContext
-	module := ctx.ModuleForTests("main_test_lib", "android_arm_armv7-a-neon_shared").Module()
+	module := ctx.ModuleForTests(t, "main_test_lib", "android_arm_armv7-a-neon_shared").Module()
 
 	entries := android.AndroidMkInfoForTest(t, ctx, module).PrimaryInfo
 	compatEntries := entries.EntryMap["LOCAL_COMPATIBILITY_SUITE"]
@@ -668,7 +668,7 @@
 	}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			module := ctx.ModuleForTests(test.name, test.variant).Module().(*Module)
+			module := ctx.ModuleForTests(t, test.name, test.variant).Module().(*Module)
 			assertString(t, module.makeLinkType, test.expected)
 		})
 	}
@@ -861,10 +861,10 @@
 	`)
 
 	variant := "android_arm64_armv8-a_static"
-	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
+	moduleA := ctx.ModuleForTests(t, "a", variant).Module().(*Module)
 	staticLibInfo, _ := android.OtherModuleProvider(ctx, moduleA, StaticLibraryInfoProvider)
 	actual := android.Paths(staticLibInfo.TransitiveStaticLibrariesForOrdering.ToList()).RelativeToTop()
-	expected := GetOutputPaths(ctx, variant, []string{"a", "c", "b", "d"})
+	expected := GetOutputPaths(t, ctx, variant, []string{"a", "c", "b", "d"})
 
 	if !reflect.DeepEqual(actual, expected) {
 		t.Errorf("staticDeps orderings were not propagated correctly"+
@@ -897,10 +897,10 @@
 	`)
 
 	variant := "android_arm64_armv8-a_static"
-	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
+	moduleA := ctx.ModuleForTests(t, "a", variant).Module().(*Module)
 	staticLibInfo, _ := android.OtherModuleProvider(ctx, moduleA, StaticLibraryInfoProvider)
 	actual := android.Paths(staticLibInfo.TransitiveStaticLibrariesForOrdering.ToList()).RelativeToTop()
-	expected := GetOutputPaths(ctx, variant, []string{"a", "c", "b"})
+	expected := GetOutputPaths(t, ctx, variant, []string{"a", "c", "b"})
 
 	if !reflect.DeepEqual(actual, expected) {
 		t.Errorf("staticDeps orderings did not account for shared libs"+
@@ -1004,12 +1004,12 @@
 	}
 	android.AssertArrayString(t, "variants for llndk stubs", expected, actual)
 
-	params := result.ModuleForTests("libllndk", "android_vendor_arm_armv7-a-neon_shared").Description("generate stub")
+	params := result.ModuleForTests(t, "libllndk", "android_vendor_arm_armv7-a-neon_shared").Description("generate stub")
 	android.AssertSame(t, "use Vendor API level for default stubs", "35", params.Args["apiLevel"])
 
 	checkExportedIncludeDirs := func(module, variant string, expectedSystemDirs []string, expectedDirs ...string) {
 		t.Helper()
-		m := result.ModuleForTests(module, variant).Module()
+		m := result.ModuleForTests(t, module, variant).Module()
 		f, _ := android.OtherModuleProvider(result, m, FlagExporterInfoProvider)
 		android.AssertPathsRelativeToTopEquals(t, "exported include dirs for "+module+"["+variant+"]",
 			expectedDirs, f.IncludeDirs)
@@ -1030,14 +1030,14 @@
 
 	checkAbiLinkerIncludeDirs := func(module string) {
 		t.Helper()
-		coreModule := result.ModuleForTests(module, coreVariant)
+		coreModule := result.ModuleForTests(t, module, coreVariant)
 		abiCheckFlags := ""
 		for _, output := range coreModule.AllOutputs() {
 			if strings.HasSuffix(output, ".so.llndk.lsdump") {
 				abiCheckFlags = coreModule.Output(output).Args["exportedHeaderFlags"]
 			}
 		}
-		vendorModule := result.ModuleForTests(module, vendorVariant).Module()
+		vendorModule := result.ModuleForTests(t, module, vendorVariant).Module()
 		vendorInfo, _ := android.OtherModuleProvider(result, vendorModule, FlagExporterInfoProvider)
 		vendorDirs := android.Concat(vendorInfo.IncludeDirs, vendorInfo.SystemIncludeDirs)
 		android.AssertStringEquals(t, module+" has different exported include dirs for vendor variant and ABI check",
@@ -1078,7 +1078,7 @@
 	`)
 
 	// _static variant is used since _shared reuses *.o from the static variant
-	cc := ctx.ModuleForTests("libvendor", "android_vendor_arm_armv7-a-neon_static").Rule("cc")
+	cc := ctx.ModuleForTests(t, "libvendor", "android_vendor_arm_armv7-a-neon_static").Rule("cc")
 	cflags := cc.Args["cFlags"]
 	if !strings.Contains(cflags, "-Imy_include") {
 		t.Errorf("cflags for libvendor must contain -Imy_include, but was %#v.", cflags)
@@ -1189,33 +1189,33 @@
 	// runtime_libs for core variants use the module names without suffixes.
 	variant := "android_arm64_armv8-a_shared"
 
-	module := ctx.ModuleForTests("libvendor_available1", variant).Module().(*Module)
+	module := ctx.ModuleForTests(t, "libvendor_available1", variant).Module().(*Module)
 	checkRuntimeLibs(t, []string{"liball_available"}, module)
 
-	module = ctx.ModuleForTests("libproduct_available1", variant).Module().(*Module)
+	module = ctx.ModuleForTests(t, "libproduct_available1", variant).Module().(*Module)
 	checkRuntimeLibs(t, []string{"liball_available"}, module)
 
-	module = ctx.ModuleForTests("libcore", variant).Module().(*Module)
+	module = ctx.ModuleForTests(t, "libcore", variant).Module().(*Module)
 	checkRuntimeLibs(t, []string{"liball_available"}, module)
 
 	// runtime_libs for vendor variants have '.vendor' suffixes if the modules have both core
 	// and vendor variants.
 	variant = "android_vendor_arm64_armv8-a_shared"
 
-	module = ctx.ModuleForTests("libvendor_available1", variant).Module().(*Module)
+	module = ctx.ModuleForTests(t, "libvendor_available1", variant).Module().(*Module)
 	checkRuntimeLibs(t, []string{"liball_available.vendor"}, module)
 
-	module = ctx.ModuleForTests("libvendor2", variant).Module().(*Module)
+	module = ctx.ModuleForTests(t, "libvendor2", variant).Module().(*Module)
 	checkRuntimeLibs(t, []string{"liball_available.vendor", "libvendor1", "libproduct_vendor.vendor"}, module)
 
 	// runtime_libs for product variants have '.product' suffixes if the modules have both core
 	// and product variants.
 	variant = "android_product_arm64_armv8-a_shared"
 
-	module = ctx.ModuleForTests("libproduct_available1", variant).Module().(*Module)
+	module = ctx.ModuleForTests(t, "libproduct_available1", variant).Module().(*Module)
 	checkRuntimeLibs(t, []string{"liball_available.product"}, module)
 
-	module = ctx.ModuleForTests("libproduct2", variant).Module().(*Module)
+	module = ctx.ModuleForTests(t, "libproduct2", variant).Module().(*Module)
 	checkRuntimeLibs(t, []string{"liball_available.product", "libproduct1", "libproduct_vendor"}, module)
 }
 
@@ -1224,11 +1224,11 @@
 	ctx := testCc(t, runtimeLibAndroidBp)
 
 	variant := "android_arm64_armv8-a_shared"
-	module := ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
+	module := ctx.ModuleForTests(t, "libvendor_available2", variant).Module().(*Module)
 	checkRuntimeLibs(t, []string{"liball_available"}, module)
 
 	variant = "android_vendor_arm64_armv8-a_shared"
-	module = ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
+	module = ctx.ModuleForTests(t, "libvendor_available2", variant).Module().(*Module)
 	checkRuntimeLibs(t, nil, module)
 }
 
@@ -1261,12 +1261,12 @@
 
 	// Check the shared version of lib2.
 	variant := "android_arm64_armv8-a_shared"
-	module := ctx.ModuleForTests("lib2", variant).Module().(*Module)
+	module := ctx.ModuleForTests(t, "lib2", variant).Module().(*Module)
 	checkStaticLibs(t, []string{"lib1", "libc++demangle", "libclang_rt.builtins"}, module)
 
 	// Check the static version of lib2.
 	variant = "android_arm64_armv8-a_static"
-	module = ctx.ModuleForTests("lib2", variant).Module().(*Module)
+	module = ctx.ModuleForTests(t, "lib2", variant).Module().(*Module)
 	// libc++_static is linked additionally.
 	checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins"}, module)
 }
@@ -1387,7 +1387,7 @@
 		t.Errorf("multilib was set to 32 for librecovery32, but its variants has %s.", arm64)
 	}
 
-	recoveryModule := ctx.ModuleForTests("libHalInRecovery", recoveryVariant).Module().(*Module)
+	recoveryModule := ctx.ModuleForTests(t, "libHalInRecovery", recoveryVariant).Module().(*Module)
 	if !recoveryModule.Platform() {
 		t.Errorf("recovery variant of libHalInRecovery must not specific to device, soc, or product")
 	}
@@ -1412,7 +1412,7 @@
 	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 
 	ctx := testCcWithConfig(t, config)
-	testingModule := ctx.ModuleForTests("main_test", "android_arm_armv7-a-neon")
+	testingModule := ctx.ModuleForTests(t, "main_test", "android_arm_armv7-a-neon")
 	module := testingModule.Module()
 	testBinary := module.(*Module).linker.(*testBinary)
 	outputFiles := testingModule.OutputFiles(ctx, t, "")
@@ -1487,14 +1487,14 @@
 		}
 	}
 
-	libBarLinkRule := ctx.ModuleForTests("libBar", "android_arm64_armv8-a_shared").Rule("ld")
+	libBarLinkRule := ctx.ModuleForTests(t, "libBar", "android_arm64_armv8-a_shared").Rule("ld")
 	libFlags := libBarLinkRule.Args["libFlags"]
 	libFoo1StubPath := "libFoo/android_arm64_armv8-a_shared_1/libFoo.so"
 	if !strings.Contains(libFlags, libFoo1StubPath) {
 		t.Errorf("%q is not found in %q", libFoo1StubPath, libFlags)
 	}
 
-	libBarCompileRule := ctx.ModuleForTests("libBar", "android_arm64_armv8-a_shared").Rule("cc")
+	libBarCompileRule := ctx.ModuleForTests(t, "libBar", "android_arm64_armv8-a_shared").Rule("cc")
 	cFlags := libBarCompileRule.Args["cFlags"]
 	libFoo1VersioningMacro := "-D__LIBFOO_API__=1"
 	if !strings.Contains(cFlags, libFoo1VersioningMacro) {
@@ -1550,7 +1550,7 @@
 		}`)
 
 	variant := "android_arm64_armv8-a_static"
-	arRule := ctx.ModuleForTests("baz", variant).Rule("ar")
+	arRule := ctx.ModuleForTests(t, "baz", variant).Rule("ar")
 
 	// For static libraries, the object files of a whole static dep are included in the archive
 	// directly
@@ -1591,7 +1591,7 @@
 		}`)
 
 	variant := "android_arm64_armv8-a_shared"
-	linkRule := ctx.ModuleForTests("baz", variant).Rule("ld")
+	linkRule := ctx.ModuleForTests(t, "baz", variant).Rule("ld")
 	libFlags := linkRule.Args["libFlags"]
 	// When dynamically linking, we expect static dependencies to be found on the command line
 	if expected := "foo.a"; !strings.Contains(libFlags, expected) {
@@ -1623,7 +1623,7 @@
 		}`)
 
 	variant := "android_arm64_armv8-a"
-	binModuleRule := ctx.ModuleForTests("static_test", variant).Rule("ld")
+	binModuleRule := ctx.ModuleForTests(t, "static_test", variant).Rule("ld")
 	libFlags := binModuleRule.Args["libFlags"]
 	systemStaticLibs := []string{"libc.a", "libm.a"}
 	for _, lib := range systemStaticLibs {
@@ -1666,9 +1666,9 @@
 			},
 		}`)
 
-	mybin := ctx.ModuleForTests("mybin", "android_arm64_armv8-a").Rule("ld")
+	mybin := ctx.ModuleForTests(t, "mybin", "android_arm64_armv8-a").Rule("ld")
 	actual := mybin.Implicits[:2]
-	expected := GetOutputPaths(ctx, "android_arm64_armv8-a_static", []string{"libfooB", "libfooC"})
+	expected := GetOutputPaths(t, ctx, "android_arm64_armv8-a_static", []string{"libfooB", "libfooC"})
 
 	if !reflect.DeepEqual(actual, expected) {
 		t.Errorf("staticDeps orderings were not propagated correctly"+
@@ -1775,7 +1775,7 @@
 
 	checkPcGuardFlag := func(
 		modName string, variantName string, shouldHave bool) {
-		cc := ctx.ModuleForTests(modName, variantName).Rule("cc")
+		cc := ctx.ModuleForTests(t, modName, variantName).Rule("cc")
 
 		cFlags, ok := cc.Args["cFlags"]
 		if !ok {
@@ -1802,9 +1802,9 @@
 	checkPcGuardFlag(moduleName, variant+"_static", false)
 	checkPcGuardFlag(moduleName, variant+"_static_fuzzer_afl", true)
 
-	ctx.ModuleForTests("afl_fuzz_shared_lib",
+	ctx.ModuleForTests(t, "afl_fuzz_shared_lib",
 		"android_arm64_armv8-a_shared").Rule("cc")
-	ctx.ModuleForTests("afl_fuzz_shared_lib",
+	ctx.ModuleForTests(t, "afl_fuzz_shared_lib",
 		"android_arm64_armv8-a_shared_fuzzer").Rule("cc")
 }
 
@@ -1833,7 +1833,7 @@
 		}`)
 
 	variant := "android_arm64_armv8-a_fuzzer"
-	ctx.ModuleForTests("fuzz_smoke_test", variant).Rule("cc")
+	ctx.ModuleForTests(t, "fuzz_smoke_test", variant).Rule("cc")
 }
 
 func assertString(t *testing.T, got, expected string) {
@@ -1897,24 +1897,24 @@
 			defaults: ["defaults"],
 		}`)
 
-	shared := ctx.ModuleForTests("libshared", "android_arm64_armv8-a_shared").Rule("ld")
+	shared := ctx.ModuleForTests(t, "libshared", "android_arm64_armv8-a_shared").Rule("ld")
 	if g, w := pathsToBase(shared.Inputs), []string{"foo.o", "baz.o"}; !reflect.DeepEqual(w, g) {
 		t.Errorf("libshared ld rule wanted %q, got %q", w, g)
 	}
-	bothShared := ctx.ModuleForTests("libboth", "android_arm64_armv8-a_shared").Rule("ld")
+	bothShared := ctx.ModuleForTests(t, "libboth", "android_arm64_armv8-a_shared").Rule("ld")
 	if g, w := pathsToBase(bothShared.Inputs), []string{"foo.o", "baz.o"}; !reflect.DeepEqual(w, g) {
 		t.Errorf("libboth ld rule wanted %q, got %q", w, g)
 	}
-	binary := ctx.ModuleForTests("binary", "android_arm64_armv8-a").Rule("ld")
+	binary := ctx.ModuleForTests(t, "binary", "android_arm64_armv8-a").Rule("ld")
 	if g, w := pathsToBase(binary.Inputs), []string{"foo.o"}; !reflect.DeepEqual(w, g) {
 		t.Errorf("binary ld rule wanted %q, got %q", w, g)
 	}
 
-	static := ctx.ModuleForTests("libstatic", "android_arm64_armv8-a_static").Rule("ar")
+	static := ctx.ModuleForTests(t, "libstatic", "android_arm64_armv8-a_static").Rule("ar")
 	if g, w := pathsToBase(static.Inputs), []string{"foo.o", "bar.o"}; !reflect.DeepEqual(w, g) {
 		t.Errorf("libstatic ar rule wanted %q, got %q", w, g)
 	}
-	bothStatic := ctx.ModuleForTests("libboth", "android_arm64_armv8-a_static").Rule("ar")
+	bothStatic := ctx.ModuleForTests(t, "libboth", "android_arm64_armv8-a_static").Rule("ar")
 	if g, w := pathsToBase(bothStatic.Inputs), []string{"foo.o", "bar.o"}; !reflect.DeepEqual(w, g) {
 		t.Errorf("libboth ar rule wanted %q, got %q", w, g)
 	}
@@ -1973,12 +1973,12 @@
 		android.PrepareForTestWithAllowMissingDependencies,
 	).RunTestWithBp(t, bp)
 
-	libbar := result.ModuleForTests("libbar", "android_arm64_armv8-a_static").Output("libbar.a")
+	libbar := result.ModuleForTests(t, "libbar", "android_arm64_armv8-a_static").Output("libbar.a")
 	android.AssertDeepEquals(t, "libbar rule", android.ErrorRule, libbar.Rule)
 
 	android.AssertStringDoesContain(t, "libbar error", libbar.Args["error"], "missing dependencies: libmissing")
 
-	libfoo := result.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Output("libfoo.a")
+	libfoo := result.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_static").Output("libfoo.a")
 	android.AssertStringListContains(t, "libfoo.a dependencies", libfoo.Inputs.Strings(), libbar.Output.String())
 }
 
@@ -2025,11 +2025,11 @@
 	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	ctx := testCcWithConfig(t, config)
 
-	hostBin := ctx.ModuleForTests("bin", config.BuildOSTarget.String()).Description("install")
-	hostShared := ctx.ModuleForTests("libshared", config.BuildOSTarget.String()+"_shared").Description("install")
-	hostRuntime := ctx.ModuleForTests("libruntime", config.BuildOSTarget.String()+"_shared").Description("install")
-	hostTransitive := ctx.ModuleForTests("libtransitive", config.BuildOSTarget.String()+"_shared").Description("install")
-	hostTool := ctx.ModuleForTests("tool", config.BuildOSTarget.String()).Description("install")
+	hostBin := ctx.ModuleForTests(t, "bin", config.BuildOSTarget.String()).Description("install")
+	hostShared := ctx.ModuleForTests(t, "libshared", config.BuildOSTarget.String()+"_shared").Description("install")
+	hostRuntime := ctx.ModuleForTests(t, "libruntime", config.BuildOSTarget.String()+"_shared").Description("install")
+	hostTransitive := ctx.ModuleForTests(t, "libtransitive", config.BuildOSTarget.String()+"_shared").Description("install")
+	hostTool := ctx.ModuleForTests(t, "tool", config.BuildOSTarget.String()).Description("install")
 
 	if g, w := hostBin.Implicits.Strings(), hostShared.Output.String(); !android.InList(w, g) {
 		t.Errorf("expected host bin dependency %q, got %q", w, g)
@@ -2051,10 +2051,10 @@
 		t.Errorf("expected no host bin dependency %q, got %q", w, g)
 	}
 
-	deviceBin := ctx.ModuleForTests("bin", "android_arm64_armv8-a").Description("install")
-	deviceShared := ctx.ModuleForTests("libshared", "android_arm64_armv8-a_shared").Description("install")
-	deviceTransitive := ctx.ModuleForTests("libtransitive", "android_arm64_armv8-a_shared").Description("install")
-	deviceRuntime := ctx.ModuleForTests("libruntime", "android_arm64_armv8-a_shared").Description("install")
+	deviceBin := ctx.ModuleForTests(t, "bin", "android_arm64_armv8-a").Description("install")
+	deviceShared := ctx.ModuleForTests(t, "libshared", "android_arm64_armv8-a_shared").Description("install")
+	deviceTransitive := ctx.ModuleForTests(t, "libtransitive", "android_arm64_armv8-a_shared").Description("install")
+	deviceRuntime := ctx.ModuleForTests(t, "libruntime", "android_arm64_armv8-a_shared").Description("install")
 
 	if g, w := deviceBin.OrderOnly.Strings(), deviceShared.Output.String(); !android.InList(w, g) {
 		t.Errorf("expected device bin dependency %q, got %q", w, g)
@@ -2104,7 +2104,7 @@
 			srcs: ["foo.c"],
 		}`)
 
-	cFlags := ctx.ModuleForTests("libclient", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+	cFlags := ctx.ModuleForTests(t, "libclient", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
 
 	if !strings.Contains(cFlags, "-Iinclude/libbar") {
 		t.Errorf("expected %q in cflags, got %q", "-Iinclude/libbar", cFlags)
@@ -2144,7 +2144,7 @@
 		}.AddToFixture(),
 	).RunTest(t).TestContext
 
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static")
+	libfoo := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_static")
 
 	android.AssertPathsRelativeToTopEquals(
 		t,
@@ -2192,7 +2192,7 @@
 		}
 	`)
 
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static")
+	libfoo := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_static")
 	manifest := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, libfoo.Output("aidl.sbox.textproto"))
 	aidlCommand := manifest.Commands[0].GetCommand()
 	expectedAidlFlag := "-Werror"
@@ -2243,7 +2243,7 @@
 					`+tc.sdkVersion+`
 				}
 			`)
-			libfoo := ctx.ModuleForTests("libfoo", tc.variant)
+			libfoo := ctx.ModuleForTests(t, "libfoo", tc.variant)
 			manifest := android.RuleBuilderSboxProtoForTests(t, ctx, libfoo.Output("aidl.sbox.textproto"))
 			aidlCommand := manifest.Commands[0].GetCommand()
 			expectedAidlFlag := "--min_sdk_version=" + tc.expected
@@ -2312,7 +2312,7 @@
 			min_sdk_version: "29",
 		}`)
 
-	cFlags := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+	cFlags := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
 	android.AssertStringDoesContain(t, "min sdk version", cFlags, "-target aarch64-linux-android29")
 }
 
@@ -2332,7 +2332,7 @@
 		}),
 	).RunTestWithBp(t, bp)
 	ctx := result.TestContext
-	cFlags := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+	cFlags := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
 	android.AssertStringDoesContain(t, "min sdk version", cFlags, "-target aarch64-linux-android31")
 }
 
@@ -2439,7 +2439,7 @@
 			export_generated_headers: ["genrule_bar"],
 		}
 		`)
-		foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+		foo := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared").Module()
 		checkIncludeDirs(t, ctx, foo,
 			expectedIncludeDirs(`
 				foo/standard
@@ -2450,7 +2450,7 @@
 			expectedOrderOnlyDeps(`.intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h`),
 		)
 
-		bar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
+		bar := ctx.ModuleForTests(t, "libbar", "android_arm64_armv8-a_shared").Module()
 		checkIncludeDirs(t, ctx, bar,
 			expectedIncludeDirs(`
 				bar/standard
@@ -2483,7 +2483,7 @@
 			export_generated_headers: ["genrule_bar"],
 		}
 		`)
-		foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+		foo := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared").Module()
 		checkIncludeDirs(t, ctx, foo,
 			expectedIncludeDirs(`
 				foo/standard
@@ -2494,7 +2494,7 @@
 			expectedOrderOnlyDeps(`.intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h`),
 		)
 
-		bar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
+		bar := ctx.ModuleForTests(t, "libbar", "android_arm64_armv8-a_shared").Module()
 		checkIncludeDirs(t, ctx, bar,
 			expectedIncludeDirs(`
 				bar/standard
@@ -2540,7 +2540,7 @@
 			}
 		}
 		`).TestContext
-		foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+		foo := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared").Module()
 		checkIncludeDirs(t, ctx, foo,
 			expectedIncludeDirs(`
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl
@@ -2580,7 +2580,7 @@
 			}
 		}
 		`)
-		foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+		foo := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared").Module()
 		checkIncludeDirs(t, ctx, foo,
 			expectedIncludeDirs(`
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/proto
@@ -2607,7 +2607,7 @@
 			],
 		}
 		`)
-		foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+		foo := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared").Module()
 		checkIncludeDirs(t, ctx, foo,
 			expectedIncludeDirs(`
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/include
@@ -2689,7 +2689,7 @@
 	cppOnly := []string{"-fPIC", "${config.CommonGlobalCppflags}", "${config.DeviceGlobalCppflags}", "${config.ArmCppflags}"}
 
 	cflags := []string{"-Werror", "-std=candcpp"}
-	cstd := []string{"-std=gnu17", "-std=conly"}
+	cstd := []string{"-std=gnu23", "-std=conly"}
 	cppstd := []string{"-std=gnu++20", "-std=cpp", "-fno-rtti"}
 
 	lastNDKFlags := []string{
@@ -2877,7 +2877,7 @@
 					PrepareForIntegrationTestWithCc,
 					android.FixtureAddTextFile("external/foo/Android.bp", bp),
 				).RunTest(t)
-				cflags := ctx.ModuleForTests("libfoo", variant).Output("obj/external/foo/foo.o").Args["cFlags"]
+				cflags := ctx.ModuleForTests(t, "libfoo", variant).Output("obj/external/foo/foo.o").Args["cFlags"]
 
 				var includes []string
 				flags := strings.Split(cflags, " ")
@@ -2931,7 +2931,7 @@
 			srcs: ["foo.c"],
 		}`)
 
-	cFlags := ctx.ModuleForTests("libclient", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+	cFlags := ctx.ModuleForTests(t, "libclient", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
 
 	if !strings.Contains(cFlags, "${config.NoOverride64GlobalCflags}") {
 		t.Errorf("expected %q in cflags, got %q", "${config.NoOverride64GlobalCflags}", cFlags)
@@ -3095,7 +3095,7 @@
  `
 	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	ctx := testCcWithConfig(t, config)
-	testingModule := ctx.ModuleForTests("test_lib", "android_arm_armv7-a-neon_shared")
+	testingModule := ctx.ModuleForTests(t, "test_lib", "android_arm_armv7-a-neon_shared")
 	outputFile := testingModule.OutputFiles(ctx, t, "stripped_all")
 	if !strings.HasSuffix(outputFile.Strings()[0], "/stripped_all/test_lib.so") {
 		t.Errorf("Unexpected output file: %s", outputFile.Strings()[0])
@@ -3140,8 +3140,8 @@
 		if imageVariant != "core" {
 			imageVariantStr = "_" + imageVariant
 		}
-		binFooModule := ctx.ModuleForTests("binfoo", "android"+imageVariantStr+"_arm64_armv8-a").Module()
-		libBarModule := ctx.ModuleForTests("libbar", "android"+imageVariantStr+"_arm64_armv8-a_shared").Module()
+		binFooModule := ctx.ModuleForTests(t, "binfoo", "android"+imageVariantStr+"_arm64_armv8-a").Module()
+		libBarModule := ctx.ModuleForTests(t, "libbar", "android"+imageVariantStr+"_arm64_armv8-a_shared").Module()
 		android.AssertBoolEquals(t, "binfoo should have dependency on libbar with image variant "+imageVariant, true, hasDep(binFooModule, libBarModule))
 	}
 
@@ -3172,7 +3172,7 @@
 
 	ctx := prepareForCcTest.RunTestWithBp(t, bp)
 	testSdkVersionFlag := func(module, variant, version string) {
-		flags := ctx.ModuleForTests(module, "android_"+variant+"_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+		flags := ctx.ModuleForTests(t, module, "android_"+variant+"_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 		android.AssertStringDoesContain(t, "target SDK version", flags, "-target aarch64-linux-android"+version)
 	}
 
@@ -3199,14 +3199,14 @@
 		}
 	`)
 
-	module := ctx.ModuleForTests("lib_no_clang_verify", "android_arm64_armv8-a_shared")
+	module := ctx.ModuleForTests(t, "lib_no_clang_verify", "android_arm64_armv8-a_shared")
 
 	cFlags_no_cv := module.Rule("cc").Args["cFlags"]
 	if strings.Contains(cFlags_no_cv, "-Xclang") || strings.Contains(cFlags_no_cv, "-verify") {
 		t.Errorf("expected %q not in cflags, got %q", "-Xclang -verify", cFlags_no_cv)
 	}
 
-	cFlags_cv := ctx.ModuleForTests("lib_clang_verify", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+	cFlags_cv := ctx.ModuleForTests(t, "lib_clang_verify", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
 	if strings.Contains(cFlags_cv, "-Xclang") && strings.Contains(cFlags_cv, "-verify") {
 		t.Errorf("expected %q in cflags, got %q", "-Xclang -verify", cFlags_cv)
 	}
diff --git a/cc/cc_test_only_property_test.go b/cc/cc_test_only_property_test.go
index 972e86b..a178cad 100644
--- a/cc/cc_test_only_property_test.go
+++ b/cc/cc_test_only_property_test.go
@@ -173,7 +173,7 @@
 
 func getTeamProtoOutput(t *testing.T, ctx *android.TestResult) *team_proto.AllTeams {
 	teams := new(team_proto.AllTeams)
-	config := ctx.SingletonForTests("all_teams")
+	config := ctx.SingletonForTests(t, "all_teams")
 	allOutputs := config.AllOutputs()
 
 	protoPath := allOutputs[0]
diff --git a/cc/ccdeps.go b/cc/ccdeps.go
index 469fe31..4247778 100644
--- a/cc/ccdeps.go
+++ b/cc/ccdeps.go
@@ -41,8 +41,6 @@
 	outputPath android.Path
 }
 
-var _ android.SingletonMakeVarsProvider = (*ccdepsGeneratorSingleton)(nil)
-
 const (
 	ccdepsJsonFileName = "module_bp_cc_deps.json"
 	cClang             = "clang"
@@ -114,13 +112,6 @@
 		Rule:   android.Touch,
 		Output: ccfpath,
 	})
-}
-
-func (c *ccdepsGeneratorSingleton) MakeVars(ctx android.MakeVarsContext) {
-	if c.outputPath == nil {
-		return
-	}
-
 	ctx.DistForGoal("general-tests", c.outputPath)
 }
 
diff --git a/cc/cmake_module_aidl.txt b/cc/cmake_module_aidl.txt
index 84755a3..3622648 100644
--- a/cc/cmake_module_aidl.txt
+++ b/cc/cmake_module_aidl.txt
@@ -1,11 +1,11 @@
 # <<.M.Name>>
 
-<<setList .M.Name "_SRCS" "" (getAidlSources .M)>>
+<<setList .M.Name "_SRCS" "" (getAidlSources .CcInfo)>>
 
-<<setList .M.Name "_AIDLFLAGS" "" (getCompilerProperties .M).AidlInterface.Flags>>
+<<setList .M.Name "_AIDLFLAGS" "" (getAidlInterface .CcInfo).Flags>>
 
-add_aidl_library(<<.M.Name>> <<(getCompilerProperties .M).AidlInterface.Lang>>
-    "${ANDROID_BUILD_TOP}/<<.Ctx.OtherModuleDir .M>>/<<(getCompilerProperties .M).AidlInterface.AidlRoot>>"
+add_aidl_library(<<.M.Name>> <<(getAidlInterface .CcInfo).Lang>>
+    "${ANDROID_BUILD_TOP}/<<.Ctx.OtherModuleDir .M>>/<<(getAidlInterface .CcInfo).AidlRoot>>"
     "${<<.M.Name>>_SRCS}"
     "${<<.M.Name>>_AIDLFLAGS}")
 add_library(android::<<.M.Name>> ALIAS <<.M.Name>>)
diff --git a/cc/cmake_module_cc.txt b/cc/cmake_module_cc.txt
index 0f6e62f..a57e053 100644
--- a/cc/cmake_module_cc.txt
+++ b/cc/cmake_module_cc.txt
@@ -1,14 +1,14 @@
-<<$srcs := getSources .M>>
-<<$includeDirs := getIncludeDirs .Ctx .M>>
-<<$cflags := getCflagsProperty .Ctx .M>>
+<<$srcs := getSources .Ctx .CcInfo>>
+<<$includeDirs := getIncludeDirs .Ctx .M .CcInfo>>
+<<$cflags := getCflagsProperty .Ctx .CcInfo>>
 <<$deps := mapLibraries .Ctx .M (concat5
-(getWholeStaticLibsProperty .Ctx .M)
-(getStaticLibsProperty .Ctx .M)
-(getSharedLibsProperty .Ctx .M)
-(getHeaderLibsProperty .Ctx .M)
-(getExtraLibs .M)
+(getWholeStaticLibsProperty .Ctx .CcInfo)
+(getStaticLibsProperty .Ctx .CcInfo)
+(getSharedLibsProperty .Ctx .CcInfo)
+(getHeaderLibsProperty .Ctx .CcInfo)
+(getExtraLibs .CcInfo)
 ) .Pprop.LibraryMapping>>
-<<$moduleType := getModuleType .M>>
+<<$moduleType := getModuleType .CcInfo>>
 <<$moduleTypeCmake := "executable">>
 <<if eq $moduleType "library">>
 <<$moduleTypeCmake = "library">>
diff --git a/cc/cmake_snapshot.go b/cc/cmake_snapshot.go
index f553f27..3f6a01d 100644
--- a/cc/cmake_snapshot.go
+++ b/cc/cmake_snapshot.go
@@ -196,39 +196,31 @@
 
 			return list.String()
 		},
-		"getSources": func(m *Module) android.Paths {
-			return m.compiler.(CompiledInterface).Srcs()
+		"getSources": func(ctx android.ModuleContext, info *CcInfo) android.Paths {
+			return info.CompilerInfo.Srcs
 		},
 		"getModuleType": getModuleType,
-		"getCompilerProperties": func(m *Module) BaseCompilerProperties {
-			return m.compiler.baseCompilerProps()
+		"getAidlInterface": func(info *CcInfo) AidlInterfaceInfo {
+			return info.CompilerInfo.AidlInterfaceInfo
 		},
-		"getCflagsProperty": func(ctx android.ModuleContext, m *Module) []string {
-			prop := m.compiler.baseCompilerProps().Cflags
-			return prop.GetOrDefault(ctx, nil)
+		"getCflagsProperty": func(ctx android.ModuleContext, info *CcInfo) []string {
+			return info.CompilerInfo.Cflags
 		},
-		"getLinkerProperties": func(m *Module) BaseLinkerProperties {
-			return m.linker.baseLinkerProps()
+		"getWholeStaticLibsProperty": func(ctx android.ModuleContext, info *CcInfo) []string {
+			return info.LinkerInfo.WholeStaticLibs
 		},
-		"getWholeStaticLibsProperty": func(ctx android.ModuleContext, m *Module) []string {
-			prop := m.linker.baseLinkerProps().Whole_static_libs
-			return prop.GetOrDefault(ctx, nil)
+		"getStaticLibsProperty": func(ctx android.ModuleContext, info *CcInfo) []string {
+			return info.LinkerInfo.StaticLibs
 		},
-		"getStaticLibsProperty": func(ctx android.ModuleContext, m *Module) []string {
-			prop := m.linker.baseLinkerProps().Static_libs
-			return prop.GetOrDefault(ctx, nil)
+		"getSharedLibsProperty": func(ctx android.ModuleContext, info *CcInfo) []string {
+			return info.LinkerInfo.SharedLibs
 		},
-		"getSharedLibsProperty": func(ctx android.ModuleContext, m *Module) []string {
-			prop := m.linker.baseLinkerProps().Shared_libs
-			return prop.GetOrDefault(ctx, nil)
-		},
-		"getHeaderLibsProperty": func(ctx android.ModuleContext, m *Module) []string {
-			prop := m.linker.baseLinkerProps().Header_libs
-			return prop.GetOrDefault(ctx, nil)
+		"getHeaderLibsProperty": func(ctx android.ModuleContext, info *CcInfo) []string {
+			return info.LinkerInfo.HeaderLibs
 		},
 		"getExtraLibs":   getExtraLibs,
 		"getIncludeDirs": getIncludeDirs,
-		"mapLibraries": func(ctx android.ModuleContext, m *Module, libs []string, mapping map[string]LibraryMappingProperty) []string {
+		"mapLibraries": func(ctx android.ModuleContext, m android.ModuleProxy, libs []string, mapping map[string]LibraryMappingProperty) []string {
 			var mappedLibs []string
 			for _, lib := range libs {
 				mappedLib, exists := mapping[lib]
@@ -249,8 +241,8 @@
 			mappedLibs = slices.Compact(mappedLibs)
 			return mappedLibs
 		},
-		"getAidlSources": func(m *Module) []string {
-			aidlInterface := m.compiler.baseCompilerProps().AidlInterface
+		"getAidlSources": func(info *CcInfo) []string {
+			aidlInterface := info.CompilerInfo.AidlInterfaceInfo
 			aidlRoot := aidlInterface.AidlRoot + string(filepath.Separator)
 			if aidlInterface.AidlRoot == "" {
 				aidlRoot = ""
@@ -340,14 +332,14 @@
 	moduleDirs := map[string][]string{}
 	sourceFiles := map[string]android.Path{}
 	visitedModules := map[string]bool{}
-	var pregeneratedModules []*Module
-	ctx.WalkDeps(func(dep_a android.Module, parent android.Module) bool {
-		moduleName := ctx.OtherModuleName(dep_a)
+	var pregeneratedModules []android.ModuleProxy
+	ctx.WalkDepsProxy(func(dep, parent android.ModuleProxy) bool {
+		moduleName := ctx.OtherModuleName(dep)
 		if visited := visitedModules[moduleName]; visited {
 			return false // visit only once
 		}
 		visitedModules[moduleName] = true
-		dep, ok := dep_a.(*Module)
+		ccInfo, ok := android.OtherModuleProvider(ctx, dep, CcInfoProvider)
 		if !ok {
 			return false // not a cc module
 		}
@@ -363,15 +355,15 @@
 		if slices.Contains(ignoredSystemLibs, moduleName) {
 			return false // system libs built in-tree for Android
 		}
-		if dep.IsPrebuilt() {
+		if ccInfo.IsPrebuilt {
 			return false // prebuilts are not supported
 		}
-		if dep.compiler == nil {
+		if ccInfo.CompilerInfo == nil {
 			return false // unsupported module type
 		}
-		isAidlModule := dep.compiler.baseCompilerProps().AidlInterface.Lang != ""
+		isAidlModule := ccInfo.CompilerInfo.AidlInterfaceInfo.Lang != ""
 
-		if !proptools.Bool(dep.Properties.Cmake_snapshot_supported) {
+		if !ccInfo.CmakeSnapshotSupported {
 			ctx.OtherModulePropertyErrorf(dep, "cmake_snapshot_supported",
 				"CMake snapshots not supported, despite being a dependency for %s",
 				ctx.OtherModuleName(parent))
@@ -389,12 +381,14 @@
 		}
 		moduleFragment := executeTemplate(templateToUse, &templateBuffer, struct {
 			Ctx      *android.ModuleContext
-			M        *Module
+			M        android.ModuleProxy
+			CcInfo   *CcInfo
 			Snapshot *CmakeSnapshot
 			Pprop    *cmakeProcessedProperties
 		}{
 			&ctx,
 			dep,
+			ccInfo,
 			m,
 			&pprop,
 		})
@@ -415,7 +409,7 @@
 	// Enumerate sources for pregenerated modules
 	if m.Properties.Include_sources {
 		for _, dep := range pregeneratedModules {
-			if !proptools.Bool(dep.Properties.Cmake_snapshot_supported) {
+			if !android.OtherModuleProviderOrDefault(ctx, dep, CcInfoProvider).CmakeSnapshotSupported {
 				ctx.OtherModulePropertyErrorf(dep, "cmake_snapshot_supported",
 					"Pregenerated CMake snapshots not supported, despite being requested for %s",
 					ctx.ModuleName())
@@ -491,7 +485,7 @@
 	if len(m.Properties.Prebuilts) > 0 {
 		var prebuiltsList android.Paths
 
-		ctx.VisitDirectDepsWithTag(cmakeSnapshotPrebuiltTag, func(dep android.Module) {
+		ctx.VisitDirectDepsProxyWithTag(cmakeSnapshotPrebuiltTag, func(dep android.ModuleProxy) {
 			for _, file := range android.OtherModuleProviderOrDefault(
 				ctx, dep, android.InstallFilesProvider).InstallFiles {
 				prebuiltsList = append(prebuiltsList, file)
@@ -523,42 +517,37 @@
 	}}
 }
 
-func getModuleType(m *Module) string {
-	switch m.linker.(type) {
-	case *binaryDecorator:
+func getModuleType(info *CcInfo) string {
+	if info.LinkerInfo.BinaryDecoratorInfo != nil {
 		return "executable"
-	case *libraryDecorator:
+	} else if info.LinkerInfo.LibraryDecoratorInfo != nil {
 		return "library"
-	case *testBinary:
+	} else if info.LinkerInfo.TestBinaryInfo != nil || info.LinkerInfo.BenchmarkDecoratorInfo != nil {
 		return "test"
-	case *benchmarkDecorator:
-		return "test"
-	case *objectLinker:
+	} else if info.LinkerInfo.ObjectLinkerInfo != nil {
 		return "object"
 	}
-	panic(fmt.Sprintf("Unexpected module type: %T", m.linker))
+	panic(fmt.Sprintf("Unexpected module type for LinkerInfo"))
 }
 
-func getExtraLibs(m *Module) []string {
-	switch decorator := m.linker.(type) {
-	case *testBinary:
-		if decorator.testDecorator.gtest() {
+func getExtraLibs(info *CcInfo) []string {
+	if info.LinkerInfo.TestBinaryInfo != nil {
+		if info.LinkerInfo.TestBinaryInfo.Gtest {
 			return []string{
 				"libgtest",
 				"libgtest_main",
 			}
 		}
-	case *benchmarkDecorator:
+	} else if info.LinkerInfo.BenchmarkDecoratorInfo != nil {
 		return []string{"libgoogle-benchmark"}
 	}
 	return nil
 }
 
-func getIncludeDirs(ctx android.ModuleContext, m *Module) []string {
+func getIncludeDirs(ctx android.ModuleContext, m android.ModuleProxy, info *CcInfo) []string {
 	moduleDir := ctx.OtherModuleDir(m) + string(filepath.Separator)
-	switch decorator := m.compiler.(type) {
-	case *libraryDecorator:
-		return sliceWithPrefix(moduleDir, decorator.flagExporter.Properties.Export_include_dirs.GetOrDefault(ctx, nil))
+	if info.CompilerInfo.LibraryDecoratorInfo != nil {
+		return sliceWithPrefix(moduleDir, info.CompilerInfo.LibraryDecoratorInfo.ExportIncludeDirs)
 	}
 	return nil
 }
diff --git a/cc/cmake_snapshot_test.go b/cc/cmake_snapshot_test.go
index b6f4369..d08096a 100644
--- a/cc/cmake_snapshot_test.go
+++ b/cc/cmake_snapshot_test.go
@@ -49,7 +49,7 @@
 		t.Skip("CMake snapshots are only supported on Linux")
 	}
 
-	snapshotModule := result.ModuleForTests("foo", "linux_glibc_x86_64")
+	snapshotModule := result.ModuleForTests(t, "foo", "linux_glibc_x86_64")
 
 	wasGenerated(t, &snapshotModule, "CMakeLists.txt", "rawFileCopy")
 	wasGenerated(t, &snapshotModule, "foo.zip", "")
@@ -77,7 +77,7 @@
 		t.Skip("CMake snapshots are only supported on Linux")
 	}
 
-	snapshotModule := result.ModuleForTests("foo", "linux_glibc_x86_64")
+	snapshotModule := result.ModuleForTests(t, "foo", "linux_glibc_x86_64")
 
 	wasGenerated(t, &snapshotModule, "some/module/CMakeLists.txt", "rawFileCopy")
 }
@@ -110,7 +110,7 @@
 		t.Skip("CMake snapshots are only supported on Linux")
 	}
 
-	snapshotModule := result.ModuleForTests("foo", "linux_glibc_x86_64")
+	snapshotModule := result.ModuleForTests(t, "foo", "linux_glibc_x86_64")
 
 	wasGenerated(t, &snapshotModule, "CMakeLists.txt", "rawFileCopy")
 	wasGenerated(t, &snapshotModule, "foo.zip", "")
diff --git a/cc/compdb.go b/cc/compdb.go
index 4132e09..3818e9c 100644
--- a/cc/compdb.go
+++ b/cc/compdb.go
@@ -193,7 +193,7 @@
 			}
 			builds[src.String()] = compDbEntry{
 				Directory: android.AbsSrcDirForExistingUseCases(),
-				Arguments: getArguments(src, ctx, ccModule, ccPath, cxxPath),
+				Arguments: args,
 				File:      src.String(),
 			}
 		}
diff --git a/cc/compiler.go b/cc/compiler.go
index 91f107c..949603e 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -324,6 +324,10 @@
 	getNamedMapForConfig(ctx.Config(), key).Store(module, true)
 }
 
+func requiresGlobalIncludes(ctx ModuleContext) bool {
+	return !(ctx.useSdk() || ctx.InVendorOrProduct()) || ctx.Host()
+}
+
 func useGnuExtensions(gnuExtensions *bool) bool {
 	return proptools.BoolDefault(gnuExtensions, true)
 }
@@ -360,6 +364,29 @@
 	}
 }
 
+func AddTargetFlags(ctx android.ModuleContext, flags Flags, tc config.Toolchain, version string, bpf bool) Flags {
+	target := "-target " + tc.ClangTriple()
+	if ctx.Os().Class == android.Device {
+		if version == "" || version == "current" {
+			target += strconv.Itoa(android.FutureApiLevelInt)
+		} else {
+			apiLevel := nativeApiLevelOrPanic(ctx, version)
+			target += strconv.Itoa(apiLevel.FinalOrFutureInt())
+		}
+	}
+
+	// bpf targets don't need the default target triple. b/308826679
+	if bpf {
+		target = "--target=bpf"
+	}
+
+	flags.Global.CFlags = append(flags.Global.CFlags, target)
+	flags.Global.AsFlags = append(flags.Global.AsFlags, target)
+	flags.Global.LdFlags = append(flags.Global.LdFlags, target)
+
+	return flags
+}
+
 // Create a Flags struct that collects the compile flags from global values,
 // per-target values, module type values, and per-module Blueprints properties
 func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags {
@@ -367,7 +394,7 @@
 	modulePath := ctx.ModuleDir()
 
 	reuseObjs := false
-	if len(ctx.GetDirectDepsWithTag(reuseObjTag)) > 0 {
+	if len(ctx.GetDirectDepsProxyWithTag(reuseObjTag)) > 0 {
 		reuseObjs = true
 	}
 
@@ -429,7 +456,7 @@
 		flags.Local.YasmFlags = append(flags.Local.YasmFlags, "-I"+modulePath)
 	}
 
-	if !(ctx.useSdk() || ctx.InVendorOrProduct()) || ctx.Host() {
+	if requiresGlobalIncludes(ctx) {
 		flags.SystemIncludeFlags = append(flags.SystemIncludeFlags,
 			"${config.CommonGlobalIncludes}",
 			tc.IncludeFlags())
@@ -513,25 +540,7 @@
 	flags.Local.ConlyFlags = config.ClangFilterUnknownCflags(flags.Local.ConlyFlags)
 	flags.Local.LdFlags = config.ClangFilterUnknownCflags(flags.Local.LdFlags)
 
-	target := "-target " + tc.ClangTriple()
-	if ctx.Os().Class == android.Device {
-		version := ctx.minSdkVersion()
-		if version == "" || version == "current" {
-			target += strconv.Itoa(android.FutureApiLevelInt)
-		} else {
-			apiLevel := nativeApiLevelOrPanic(ctx, version)
-			target += strconv.Itoa(apiLevel.FinalOrFutureInt())
-		}
-	}
-
-	// bpf targets don't need the default target triple. b/308826679
-	if proptools.Bool(compiler.Properties.Bpf_target) {
-		target = "--target=bpf"
-	}
-
-	flags.Global.CFlags = append(flags.Global.CFlags, target)
-	flags.Global.AsFlags = append(flags.Global.AsFlags, target)
-	flags.Global.LdFlags = append(flags.Global.LdFlags, target)
+	flags = AddTargetFlags(ctx, flags, tc, ctx.minSdkVersion(), Bool(compiler.Properties.Bpf_target))
 
 	hod := "Host"
 	if ctx.Os().Class == android.Device {
@@ -785,7 +794,7 @@
 	objs := compileObjs(ctx, buildFlags, "", srcs,
 		append(android.PathsForModuleSrc(ctx, compiler.Properties.Tidy_disabled_srcs), compiler.generatedSources...),
 		android.PathsForModuleSrc(ctx, compiler.Properties.Tidy_timeout_srcs),
-		pathDeps, compiler.cFlagsDeps)
+		pathDeps, compiler.cFlagsDeps, ctx.getSharedFlags())
 
 	if ctx.Failed() {
 		return Objects{}
@@ -795,10 +804,12 @@
 }
 
 // Compile a list of source files into objects a specified subdirectory
-func compileObjs(ctx ModuleContext, flags builderFlags, subdir string,
-	srcFiles, noTidySrcs, timeoutTidySrcs, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
+func compileObjs(ctx android.ModuleContext, flags builderFlags, subdir string,
+	srcFiles, noTidySrcs, timeoutTidySrcs, pathDeps android.Paths, cFlagsDeps android.Paths,
+	sharedFlags *SharedFlags) Objects {
 
-	return transformSourceToObj(ctx, subdir, srcFiles, noTidySrcs, timeoutTidySrcs, flags, pathDeps, cFlagsDeps)
+	return transformSourceToObj(ctx, subdir, srcFiles, noTidySrcs, timeoutTidySrcs, flags, pathDeps, cFlagsDeps,
+		sharedFlags)
 }
 
 // Properties for rust_bindgen related to generating rust bindings.
diff --git a/cc/config/OWNERS b/cc/config/OWNERS
index c78b6d5..2668fdd 100644
--- a/cc/config/OWNERS
+++ b/cc/config/OWNERS
@@ -1,3 +1,2 @@
 per-file vndk.go = smoreland@google.com, victoryang@google.com
-per-file clang.go,global.go,tidy.go = appujee@google.com, pirama@google.com, srhines@google.com, yabinc@google.com, yikong@google.com, zijunzhao@google.com
-
+per-file clang.go,global.go,tidy.go = appujee@google.com, pirama@google.com, srhines@google.com, yabinc@google.com, yikong@google.com, rprichard@google.com, sharjeelkhan@google.com
\ No newline at end of file
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 0dcf2cf..25edb79 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -44,7 +44,7 @@
 		// On ARMv9 and later, Pointer Authentication Codes (PAC) are mandatory,
 		// so -fstack-protector is unnecessary.
 		"armv9-a": []string{
-			"-march=armv8.2-a+dotprod",
+			"-march=armv9-a",
 			"-mbranch-protection=standard",
 			"-fno-stack-protector",
 		},
@@ -53,6 +53,16 @@
 			"-mbranch-protection=standard",
 			"-fno-stack-protector",
 		},
+		"armv9-3a": []string{
+			"-march=armv9.3-a",
+			"-mbranch-protection=standard",
+			"-fno-stack-protector",
+		},
+		"armv9-4a": []string{
+			"-march=armv9.4-a",
+			"-mbranch-protection=standard",
+			"-fno-stack-protector",
+		},
 	}
 
 	arm64Ldflags = []string{
diff --git a/cc/config/darwin_host.go b/cc/config/darwin_host.go
index 1783f49..716965a 100644
--- a/cc/config/darwin_host.go
+++ b/cc/config/darwin_host.go
@@ -54,6 +54,7 @@
 		"12",
 		"13",
 		"14",
+		"15",
 	}
 
 	darwinAvailableLibraries = append(
diff --git a/cc/config/global.go b/cc/config/global.go
index b19682d..e81ac0d 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -290,11 +290,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-r522817
 		"-Wno-error=invalid-offsetof",
-		"-Wno-error=thread-safety-reference-return",
 
 		// Allow using VLA CXX extension.
 		"-Wno-vla-cxx-extension",
@@ -366,8 +363,6 @@
 		"-Wno-array-parameter",
 		"-Wno-gnu-offsetof-extensions",
 		"-Wno-pessimizing-move",
-		// TODO: Enable this warning http://b/315245071
-		"-Wno-fortify-source",
 	}
 
 	llvmNextExtraCommonGlobalCflags = []string{
@@ -378,17 +373,20 @@
 	// Flags that must not appear in any command line.
 	IllegalFlags = []string{
 		"-w",
+		"-pedantic",
+		"-pedantic-errors",
+		"-Werror=pedantic",
 	}
 
-	CStdVersion               = "gnu17"
+	CStdVersion               = "gnu23"
 	CppStdVersion             = "gnu++20"
 	ExperimentalCStdVersion   = "gnu2x"
 	ExperimentalCppStdVersion = "gnu++2b"
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r536225"
-	ClangDefaultShortVersion = "19"
+	ClangDefaultVersion      = "clang-r547379"
+	ClangDefaultShortVersion = "20"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
diff --git a/cc/config/x86_linux_bionic_host.go b/cc/config/x86_linux_bionic_host.go
index ddc86c2..d2f88ef 100644
--- a/cc/config/x86_linux_bionic_host.go
+++ b/cc/config/x86_linux_bionic_host.go
@@ -28,7 +28,7 @@
 		"-fno-omit-frame-pointer",
 
 		"-U_FORTIFY_SOURCE",
-		"-D_FORTIFY_SOURCE=2",
+		"-D_FORTIFY_SOURCE=3",
 		"-fstack-protector-strong",
 
 		// From x86_64_device
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 287967c..c3f25aa 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -29,7 +29,7 @@
 		"-fno-omit-frame-pointer",
 
 		"-U_FORTIFY_SOURCE",
-		"-D_FORTIFY_SOURCE=2",
+		"-D_FORTIFY_SOURCE=3",
 		"-fstack-protector",
 
 		"--gcc-toolchain=${LinuxGccRoot}",
@@ -41,7 +41,6 @@
 	}
 
 	linuxMuslCflags = []string{
-		"-D_LIBCPP_HAS_MUSL_LIBC",
 		"-DANDROID_HOST_MUSL",
 		"-nostdlibinc",
 		"--sysroot /dev/null",
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index a4d43b9..505ddfa 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -106,10 +106,13 @@
 	}
 
 	windowsAvailableLibraries = addPrefix([]string{
+		"bcrypt",
+		"dbghelp",
 		"gdi32",
 		"imagehlp",
 		"iphlpapi",
 		"netapi32",
+		"ntdll",
 		"oleaut32",
 		"ole32",
 		"opengl32",
diff --git a/cc/coverage.go b/cc/coverage.go
index a7618dd..757641c 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -145,7 +145,7 @@
 	// Even if we don't have coverage enabled, if any of our object files were compiled
 	// with coverage, then we need to add --coverage to our ldflags.
 	if !cov.linkCoverage {
-		if ctx.static() && !ctx.staticBinary() {
+		if ctx.staticLibrary() {
 			// For static libraries, the only thing that changes our object files
 			// are included whole static libraries, so check to see if any of
 			// those have coverage enabled.
@@ -273,8 +273,6 @@
 
 type coverageTransitionMutator struct{}
 
-var _ android.TransitionMutator = (*coverageTransitionMutator)(nil)
-
 func (c coverageTransitionMutator) Split(ctx android.BaseModuleContext) []string {
 	if c, ok := ctx.Module().(*Module); ok && c.coverage != nil {
 		if c.coverage.Properties.NeedCoverageVariant {
@@ -354,10 +352,10 @@
 	}
 }
 
-func parseSymbolFileForAPICoverage(ctx ModuleContext, symbolFile string) android.ModuleOutPath {
+func ParseSymbolFileForAPICoverage(ctx android.ModuleContext, symbolFile string) android.ModuleOutPath {
 	apiLevelsJson := android.GetApiLevelsJson(ctx)
 	symbolFilePath := android.PathForModuleSrc(ctx, symbolFile)
-	outputFile := ctx.baseModuleName() + ".xml"
+	outputFile := ctx.Module().(LinkableInterface).BaseModuleName() + ".xml"
 	parsedApiCoveragePath := android.PathForModuleOut(ctx, outputFile)
 	rule := android.NewRuleBuilder(pctx, ctx)
 	rule.Command().
diff --git a/cc/fdo_profile.go b/cc/fdo_profile.go
index 1a33957..c79ea10 100644
--- a/cc/fdo_profile.go
+++ b/cc/fdo_profile.go
@@ -17,6 +17,8 @@
 import (
 	"android/soong/android"
 	"github.com/google/blueprint"
+
+	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -34,7 +36,7 @@
 }
 
 type fdoProfileProperties struct {
-	Profile *string `android:"arch_variant"`
+	Profile proptools.Configurable[string] `android:"arch_variant,replace_instead_of_append"`
 }
 
 // FdoProfileInfo is provided by FdoProfileProvider
@@ -47,8 +49,9 @@
 
 // GenerateAndroidBuildActions of fdo_profile does not have any build actions
 func (fp *fdoProfile) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	if fp.properties.Profile != nil {
-		path := android.PathForModuleSrc(ctx, *fp.properties.Profile)
+	profile := fp.properties.Profile.GetOrDefault(ctx, "")
+	if profile != "" {
+		path := android.PathForModuleSrc(ctx, profile)
 		android.SetProvider(ctx, FdoProfileProvider, FdoProfileInfo{
 			Path: path,
 		})
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 8a974c0..79874fc 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -211,29 +211,30 @@
 	moduleInfoJSON.Class = []string{"EXECUTABLES"}
 }
 
-// IsValidSharedDependency takes a module and determines if it is a unique shared library
+// isValidSharedDependency takes a module and determines if it is a unique shared library
 // that should be installed in the fuzz target output directories. This function
 // returns true, unless:
 //   - The module is not an installable shared library, or
 //   - The module is a header or stub, or
 //   - The module is a prebuilt and its source is available, or
 //   - The module is a versioned member of an SDK snapshot.
-func IsValidSharedDependency(dependency android.Module) bool {
+func isValidSharedDependency(ctx android.ModuleContext, dependency android.ModuleProxy) bool {
 	// TODO(b/144090547): We should be parsing these modules using
 	// ModuleDependencyTag instead of the current brute-force checking.
 
-	linkable, ok := dependency.(LinkableInterface)
-	if !ok || !linkable.CcLibraryInterface() {
+	linkable, ok := android.OtherModuleProvider(ctx, dependency, LinkableInfoProvider)
+	if !ok || !linkable.CcLibraryInterface {
 		// Discard non-linkables.
 		return false
 	}
 
-	if !linkable.Shared() {
+	if !linkable.Shared {
 		// Discard static libs.
 		return false
 	}
 
-	if lib := moduleLibraryInterface(dependency); lib != nil && lib.buildStubs() && linkable.CcLibrary() {
+	ccInfo, hasCcInfo := android.OtherModuleProvider(ctx, dependency, CcInfoProvider)
+	if hasCcInfo && ccInfo.LibraryInfo != nil && ccInfo.LibraryInfo.BuildStubs && linkable.CcLibrary {
 		// Discard stubs libs (only CCLibrary variants). Prebuilt libraries should not
 		// be excluded on the basis of they're not CCLibrary()'s.
 		return false
@@ -242,13 +243,13 @@
 	// We discarded module stubs libraries above, but the LLNDK prebuilts stubs
 	// libraries must be handled differently - by looking for the stubDecorator.
 	// Discard LLNDK prebuilts stubs as well.
-	if ccLibrary, isCcLibrary := dependency.(*Module); isCcLibrary {
-		if _, isLLndkStubLibrary := ccLibrary.linker.(*stubDecorator); isLLndkStubLibrary {
+	if hasCcInfo {
+		if ccInfo.LinkerInfo != nil && ccInfo.LinkerInfo.StubDecoratorInfo != nil {
 			return false
 		}
 		// Discard installable:false libraries because they are expected to be absent
 		// in runtime.
-		if !proptools.BoolDefault(ccLibrary.Installable(), true) {
+		if !proptools.BoolDefault(linkable.Installable, true) {
 			return false
 		}
 	}
@@ -256,7 +257,7 @@
 	// If the same library is present both as source and a prebuilt we must pick
 	// only one to avoid a conflict. Always prefer the source since the prebuilt
 	// probably won't be built with sanitizers enabled.
-	if prebuilt := android.GetEmbeddedPrebuilt(dependency); prebuilt != nil && prebuilt.SourceExists() {
+	if prebuilt, ok := android.OtherModuleProvider(ctx, dependency, android.PrebuiltModuleInfoProvider); ok && prebuilt.SourceExists {
 		return false
 	}
 
@@ -288,7 +289,7 @@
 }
 
 func (fuzzBin *fuzzBinary) install(ctx ModuleContext, file android.Path) {
-	fuzzBin.fuzzPackagedModule = PackageFuzzModule(ctx, fuzzBin.fuzzPackagedModule, pctx)
+	fuzzBin.fuzzPackagedModule = PackageFuzzModule(ctx, fuzzBin.fuzzPackagedModule)
 
 	installBase := "fuzz"
 
@@ -345,11 +346,14 @@
 	fuzzBin.binaryDecorator.baseInstaller.install(ctx, file)
 }
 
-func PackageFuzzModule(ctx android.ModuleContext, fuzzPackagedModule fuzz.FuzzPackagedModule, pctx android.PackageContext) fuzz.FuzzPackagedModule {
+func PackageFuzzModule(ctx android.ModuleContext, fuzzPackagedModule fuzz.FuzzPackagedModule) fuzz.FuzzPackagedModule {
 	fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Corpus)
 	fuzzPackagedModule.Corpus = append(fuzzPackagedModule.Corpus, android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Device_common_corpus)...)
 
 	fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Data)
+	fuzzPackagedModule.Data = append(fuzzPackagedModule.Data, android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Device_common_data)...)
+	fuzzPackagedModule.Data = append(fuzzPackagedModule.Data, android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Device_first_data)...)
+	fuzzPackagedModule.Data = append(fuzzPackagedModule.Data, android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Host_common_data)...)
 
 	if fuzzPackagedModule.FuzzProperties.Dictionary != nil {
 		fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *fuzzPackagedModule.FuzzProperties.Dictionary)
@@ -466,43 +470,37 @@
 	// multiple fuzzers that depend on the same shared library.
 	sharedLibraryInstalled := make(map[string]bool)
 
-	ctx.VisitAllModules(func(module android.Module) {
-		ccModule, ok := module.(LinkableInterface)
-		if !ok || ccModule.PreventInstall() {
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+		ccModule, ok := android.OtherModuleProvider(ctx, module, LinkableInfoProvider)
+		if !ok {
 			return
 		}
 		// Discard non-fuzz targets.
-		if ok := fuzz.IsValid(ctx, ccModule.FuzzModuleStruct()); !ok {
+		fuzzInfo, ok := android.OtherModuleProvider(ctx, module, fuzz.FuzzPackagedModuleInfoProvider)
+		if !ok {
 			return
 		}
 
 		sharedLibsInstallDirPrefix := "lib"
-		if ccModule.InVendor() {
+		if ccModule.InVendor {
 			sharedLibsInstallDirPrefix = "lib/vendor"
 		}
 
-		if !ccModule.IsFuzzModule() {
-			return
-		}
-
+		commonInfo := android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider)
+		isHost := commonInfo.Target.Os.Class == android.Host
 		hostOrTargetString := "target"
-		if ccModule.Target().HostCross {
+		if commonInfo.Target.HostCross {
 			hostOrTargetString = "host_cross"
-		} else if ccModule.Host() {
+		} else if isHost {
 			hostOrTargetString = "host"
 		}
 		if s.onlyIncludePresubmits == true {
 			hostOrTargetString = "presubmit-" + hostOrTargetString
 		}
 
-		fpm := fuzz.FuzzPackagedModule{}
-		if ok {
-			fpm = ccModule.FuzzPackagedModule()
-		}
-
 		intermediatePath := "fuzz"
 
-		archString := ccModule.Target().Arch.ArchType.String()
+		archString := commonInfo.Target.Arch.ArchType.String()
 		archDir := android.PathForIntermediates(ctx, intermediatePath, hostOrTargetString, archString)
 		archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
 
@@ -510,23 +508,24 @@
 		builder := android.NewRuleBuilder(pctx, ctx)
 
 		// Package the corpus, data, dict and config into a zipfile.
-		files = s.PackageArtifacts(ctx, module, fpm, archDir, builder)
+		files = s.PackageArtifacts(ctx, module, &fuzzInfo, archDir, builder)
 
 		// Package shared libraries
-		files = append(files, GetSharedLibsToZip(ccModule.FuzzSharedLibraries(), ccModule, &s.FuzzPackager, archString, sharedLibsInstallDirPrefix, &sharedLibraryInstalled)...)
+		files = append(files, GetSharedLibsToZip(ccModule.FuzzSharedLibraries, isHost, ccModule.InVendor, &s.FuzzPackager,
+			archString, sharedLibsInstallDirPrefix, &sharedLibraryInstalled)...)
 
 		// The executable.
-		files = append(files, fuzz.FileToZip{SourceFilePath: android.OutputFileForModule(ctx, ccModule, "unstripped")})
+		files = append(files, fuzz.FileToZip{SourceFilePath: android.OutputFileForModule(ctx, module, "unstripped")})
 
 		if s.onlyIncludePresubmits == true {
-			if fpm.FuzzProperties.Fuzz_config == nil {
+			if fuzzInfo.FuzzConfig == nil {
 				return
 			}
-			if !BoolDefault(fpm.FuzzProperties.Fuzz_config.Use_for_presubmit, false) {
+			if !fuzzInfo.FuzzConfig.UseForPresubmit {
 				return
 			}
 		}
-		archDirs[archOs], ok = s.BuildZipFile(ctx, module, fpm, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
+		archDirs[archOs], ok = s.BuildZipFile(ctx, module, &fuzzInfo, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
 		if !ok {
 			return
 		}
@@ -555,7 +554,8 @@
 
 // GetSharedLibsToZip finds and marks all the transiently-dependent shared libraries for
 // packaging.
-func GetSharedLibsToZip(sharedLibraries android.RuleBuilderInstalls, module LinkableInterface, s *fuzz.FuzzPackager, archString string, destinationPathPrefix string, sharedLibraryInstalled *map[string]bool) []fuzz.FileToZip {
+func GetSharedLibsToZip(sharedLibraries android.RuleBuilderInstalls, isHost bool, inVendor bool, s *fuzz.FuzzPackager,
+	archString string, destinationPathPrefix string, sharedLibraryInstalled *map[string]bool) []fuzz.FileToZip {
 	var files []fuzz.FileToZip
 
 	fuzzDir := "fuzz"
@@ -573,7 +573,7 @@
 		// install it to the output directory. Setup the install destination here,
 		// which will be used by $(copy-many-files) in the Make backend.
 		installDestination := SharedLibraryInstallLocation(
-			install, module.Host(), module.InVendor(), fuzzDir, archString)
+			install, isHost, inVendor, fuzzDir, archString)
 		if (*sharedLibraryInstalled)[installDestination] {
 			continue
 		}
@@ -590,8 +590,8 @@
 		// dir. Symbolized DSO's are always installed to the device when fuzzing, but
 		// we want symbolization tools (like `stack`) to be able to find the symbols
 		// in $ANDROID_PRODUCT_OUT/symbols automagically.
-		if !module.Host() {
-			symbolsInstallDestination := SharedLibrarySymbolsInstallLocation(install, module.InVendor(), fuzzDir, archString)
+		if !isHost {
+			symbolsInstallDestination := SharedLibrarySymbolsInstallLocation(install, inVendor, fuzzDir, archString)
 			symbolsInstallDestination = strings.ReplaceAll(symbolsInstallDestination, "$", "$$")
 			s.SharedLibInstallStrings = append(s.SharedLibInstallStrings,
 				library.String()+":"+symbolsInstallDestination)
@@ -605,17 +605,17 @@
 // VisitDirectDeps is used first to avoid incorrectly using the core libraries (sanitizer
 // runtimes, libc, libdl, etc.) from a dependency. This may cause issues when dependencies
 // have explicit sanitizer tags, as we may get a dependency on an unsanitized libc, etc.
-func CollectAllSharedDependencies(ctx android.ModuleContext) (android.RuleBuilderInstalls, []android.Module) {
+func CollectAllSharedDependencies(ctx android.ModuleContext) (android.RuleBuilderInstalls, []android.ModuleProxy) {
 	seen := make(map[string]bool)
 	recursed := make(map[string]bool)
-	deps := []android.Module{}
+	deps := []android.ModuleProxy{}
 
 	var sharedLibraries android.RuleBuilderInstalls
 
 	// Enumerate the first level of dependencies, as we discard all non-library
 	// modules in the BFS loop below.
-	ctx.VisitDirectDeps(func(dep android.Module) {
-		if !IsValidSharedDependency(dep) {
+	ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
+		if !isValidSharedDependency(ctx, dep) {
 			return
 		}
 		sharedLibraryInfo, hasSharedLibraryInfo := android.OtherModuleProvider(ctx, dep, SharedLibraryInfoProvider)
@@ -633,19 +633,21 @@
 		sharedLibraries = append(sharedLibraries, ruleBuilderInstall)
 	})
 
-	ctx.WalkDeps(func(child, parent android.Module) bool {
+	ctx.WalkDepsProxy(func(child, _ android.ModuleProxy) bool {
 
 		// If this is a Rust module which is not rust_ffi_shared, we still want to bundle any transitive
-		// shared dependencies (even for rust_ffi_rlib or rust_ffi_static)
-		if rustmod, ok := child.(LinkableInterface); ok && rustmod.RustLibraryInterface() && !rustmod.Shared() {
-			if recursed[ctx.OtherModuleName(child)] {
-				return false
+		// shared dependencies (even for rust_ffi_static)
+		if info, ok := android.OtherModuleProvider(ctx, child, LinkableInfoProvider); ok {
+			if info.RustLibraryInterface && !info.Shared {
+				if recursed[ctx.OtherModuleName(child)] {
+					return false
+				}
+				recursed[ctx.OtherModuleName(child)] = true
+				return true
 			}
-			recursed[ctx.OtherModuleName(child)] = true
-			return true
 		}
 
-		if !IsValidSharedDependency(child) {
+		if !isValidSharedDependency(ctx, child) {
 			return false
 		}
 		sharedLibraryInfo, hasSharedLibraryInfo := android.OtherModuleProvider(ctx, child, SharedLibraryInfoProvider)
diff --git a/cc/gen_test.go b/cc/gen_test.go
index 439f0a9..dde0dcf 100644
--- a/cc/gen_test.go
+++ b/cc/gen_test.go
@@ -33,8 +33,8 @@
 			],
 		}`)
 
-		aidl := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("aidl")
-		libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Module().(*Module)
+		aidl := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_shared").Rule("aidl")
+		libfoo := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_shared").Module().(*Module)
 
 		expected := "-I" + filepath.Dir(aidl.Output.String())
 		actual := android.StringsRelativeToTop(ctx.Config(), libfoo.flags.Local.CommonFlags)
@@ -59,9 +59,9 @@
 			],
 		}`)
 
-		aidl := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("aidl")
-		aidlManifest := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Output("aidl.sbox.textproto")
-		libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Module().(*Module)
+		aidl := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_shared").Rule("aidl")
+		aidlManifest := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_shared").Output("aidl.sbox.textproto")
+		libfoo := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_shared").Module().(*Module)
 
 		if !inList("-I"+filepath.Dir(aidl.Output.String()), android.StringsRelativeToTop(ctx.Config(), libfoo.flags.Local.CommonFlags)) {
 			t.Errorf("missing aidl includes in global flags")
@@ -84,7 +84,7 @@
 		}`)
 
 		outDir := "out/soong/.intermediates/libsysprop/android_arm64_armv8-a_static/gen"
-		syspropBuildParams := ctx.ModuleForTests("libsysprop", "android_arm64_armv8-a_static").Rule("sysprop")
+		syspropBuildParams := ctx.ModuleForTests(t, "libsysprop", "android_arm64_armv8-a_static").Rule("sysprop")
 
 		android.AssertStringEquals(t, "header output directory does not match", outDir+"/sysprop/include/path/to", syspropBuildParams.Args["headerOutDir"])
 		android.AssertStringEquals(t, "public output directory does not match", outDir+"/sysprop/public/include/path/to", syspropBuildParams.Args["publicOutDir"])
diff --git a/cc/genrule_test.go b/cc/genrule_test.go
index 9a8049b..4e700a2 100644
--- a/cc/genrule_test.go
+++ b/cc/genrule_test.go
@@ -17,6 +17,7 @@
 import (
 	"reflect"
 	"slices"
+	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -64,13 +65,13 @@
 		t.Fatal(errs)
 	}
 
-	gen := ctx.ModuleForTests("gen", "android_arm_armv7-a-neon").Output("out_arm")
+	gen := ctx.ModuleForTests(t, "gen", "android_arm_armv7-a-neon").Output("out_arm")
 	expected := []string{"foo"}
 	if !reflect.DeepEqual(expected, gen.Implicits.Strings()[:len(expected)]) {
 		t.Errorf(`want arm inputs %v, got %v`, expected, gen.Implicits.Strings())
 	}
 
-	gen = ctx.ModuleForTests("gen", "android_arm64_armv8-a").Output("out_arm")
+	gen = ctx.ModuleForTests(t, "gen", "android_arm64_armv8-a").Output("out_arm")
 	expected = []string{"bar"}
 	if !reflect.DeepEqual(expected, gen.Implicits.Strings()[:len(expected)]) {
 		t.Errorf(`want arm64 inputs %v, got %v`, expected, gen.Implicits.Strings())
@@ -105,7 +106,7 @@
 		`
 	ctx := testCc(t, bp)
 
-	gen := ctx.ModuleForTests("gen", "android_arm_armv7-a-neon").Output("out")
+	gen := ctx.ModuleForTests(t, "gen", "android_arm_armv7-a-neon").Output("out")
 	expected := []string{"libboth.so", "libshared.so", "libstatic.a"}
 	var got []string
 	for _, input := range gen.Implicits {
@@ -178,7 +179,7 @@
 				PrepareForIntegrationTestWithCc,
 				android.OptionalFixturePreparer(tt.preparer),
 			).RunTestWithBp(t, bp)
-			gen := result.ModuleForTests("gen", tt.variant)
+			gen := result.ModuleForTests(t, "gen", tt.variant)
 			sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, gen.Output("genrule.sbox.textproto"))
 			cmd := *sboxProto.Commands[0].Command
 			android.AssertStringDoesContain(t, "incorrect CC_ARCH", cmd, "CC_ARCH="+tt.arch+" ")
@@ -236,7 +237,7 @@
 	}
 	`
 	result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, bp)
-	gen_32bit := result.ModuleForTests("gen", "android_arm_armv7-a-neon").OutputFiles(result.TestContext, t, "")
+	gen_32bit := result.ModuleForTests(t, "gen", "android_arm_armv7-a-neon").OutputFiles(result.TestContext, t, "")
 	android.AssertPathsEndWith(t,
 		"genrule_out",
 		[]string{
@@ -245,7 +246,7 @@
 		gen_32bit,
 	)
 
-	gen_64bit := result.ModuleForTests("gen", "android_arm64_armv8-a").OutputFiles(result.TestContext, t, "")
+	gen_64bit := result.ModuleForTests(t, "gen", "android_arm64_armv8-a").OutputFiles(result.TestContext, t, "")
 	android.AssertPathsEndWith(t,
 		"genrule_out",
 		[]string{
@@ -254,3 +255,42 @@
 		gen_64bit,
 	)
 }
+
+// Test that a genrule can depend on a tool with symlinks. The symlinks are ignored, but
+// at least it doesn't cause errors.
+func TestGenruleToolWithSymlinks(t *testing.T) {
+	bp := `
+	genrule {
+		name: "gen",
+		tools: ["tool_with_symlinks"],
+		cmd: "$(location tool_with_symlinks) $(in) $(out)",
+		out: ["out"],
+	}
+
+	cc_binary_host {
+		name: "tool_with_symlinks",
+		symlinks: ["symlink1", "symlink2"],
+	}
+	`
+	ctx := PrepareForIntegrationTestWithCc.
+		ExtendWithErrorHandler(android.FixtureExpectsNoErrors).
+		RunTestWithBp(t, bp)
+	gen := ctx.ModuleForTests(t, "gen", "").Output("out")
+	toolFound := false
+	symlinkFound := false
+	for _, dep := range gen.RuleParams.CommandDeps {
+		if strings.HasSuffix(dep, "/tool_with_symlinks") {
+			toolFound = true
+		}
+		if strings.HasSuffix(dep, "/symlink1") || strings.HasSuffix(dep, "/symlink2") {
+			symlinkFound = true
+		}
+	}
+	if !toolFound {
+		t.Errorf("Tool not found")
+	}
+	// We may want to change genrules to include symlinks later
+	if symlinkFound {
+		t.Errorf("Symlinks found")
+	}
+}
diff --git a/cc/installer.go b/cc/installer.go
index 30f9612..d7d8c6d 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -107,6 +107,10 @@
 	installer.installDeps = append(installer.installDeps, installedData...)
 }
 
+func (installer *baseInstaller) installStandaloneTestDep(ctx ModuleContext, standaloneTestDep android.PackagingSpec) {
+	installer.installTestData(ctx, []android.DataPath{{SrcPath: standaloneTestDep.ToGob().SrcPath, RelativeInstallPath: "standalone-libs"}})
+}
+
 func (installer *baseInstaller) everInstallable() bool {
 	// Most cc modules are installable.
 	return true
diff --git a/cc/library.go b/cc/library.go
index ef3ccd2..7b85486 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -25,6 +25,7 @@
 	"sync"
 
 	"android/soong/android"
+	"android/soong/cc/config"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/depset"
@@ -34,6 +35,8 @@
 
 // LibraryProperties is a collection of properties shared by cc library rules/cc.
 type LibraryProperties struct {
+	// local file name to pass to the linker as -exported_symbols_list
+	Exported_symbols_list *string `android:"path,arch_variant"`
 	// local file name to pass to the linker as -unexported_symbols_list
 	Unexported_symbols_list *string `android:"path,arch_variant"`
 	// local file name to pass to the linker as -force_symbols_not_weak_list
@@ -62,21 +65,7 @@
 	Static_ndk_lib *bool
 
 	// Generate stubs to make this library accessible to APEXes.
-	Stubs struct {
-		// Relative path to the symbol map. The symbol map provides the list of
-		// symbols that are exported for stubs variant of this library.
-		Symbol_file *string `android:"path,arch_variant"`
-
-		// List versions to generate stubs libs for. The version name "current" is always
-		// implicitly added.
-		Versions []string
-
-		// Whether to not require the implementation of the library to be installed if a
-		// client of the stubs is installed. Defaults to true; set to false if the
-		// implementation is made available by some other means, e.g. in a Microdroid
-		// virtual machine.
-		Implementation_installable *bool
-	} `android:"arch_variant"`
+	Stubs StubsProperties `android:"arch_variant"`
 
 	// set the name of the output
 	Stem *string `android:"arch_variant"`
@@ -125,6 +114,22 @@
 	Vendor_public_library vendorPublicLibraryProperties
 }
 
+type StubsProperties struct {
+	// Relative path to the symbol map. The symbol map provides the list of
+	// symbols that are exported for stubs variant of this library.
+	Symbol_file *string `android:"path,arch_variant"`
+
+	// List versions to generate stubs libs for. The version name "current" is always
+	// implicitly added.
+	Versions []string
+
+	// Whether to not require the implementation of the library to be installed if a
+	// client of the stubs is installed. Defaults to true; set to false if the
+	// implementation is made available by some other means, e.g. in a Microdroid
+	// virtual machine.
+	Implementation_installable *bool
+}
+
 // StaticProperties is a properties stanza to affect only attributes of the "static" variants of a
 // library module.
 type StaticProperties struct {
@@ -218,6 +223,7 @@
 
 func RegisterLibraryBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("cc_library_static", LibraryStaticFactory)
+	ctx.RegisterModuleType("cc_rustlibs_for_make", LibraryMakeRustlibsFactory)
 	ctx.RegisterModuleType("cc_library_shared", LibrarySharedFactory)
 	ctx.RegisterModuleType("cc_library", LibraryFactory)
 	ctx.RegisterModuleType("cc_library_host_static", LibraryHostStaticFactory)
@@ -247,6 +253,19 @@
 	return module.Init()
 }
 
+// cc_rustlibs_for_make creates a static library which bundles together rust_ffi_static
+// deps for Make. This should not be depended on in Soong, and is probably not the
+// module you need unless you are sure of what you're doing. These should only
+// be declared as dependencies in Make. To ensure inclusion, rust_ffi_static modules
+// should be declared in the whole_static_libs property.
+func LibraryMakeRustlibsFactory() android.Module {
+	module, library := NewLibrary(android.HostAndDeviceSupported)
+	library.BuildOnlyStatic()
+	library.wideStaticlibForMake = true
+	module.sdkMemberTypes = []android.SdkMemberType{staticLibrarySdkMemberType}
+	return module.Init()
+}
+
 // cc_library_shared creates a shared library for a device and/or host.
 func LibrarySharedFactory() android.Module {
 	module, library := NewLibrary(android.HostAndDeviceSupported)
@@ -421,8 +440,8 @@
 	// Location of the linked, stripped library for shared libraries, strip: "all"
 	strippedAllOutputFile android.Path
 
-	// Location of the file that should be copied to dist dir when requested
-	distFile android.Path
+	// Location of the file that should be copied to dist dir when no explicit tag is requested
+	defaultDistFile android.Path
 
 	versionScriptPath android.OptionalPath
 
@@ -439,6 +458,10 @@
 
 	// Path to the file containing the APIs exported by this library
 	stubsSymbolFilePath android.Path
+
+	// Forces production of the generated Rust staticlib for cc_library_static.
+	// Intended to be used to provide these generated staticlibs for Make.
+	wideStaticlibForMake bool
 }
 
 // linkerProps returns the list of properties structs relevant for this library. (For example, if
@@ -462,11 +485,15 @@
 	return props
 }
 
-// linkerFlags takes a Flags struct and augments it to contain linker flags that are defined by this
-// library, or that are implied by attributes of this library (such as whether this library is a
-// shared library).
-func (library *libraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
-	flags = library.baseLinker.linkerFlags(ctx, flags)
+func CommonLibraryLinkerFlags(ctx android.ModuleContext, flags Flags,
+	toolchain config.Toolchain, libName string) Flags {
+
+	mod, ok := ctx.Module().(LinkableInterface)
+
+	if !ok {
+		ctx.ModuleErrorf("trying to add linker flags to a non-LinkableInterface module.")
+		return flags
+	}
 
 	// MinGW spits out warnings about -fPIC even for -fpie?!) being ignored because
 	// all code is position independent, and then those warnings get promoted to
@@ -474,27 +501,18 @@
 	if !ctx.Windows() {
 		flags.Global.CFlags = append(flags.Global.CFlags, "-fPIC")
 	}
-
-	if library.static() {
-		flags.Local.CFlags = append(flags.Local.CFlags, library.StaticProperties.Static.Cflags.GetOrDefault(ctx, nil)...)
-	} else if library.shared() {
-		flags.Local.CFlags = append(flags.Local.CFlags, library.SharedProperties.Shared.Cflags.GetOrDefault(ctx, nil)...)
-	}
-
-	if library.shared() {
-		libName := library.getLibName(ctx)
+	if mod.Shared() {
 		var f []string
-		if ctx.toolchain().Bionic() {
+		if toolchain.Bionic() {
 			f = append(f,
 				"-nostdlib",
 				"-Wl,--gc-sections",
 			)
 		}
-
 		if ctx.Darwin() {
 			f = append(f,
 				"-dynamiclib",
-				"-install_name @rpath/"+libName+flags.Toolchain.ShlibSuffix(),
+				"-install_name @rpath/"+libName+toolchain.ShlibSuffix(),
 			)
 			if ctx.Arch().ArchType == android.X86 {
 				f = append(f,
@@ -504,16 +522,30 @@
 		} else {
 			f = append(f, "-shared")
 			if !ctx.Windows() {
-				f = append(f, "-Wl,-soname,"+libName+flags.Toolchain.ShlibSuffix())
+				f = append(f, "-Wl,-soname,"+libName+toolchain.ShlibSuffix())
 			}
 		}
-
 		flags.Global.LdFlags = append(flags.Global.LdFlags, f...)
 	}
 
 	return flags
 }
 
+// linkerFlags takes a Flags struct and augments it to contain linker flags that are defined by this
+// library, or that are implied by attributes of this library (such as whether this library is a
+// shared library).
+func (library *libraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+	flags = library.baseLinker.linkerFlags(ctx, flags)
+	flags = CommonLibraryLinkerFlags(ctx, flags, ctx.toolchain(), library.getLibName(ctx))
+	if library.static() {
+		flags.Local.CFlags = append(flags.Local.CFlags, library.StaticProperties.Static.Cflags.GetOrDefault(ctx, nil)...)
+	} else if library.shared() {
+		flags.Local.CFlags = append(flags.Local.CFlags, library.SharedProperties.Shared.Cflags.GetOrDefault(ctx, nil)...)
+	}
+
+	return flags
+}
+
 // compilerFlags takes a Flags and augments it to contain compile flags from global values,
 // per-target values, module type values, per-module Blueprints properties, extra flags from
 // `flags`, and generated sources from `deps`.
@@ -532,7 +564,7 @@
 		// Wipe all the module-local properties, leaving only the global properties.
 		flags.Local = LocalOrGlobalFlags{}
 	}
-	if library.buildStubs() {
+	if library.BuildStubs() {
 		// Remove -include <file> when compiling stubs. Otherwise, the force included
 		// headers might cause conflicting types error with the symbols in the
 		// generated stubs source code. e.g.
@@ -551,7 +583,7 @@
 		flags.Local.CommonFlags = removeInclude(flags.Local.CommonFlags)
 		flags.Local.CFlags = removeInclude(flags.Local.CFlags)
 
-		flags = addStubLibraryCompilerFlags(flags)
+		flags = AddStubLibraryCompilerFlags(flags)
 	}
 	return flags
 }
@@ -572,6 +604,8 @@
 }
 
 func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
+	sharedFlags := ctx.getSharedFlags()
+
 	if ctx.IsLlndk() {
 		// Get the matching SDK version for the vendor API level.
 		version, err := android.GetSdkVersionForVendorApiLevel(ctx.Config().VendorApiLevel())
@@ -579,28 +613,40 @@
 			panic(err)
 		}
 
+		llndkFlag := "--llndk"
+		if ctx.baseModuleName() == "libbinder_ndk" && ctx.inProduct() {
+			// This is a special case only for the libbinder_ndk. As the product partition is in the
+			// framework side along with system and system_ext partitions in Treble, libbinder_ndk
+			// provides different binder interfaces between product and vendor modules.
+			// In libbinder_ndk, 'llndk' annotation is for the vendor APIs; while 'systemapi'
+			// annotation is for the product APIs.
+			// Use '--systemapi' flag for building the llndk stub of product variant for the
+			// libbinder_ndk.
+			llndkFlag = "--systemapi"
+		}
+
 		// This is the vendor variant of an LLNDK library, build the LLNDK stubs.
-		nativeAbiResult := parseNativeAbiDefinition(ctx,
+		nativeAbiResult := ParseNativeAbiDefinition(ctx,
 			String(library.Properties.Llndk.Symbol_file),
-			nativeClampedApiLevel(ctx, version), "--llndk")
-		objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
+			nativeClampedApiLevel(ctx, version), llndkFlag)
+		objs := CompileStubLibrary(ctx, flags, nativeAbiResult.StubSrc, sharedFlags)
 		if !Bool(library.Properties.Llndk.Unversioned) {
 			library.versionScriptPath = android.OptionalPathForPath(
-				nativeAbiResult.versionScript)
+				nativeAbiResult.VersionScript)
 		}
 		return objs
 	}
 	if ctx.IsVendorPublicLibrary() {
-		nativeAbiResult := parseNativeAbiDefinition(ctx,
+		nativeAbiResult := ParseNativeAbiDefinition(ctx,
 			String(library.Properties.Vendor_public_library.Symbol_file),
 			android.FutureApiLevel, "")
-		objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
+		objs := CompileStubLibrary(ctx, flags, nativeAbiResult.StubSrc, sharedFlags)
 		if !Bool(library.Properties.Vendor_public_library.Unversioned) {
-			library.versionScriptPath = android.OptionalPathForPath(nativeAbiResult.versionScript)
+			library.versionScriptPath = android.OptionalPathForPath(nativeAbiResult.VersionScript)
 		}
 		return objs
 	}
-	if library.buildStubs() {
+	if library.BuildStubs() {
 		return library.compileModuleLibApiStubs(ctx, flags, deps)
 	}
 
@@ -621,10 +667,17 @@
 	}
 	if library.sabi.shouldCreateSourceAbiDump() {
 		dirs := library.exportedIncludeDirsForAbiCheck(ctx)
-		flags.SAbiFlags = make([]string, 0, len(dirs))
+		flags.SAbiFlags = make([]string, 0, len(dirs)+1)
 		for _, dir := range dirs {
 			flags.SAbiFlags = append(flags.SAbiFlags, "-I"+dir)
 		}
+		// If this library does not export any include directory, do not append the flags
+		// so that the ABI tool dumps everything without filtering by the include directories.
+		// requiresGlobalIncludes returns whether this library can include CommonGlobalIncludes.
+		// If the library cannot include them, it cannot export them.
+		if len(dirs) > 0 && requiresGlobalIncludes(ctx) {
+			flags.SAbiFlags = append(flags.SAbiFlags, "${config.CommonGlobalIncludes}")
+		}
 		totalLength := len(srcs) + len(deps.GeneratedSources) +
 			len(sharedSrcs) + len(staticSrcs)
 		if totalLength > 0 {
@@ -640,18 +693,65 @@
 		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceStaticLibrary, srcs,
 			android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Tidy_disabled_srcs),
 			android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Tidy_timeout_srcs),
-			library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
+			library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps, sharedFlags))
 	} else if library.shared() {
 		srcs := android.PathsForModuleSrc(ctx, sharedSrcs)
 		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceSharedLibrary, srcs,
 			android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Tidy_disabled_srcs),
 			android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Tidy_timeout_srcs),
-			library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
+			library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps, sharedFlags))
 	}
 
 	return objs
 }
 
+type ApiStubsParams struct {
+	NotInPlatform  bool
+	IsNdk          bool
+	BaseModuleName string
+	ModuleName     string
+}
+
+// GetApiStubsFlags calculates the genstubFlags string to pass to ParseNativeAbiDefinition
+func GetApiStubsFlags(api ApiStubsParams) string {
+	var flag string
+
+	// b/239274367 --apex and --systemapi filters symbols tagged with # apex and #
+	// systemapi, respectively. The former is for symbols defined in platform libraries
+	// and the latter is for symbols defined in APEXes.
+	// A single library can contain either # apex or # systemapi, but not both.
+	// The stub generator (ndkstubgen) is additive, so passing _both_ of these to it should be a no-op.
+	// However, having this distinction helps guard accidental
+	// promotion or demotion of API and also helps the API review process b/191371676
+	if api.NotInPlatform {
+		flag = "--apex"
+	} else {
+		flag = "--systemapi"
+	}
+
+	// b/184712170, unless the lib is an NDK library, exclude all public symbols from
+	// the stub so that it is mandated that all symbols are explicitly marked with
+	// either apex or systemapi.
+	if !api.IsNdk &&
+		// the symbol files of libclang libs are autogenerated and do not contain systemapi tags
+		// TODO (spandandas): Update mapfile.py to include #systemapi tag on all symbols
+		!strings.Contains(api.ModuleName, "libclang_rt") {
+		flag = flag + " --no-ndk"
+	}
+
+	// TODO(b/361303067): Remove this special case if bionic/ projects are added to ART development branches.
+	if isBionic(api.BaseModuleName) {
+		// set the flags explicitly for bionic libs.
+		// this is necessary for development in minimal branches which does not contain bionic/*.
+		// In such minimal branches, e.g. on the prebuilt libc stubs
+		// 1. IsNdk will return false (since the ndk_library definition for libc does not exist)
+		// 2. NotInPlatform will return true (since the source com.android.runtime does not exist)
+		flag = "--apex"
+	}
+
+	return flag
+}
+
 // Compile stubs for the API surface between platform and apex
 // This method will be used by source and prebuilt cc module types.
 func (library *libraryDecorator) compileModuleLibApiStubs(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
@@ -659,56 +759,34 @@
 	if library.Properties.Stubs.Symbol_file == nil {
 		return Objects{}
 	}
+
 	symbolFile := String(library.Properties.Stubs.Symbol_file)
 	library.stubsSymbolFilePath = android.PathForModuleSrc(ctx, symbolFile)
-	// b/239274367 --apex and --systemapi filters symbols tagged with # apex and #
-	// systemapi, respectively. The former is for symbols defined in platform libraries
-	// and the latter is for symbols defined in APEXes.
-	// A single library can contain either # apex or # systemapi, but not both.
-	// The stub generator (ndkstubgen) is additive, so passing _both_ of these to it should be a no-op.
-	// However, having this distinction helps guard accidental
-	// promotion or demotion of API and also helps the API review process b/191371676
-	var flag string
-	if ctx.notInPlatform() {
-		flag = "--apex"
-	} else {
-		flag = "--systemapi"
+
+	apiParams := ApiStubsParams{
+		NotInPlatform:  ctx.notInPlatform(),
+		IsNdk:          ctx.Module().(*Module).IsNdk(ctx.Config()),
+		BaseModuleName: ctx.baseModuleName(),
+		ModuleName:     ctx.ModuleName(),
 	}
-	// b/184712170, unless the lib is an NDK library, exclude all public symbols from
-	// the stub so that it is mandated that all symbols are explicitly marked with
-	// either apex or systemapi.
-	if !ctx.Module().(*Module).IsNdk(ctx.Config()) &&
-		// the symbol files of libclang libs are autogenerated and do not contain systemapi tags
-		// TODO (spandandas): Update mapfile.py to include #systemapi tag on all symbols
-		!strings.Contains(ctx.ModuleName(), "libclang_rt") {
-		flag = flag + " --no-ndk"
-	}
-	// TODO(b/361303067): Remove this special case if bionic/ projects are added to ART development branches.
-	if isBionic(ctx.baseModuleName()) {
-		// set the flags explicitly for bionic libs.
-		// this is necessary for development in minimal branches which does not contain bionic/*.
-		// In such minimal branches, e.g. on the prebuilt libc stubs
-		// 1. IsNdk will return false (since the ndk_library definition for libc does not exist)
-		// 2. NotInPlatform will return true (since the source com.android.runtime does not exist)
-		flag = "--apex"
-	}
-	nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile,
+	flag := GetApiStubsFlags(apiParams)
+
+	nativeAbiResult := ParseNativeAbiDefinition(ctx, symbolFile,
 		android.ApiLevelOrPanic(ctx, library.MutatedProperties.StubsVersion), flag)
-	objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
+	objs := CompileStubLibrary(ctx, flags, nativeAbiResult.StubSrc, ctx.getSharedFlags())
 
 	library.versionScriptPath = android.OptionalPathForPath(
-		nativeAbiResult.versionScript)
-
+		nativeAbiResult.VersionScript)
 	// Parse symbol file to get API list for coverage
-	if library.stubsVersion() == "current" && ctx.PrimaryArch() && !ctx.inRecovery() && !ctx.inProduct() && !ctx.inVendor() {
-		library.apiListCoverageXmlPath = parseSymbolFileForAPICoverage(ctx, symbolFile)
+	if library.StubsVersion() == "current" && ctx.PrimaryArch() && !ctx.inRecovery() && !ctx.inProduct() && !ctx.inVendor() {
+		library.apiListCoverageXmlPath = ParseSymbolFileForAPICoverage(ctx, symbolFile)
 	}
 
 	return objs
 }
 
 type libraryInterface interface {
-	versionedInterface
+	VersionedInterface
 
 	static() bool
 	shared() bool
@@ -730,34 +808,51 @@
 	// Write LOCAL_ADDITIONAL_DEPENDENCIES for ABI diff
 	androidMkWriteAdditionalDependenciesForSourceAbiDiff(w io.Writer)
 
-	availableFor(string) bool
+	apexAvailable() []string
 
-	getAPIListCoverageXMLPath() android.ModuleOutPath
+	setAPIListCoverageXMLPath(out android.ModuleOutPath)
+	symbolsFile() *string
+	setSymbolFilePath(path android.Path)
+	setVersionScriptPath(path android.OptionalPath)
 
 	installable() *bool
 }
 
-type versionedInterface interface {
-	buildStubs() bool
-	setBuildStubs(isLatest bool)
-	hasStubsVariants() bool
-	isStubsImplementationRequired() bool
-	setStubsVersion(string)
-	stubsVersion() string
+func (library *libraryDecorator) symbolsFile() *string {
+	return library.Properties.Stubs.Symbol_file
+}
 
-	stubsVersions(ctx android.BaseModuleContext) []string
-	setAllStubsVersions([]string)
-	allStubsVersions() []string
+func (library *libraryDecorator) setSymbolFilePath(path android.Path) {
+	library.stubsSymbolFilePath = path
+}
 
-	implementationModuleName(name string) string
-	hasLLNDKStubs() bool
-	hasLLNDKHeaders() bool
-	hasVendorPublicLibrary() bool
-	isLLNDKMovedToApex() bool
+func (library *libraryDecorator) setVersionScriptPath(path android.OptionalPath) {
+	library.versionScriptPath = path
+}
+
+type VersionedInterface interface {
+	BuildStubs() bool
+	SetBuildStubs(isLatest bool)
+	HasStubsVariants() bool
+	IsStubsImplementationRequired() bool
+	SetStubsVersion(string)
+	StubsVersion() string
+
+	StubsVersions(ctx android.BaseModuleContext) []string
+	SetAllStubsVersions([]string)
+	AllStubsVersions() []string
+
+	ImplementationModuleName(name string) string
+	HasLLNDKStubs() bool
+	HasLLNDKHeaders() bool
+	HasVendorPublicLibrary() bool
+	IsLLNDKMovedToApex() bool
+
+	GetAPIListCoverageXMLPath() android.ModuleOutPath
 }
 
 var _ libraryInterface = (*libraryDecorator)(nil)
-var _ versionedInterface = (*libraryDecorator)(nil)
+var _ VersionedInterface = (*libraryDecorator)(nil)
 
 func (library *libraryDecorator) getLibNameHelper(baseModuleName string, inVendor bool, inProduct bool) string {
 	name := library.libName
@@ -806,9 +901,9 @@
 	library.baseLinker.linkerInit(ctx)
 	// Let baseLinker know whether this variant is for stubs or not, so that
 	// it can omit things that are not required for linking stubs.
-	library.baseLinker.dynamicProperties.BuildStubs = library.buildStubs()
+	library.baseLinker.dynamicProperties.BuildStubs = library.BuildStubs()
 
-	if library.buildStubs() {
+	if library.BuildStubs() {
 		macroNames := versioningMacroNamesList(ctx.Config())
 		myName := versioningMacroName(ctx.ModuleName())
 		versioningMacroNamesListMutex.Lock()
@@ -967,8 +1062,8 @@
 		moduleInfoJSON.Uninstallable = true
 	}
 
-	if library.buildStubs() && library.stubsVersion() != "" {
-		moduleInfoJSON.SubName += "." + library.stubsVersion()
+	if library.BuildStubs() && library.StubsVersion() != "" {
+		moduleInfoJSON.SubName += "." + library.StubsVersion()
 	}
 
 	// If a library providing a stub is included in an APEX, the private APIs of the library
@@ -979,10 +1074,10 @@
 	// very early stage in the boot process).
 	if len(library.Properties.Stubs.Versions) > 0 && !ctx.Host() && ctx.notInPlatform() &&
 		!ctx.inRamdisk() && !ctx.inVendorRamdisk() && !ctx.inRecovery() && !ctx.useVndk() && !ctx.static() {
-		if library.buildStubs() && library.isLatestStubVersion() {
+		if library.BuildStubs() && library.isLatestStubVersion() {
 			moduleInfoJSON.SubName = ""
 		}
-		if !library.buildStubs() {
+		if !library.BuildStubs() {
 			moduleInfoJSON.SubName = ".bootstrap"
 		}
 	}
@@ -990,6 +1085,10 @@
 	library.baseLinker.moduleInfoJSON(ctx, moduleInfoJSON)
 }
 
+func (library *libraryDecorator) testSuiteInfo(ctx ModuleContext) {
+	// not a test
+}
+
 func (library *libraryDecorator) linkStatic(ctx ModuleContext,
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 
@@ -997,6 +1096,16 @@
 	library.objects = library.objects.Append(objs)
 	library.wholeStaticLibsFromPrebuilts = android.CopyOfPaths(deps.WholeStaticLibsFromPrebuilts)
 
+	if library.wideStaticlibForMake {
+		if generatedLib := GenerateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil {
+			// WholeStaticLibsFromPrebuilts are .a files that get included whole into the resulting staticlib
+			// so reuse that here for our Rust staticlibs because we don't have individual object files for
+			// these.
+			deps.WholeStaticLibsFromPrebuilts = append(deps.WholeStaticLibsFromPrebuilts, generatedLib)
+		}
+
+	}
+
 	fileName := ctx.ModuleName() + staticLibraryExtension
 	outputFile := android.PathForModuleOut(ctx, fileName)
 	builderFlags := flagsToBuilderFlags(flags)
@@ -1008,7 +1117,7 @@
 			library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
 		} else {
 			versionedOutputFile := android.PathForModuleOut(ctx, "versioned", fileName)
-			library.distFile = versionedOutputFile
+			library.defaultDistFile = versionedOutputFile
 			library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
 		}
 	}
@@ -1056,10 +1165,14 @@
 	linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
 	linkerDeps = append(linkerDeps, ndkSharedLibDeps(ctx)...)
 
+	exportedSymbols := ctx.ExpandOptionalSource(library.Properties.Exported_symbols_list, "exported_symbols_list")
 	unexportedSymbols := ctx.ExpandOptionalSource(library.Properties.Unexported_symbols_list, "unexported_symbols_list")
 	forceNotWeakSymbols := ctx.ExpandOptionalSource(library.Properties.Force_symbols_not_weak_list, "force_symbols_not_weak_list")
 	forceWeakSymbols := ctx.ExpandOptionalSource(library.Properties.Force_symbols_weak_list, "force_symbols_weak_list")
 	if !ctx.Darwin() {
+		if exportedSymbols.Valid() {
+			ctx.PropertyErrorf("exported_symbols_list", "Only supported on Darwin")
+		}
 		if unexportedSymbols.Valid() {
 			ctx.PropertyErrorf("unexported_symbols_list", "Only supported on Darwin")
 		}
@@ -1070,6 +1183,10 @@
 			ctx.PropertyErrorf("force_symbols_weak_list", "Only supported on Darwin")
 		}
 	} else {
+		if exportedSymbols.Valid() {
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-exported_symbols_list,"+exportedSymbols.String())
+			linkerDeps = append(linkerDeps, exportedSymbols.Path())
+		}
 		if unexportedSymbols.Valid() {
 			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-unexported_symbols_list,"+unexportedSymbols.String())
 			linkerDeps = append(linkerDeps, unexportedSymbols.Path())
@@ -1117,7 +1234,7 @@
 
 	stripFlags := flagsToStripFlags(flags)
 	needsStrip := library.stripper.NeedsStrip(ctx)
-	if library.buildStubs() {
+	if library.BuildStubs() {
 		// No need to strip stubs libraries
 		needsStrip = false
 	}
@@ -1140,11 +1257,11 @@
 			library.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
 		} else {
 			versionedOutputFile := android.PathForModuleOut(ctx, "versioned", fileName)
-			library.distFile = versionedOutputFile
+			library.defaultDistFile = versionedOutputFile
 
 			if library.stripper.NeedsStrip(ctx) {
 				out := android.PathForModuleOut(ctx, "versioned-stripped", fileName)
-				library.distFile = out
+				library.defaultDistFile = out
 				library.stripper.StripExecutableOrSharedLib(ctx, versionedOutputFile, out, stripFlags)
 			}
 
@@ -1171,7 +1288,7 @@
 	linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
 	linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
 
-	if generatedLib := generateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil && !library.buildStubs() {
+	if generatedLib := GenerateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil && !library.BuildStubs() {
 		if ctx.Module().(*Module).WholeRustStaticlib {
 			deps.WholeStaticLibs = append(deps.WholeStaticLibs, generatedLib)
 		} else {
@@ -1192,7 +1309,7 @@
 	library.linkSAbiDumpFiles(ctx, deps, objs, fileName, unstrippedOutputFile)
 
 	var transitiveStaticLibrariesForOrdering depset.DepSet[android.Path]
-	if static := ctx.GetDirectDepsWithTag(staticVariantTag); len(static) > 0 {
+	if static := ctx.GetDirectDepsProxyWithTag(staticVariantTag); len(static) > 0 {
 		s, _ := android.OtherModuleProvider(ctx, static[0], StaticLibraryInfoProvider)
 		transitiveStaticLibrariesForOrdering = s.TransitiveStaticLibrariesForOrdering
 	}
@@ -1202,18 +1319,18 @@
 		SharedLibrary:                        unstrippedOutputFile,
 		TransitiveStaticLibrariesForOrdering: transitiveStaticLibrariesForOrdering,
 		Target:                               ctx.Target(),
-		IsStubs:                              library.buildStubs(),
+		IsStubs:                              library.BuildStubs(),
 	})
 
-	addStubDependencyProviders(ctx)
+	AddStubDependencyProviders(ctx)
 
 	return unstrippedOutputFile
 }
 
 // Visits the stub variants of the library and returns a struct containing the stub .so paths
-func addStubDependencyProviders(ctx ModuleContext) []SharedStubLibrary {
+func AddStubDependencyProviders(ctx android.BaseModuleContext) []SharedStubLibrary {
 	stubsInfo := []SharedStubLibrary{}
-	stubs := ctx.GetDirectDepsWithTag(stubImplDepTag)
+	stubs := ctx.GetDirectDepsProxyWithTag(StubImplDepTag)
 	if len(stubs) > 0 {
 		for _, stub := range stubs {
 			stubInfo, ok := android.OtherModuleProvider(ctx, stub, SharedLibraryInfoProvider)
@@ -1222,8 +1339,11 @@
 				continue
 			}
 			flagInfo, _ := android.OtherModuleProvider(ctx, stub, FlagExporterInfoProvider)
+			if _, ok = android.OtherModuleProvider(ctx, stub, CcInfoProvider); !ok {
+				panic(fmt.Errorf("stub is not a cc module %s", stub))
+			}
 			stubsInfo = append(stubsInfo, SharedStubLibrary{
-				Version:           moduleLibraryInterface(stub).stubsVersion(),
+				Version:           android.OtherModuleProviderOrDefault(ctx, stub, LinkableInfoProvider).StubsVersion,
 				SharedLibraryInfo: stubInfo,
 				FlagExporterInfo:  flagInfo,
 			})
@@ -1231,10 +1351,11 @@
 		if len(stubsInfo) > 0 {
 			android.SetProvider(ctx, SharedLibraryStubsProvider, SharedLibraryStubsInfo{
 				SharedStubLibraries: stubsInfo,
-				IsLLNDK:             ctx.IsLlndk(),
+				IsLLNDK:             ctx.Module().(LinkableInterface).IsLlndk(),
 			})
 		}
 	}
+
 	return stubsInfo
 }
 
@@ -1251,7 +1372,7 @@
 }
 
 func (library *libraryDecorator) nativeCoverage() bool {
-	if library.header() || library.buildStubs() {
+	if library.header() || library.BuildStubs() {
 		return false
 	}
 	return true
@@ -1299,13 +1420,15 @@
 	deps PathDeps, sAbiDumpFiles android.Paths, soFile android.Path, libFileName string,
 	excludeSymbolVersions, excludeSymbolTags []string,
 	sdkVersionForVendorApiLevel string) android.Path {
+	// Though LLNDK is implemented in system, the callers in vendor cannot include CommonGlobalIncludes,
+	// so commonGlobalIncludes is false.
 	return transformDumpToLinkedDump(ctx,
 		sAbiDumpFiles, soFile, libFileName+".llndk",
 		library.llndkIncludeDirsForAbiCheck(ctx, deps),
 		android.OptionalPathForModuleSrc(ctx, library.Properties.Llndk.Symbol_file),
 		append([]string{"*_PLATFORM", "*_PRIVATE"}, excludeSymbolVersions...),
 		append([]string{"platform-only"}, excludeSymbolTags...),
-		[]string{"llndk"}, sdkVersionForVendorApiLevel)
+		[]string{"llndk"}, sdkVersionForVendorApiLevel, false /* commonGlobalIncludes */)
 }
 
 func (library *libraryDecorator) linkApexSAbiDumpFiles(ctx ModuleContext,
@@ -1318,7 +1441,7 @@
 		android.OptionalPathForModuleSrc(ctx, library.Properties.Stubs.Symbol_file),
 		append([]string{"*_PLATFORM", "*_PRIVATE"}, excludeSymbolVersions...),
 		append([]string{"platform-only"}, excludeSymbolTags...),
-		[]string{"apex", "systemapi"}, sdkVersion)
+		[]string{"apex", "systemapi"}, sdkVersion, requiresGlobalIncludes(ctx))
 }
 
 func getRefAbiDumpFile(ctx android.ModuleInstallPathContext,
@@ -1380,6 +1503,11 @@
 		extraFlags = append(extraFlags,
 			"-allow-unreferenced-changes",
 			"-allow-unreferenced-elf-symbol-changes")
+		// The functions in standard libraries are not always declared in the headers.
+		// Allow them to be added or removed without changing the symbols.
+		if isBionic(ctx.ModuleName()) {
+			extraFlags = append(extraFlags, "-allow-adding-removing-referenced-apis")
+		}
 	}
 	if isLlndk {
 		extraFlags = append(extraFlags, "-consider-opaque-types-different")
@@ -1456,7 +1584,7 @@
 			android.OptionalPathForModuleSrc(ctx, library.symbolFileForAbiCheck(ctx)),
 			headerAbiChecker.Exclude_symbol_versions,
 			headerAbiChecker.Exclude_symbol_tags,
-			[]string{} /* includeSymbolTags */, currSdkVersion)
+			[]string{} /* includeSymbolTags */, currSdkVersion, requiresGlobalIncludes(ctx))
 
 		var llndkDump, apexVariantDump android.Path
 		tags := classifySourceAbiDump(ctx.Module().(*Module))
@@ -1716,9 +1844,9 @@
 }
 
 func (library *libraryDecorator) exportVersioningMacroIfNeeded(ctx android.BaseModuleContext) {
-	if library.buildStubs() && library.stubsVersion() != "" && !library.skipAPIDefine {
+	if library.BuildStubs() && library.StubsVersion() != "" && !library.skipAPIDefine {
 		name := versioningMacroName(ctx.Module().(*Module).ImplementationModuleName(ctx))
-		apiLevel, err := android.ApiLevelFromUser(ctx, library.stubsVersion())
+		apiLevel, err := android.ApiLevelFromUser(ctx, library.StubsVersion())
 		if err != nil {
 			ctx.ModuleErrorf("Can't export version macro: %s", err.Error())
 		}
@@ -1770,8 +1898,8 @@
 func (library *libraryDecorator) install(ctx ModuleContext, file android.Path) {
 	if library.shared() {
 		translatedArch := ctx.Target().NativeBridge == android.NativeBridgeEnabled
-		if library.hasStubsVariants() && !ctx.Host() && !ctx.isSdkVariant() &&
-			InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !library.buildStubs() &&
+		if library.HasStubsVariants() && !ctx.Host() && !ctx.isSdkVariant() &&
+			InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !library.BuildStubs() &&
 			!translatedArch && !ctx.inRamdisk() && !ctx.inVendorRamdisk() && !ctx.inRecovery() {
 			// Bionic libraries (e.g. libc.so) is installed to the bootstrap subdirectory.
 			// The original path becomes a symlink to the corresponding file in the
@@ -1788,7 +1916,7 @@
 	if Bool(library.Properties.Static_ndk_lib) && library.static() &&
 		!ctx.InVendorOrProduct() && !ctx.inRamdisk() && !ctx.inVendorRamdisk() && !ctx.inRecovery() && ctx.Device() &&
 		library.baseLinker.sanitize.isUnsanitizedVariant() &&
-		ctx.isForPlatform() && !ctx.isPreventInstall() {
+		CtxIsForPlatform(ctx) && !ctx.isPreventInstall() {
 		installPath := getUnversionedLibraryInstallPath(ctx).Join(ctx, file.Base())
 
 		ctx.ModuleBuild(pctx, android.ModuleBuildParams{
@@ -1808,12 +1936,17 @@
 	return library.shared() || library.static()
 }
 
-// static returns true if this library is for a "static' variant.
+// static returns true if this library is for a "static" variant.
 func (library *libraryDecorator) static() bool {
 	return library.MutatedProperties.VariantIsStatic
 }
 
-// shared returns true if this library is for a "shared' variant.
+// staticLibrary returns true if this library is for a "static"" variant.
+func (library *libraryDecorator) staticLibrary() bool {
+	return library.static()
+}
+
+// shared returns true if this library is for a "shared" variant.
 func (library *libraryDecorator) shared() bool {
 	return library.MutatedProperties.VariantIsShared
 }
@@ -1853,32 +1986,32 @@
 	library.MutatedProperties.BuildStatic = false
 }
 
-// hasLLNDKStubs returns true if this cc_library module has a variant that will build LLNDK stubs.
-func (library *libraryDecorator) hasLLNDKStubs() bool {
+// HasLLNDKStubs returns true if this cc_library module has a variant that will build LLNDK stubs.
+func (library *libraryDecorator) HasLLNDKStubs() bool {
 	return String(library.Properties.Llndk.Symbol_file) != ""
 }
 
 // hasLLNDKStubs returns true if this cc_library module has a variant that will build LLNDK stubs.
-func (library *libraryDecorator) hasLLNDKHeaders() bool {
+func (library *libraryDecorator) HasLLNDKHeaders() bool {
 	return Bool(library.Properties.Llndk.Llndk_headers)
 }
 
-// isLLNDKMovedToApex returns true if this cc_library module sets the llndk.moved_to_apex property.
-func (library *libraryDecorator) isLLNDKMovedToApex() bool {
+// IsLLNDKMovedToApex returns true if this cc_library module sets the llndk.moved_to_apex property.
+func (library *libraryDecorator) IsLLNDKMovedToApex() bool {
 	return Bool(library.Properties.Llndk.Moved_to_apex)
 }
 
-// hasVendorPublicLibrary returns true if this cc_library module has a variant that will build
+// HasVendorPublicLibrary returns true if this cc_library module has a variant that will build
 // vendor public library stubs.
-func (library *libraryDecorator) hasVendorPublicLibrary() bool {
+func (library *libraryDecorator) HasVendorPublicLibrary() bool {
 	return String(library.Properties.Vendor_public_library.Symbol_file) != ""
 }
 
-func (library *libraryDecorator) implementationModuleName(name string) string {
+func (library *libraryDecorator) ImplementationModuleName(name string) string {
 	return name
 }
 
-func (library *libraryDecorator) buildStubs() bool {
+func (library *libraryDecorator) BuildStubs() bool {
 	return library.MutatedProperties.BuildStubs
 }
 
@@ -1886,7 +2019,7 @@
 	if props := library.getHeaderAbiCheckerProperties(ctx.Module().(*Module)); props.Symbol_file != nil {
 		return props.Symbol_file
 	}
-	if library.hasStubsVariants() && library.Properties.Stubs.Symbol_file != nil {
+	if library.HasStubsVariants() && library.Properties.Stubs.Symbol_file != nil {
 		return library.Properties.Stubs.Symbol_file
 	}
 	// TODO(b/309880485): Distinguish platform, NDK, LLNDK, and APEX version scripts.
@@ -1896,35 +2029,35 @@
 	return nil
 }
 
-func (library *libraryDecorator) hasStubsVariants() bool {
+func (library *libraryDecorator) HasStubsVariants() bool {
 	// Just having stubs.symbol_file is enough to create a stub variant. In that case
 	// the stub for the future API level is created.
 	return library.Properties.Stubs.Symbol_file != nil ||
 		len(library.Properties.Stubs.Versions) > 0
 }
 
-func (library *libraryDecorator) isStubsImplementationRequired() bool {
+func (library *libraryDecorator) IsStubsImplementationRequired() bool {
 	return BoolDefault(library.Properties.Stubs.Implementation_installable, true)
 }
 
-func (library *libraryDecorator) stubsVersions(ctx android.BaseModuleContext) []string {
-	if !library.hasStubsVariants() {
+func (library *libraryDecorator) StubsVersions(ctx android.BaseModuleContext) []string {
+	if !library.HasStubsVariants() {
 		return nil
 	}
 
-	if library.hasLLNDKStubs() && ctx.Module().(*Module).InVendorOrProduct() {
+	if library.HasLLNDKStubs() && ctx.Module().(*Module).InVendorOrProduct() {
 		// LLNDK libraries only need a single stubs variant (""), which is
 		// added automatically in createVersionVariations().
 		return nil
 	}
 
 	// Future API level is implicitly added if there isn't
-	versions := addCurrentVersionIfNotPresent(library.Properties.Stubs.Versions)
-	normalizeVersions(ctx, versions)
+	versions := AddCurrentVersionIfNotPresent(library.Properties.Stubs.Versions)
+	NormalizeVersions(ctx, versions)
 	return versions
 }
 
-func addCurrentVersionIfNotPresent(vers []string) []string {
+func AddCurrentVersionIfNotPresent(vers []string) []string {
 	if inList(android.FutureApiLevel.String(), vers) {
 		return vers
 	}
@@ -1937,24 +2070,24 @@
 	return append(vers, android.FutureApiLevel.String())
 }
 
-func (library *libraryDecorator) setStubsVersion(version string) {
+func (library *libraryDecorator) SetStubsVersion(version string) {
 	library.MutatedProperties.StubsVersion = version
 }
 
-func (library *libraryDecorator) stubsVersion() string {
+func (library *libraryDecorator) StubsVersion() string {
 	return library.MutatedProperties.StubsVersion
 }
 
-func (library *libraryDecorator) setBuildStubs(isLatest bool) {
+func (library *libraryDecorator) SetBuildStubs(isLatest bool) {
 	library.MutatedProperties.BuildStubs = true
 	library.MutatedProperties.IsLatestVersion = isLatest
 }
 
-func (library *libraryDecorator) setAllStubsVersions(versions []string) {
+func (library *libraryDecorator) SetAllStubsVersions(versions []string) {
 	library.MutatedProperties.AllStubsVersions = versions
 }
 
-func (library *libraryDecorator) allStubsVersions() []string {
+func (library *libraryDecorator) AllStubsVersions() []string {
 	return library.MutatedProperties.AllStubsVersions
 }
 
@@ -1962,17 +2095,15 @@
 	return library.MutatedProperties.IsLatestVersion
 }
 
-func (library *libraryDecorator) availableFor(what string) bool {
+func (library *libraryDecorator) apexAvailable() []string {
 	var list []string
 	if library.static() {
 		list = library.StaticProperties.Static.Apex_available
 	} else if library.shared() {
 		list = library.SharedProperties.Shared.Apex_available
 	}
-	if len(list) == 0 {
-		return false
-	}
-	return android.CheckAvailableForApex(what, list)
+
+	return list
 }
 
 func (library *libraryDecorator) installable() *bool {
@@ -1985,7 +2116,7 @@
 }
 
 func (library *libraryDecorator) makeUninstallable(mod *Module) {
-	if library.static() && library.buildStatic() && !library.buildStubs() {
+	if library.static() && library.buildStatic() && !library.BuildStubs() {
 		// If we're asked to make a static library uninstallable we don't do
 		// anything since AndroidMkEntries always sets LOCAL_UNINSTALLABLE_MODULE
 		// for these entries. This is done to still get the make targets for NOTICE
@@ -1999,14 +2130,25 @@
 	return library.path.Partition()
 }
 
-func (library *libraryDecorator) getAPIListCoverageXMLPath() android.ModuleOutPath {
+func (library *libraryDecorator) GetAPIListCoverageXMLPath() android.ModuleOutPath {
 	return library.apiListCoverageXmlPath
 }
 
+func (library *libraryDecorator) setAPIListCoverageXMLPath(xml android.ModuleOutPath) {
+	library.apiListCoverageXmlPath = xml
+}
+
 func (library *libraryDecorator) overriddenModules() []string {
 	return library.Properties.Overrides
 }
 
+func (library *libraryDecorator) defaultDistFiles() []android.Path {
+	if library.defaultDistFile == nil {
+		return nil
+	}
+	return []android.Path{library.defaultDistFile}
+}
+
 var _ overridable = (*libraryDecorator)(nil)
 
 var versioningMacroNamesListKey = android.NewOnceKey("versioningMacroNamesList")
@@ -2117,7 +2259,7 @@
 		} else {
 			// Header only
 		}
-	} else if library, ok := ctx.Module().(LinkableInterface); ok && (library.CcLibraryInterface() || library.RustLibraryInterface()) {
+	} else if library, ok := ctx.Module().(LinkableInterface); ok && (library.CcLibraryInterface()) {
 		// Non-cc.Modules may need an empty variant for their mutators.
 		variations := []string{}
 		if library.NonCcVariants() {
@@ -2146,6 +2288,9 @@
 }
 
 func (linkageTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	if ctx.DepTag() == android.PrebuiltDepTag {
+		return sourceVariation
+	}
 	return ""
 }
 
@@ -2172,7 +2317,7 @@
 		}
 		buildStatic := library.BuildStaticVariant() && !isLLNDK
 		buildShared := library.BuildSharedVariant()
-		if library.BuildRlibVariant() && library.IsRustFFI() && !buildStatic && (incomingVariation == "static" || incomingVariation == "") {
+		if library.BuildRlibVariant() && !buildStatic && (incomingVariation == "static" || incomingVariation == "") {
 			// Rust modules do not build static libs, but rlibs are used as if they
 			// were via `static_libs`. Thus we need to alias the BuildRlibVariant
 			// to "static" for Rust FFI libraries.
@@ -2202,11 +2347,13 @@
 			library.setStatic()
 			if !library.buildStatic() {
 				library.disablePrebuilt()
+				ctx.Module().(*Module).Prebuilt().SetUsePrebuilt(false)
 			}
 		} else if variation == "shared" {
 			library.setShared()
 			if !library.buildShared() {
 				library.disablePrebuilt()
+				ctx.Module().(*Module).Prebuilt().SetUsePrebuilt(false)
 			}
 		}
 	} else if library, ok := ctx.Module().(LinkableInterface); ok && library.CcLibraryInterface() {
@@ -2229,10 +2376,10 @@
 	}
 }
 
-// normalizeVersions modifies `versions` in place, so that each raw version
+// NormalizeVersions modifies `versions` in place, so that each raw version
 // string becomes its normalized canonical form.
 // Validates that the versions in `versions` are specified in least to greatest order.
-func normalizeVersions(ctx android.BaseModuleContext, versions []string) {
+func NormalizeVersions(ctx android.BaseModuleContext, versions []string) {
 	var previous android.ApiLevel
 	for i, v := range versions {
 		ver, err := android.ApiLevelFromUser(ctx, v)
@@ -2249,7 +2396,7 @@
 }
 
 func perApiVersionVariations(mctx android.BaseModuleContext, minSdkVersion string) []string {
-	from, err := nativeApiLevelFromUser(mctx, minSdkVersion)
+	from, err := NativeApiLevelFromUser(mctx, minSdkVersion)
 	if err != nil {
 		mctx.PropertyErrorf("min_sdk_version", err.Error())
 		return []string{""}
@@ -2277,25 +2424,25 @@
 		module.CcLibraryInterface() && module.Shared()
 }
 
-func moduleLibraryInterface(module blueprint.Module) libraryInterface {
-	if m, ok := module.(*Module); ok {
-		return m.library
+func moduleVersionedInterface(module blueprint.Module) VersionedInterface {
+	if m, ok := module.(VersionedLinkableInterface); ok {
+		return m.VersionedInterface()
 	}
 	return nil
 }
 
 // setStubsVersions normalizes the versions in the Stubs.Versions property into MutatedProperties.AllStubsVersions.
-func setStubsVersions(mctx android.BaseModuleContext, library libraryInterface, module *Module) {
-	if !library.buildShared() || !canBeVersionVariant(module) {
+func setStubsVersions(mctx android.BaseModuleContext, module VersionedLinkableInterface) {
+	if !module.BuildSharedVariant() || !canBeVersionVariant(module) {
 		return
 	}
-	versions := library.stubsVersions(mctx)
+	versions := module.VersionedInterface().StubsVersions(mctx)
 	if mctx.Failed() {
 		return
 	}
 	// Set the versions on the pre-mutated module so they can be read by any llndk modules that
 	// depend on the implementation library and haven't been mutated yet.
-	library.setAllStubsVersions(versions)
+	module.VersionedInterface().SetAllStubsVersions(versions)
 }
 
 // versionTransitionMutator splits a module into the mandatory non-stubs variant
@@ -2306,20 +2453,22 @@
 	if ctx.Os() != android.Android {
 		return []string{""}
 	}
-
-	m, ok := ctx.Module().(*Module)
-	if library := moduleLibraryInterface(ctx.Module()); library != nil && canBeVersionVariant(m) {
-		setStubsVersions(ctx, library, m)
-
-		return append(slices.Clone(library.allStubsVersions()), "")
-	} else if ok && m.SplitPerApiLevel() && m.IsSdkVariant() {
-		return perApiVersionVariations(ctx, m.MinSdkVersion())
+	if m, ok := ctx.Module().(VersionedLinkableInterface); ok {
+		if m.CcLibraryInterface() && canBeVersionVariant(m) {
+			setStubsVersions(ctx, m)
+			return append(slices.Clone(m.VersionedInterface().AllStubsVersions()), "")
+		} else if m.SplitPerApiLevel() && m.IsSdkVariant() {
+			return perApiVersionVariations(ctx, m.MinSdkVersion())
+		}
 	}
 
 	return []string{""}
 }
 
 func (versionTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	if ctx.DepTag() == android.PrebuiltDepTag {
+		return sourceVariation
+	}
 	return ""
 }
 
@@ -2327,11 +2476,11 @@
 	if ctx.Os() != android.Android {
 		return ""
 	}
-	m, ok := ctx.Module().(*Module)
-	if library := moduleLibraryInterface(ctx.Module()); library != nil && canBeVersionVariant(m) {
+	m, ok := ctx.Module().(VersionedLinkableInterface)
+	if library := moduleVersionedInterface(ctx.Module()); library != nil && canBeVersionVariant(m) {
 		if incomingVariation == "latest" {
 			latestVersion := ""
-			versions := library.allStubsVersions()
+			versions := library.AllStubsVersions()
 			if len(versions) > 0 {
 				latestVersion = versions[len(versions)-1]
 			}
@@ -2356,39 +2505,39 @@
 		return
 	}
 
-	m, ok := ctx.Module().(*Module)
-	if library := moduleLibraryInterface(ctx.Module()); library != nil && canBeVersionVariant(m) {
+	m, ok := ctx.Module().(VersionedLinkableInterface)
+	if library := moduleVersionedInterface(ctx.Module()); library != nil && canBeVersionVariant(m) {
 		isLLNDK := m.IsLlndk()
 		isVendorPublicLibrary := m.IsVendorPublicLibrary()
 
 		if variation != "" || isLLNDK || isVendorPublicLibrary {
 			// A stubs or LLNDK stubs variant.
-			if m.sanitize != nil {
-				m.sanitize.Properties.ForceDisable = true
+			if sm, ok := ctx.Module().(PlatformSanitizeable); ok && sm.SanitizePropDefined() {
+				sm.ForceDisableSanitizers()
 			}
-			if m.stl != nil {
-				m.stl.Properties.Stl = StringPtr("none")
-			}
-			m.Properties.PreventInstall = true
-			lib := moduleLibraryInterface(m)
-			allStubsVersions := library.allStubsVersions()
+			m.SetStl("none")
+			m.SetPreventInstall()
+			allStubsVersions := m.VersionedInterface().AllStubsVersions()
 			isLatest := len(allStubsVersions) > 0 && variation == allStubsVersions[len(allStubsVersions)-1]
-			lib.setBuildStubs(isLatest)
+			m.VersionedInterface().SetBuildStubs(isLatest)
 		}
 		if variation != "" {
 			// A non-LLNDK stubs module is hidden from make
-			library.setStubsVersion(variation)
-			m.Properties.HideFromMake = true
+			m.VersionedInterface().SetStubsVersion(variation)
+			m.SetHideFromMake()
 		} else {
 			// A non-LLNDK implementation module has a dependency to all stubs versions
-			for _, version := range library.allStubsVersions() {
-				ctx.AddVariationDependencies([]blueprint.Variation{{"version", version}},
-					stubImplDepTag, ctx.ModuleName())
+			for _, version := range m.VersionedInterface().AllStubsVersions() {
+				ctx.AddVariationDependencies(
+					[]blueprint.Variation{
+						{Mutator: "version", Variation: version},
+						{Mutator: "link", Variation: "shared"}},
+					StubImplDepTag, ctx.ModuleName())
 			}
 		}
 	} else if ok && m.SplitPerApiLevel() && m.IsSdkVariant() {
-		m.Properties.Sdk_version = StringPtr(variation)
-		m.Properties.Min_sdk_version = StringPtr(variation)
+		m.SetSdkVersion(variation)
+		m.SetMinSdkVersion(variation)
 	}
 }
 
@@ -2400,13 +2549,12 @@
 	inject *bool, fileName string) android.ModuleOutPath {
 	// TODO(b/137267623): Remove this in favor of a cc_genrule when they support operating on shared libraries.
 	injectBoringSSLHash := Bool(inject)
-	ctx.VisitDirectDeps(func(dep android.Module) {
+	ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
 		if tag, ok := ctx.OtherModuleDependencyTag(dep).(libraryDependencyTag); ok && tag.static() {
-			if cc, ok := dep.(*Module); ok {
-				if library, ok := cc.linker.(*libraryDecorator); ok {
-					if Bool(library.Properties.Inject_bssl_hash) {
-						injectBoringSSLHash = true
-					}
+			if ccInfo, ok := android.OtherModuleProvider(ctx, dep, CcInfoProvider); ok &&
+				ccInfo.LinkerInfo != nil && ccInfo.LinkerInfo.LibraryDecoratorInfo != nil {
+				if ccInfo.LinkerInfo.LibraryDecoratorInfo.InjectBsslHash {
+					injectBoringSSLHash = true
 				}
 			}
 		}
diff --git a/cc/library_headers_test.go b/cc/library_headers_test.go
index 5a45767..88ccd43 100644
--- a/cc/library_headers_test.go
+++ b/cc/library_headers_test.go
@@ -41,11 +41,11 @@
 			ctx := testCc(t, fmt.Sprintf(bp, headerModule))
 
 			// test if header search paths are correctly added
-			cc := ctx.ModuleForTests("lib", "android_arm64_armv8-a_static").Rule("cc")
+			cc := ctx.ModuleForTests(t, "lib", "android_arm64_armv8-a_static").Rule("cc")
 			android.AssertStringDoesContain(t, "cFlags for lib module", cc.Args["cFlags"], " -Imy_include ")
 
 			// Test that there's a valid AndroidMk entry.
-			headers := ctx.ModuleForTests("headers", "android_arm64_armv8-a").Module()
+			headers := ctx.ModuleForTests(t, "headers", "android_arm64_armv8-a").Module()
 			e := android.AndroidMkInfoForTest(t, ctx, headers).PrimaryInfo
 
 			// This duplicates the tests done in AndroidMkEntries.write. It would be
@@ -80,9 +80,9 @@
 	for _, prebuiltPreferred := range []bool{false, true} {
 		t.Run(fmt.Sprintf("prebuilt prefer %t", prebuiltPreferred), func(t *testing.T) {
 			ctx := testCc(t, fmt.Sprintf(bp, prebuiltPreferred))
-			lib := ctx.ModuleForTests("lib", "android_arm64_armv8-a_static")
-			sourceDep := ctx.ModuleForTests("headers", "android_arm64_armv8-a")
-			prebuiltDep := ctx.ModuleForTests("prebuilt_headers", "android_arm64_armv8-a")
+			lib := ctx.ModuleForTests(t, "lib", "android_arm64_armv8-a_static")
+			sourceDep := ctx.ModuleForTests(t, "headers", "android_arm64_armv8-a")
+			prebuiltDep := ctx.ModuleForTests(t, "prebuilt_headers", "android_arm64_armv8-a")
 			hasSourceDep := false
 			hasPrebuiltDep := false
 			ctx.VisitDirectDeps(lib.Module(), func(dep blueprint.Module) {
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index af3658d..4629030 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -546,7 +546,7 @@
 		specifiedDeps = ccModule.linker.linkerSpecifiedDeps(ctx.SdkModuleContext(), ccModule, specifiedDeps)
 
 		if lib := ccModule.library; lib != nil {
-			if !lib.hasStubsVariants() {
+			if !lib.HasStubsVariants() {
 				// Propagate dynamic dependencies for implementation libs, but not stubs.
 				p.SharedLibs = specifiedDeps.sharedLibs
 			} else {
@@ -554,8 +554,8 @@
 				// ccModule.StubsVersion()) if the module is versioned. 2. Ensure that all
 				// the versioned stub libs are retained in the prebuilt tree; currently only
 				// the stub corresponding to ccModule.StubsVersion() is.
-				p.StubsVersions = lib.allStubsVersions()
-				if lib.buildStubs() && ccModule.stubsSymbolFilePath() == nil {
+				p.StubsVersions = lib.AllStubsVersions()
+				if lib.BuildStubs() && ccModule.stubsSymbolFilePath() == nil {
 					ctx.ModuleErrorf("Could not determine symbol_file")
 				} else {
 					p.StubsSymbolFilePath = ccModule.stubsSymbolFilePath()
@@ -573,9 +573,8 @@
 
 func getRequiredMemberOutputFile(ctx android.SdkMemberContext, ccModule *Module) android.Path {
 	var path android.Path
-	outputFile := ccModule.OutputFile()
-	if outputFile.Valid() {
-		path = outputFile.Path()
+	if info, ok := android.OtherModuleProvider(ctx.SdkModuleContext(), ccModule, LinkableInfoProvider); ok && info.OutputFile.Valid() {
+		path = info.OutputFile.Path()
 	} else {
 		ctx.SdkModuleContext().ModuleErrorf("member variant %s does not have a valid output file", ccModule)
 	}
diff --git a/cc/library_test.go b/cc/library_test.go
index 2ed2d76..8b7fed2 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -30,8 +30,8 @@
 			srcs: ["foo.c", "baz.o"],
 		}`)
 
-		libfooShared := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
-		libfooStatic := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_static").Output("libfoo.a")
+		libfooShared := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+		libfooStatic := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_static").Output("libfoo.a")
 
 		if len(libfooShared.Inputs) != 2 {
 			t.Fatalf("unexpected inputs to libfoo shared: %#v", libfooShared.Inputs.Strings())
@@ -59,8 +59,8 @@
 			},
 		}`)
 
-		libfooShared := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
-		libfooStatic := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_static").Output("libfoo.a")
+		libfooShared := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+		libfooStatic := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_static").Output("libfoo.a")
 
 		if len(libfooShared.Inputs) != 1 {
 			t.Fatalf("unexpected inputs to libfoo shared: %#v", libfooShared.Inputs.Strings())
@@ -85,8 +85,8 @@
 			},
 		}`)
 
-		libfooShared := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
-		libfooStatic := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_static").Output("libfoo.a")
+		libfooShared := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+		libfooStatic := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_static").Output("libfoo.a")
 
 		if len(libfooShared.Inputs) != 2 {
 			t.Fatalf("unexpected inputs to libfoo shared: %#v", libfooShared.Inputs.Strings())
@@ -111,8 +111,8 @@
 			},
 		}`)
 
-		libfooShared := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
-		libfooStatic := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_static").Output("libfoo.a")
+		libfooShared := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+		libfooStatic := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_static").Output("libfoo.a")
 
 		if len(libfooShared.Inputs) != 1 {
 			t.Fatalf("unexpected inputs to libfoo shared: %#v", libfooShared.Inputs.Strings())
@@ -137,8 +137,8 @@
 			},
 		}`)
 
-		libfooShared := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
-		libfooStatic := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_static").Output("libfoo.a")
+		libfooShared := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+		libfooStatic := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_static").Output("libfoo.a")
 
 		if len(libfooShared.Inputs) != 1 {
 			t.Fatalf("unexpected inputs to libfoo shared: %#v", libfooShared.Inputs.Strings())
@@ -168,8 +168,8 @@
 			},
 		}`)
 
-		libfooShared := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
-		libfooStatic := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_static").Output("libfoo.a")
+		libfooShared := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+		libfooStatic := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_static").Output("libfoo.a")
 
 		if len(libfooShared.Inputs) != 3 {
 			t.Fatalf("unexpected inputs to libfoo shared: %#v", libfooShared.Inputs.Strings())
@@ -183,7 +183,7 @@
 			t.Errorf("static objects not reused for shared library")
 		}
 
-		libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Module().(*Module)
+		libfoo := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_shared").Module().(*Module)
 		if !inList("-DGOOGLE_PROTOBUF_NO_RTTI", libfoo.flags.Local.CFlags) {
 			t.Errorf("missing protobuf cflags")
 		}
@@ -254,7 +254,7 @@
 			version_script: "foo.map.txt",
 		}`)
 
-	libfoo := result.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("ld")
+	libfoo := result.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared").Rule("ld")
 
 	android.AssertStringListContains(t, "missing dependency on version_script",
 		libfoo.Implicits.Strings(), "foo.map.txt")
@@ -272,7 +272,7 @@
 			dynamic_list: "foo.dynamic.txt",
 		}`)
 
-	libfoo := result.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("ld")
+	libfoo := result.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared").Rule("ld")
 
 	android.AssertStringListContains(t, "missing dependency on dynamic_list",
 		libfoo.Implicits.Strings(), "foo.dynamic.txt")
@@ -312,14 +312,14 @@
 		}
 	`)
 
-	libdirect := result.ModuleForTests("libdirect", "android_arm64_armv8-a_static").Rule("arWithLibs")
-	libtransitive := result.ModuleForTests("libtransitive", "android_arm64_armv8-a_static").Rule("arWithLibs")
+	libdirect := result.ModuleForTests(t, "libdirect", "android_arm64_armv8-a_static").Rule("arWithLibs")
+	libtransitive := result.ModuleForTests(t, "libtransitive", "android_arm64_armv8-a_static").Rule("arWithLibs")
 
-	libdirectWithSrcs := result.ModuleForTests("libdirect_with_srcs", "android_arm64_armv8-a_static").Rule("arWithLibs")
-	libtransitiveWithSrcs := result.ModuleForTests("libtransitive_with_srcs", "android_arm64_armv8-a_static").Rule("arWithLibs")
+	libdirectWithSrcs := result.ModuleForTests(t, "libdirect_with_srcs", "android_arm64_armv8-a_static").Rule("arWithLibs")
+	libtransitiveWithSrcs := result.ModuleForTests(t, "libtransitive_with_srcs", "android_arm64_armv8-a_static").Rule("arWithLibs")
 
-	barObj := result.ModuleForTests("libdirect_with_srcs", "android_arm64_armv8-a_static").Rule("cc")
-	bazObj := result.ModuleForTests("libtransitive_with_srcs", "android_arm64_armv8-a_static").Rule("cc")
+	barObj := result.ModuleForTests(t, "libdirect_with_srcs", "android_arm64_armv8-a_static").Rule("cc")
+	bazObj := result.ModuleForTests(t, "libtransitive_with_srcs", "android_arm64_armv8-a_static").Rule("cc")
 
 	android.AssertStringListContains(t, "missing dependency on foo.a",
 		libdirect.Inputs.Strings(), "foo.a")
diff --git a/cc/linkable.go b/cc/linkable.go
index 1a9a9ab..f3aff15 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -53,6 +53,9 @@
 
 	// SanitizableDepTagChecker returns a SantizableDependencyTagChecker function type.
 	SanitizableDepTagChecker() SantizableDependencyTagChecker
+
+	// ForceDisableSanitizers sets the ForceDisable sanitize property
+	ForceDisableSanitizers()
 }
 
 // SantizableDependencyTagChecker functions check whether or not a dependency
@@ -63,6 +66,30 @@
 // implementation should handle tags from both.
 type SantizableDependencyTagChecker func(tag blueprint.DependencyTag) bool
 
+type VersionedLinkableInterface interface {
+	LinkableInterface
+	android.ApexModule
+
+	// VersionedInterface returns the VersionedInterface for this module
+	// (e.g. c.library), or nil if this is module is not a VersionedInterface.
+	VersionedInterface() VersionedInterface
+
+	// HasStubsVariants true if this module is a stub or has a sibling variant
+	// that is a stub.
+	HasStubsVariants() bool
+
+	// SetStl sets the stl property for CC modules. Does not panic if for other module types.
+	SetStl(string)
+	SetSdkVersion(string)
+	SetMinSdkVersion(version string)
+	ApexSdkVersion() android.ApiLevel
+	ImplementationModuleNameForMake() string
+
+	// RustApexExclude returns ApexExclude() for Rust modules; always returns false for all non-Rust modules.
+	// TODO(b/362509506): remove this once all apex_exclude uses are switched to stubs.
+	RustApexExclude() bool
+}
+
 // LinkableInterface is an interface for a type of module that is linkable in a C++ library.
 type LinkableInterface interface {
 	android.Module
@@ -102,9 +129,6 @@
 	IsPrebuilt() bool
 	Toc() android.OptionalPath
 
-	// IsRustFFI returns true if this is a Rust FFI library.
-	IsRustFFI() bool
-
 	// IsFuzzModule returns true if this a *_fuzz module.
 	IsFuzzModule() bool
 
@@ -135,28 +159,18 @@
 	// IsNdk returns true if the library is in the configs known NDK list.
 	IsNdk(config android.Config) bool
 
-	// HasStubsVariants true if this module is a stub or has a sibling variant
-	// that is a stub.
-	HasStubsVariants() bool
-
 	// IsStubs returns true if the this is a stubs library.
 	IsStubs() bool
 
 	// IsLlndk returns true for both LLNDK (public) and LLNDK-private libs.
 	IsLlndk() bool
 
-	// HasLlndkStubs returns true if this library has a variant that will build LLNDK stubs.
-	HasLlndkStubs() bool
-
 	// NeedsLlndkVariants returns true if this module has LLNDK stubs or provides LLNDK headers.
 	NeedsLlndkVariants() bool
 
 	// NeedsVendorPublicLibraryVariants returns true if this module has vendor public library stubs.
 	NeedsVendorPublicLibraryVariants() bool
 
-	//StubsVersion returns the stubs version for this module.
-	StubsVersion() string
-
 	// UseVndk returns true if the module is using VNDK libraries instead of the libraries in /system/lib or /system/lib64.
 	// "product" and "vendor" variant modules return true for this function.
 	// When BOARD_VNDK_VERSION is set, vendor variants of "vendor_available: true", "vendor: true",
@@ -185,6 +199,7 @@
 	MinSdkVersion() string
 	AlwaysSdk() bool
 	IsSdkVariant() bool
+	Multilib() string
 
 	SplitPerApiLevel() bool
 
@@ -253,6 +268,7 @@
 
 	// FuzzModule returns the fuzz.FuzzModule associated with the module.
 	FuzzModuleStruct() fuzz.FuzzModule
+	IsCrt() bool
 }
 
 var (
diff --git a/cc/linker.go b/cc/linker.go
index b96d139..f854937 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -457,7 +457,7 @@
 	if ctx.minSdkVersion() == "current" {
 		return true
 	}
-	parsedSdkVersion, err := nativeApiLevelFromUser(ctx, ctx.minSdkVersion())
+	parsedSdkVersion, err := NativeApiLevelFromUser(ctx, ctx.minSdkVersion())
 	if err != nil {
 		ctx.PropertyErrorf("min_sdk_version",
 			"Invalid min_sdk_version value (must be int or current): %q",
@@ -471,16 +471,77 @@
 
 // ModuleContext extends BaseModuleContext
 // BaseModuleContext should know if LLD is used?
-func (linker *baseLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags {
-	toolchain := ctx.toolchain()
-
+func CommonLinkerFlags(ctx android.ModuleContext, flags Flags, useClangLld bool,
+	toolchain config.Toolchain, allow_undefined_symbols bool) Flags {
 	hod := "Host"
 	if ctx.Os().Class == android.Device {
 		hod = "Device"
 	}
 
-	if linker.useClangLld(ctx) {
+	mod, ok := ctx.Module().(LinkableInterface)
+	if !ok {
+		ctx.ModuleErrorf("trying to add CommonLinkerFlags to non-LinkableInterface module.")
+		return flags
+	}
+	if useClangLld {
 		flags.Global.LdFlags = append(flags.Global.LdFlags, fmt.Sprintf("${config.%sGlobalLldflags}", hod))
+	} else {
+		flags.Global.LdFlags = append(flags.Global.LdFlags, fmt.Sprintf("${config.%sGlobalLdflags}", hod))
+	}
+
+	if allow_undefined_symbols {
+		if ctx.Darwin() {
+			// darwin defaults to treating undefined symbols as errors
+			flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,-undefined,dynamic_lookup")
+		}
+	} else if !ctx.Darwin() && !ctx.Windows() {
+		flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--no-undefined")
+	}
+
+	if useClangLld {
+		flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.Lldflags())
+	} else {
+		flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.Ldflags())
+	}
+
+	if !toolchain.Bionic() && ctx.Os() != android.LinuxMusl {
+		if !ctx.Windows() {
+			// Add -ldl, -lpthread, -lm and -lrt to host builds to match the default behavior of device
+			// builds
+			flags.Global.LdFlags = append(flags.Global.LdFlags,
+				"-ldl",
+				"-lpthread",
+				"-lm",
+			)
+			if !ctx.Darwin() {
+				flags.Global.LdFlags = append(flags.Global.LdFlags, "-lrt")
+			}
+		}
+	}
+	staticLib := mod.CcLibraryInterface() && mod.Static()
+	if ctx.Host() && !ctx.Windows() && !staticLib {
+		flags.Global.LdFlags = append(flags.Global.LdFlags, RpathFlags(ctx)...)
+	}
+
+	flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.ToolchainLdflags())
+	return flags
+}
+
+func (linker *baseLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+	toolchain := ctx.toolchain()
+	allow_undefined_symbols := Bool(linker.Properties.Allow_undefined_symbols)
+
+	flags = CommonLinkerFlags(ctx, flags, linker.useClangLld(ctx), toolchain,
+		allow_undefined_symbols)
+
+	if !toolchain.Bionic() && ctx.Os() != android.LinuxMusl {
+		CheckBadHostLdlibs(ctx, "host_ldlibs", linker.Properties.Host_ldlibs)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, linker.Properties.Host_ldlibs...)
+	}
+
+	CheckBadLinkerFlags(ctx, "ldflags", linker.Properties.Ldflags)
+
+	if linker.useClangLld(ctx) {
 		if !BoolDefault(linker.Properties.Pack_relocations, packRelocationsDefault) {
 			flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--pack-dyn-relocs=none")
 		} else if ctx.Device() {
@@ -498,53 +559,10 @@
 				flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--pack-dyn-relocs=android")
 			}
 		}
-	} else {
-		flags.Global.LdFlags = append(flags.Global.LdFlags, fmt.Sprintf("${config.%sGlobalLdflags}", hod))
 	}
-	if Bool(linker.Properties.Allow_undefined_symbols) {
-		if ctx.Darwin() {
-			// darwin defaults to treating undefined symbols as errors
-			flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,-undefined,dynamic_lookup")
-		}
-	} else if !ctx.Darwin() && !ctx.Windows() {
-		flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--no-undefined")
-	}
-
-	if linker.useClangLld(ctx) {
-		flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.Lldflags())
-	} else {
-		flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.Ldflags())
-	}
-
-	if !ctx.toolchain().Bionic() && ctx.Os() != android.LinuxMusl {
-		CheckBadHostLdlibs(ctx, "host_ldlibs", linker.Properties.Host_ldlibs)
-
-		flags.Local.LdFlags = append(flags.Local.LdFlags, linker.Properties.Host_ldlibs...)
-
-		if !ctx.Windows() {
-			// Add -ldl, -lpthread, -lm and -lrt to host builds to match the default behavior of device
-			// builds
-			flags.Global.LdFlags = append(flags.Global.LdFlags,
-				"-ldl",
-				"-lpthread",
-				"-lm",
-			)
-			if !ctx.Darwin() {
-				flags.Global.LdFlags = append(flags.Global.LdFlags, "-lrt")
-			}
-		}
-	}
-
-	CheckBadLinkerFlags(ctx, "ldflags", linker.Properties.Ldflags)
 
 	flags.Local.LdFlags = append(flags.Local.LdFlags, proptools.NinjaAndShellEscapeList(linker.Properties.Ldflags)...)
 
-	if ctx.Host() && !ctx.Windows() && !ctx.static() {
-		flags.Global.LdFlags = append(flags.Global.LdFlags, RpathFlags(ctx)...)
-	}
-
-	flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.ToolchainLdflags())
-
 	// Version_script is not needed when linking stubs lib where the version
 	// script is created from the symbol map file.
 	if !linker.dynamicProperties.BuildStubs {
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 162dd54..b119fda 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -63,23 +63,51 @@
 }
 
 func makeLlndkVars(ctx android.MakeVarsContext) {
-	// Make uses LLNDK_MOVED_TO_APEX_LIBRARIES to generate the linker config.
-	movedToApexLlndkLibraries := make(map[string]bool)
-	ctx.VisitAllModules(func(module android.Module) {
-		if library := moduleLibraryInterface(module); library != nil && library.hasLLNDKStubs() {
-			if library.isLLNDKMovedToApex() {
-				name := library.implementationModuleName(module.(*Module).BaseModuleName())
-				movedToApexLlndkLibraries[name] = true
-			}
-		}
-	})
 
-	ctx.Strict("LLNDK_MOVED_TO_APEX_LIBRARIES",
-		strings.Join(android.SortedKeys(movedToApexLlndkLibraries), " "))
 }
 
 func init() {
 	RegisterLlndkLibraryTxtType(android.InitRegistrationContext)
+	android.RegisterParallelSingletonType("movedToApexLlndkLibraries", movedToApexLlndkLibrariesFactory)
+}
+
+func movedToApexLlndkLibrariesFactory() android.Singleton {
+	return &movedToApexLlndkLibraries{}
+}
+
+type movedToApexLlndkLibraries struct {
+	movedToApexLlndkLibraries []string
+}
+
+func (s *movedToApexLlndkLibraries) GenerateBuildActions(ctx android.SingletonContext) {
+	// Make uses LLNDK_MOVED_TO_APEX_LIBRARIES to generate the linker config.
+	movedToApexLlndkLibrariesMap := make(map[string]bool)
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+		if library, ok := android.OtherModuleProvider(ctx, module, LinkableInfoProvider); ok {
+			if library.HasLLNDKStubs && library.IsLLNDKMovedToApex {
+				movedToApexLlndkLibrariesMap[library.ImplementationModuleName] = true
+			}
+		}
+	})
+	s.movedToApexLlndkLibraries = android.SortedKeys(movedToApexLlndkLibrariesMap)
+
+	var sb strings.Builder
+	for i, l := range s.movedToApexLlndkLibraries {
+		if i > 0 {
+			sb.WriteRune(' ')
+		}
+		sb.WriteString(l)
+		sb.WriteString(".so")
+	}
+	android.WriteFileRule(ctx, MovedToApexLlndkLibrariesFile(ctx), sb.String())
+}
+
+func MovedToApexLlndkLibrariesFile(ctx android.PathContext) android.WritablePath {
+	return android.PathForIntermediates(ctx, "moved_to_apex_llndk_libraries.txt")
+}
+
+func (s *movedToApexLlndkLibraries) MakeVars(ctx android.MakeVarsContext) {
+	ctx.Strict("LLNDK_MOVED_TO_APEX_LIBRARIES", strings.Join(s.movedToApexLlndkLibraries, " "))
 }
 
 func RegisterLlndkLibraryTxtType(ctx android.RegistrationContext) {
@@ -118,16 +146,20 @@
 	ctx.InstallFile(installPath, filename, txt.outputFile)
 
 	ctx.SetOutputFiles(android.Paths{txt.outputFile}, "")
+
+	etc.SetCommonPrebuiltEtcInfo(ctx, txt)
 }
 
-func getVndkFileName(m *Module) (string, error) {
-	if library, ok := m.linker.(*libraryDecorator); ok {
-		return library.getLibNameHelper(m.BaseModuleName(), true, false) + ".so", nil
+func getVndkFileName(info *LinkerInfo) (string, error) {
+	if info != nil {
+		if info.LibraryDecoratorInfo != nil {
+			return info.LibraryDecoratorInfo.VndkFileName, nil
+		}
+		if info.PrebuiltLibraryLinkerInfo != nil {
+			return info.PrebuiltLibraryLinkerInfo.VndkFileName, nil
+		}
 	}
-	if prebuilt, ok := m.linker.(*prebuiltLibraryLinker); ok {
-		return prebuilt.libraryDecorator.getLibNameHelper(m.BaseModuleName(), true, false) + ".so", nil
-	}
-	return "", fmt.Errorf("VNDK library should have libraryDecorator or prebuiltLibraryLinker as linker: %T", m.linker)
+	return "", fmt.Errorf("VNDK library should have libraryDecorator or prebuiltLibraryLinker as linker: %T", info)
 }
 
 func (txt *llndkLibrariesTxtModule) GenerateSingletonBuildActions(ctx android.SingletonContext) {
@@ -136,9 +168,17 @@
 		return
 	}
 
-	ctx.VisitAllModules(func(m android.Module) {
-		if c, ok := m.(*Module); ok && c.VendorProperties.IsLLNDK && !c.Header() && !c.IsVndkPrebuiltLibrary() {
-			filename, err := getVndkFileName(c)
+	ctx.VisitAllModuleProxies(func(m android.ModuleProxy) {
+		ccInfo, ok := android.OtherModuleProvider(ctx, m, CcInfoProvider)
+		if !ok {
+			return
+		}
+		linkableInfo, ok := android.OtherModuleProvider(ctx, m, LinkableInfoProvider)
+		if !ok {
+			return
+		}
+		if linkableInfo.IsLlndk && !linkableInfo.Header && !linkableInfo.IsVndkPrebuiltLibrary {
+			filename, err := getVndkFileName(ccInfo.LinkerInfo)
 			if err != nil {
 				ctx.ModuleErrorf(m, "%s", err)
 			}
@@ -194,10 +234,10 @@
 	lib, isLib := m.linker.(*libraryDecorator)
 	prebuiltLib, isPrebuiltLib := m.linker.(*prebuiltLibraryLinker)
 
-	if m.InVendorOrProduct() && isLib && lib.hasLLNDKStubs() {
+	if m.InVendorOrProduct() && isLib && lib.HasLLNDKStubs() {
 		m.VendorProperties.IsLLNDK = true
 	}
-	if m.InVendorOrProduct() && isPrebuiltLib && prebuiltLib.hasLLNDKStubs() {
+	if m.InVendorOrProduct() && isPrebuiltLib && prebuiltLib.HasLLNDKStubs() {
 		m.VendorProperties.IsLLNDK = true
 	}
 
diff --git a/cc/lto_test.go b/cc/lto_test.go
index e4b5a3a..3fb1f3c 100644
--- a/cc/lto_test.go
+++ b/cc/lto_test.go
@@ -70,24 +70,24 @@
 
 	result := LTOPreparer.RunTestWithBp(t, bp)
 
-	libLto := result.ModuleForTests("lto_enabled", "android_arm64_armv8-a_shared").Module()
+	libLto := result.ModuleForTests(t, "lto_enabled", "android_arm64_armv8-a_shared").Module()
 
-	libFoo := result.ModuleForTests("foo", "android_arm64_armv8-a_static").Module()
+	libFoo := result.ModuleForTests(t, "foo", "android_arm64_armv8-a_static").Module()
 	if !hasDep(result, libLto, libFoo) {
 		t.Errorf("'lto_enabled' missing dependency on the default variant of 'foo'")
 	}
 
-	libBaz := result.ModuleForTests("baz", "android_arm64_armv8-a_static").Module()
+	libBaz := result.ModuleForTests(t, "baz", "android_arm64_armv8-a_static").Module()
 	if !hasDep(result, libFoo, libBaz) {
 		t.Errorf("'foo' missing dependency on the default variant of transitive dep 'baz'")
 	}
 
-	libNeverLto := result.ModuleForTests("lib_never_lto", "android_arm64_armv8-a_static").Module()
+	libNeverLto := result.ModuleForTests(t, "lib_never_lto", "android_arm64_armv8-a_static").Module()
 	if !hasDep(result, libLto, libNeverLto) {
 		t.Errorf("'lto_enabled' missing dependency on the default variant of 'lib_never_lto'")
 	}
 
-	libBar := result.ModuleForTests("bar", "android_arm64_armv8-a_shared").Module()
+	libBar := result.ModuleForTests(t, "bar", "android_arm64_armv8-a_shared").Module()
 	if !hasDep(result, libLto, libBar) {
 		t.Errorf("'lto_enabled' missing dependency on the default variant of 'bar'")
 	}
@@ -138,15 +138,15 @@
 
 	result := LTOPreparer.RunTestWithBp(t, bp)
 
-	libRoot := result.ModuleForTests("root", "android_arm64_armv8-a_shared").Module()
-	libRootLtoNever := result.ModuleForTests("root_no_lto", "android_arm64_armv8-a_shared").Module()
+	libRoot := result.ModuleForTests(t, "root", "android_arm64_armv8-a_shared").Module()
+	libRootLtoNever := result.ModuleForTests(t, "root_no_lto", "android_arm64_armv8-a_shared").Module()
 
-	libFoo := result.ModuleForTests("foo", "android_arm64_armv8-a_static")
+	libFoo := result.ModuleForTests(t, "foo", "android_arm64_armv8-a_static")
 	if !hasDep(result, libRoot, libFoo.Module()) {
 		t.Errorf("'root' missing dependency on the default variant of 'foo'")
 	}
 
-	libFooNoLto := result.ModuleForTests("foo", "android_arm64_armv8-a_static_lto-none")
+	libFooNoLto := result.ModuleForTests(t, "foo", "android_arm64_armv8-a_static_lto-none")
 	if !hasDep(result, libRootLtoNever, libFooNoLto.Module()) {
 		t.Errorf("'root_no_lto' missing dependency on the lto_none variant of 'foo'")
 	}
@@ -156,7 +156,7 @@
 		t.Errorf("'foo' expected to have flags %q, but got %q", w, libFooCFlags)
 	}
 
-	libBaz := result.ModuleForTests("baz", "android_arm64_armv8-a_static")
+	libBaz := result.ModuleForTests(t, "baz", "android_arm64_armv8-a_static")
 	if !hasDep(result, libFoo.Module(), libBaz.Module()) {
 		t.Errorf("'foo' missing dependency on the default variant of transitive dep 'baz'")
 	}
@@ -187,8 +187,8 @@
 	}`
 	result := LTOPreparer.RunTestWithBp(t, bp)
 
-	libFooWithLto := result.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
-	libFooWithoutLto := result.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("ld")
+	libFooWithLto := result.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+	libFooWithoutLto := result.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared").Rule("ld")
 
 	android.AssertStringDoesContain(t, "missing flag for LTO in variant that expects it",
 		libFooWithLto.Args["ldFlags"], "-flto=thin")
@@ -215,8 +215,8 @@
 
 	result := LTOPreparer.RunTestWithBp(t, bp)
 
-	libFoo := result.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
-	libBar := result.ModuleForTests("runtime_libbar", "android_arm_armv7-a-neon_shared").Rule("ld")
+	libFoo := result.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_shared").Rule("ld")
+	libBar := result.ModuleForTests(t, "runtime_libbar", "android_arm_armv7-a-neon_shared").Rule("ld")
 
 	android.AssertStringDoesContain(t, "missing flag for LTO in LTO enabled library",
 		libFoo.Args["ldFlags"], "-flto=thin")
diff --git a/cc/makevars.go b/cc/makevars.go
index 4cb98e7..9358755 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -100,15 +100,7 @@
 
 	// Filter vendor_public_library that are exported to make
 	var exportedVendorPublicLibraries []string
-	var warningsAllowed []string
-	var usingWnoErrors []string
-	var missingProfiles []string
 	ctx.VisitAllModules(func(module android.Module) {
-		if v, ok := android.OtherModuleProvider(ctx, module, CcMakeVarsInfoProvider); ok {
-			warningsAllowed = android.AppendIfNotZero(warningsAllowed, v.WarningsAllowed)
-			usingWnoErrors = android.AppendIfNotZero(usingWnoErrors, v.UsingWnoError)
-			missingProfiles = android.AppendIfNotZero(missingProfiles, v.MissingProfile)
-		}
 		if ccModule, ok := module.(*Module); ok {
 			baseName := ccModule.BaseModuleName()
 			if ccModule.IsVendorPublicLibrary() && module.ExportedToMake() {
@@ -123,9 +115,6 @@
 	ctx.Strict("LSDUMP_PATHS", strings.Join(lsdumpPaths, " "))
 
 	ctx.Strict("ANDROID_WARNING_ALLOWED_PROJECTS", makeStringOfWarningAllowedProjects())
-	ctx.Strict("SOONG_MODULES_WARNINGS_ALLOWED", makeVarsString(warningsAllowed))
-	ctx.Strict("SOONG_MODULES_USING_WNO_ERROR", makeVarsString(usingWnoErrors))
-	ctx.Strict("SOONG_MODULES_MISSING_PGO_PROFILE_FILE", makeVarsString(missingProfiles))
 
 	ctx.Strict("CLANG_COVERAGE_CONFIG_CFLAGS", strings.Join(clangCoverageCFlags, " "))
 	ctx.Strict("CLANG_COVERAGE_CONFIG_COMMFLAGS", strings.Join(clangCoverageCommonFlags, " "))
@@ -175,19 +164,19 @@
 	sort.Strings(ndkKnownLibs)
 	ctx.Strict("NDK_KNOWN_LIBS", strings.Join(ndkKnownLibs, " "))
 
-	hostTargets := ctx.Config().Targets[ctx.Config().BuildOS]
-	makeVarsToolchain(ctx, "", hostTargets[0])
-	if len(hostTargets) > 1 {
-		makeVarsToolchain(ctx, "2ND_", hostTargets[1])
+	if hostTargets := ctx.Config().Targets[ctx.Config().BuildOS]; len(hostTargets) > 0 {
+		makeVarsToolchain(ctx, "", hostTargets[0])
+		if len(hostTargets) > 1 {
+			makeVarsToolchain(ctx, "2ND_", hostTargets[1])
+		}
 	}
 
-	deviceTargets := ctx.Config().Targets[android.Android]
-	makeVarsToolchain(ctx, "", deviceTargets[0])
-	if len(deviceTargets) > 1 {
-		makeVarsToolchain(ctx, "2ND_", deviceTargets[1])
+	if deviceTargets := ctx.Config().Targets[android.Android]; len(deviceTargets) > 0 {
+		makeVarsToolchain(ctx, "", deviceTargets[0])
+		if len(deviceTargets) > 1 {
+			makeVarsToolchain(ctx, "2ND_", deviceTargets[1])
+		}
 	}
-
-	makeLlndkVars(ctx)
 }
 
 func makeVarsToolchain(ctx android.MakeVarsContext, secondPrefix string,
diff --git a/cc/misc_disted_files.go b/cc/misc_disted_files.go
new file mode 100644
index 0000000..4bdffaa
--- /dev/null
+++ b/cc/misc_disted_files.go
@@ -0,0 +1,89 @@
+// Copyright 2025 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"
+	"strings"
+)
+
+func init() {
+	android.InitRegistrationContext.RegisterSingletonType("cc_misc_disted_files", ccMiscDistedFilesSingletonFactory)
+}
+
+func ccMiscDistedFilesSingletonFactory() android.Singleton {
+	return &ccMiscDistedFilesSingleton{}
+}
+
+type ccMiscDistedFilesSingleton struct {
+	warningsAllowed []string
+	usingWnoErrors  []string
+}
+
+func (s *ccMiscDistedFilesSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	var warningsAllowed []string
+	var usingWnoErrors []string
+	var missingProfiles []string
+	ctx.VisitAllModules(func(module android.Module) {
+		if v, ok := android.OtherModuleProvider(ctx, module, CcMakeVarsInfoProvider); ok {
+			warningsAllowed = android.AppendIfNotZero(warningsAllowed, v.WarningsAllowed)
+			usingWnoErrors = android.AppendIfNotZero(usingWnoErrors, v.UsingWnoError)
+			missingProfiles = android.AppendIfNotZero(missingProfiles, v.MissingProfile)
+		}
+	})
+
+	warningsAllowed = android.SortedUniqueStrings(warningsAllowed)
+	usingWnoErrors = android.SortedUniqueStrings(usingWnoErrors)
+	missingProfiles = android.SortedUniqueStrings(missingProfiles)
+
+	s.warningsAllowed = warningsAllowed
+	s.usingWnoErrors = usingWnoErrors
+
+	var sb strings.Builder
+	sb.WriteString("# Modules using -Wno-error\n")
+	for _, nwe := range usingWnoErrors {
+		sb.WriteString(nwe)
+		sb.WriteString("\n")
+	}
+	sb.WriteString("# Modules that allow warnings\n")
+	for _, wa := range warningsAllowed {
+		sb.WriteString(wa)
+		sb.WriteString("\n")
+	}
+	wallWerrFile := android.PathForOutput(ctx, "wall_werror.txt")
+	android.WriteFileRuleVerbatim(ctx, wallWerrFile, sb.String())
+
+	// Only dist this file in soong-only builds. In soong+make builds, it contains information
+	// from make modules, so we'll still rely on make to build and dist it.
+	if !ctx.Config().KatiEnabled() {
+		ctx.DistForGoal("droidcore-unbundled", wallWerrFile)
+	}
+
+	var sb2 strings.Builder
+	sb2.WriteString("# Modules missing PGO profile files\n")
+	for _, mp := range missingProfiles {
+		sb2.WriteString(mp)
+		sb2.WriteString("\n")
+	}
+	profileMissingFile := android.PathForOutput(ctx, "pgo_profile_file_missing.txt")
+	android.WriteFileRuleVerbatim(ctx, profileMissingFile, sb2.String())
+
+	ctx.DistForGoal("droidcore-unbundled", profileMissingFile)
+}
+
+func (s *ccMiscDistedFilesSingleton) MakeVars(ctx android.MakeVarsContext) {
+	ctx.Strict("SOONG_MODULES_WARNINGS_ALLOWED", strings.Join(s.warningsAllowed, " "))
+	ctx.Strict("SOONG_MODULES_USING_WNO_ERROR", strings.Join(s.usingWnoErrors, " "))
+}
diff --git a/cc/ndk_abi.go b/cc/ndk_abi.go
index 2706261..b96a779 100644
--- a/cc/ndk_abi.go
+++ b/cc/ndk_abi.go
@@ -39,15 +39,15 @@
 
 func (n *ndkAbiDumpSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var depPaths android.Paths
-	ctx.VisitAllModules(func(module android.Module) {
-		if !module.Enabled(ctx) {
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+		if !android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider).Enabled {
 			return
 		}
 
-		if m, ok := module.(*Module); ok {
-			if installer, ok := m.installer.(*stubDecorator); ok {
-				if installer.hasAbiDump {
-					depPaths = append(depPaths, installer.abiDumpPath)
+		if ccInfo, ok := android.OtherModuleProvider(ctx, module, CcInfoProvider); ok {
+			if ccInfo.InstallerInfo != nil && ccInfo.InstallerInfo.StubDecoratorInfo != nil {
+				if ccInfo.InstallerInfo.StubDecoratorInfo.HasAbiDump {
+					depPaths = append(depPaths, ccInfo.InstallerInfo.StubDecoratorInfo.AbiDumpPath)
 				}
 			}
 		}
@@ -77,14 +77,14 @@
 
 func (n *ndkAbiDiffSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var depPaths android.Paths
-	ctx.VisitAllModules(func(module android.Module) {
-		if m, ok := module.(android.Module); ok && !m.Enabled(ctx) {
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+		if !android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider).Enabled {
 			return
 		}
 
-		if m, ok := module.(*Module); ok {
-			if installer, ok := m.installer.(*stubDecorator); ok {
-				depPaths = append(depPaths, installer.abiDiffPaths...)
+		if ccInfo, ok := android.OtherModuleProvider(ctx, module, CcInfoProvider); ok {
+			if ccInfo.InstallerInfo != nil && ccInfo.InstallerInfo.StubDecoratorInfo != nil {
+				depPaths = append(depPaths, ccInfo.InstallerInfo.StubDecoratorInfo.AbiDiffPaths...)
 			}
 		}
 	})
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 7481954..6e26d4c 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -80,6 +80,20 @@
 	licensePath  android.Path
 }
 
+type NdkHeaderInfo struct {
+	SrcPaths     android.Paths
+	InstallPaths android.Paths
+	LicensePath  android.Path
+	// Set to true if the headers installed by this module should skip
+	// verification. This step ensures that each header is self-contained (can
+	// be #included alone) and is valid C. This should not be disabled except in
+	// rare cases. Outside bionic and external, if you're using this option
+	// you've probably made a mistake.
+	SkipVerification bool
+}
+
+var NdkHeaderInfoProvider = blueprint.NewProvider[NdkHeaderInfo]()
+
 func getHeaderInstallDir(ctx android.ModuleContext, header android.Path, from string,
 	to string) android.OutputPath {
 	// Output path is the sysroot base + "usr/include" + to directory + directory component
@@ -135,6 +149,13 @@
 	if len(m.installPaths) == 0 {
 		ctx.ModuleErrorf("srcs %q matched zero files", m.properties.Srcs)
 	}
+
+	android.SetProvider(ctx, NdkHeaderInfoProvider, NdkHeaderInfo{
+		SrcPaths:         m.srcPaths,
+		InstallPaths:     m.installPaths,
+		LicensePath:      m.licensePath,
+		SkipVerification: Bool(m.properties.Skip_verification),
+	})
 }
 
 // ndk_headers installs the sets of ndk headers defined in the srcs property
@@ -203,6 +224,8 @@
 	licensePath  android.Path
 }
 
+var NdkPreprocessedHeaderInfoProvider = blueprint.NewProvider[NdkHeaderInfo]()
+
 func (m *preprocessedHeadersModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if String(m.properties.License) == "" {
 		ctx.PropertyErrorf("license", "field is required")
@@ -231,6 +254,13 @@
 	if len(m.installPaths) == 0 {
 		ctx.ModuleErrorf("srcs %q matched zero files", m.properties.Srcs)
 	}
+
+	android.SetProvider(ctx, NdkPreprocessedHeaderInfoProvider, NdkHeaderInfo{
+		SrcPaths:         m.srcPaths,
+		InstallPaths:     m.installPaths,
+		LicensePath:      m.licensePath,
+		SkipVerification: Bool(m.properties.Skip_verification),
+	})
 }
 
 // preprocessed_ndk_headers preprocesses all the ndk headers listed in the srcs
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 2411614..c21fe56 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -133,13 +133,13 @@
 	unversionedUntil android.ApiLevel
 }
 
-var _ versionedInterface = (*stubDecorator)(nil)
+var _ VersionedInterface = (*stubDecorator)(nil)
 
 func shouldUseVersionScript(ctx BaseModuleContext, stub *stubDecorator) bool {
 	return stub.apiLevel.GreaterThanOrEqualTo(stub.unversionedUntil)
 }
 
-func (stub *stubDecorator) implementationModuleName(name string) string {
+func (stub *stubDecorator) ImplementationModuleName(name string) string {
 	return strings.TrimSuffix(name, ndkLibrarySuffix)
 }
 
@@ -155,7 +155,7 @@
 	return versionStrs
 }
 
-func (this *stubDecorator) stubsVersions(ctx android.BaseModuleContext) []string {
+func (this *stubDecorator) StubsVersions(ctx android.BaseModuleContext) []string {
 	if !ctx.Module().Enabled(ctx) {
 		return nil
 	}
@@ -163,7 +163,7 @@
 		ctx.Module().Disable()
 		return nil
 	}
-	firstVersion, err := nativeApiLevelFromUser(ctx,
+	firstVersion, err := NativeApiLevelFromUser(ctx,
 		String(this.properties.First_version))
 	if err != nil {
 		ctx.PropertyErrorf("first_version", err.Error())
@@ -173,10 +173,10 @@
 }
 
 func (this *stubDecorator) initializeProperties(ctx BaseModuleContext) bool {
-	this.apiLevel = nativeApiLevelOrPanic(ctx, this.stubsVersion())
+	this.apiLevel = nativeApiLevelOrPanic(ctx, this.StubsVersion())
 
 	var err error
-	this.firstVersion, err = nativeApiLevelFromUser(ctx,
+	this.firstVersion, err = NativeApiLevelFromUser(ctx,
 		String(this.properties.First_version))
 	if err != nil {
 		ctx.PropertyErrorf("first_version", err.Error())
@@ -184,7 +184,7 @@
 	}
 
 	str := proptools.StringDefault(this.properties.Unversioned_until, "minimum")
-	this.unversionedUntil, err = nativeApiLevelFromUser(ctx, str)
+	this.unversionedUntil, err = NativeApiLevelFromUser(ctx, str)
 	if err != nil {
 		ctx.PropertyErrorf("unversioned_until", err.Error())
 		return false
@@ -236,7 +236,7 @@
 	pctx.StaticVariable("StubLibraryCompilerFlags", strings.Join(stubLibraryCompilerFlags, " "))
 }
 
-func addStubLibraryCompilerFlags(flags Flags) Flags {
+func AddStubLibraryCompilerFlags(flags Flags) Flags {
 	flags.Global.CFlags = append(flags.Global.CFlags, stubLibraryCompilerFlags...)
 	// All symbols in the stubs library should be visible.
 	if inList("-fvisibility=hidden", flags.Local.CFlags) {
@@ -247,17 +247,17 @@
 
 func (stub *stubDecorator) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags {
 	flags = stub.baseCompiler.compilerFlags(ctx, flags, deps)
-	return addStubLibraryCompilerFlags(flags)
+	return AddStubLibraryCompilerFlags(flags)
 }
 
-type ndkApiOutputs struct {
-	stubSrc       android.ModuleGenPath
-	versionScript android.ModuleGenPath
+type NdkApiOutputs struct {
+	StubSrc       android.ModuleGenPath
+	VersionScript android.ModuleGenPath
 	symbolList    android.ModuleGenPath
 }
 
-func parseNativeAbiDefinition(ctx ModuleContext, symbolFile string,
-	apiLevel android.ApiLevel, genstubFlags string) ndkApiOutputs {
+func ParseNativeAbiDefinition(ctx android.ModuleContext, symbolFile string,
+	apiLevel android.ApiLevel, genstubFlags string) NdkApiOutputs {
 
 	stubSrcPath := android.PathForModuleGen(ctx, "stub.c")
 	versionScriptPath := android.PathForModuleGen(ctx, "stub.map")
@@ -279,37 +279,36 @@
 		},
 	})
 
-	return ndkApiOutputs{
-		stubSrc:       stubSrcPath,
-		versionScript: versionScriptPath,
+	return NdkApiOutputs{
+		StubSrc:       stubSrcPath,
+		VersionScript: versionScriptPath,
 		symbolList:    symbolListPath,
 	}
 }
 
-func compileStubLibrary(ctx ModuleContext, flags Flags, src android.Path) Objects {
+func CompileStubLibrary(ctx android.ModuleContext, flags Flags, src android.Path, sharedFlags *SharedFlags) Objects {
 	// libc/libm stubs libraries end up mismatching with clang's internal definition of these
 	// functions (which have noreturn attributes and other things). Because we just want to create a
 	// stub with symbol definitions, and types aren't important in C, ignore the mismatch.
 	flags.Local.ConlyFlags = append(flags.Local.ConlyFlags, "-fno-builtin")
 	return compileObjs(ctx, flagsToBuilderFlags(flags), "",
-		android.Paths{src}, nil, nil, nil, nil)
+		android.Paths{src}, nil, nil, nil, nil, sharedFlags)
 }
 
 func (this *stubDecorator) findImplementationLibrary(ctx ModuleContext) android.Path {
-	dep := ctx.GetDirectDepWithTag(strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix),
+	dep := ctx.GetDirectDepProxyWithTag(strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix),
 		stubImplementation)
 	if dep == nil {
-		ctx.ModuleErrorf("Could not find implementation for stub")
+		ctx.ModuleErrorf("Could not find implementation for stub: ")
 		return nil
 	}
-	impl, ok := dep.(*Module)
-	if !ok {
+	if _, ok := android.OtherModuleProvider(ctx, *dep, CcInfoProvider); !ok {
 		ctx.ModuleErrorf("Implementation for stub is not correct module type")
 		return nil
 	}
-	output := impl.UnstrippedOutputFile()
+	output := android.OtherModuleProviderOrDefault(ctx, *dep, LinkableInfoProvider).UnstrippedOutputFile
 	if output == nil {
-		ctx.ModuleErrorf("implementation module (%s) has no output", impl)
+		ctx.ModuleErrorf("implementation module (%s) has no output", *dep)
 		return nil
 	}
 
@@ -490,7 +489,7 @@
 		ctx.PropertyErrorf("symbol_file", "must end with .map.txt")
 	}
 
-	if !c.buildStubs() {
+	if !c.BuildStubs() {
 		// NDK libraries have no implementation variant, nothing to do
 		return Objects{}
 	}
@@ -501,9 +500,9 @@
 	}
 
 	symbolFile := String(c.properties.Symbol_file)
-	nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile, c.apiLevel, "")
-	objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
-	c.versionScriptPath = nativeAbiResult.versionScript
+	nativeAbiResult := ParseNativeAbiDefinition(ctx, symbolFile, c.apiLevel, "")
+	objs := CompileStubLibrary(ctx, flags, nativeAbiResult.StubSrc, ctx.getSharedFlags())
+	c.versionScriptPath = nativeAbiResult.VersionScript
 	if c.canDumpAbi(ctx) {
 		c.dumpAbi(ctx, nativeAbiResult.symbolList)
 		if c.canDiffAbi(ctx.Config()) {
@@ -511,7 +510,7 @@
 		}
 	}
 	if c.apiLevel.IsCurrent() && ctx.PrimaryArch() {
-		c.parsedCoverageXmlPath = parseSymbolFileForAPICoverage(ctx, symbolFile)
+		c.parsedCoverageXmlPath = ParseSymbolFileForAPICoverage(ctx, symbolFile)
 	}
 	return objs
 }
@@ -542,7 +541,7 @@
 func (stub *stubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps,
 	objs Objects) android.Path {
 
-	if !stub.buildStubs() {
+	if !stub.BuildStubs() {
 		// NDK libraries have no implementation variant, nothing to do
 		return nil
 	}
@@ -561,6 +560,10 @@
 	return false
 }
 
+func (stub *stubDecorator) defaultDistFiles() []android.Path {
+	return nil
+}
+
 // Returns the install path for unversioned NDK libraries (currently only static
 // libraries).
 func getUnversionedLibraryInstallPath(ctx ModuleContext) android.OutputPath {
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index 92da172..1677862 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -53,11 +53,12 @@
 // TODO(danalbert): Write `ndk_static_library` rule.
 
 import (
-	"android/soong/android"
 	"fmt"
 	"path/filepath"
 	"strings"
 
+	"android/soong/android"
+
 	"github.com/google/blueprint"
 )
 
@@ -209,57 +210,61 @@
 	var headerCCompatVerificationTimestampPaths android.Paths
 	var installPaths android.Paths
 	var licensePaths android.Paths
-	ctx.VisitAllModules(func(module android.Module) {
-		if m, ok := module.(android.Module); ok && !m.Enabled(ctx) {
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+
+		if !android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider).Enabled {
 			return
 		}
 
-		if m, ok := module.(*headerModule); ok {
-			headerSrcPaths = append(headerSrcPaths, m.srcPaths...)
-			headerInstallPaths = append(headerInstallPaths, m.installPaths...)
-			if !Bool(m.properties.Skip_verification) {
-				for i, installPath := range m.installPaths {
+		if m, ok := android.OtherModuleProvider(ctx, module, NdkHeaderInfoProvider); ok {
+			headerSrcPaths = append(headerSrcPaths, m.SrcPaths...)
+			headerInstallPaths = append(headerInstallPaths, m.InstallPaths...)
+			if !m.SkipVerification {
+				for i, installPath := range m.InstallPaths {
 					headersToVerify = append(headersToVerify, srcDestPair{
-						src:  m.srcPaths[i],
+						src:  m.SrcPaths[i],
 						dest: installPath,
 					})
 				}
 			}
-			installPaths = append(installPaths, m.installPaths...)
-			licensePaths = append(licensePaths, m.licensePath)
+			installPaths = append(installPaths, m.InstallPaths...)
+			licensePaths = append(licensePaths, m.LicensePath)
 		}
 
-		if m, ok := module.(*preprocessedHeadersModule); ok {
-			headerSrcPaths = append(headerSrcPaths, m.srcPaths...)
-			headerInstallPaths = append(headerInstallPaths, m.installPaths...)
-			if !Bool(m.properties.Skip_verification) {
-				for i, installPath := range m.installPaths {
+		if m, ok := android.OtherModuleProvider(ctx, module, NdkPreprocessedHeaderInfoProvider); ok {
+			headerSrcPaths = append(headerSrcPaths, m.SrcPaths...)
+			headerInstallPaths = append(headerInstallPaths, m.InstallPaths...)
+			if !m.SkipVerification {
+				for i, installPath := range m.InstallPaths {
 					headersToVerify = append(headersToVerify, srcDestPair{
-						src:  m.srcPaths[i],
+						src:  m.SrcPaths[i],
 						dest: installPath,
 					})
 				}
 			}
-			installPaths = append(installPaths, m.installPaths...)
-			licensePaths = append(licensePaths, m.licensePath)
+			installPaths = append(installPaths, m.InstallPaths...)
+			licensePaths = append(licensePaths, m.LicensePath)
 		}
 
-		if m, ok := module.(*Module); ok {
-			if installer, ok := m.installer.(*stubDecorator); ok && m.library.buildStubs() {
-				installPaths = append(installPaths, installer.installPath)
+		if ccInfo, ok := android.OtherModuleProvider(ctx, module, CcInfoProvider); ok {
+			if installer := ccInfo.InstallerInfo; installer != nil && installer.StubDecoratorInfo != nil &&
+				ccInfo.LibraryInfo != nil && ccInfo.LibraryInfo.BuildStubs {
+				installPaths = append(installPaths, installer.StubDecoratorInfo.InstallPath)
 			}
 
-			if library, ok := m.linker.(*libraryDecorator); ok {
-				if library.ndkSysrootPath != nil {
-					staticLibInstallPaths = append(
-						staticLibInstallPaths, library.ndkSysrootPath)
+			if ccInfo.LinkerInfo != nil {
+				if library := ccInfo.LinkerInfo.LibraryDecoratorInfo; library != nil {
+					if library.NdkSysrootPath != nil {
+						staticLibInstallPaths = append(
+							staticLibInstallPaths, library.NdkSysrootPath)
+					}
 				}
-			}
 
-			if object, ok := m.linker.(*objectLinker); ok {
-				if object.ndkSysrootPath != nil {
-					staticLibInstallPaths = append(
-						staticLibInstallPaths, object.ndkSysrootPath)
+				if object := ccInfo.LinkerInfo.ObjectLinkerInfo; object != nil {
+					if object.NdkSysrootPath != nil {
+						staticLibInstallPaths = append(
+							staticLibInstallPaths, object.NdkSysrootPath)
+					}
 				}
 			}
 		}
diff --git a/cc/ndk_test.go b/cc/ndk_test.go
index f20d3c6..8574bf1 100644
--- a/cc/ndk_test.go
+++ b/cc/ndk_test.go
@@ -50,7 +50,7 @@
 	}
 	`
 	ctx := prepareForCcTest.RunTestWithBp(t, bp)
-	libfoo := ctx.ModuleForTests("libfoo.ndk", "android_arm64_armv8-a_sdk_shared")
-	libfoo_headers := ctx.ModuleForTests("libfoo_headers", "")
+	libfoo := ctx.ModuleForTests(t, "libfoo.ndk", "android_arm64_armv8-a_sdk_shared")
+	libfoo_headers := ctx.ModuleForTests(t, "libfoo_headers", "")
 	android.AssertBoolEquals(t, "Could not find headers of ndk_library", true, isDep(ctx, libfoo.Module(), libfoo_headers.Module()))
 }
diff --git a/cc/object.go b/cc/object.go
index c89520a..ea3ed61 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -159,7 +159,7 @@
 	// isForPlatform is terribly named and actually means isNotApex.
 	if Bool(object.Properties.Crt) &&
 		!Bool(object.Properties.Exclude_from_ndk_sysroot) && ctx.useSdk() &&
-		ctx.isSdkVariant() && ctx.isForPlatform() {
+		ctx.isSdkVariant() && CtxIsForPlatform(ctx) {
 
 		output = getVersionedLibraryInstallPath(ctx,
 			nativeApiLevelOrPanic(ctx, ctx.sdkVersion())).Join(ctx, outputName)
@@ -242,7 +242,15 @@
 	return Bool(object.Properties.Crt)
 }
 
+func (object *objectLinker) defaultDistFiles() []android.Path {
+	return nil
+}
+
 func (object *objectLinker) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
 	object.baseLinker.moduleInfoJSON(ctx, moduleInfoJSON)
 	moduleInfoJSON.Class = []string{"STATIC_LIBRARIES"}
 }
+
+func (object *objectLinker) testSuiteInfo(ctx ModuleContext) {
+	// not a test
+}
diff --git a/cc/object_test.go b/cc/object_test.go
index 004dfd3..6d82265 100644
--- a/cc/object_test.go
+++ b/cc/object_test.go
@@ -46,13 +46,13 @@
 
 	ctx := prepareForCcTest.RunTestWithBp(t, bp)
 	for _, v := range variants {
-		cflags := ctx.ModuleForTests("crt_foo", v.variant).Rule("cc").Args["cFlags"]
+		cflags := ctx.ModuleForTests(t, "crt_foo", v.variant).Rule("cc").Args["cFlags"]
 		expected := "-target aarch64-linux-android" + v.num + " "
 		android.AssertStringDoesContain(t, "cflag", cflags, expected)
 	}
 	ctx = prepareForCcTest.RunTestWithBp(t, bp)
 	android.AssertStringDoesContain(t, "cflag",
-		ctx.ModuleForTests("crt_foo", "android_vendor_arm64_armv8-a").Rule("cc").Args["cFlags"],
+		ctx.ModuleForTests(t, "crt_foo", "android_vendor_arm64_armv8-a").Rule("cc").Args["cFlags"],
 		"-target aarch64-linux-android10000 ")
 }
 
@@ -69,13 +69,13 @@
 
 	// Sdk variant uses the crt object of the matching min_sdk_version
 	variant := "android_arm64_armv8-a_sdk"
-	crt := ctx.ModuleForTests("bin", variant).Rule("ld").Args["crtBegin"]
+	crt := ctx.ModuleForTests(t, "bin", variant).Rule("ld").Args["crtBegin"]
 	android.AssertStringDoesContain(t, "crt dep of sdk variant", crt,
 		"29/crtbegin_dynamic.o")
 
 	// platform variant uses the crt object built for platform
 	variant = "android_arm64_armv8-a"
-	crt = ctx.ModuleForTests("bin", variant).Rule("ld").Args["crtBegin"]
+	crt = ctx.ModuleForTests(t, "bin", variant).Rule("ld").Args["crtBegin"]
 	android.AssertStringDoesContain(t, "crt dep of platform variant", crt,
 		variant+"/crtbegin_dynamic.o")
 }
@@ -147,7 +147,7 @@
 			ctx := PrepareForIntegrationTestWithCc.RunTestWithBp(t, bp)
 			android.AssertPathRelativeToTopEquals(t, "expected output file foo.o",
 				fmt.Sprintf("out/soong/.intermediates/%s/android_arm64_armv8-a/foo.o", testcase.moduleName),
-				ctx.ModuleForTests(testcase.moduleName, "android_arm64_armv8-a").Output("foo.o").Output)
+				ctx.ModuleForTests(t, testcase.moduleName, "android_arm64_armv8-a").Output("foo.o").Output)
 		})
 	}
 
diff --git a/cc/orderfile.go b/cc/orderfile.go
index 6e08da7..c07a098 100644
--- a/cc/orderfile.go
+++ b/cc/orderfile.go
@@ -153,7 +153,7 @@
 	}
 
 	// Currently, we are not enabling orderfiles to begin from static libraries
-	if ctx.static() && !ctx.staticBinary() {
+	if ctx.staticLibrary() {
 		return
 	}
 
diff --git a/cc/orderfile_test.go b/cc/orderfile_test.go
index 3486f96..41253ad 100644
--- a/cc/orderfile_test.go
+++ b/cc/orderfile_test.go
@@ -41,7 +41,7 @@
 
 	expectedCFlag := "-forder-file-instrumentation"
 
-	libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared")
+	libTest := result.ModuleForTests(t, "libTest", "android_arm64_armv8-a_shared")
 
 	// Check cFlags of orderfile-enabled module
 	cFlags := libTest.Rule("cc").Args["cFlags"]
@@ -77,7 +77,7 @@
 
 	expectedCFlag := "-Wl,--symbol-ordering-file=toolchain/pgo-profiles/orderfiles/libTest.orderfile"
 
-	libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared")
+	libTest := result.ModuleForTests(t, "libTest", "android_arm64_armv8-a_shared")
 
 	// Check ldFlags of orderfile-enabled module
 	ldFlags := libTest.Rule("ld").Args["ldFlags"]
@@ -106,7 +106,7 @@
 
 	expectedCFlag := "-forder-file-instrumentation"
 
-	test := result.ModuleForTests("test", "android_arm64_armv8-a")
+	test := result.ModuleForTests(t, "test", "android_arm64_armv8-a")
 
 	// Check cFlags of orderfile-enabled module
 	cFlags := test.Rule("cc").Args["cFlags"]
@@ -142,7 +142,7 @@
 
 	expectedCFlag := "-Wl,--symbol-ordering-file=toolchain/pgo-profiles/orderfiles/test.orderfile"
 
-	test := result.ModuleForTests("test", "android_arm64_armv8-a")
+	test := result.ModuleForTests(t, "test", "android_arm64_armv8-a")
 
 	// Check ldFlags of orderfile-enabled module
 	ldFlags := test.Rule("ld").Args["ldFlags"]
@@ -185,7 +185,7 @@
 	expectedCFlag := "-forder-file-instrumentation"
 
 	// Check cFlags of orderfile-enabled module
-	libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared")
+	libTest := result.ModuleForTests(t, "libTest", "android_arm64_armv8-a_shared")
 
 	cFlags := libTest.Rule("cc").Args["cFlags"]
 	if !strings.Contains(cFlags, expectedCFlag) {
@@ -193,8 +193,8 @@
 	}
 
 	// Check cFlags of orderfile variant static libraries
-	libFooOfVariant := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_orderfile")
-	libBarOfVariant := result.ModuleForTests("libBar", "android_arm64_armv8-a_static_orderfile")
+	libFooOfVariant := result.ModuleForTests(t, "libFoo", "android_arm64_armv8-a_static_orderfile")
+	libBarOfVariant := result.ModuleForTests(t, "libBar", "android_arm64_armv8-a_static_orderfile")
 
 	cFlags = libFooOfVariant.Rule("cc").Args["cFlags"]
 	if !strings.Contains(cFlags, expectedCFlag) {
@@ -216,8 +216,8 @@
 	}
 
 	// Check cFlags of the non-orderfile variant static libraries
-	libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static")
-	libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static")
+	libFoo := result.ModuleForTests(t, "libFoo", "android_arm64_armv8-a_static")
+	libBar := result.ModuleForTests(t, "libBar", "android_arm64_armv8-a_static")
 
 	cFlags = libFoo.Rule("cc").Args["cFlags"]
 	if strings.Contains(cFlags, expectedCFlag) {
@@ -274,15 +274,15 @@
 	expectedCFlag := "-Wl,--symbol-ordering-file=toolchain/pgo-profiles/orderfiles/test.orderfile"
 
 	// Check ldFlags of orderfile-enabled module
-	libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared")
+	libTest := result.ModuleForTests(t, "libTest", "android_arm64_armv8-a_shared")
 
 	ldFlags := libTest.Rule("ld").Args["ldFlags"]
 	if !strings.Contains(ldFlags, expectedCFlag) {
 		t.Errorf("Expected 'libTest' to load orderfile, but did not find %q in ldFlags %q", expectedCFlag, ldFlags)
 	}
 
-	libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static")
-	libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static")
+	libFoo := result.ModuleForTests(t, "libFoo", "android_arm64_armv8-a_static")
+	libBar := result.ModuleForTests(t, "libBar", "android_arm64_armv8-a_static")
 
 	// Check dependency edge from orderfile-enabled module to non-orderfile variant static libraries
 	if !hasDirectDep(result, libTest.Module(), libFoo.Module()) {
@@ -343,7 +343,7 @@
 	expectedCFlag := "-forder-file-instrumentation"
 
 	// Check cFlags of orderfile-enabled module
-	libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared")
+	libTest := result.ModuleForTests(t, "libTest", "android_arm64_armv8-a_shared")
 
 	cFlags := libTest.Rule("cc").Args["cFlags"]
 	if !strings.Contains(cFlags, expectedCFlag) {
@@ -351,8 +351,8 @@
 	}
 
 	// Check cFlags of the static and shared libraries
-	libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_shared")
-	libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static")
+	libFoo := result.ModuleForTests(t, "libFoo", "android_arm64_armv8-a_shared")
+	libBar := result.ModuleForTests(t, "libBar", "android_arm64_armv8-a_static")
 
 	cFlags = libFoo.Rule("cc").Args["cFlags"]
 	if strings.Contains(cFlags, expectedCFlag) {
@@ -423,7 +423,7 @@
 	expectedCFlag := "-forder-file-instrumentation"
 
 	// Check cFlags of module
-	libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_static")
+	libTest := result.ModuleForTests(t, "libTest", "android_arm64_armv8-a_static")
 
 	cFlags := libTest.Rule("cc").Args["cFlags"]
 	if strings.Contains(cFlags, expectedCFlag) {
@@ -431,8 +431,8 @@
 	}
 
 	// Check cFlags of the static libraries
-	libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static")
-	libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static")
+	libFoo := result.ModuleForTests(t, "libFoo", "android_arm64_armv8-a_static")
+	libBar := result.ModuleForTests(t, "libBar", "android_arm64_armv8-a_static")
 
 	cFlags = libFoo.Rule("cc").Args["cFlags"]
 	if strings.Contains(cFlags, expectedCFlag) {
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 96a07bc..70ee5e3 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -114,10 +114,10 @@
 
 	// TODO(ccross): verify shared library dependencies
 	srcs := p.prebuiltSrcs(ctx)
-	stubInfo := addStubDependencyProviders(ctx)
+	stubInfo := AddStubDependencyProviders(ctx)
 
 	// Stub variants will create a stub .so file from stub .c files
-	if p.buildStubs() && objs.objFiles != nil {
+	if p.BuildStubs() && objs.objFiles != nil {
 		// TODO (b/275273834): Make objs.objFiles == nil a hard error when the symbol files have been added to module sdk.
 		return p.linkShared(ctx, flags, deps, objs)
 	}
@@ -204,7 +204,7 @@
 				Target:        ctx.Target(),
 
 				TableOfContents: p.tocFile,
-				IsStubs:         p.buildStubs(),
+				IsStubs:         p.BuildStubs(),
 			})
 
 			return outputFile
@@ -268,7 +268,7 @@
 }
 
 // Implements versionedInterface
-func (p *prebuiltLibraryLinker) implementationModuleName(name string) string {
+func (p *prebuiltLibraryLinker) ImplementationModuleName(name string) string {
 	return android.RemoveOptionalPrebuiltPrefix(name)
 }
 
@@ -298,7 +298,7 @@
 }
 
 func (p *prebuiltLibraryLinker) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
-	if p.buildStubs() && p.stubsVersion() != "" {
+	if p.BuildStubs() && p.StubsVersion() != "" {
 		return p.compileModuleLibApiStubs(ctx, flags, deps)
 	}
 	return Objects{}
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index 3214fb4..af68ca6 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -116,21 +116,21 @@
 	})
 
 	// Verify that all the modules exist and that their dependencies were connected correctly
-	liba := ctx.ModuleForTests("liba", "android_arm64_armv8-a_shared").Module()
-	libb := ctx.ModuleForTests("libb", "android_arm64_armv8-a_static").Module()
-	libd := ctx.ModuleForTests("libd", "android_arm64_armv8-a_shared").Module()
-	libe := ctx.ModuleForTests("libe", "android_arm64_armv8-a_static").Module()
-	libfStatic := ctx.ModuleForTests("libf", "android_arm64_armv8-a_static").Module()
-	libfShared := ctx.ModuleForTests("libf", "android_arm64_armv8-a_shared").Module()
-	crtx := ctx.ModuleForTests("crtx", "android_arm64_armv8-a").Module()
+	liba := ctx.ModuleForTests(t, "liba", "android_arm64_armv8-a_shared").Module()
+	libb := ctx.ModuleForTests(t, "libb", "android_arm64_armv8-a_static").Module()
+	libd := ctx.ModuleForTests(t, "libd", "android_arm64_armv8-a_shared").Module()
+	libe := ctx.ModuleForTests(t, "libe", "android_arm64_armv8-a_static").Module()
+	libfStatic := ctx.ModuleForTests(t, "libf", "android_arm64_armv8-a_static").Module()
+	libfShared := ctx.ModuleForTests(t, "libf", "android_arm64_armv8-a_shared").Module()
+	crtx := ctx.ModuleForTests(t, "crtx", "android_arm64_armv8-a").Module()
 
-	prebuiltLiba := ctx.ModuleForTests("prebuilt_liba", "android_arm64_armv8-a_shared").Module()
-	prebuiltLibb := ctx.ModuleForTests("prebuilt_libb", "android_arm64_armv8-a_static").Module()
-	prebuiltLibd := ctx.ModuleForTests("prebuilt_libd", "android_arm64_armv8-a_shared").Module()
-	prebuiltLibe := ctx.ModuleForTests("prebuilt_libe", "android_arm64_armv8-a_static").Module()
-	prebuiltLibfStatic := ctx.ModuleForTests("prebuilt_libf", "android_arm64_armv8-a_static").Module()
-	prebuiltLibfShared := ctx.ModuleForTests("prebuilt_libf", "android_arm64_armv8-a_shared").Module()
-	prebuiltCrtx := ctx.ModuleForTests("prebuilt_crtx", "android_arm64_armv8-a").Module()
+	prebuiltLiba := ctx.ModuleForTests(t, "prebuilt_liba", "android_arm64_armv8-a_shared").Module()
+	prebuiltLibb := ctx.ModuleForTests(t, "prebuilt_libb", "android_arm64_armv8-a_static").Module()
+	prebuiltLibd := ctx.ModuleForTests(t, "prebuilt_libd", "android_arm64_armv8-a_shared").Module()
+	prebuiltLibe := ctx.ModuleForTests(t, "prebuilt_libe", "android_arm64_armv8-a_static").Module()
+	prebuiltLibfStatic := ctx.ModuleForTests(t, "prebuilt_libf", "android_arm64_armv8-a_static").Module()
+	prebuiltLibfShared := ctx.ModuleForTests(t, "prebuilt_libf", "android_arm64_armv8-a_shared").Module()
+	prebuiltCrtx := ctx.ModuleForTests(t, "prebuilt_crtx", "android_arm64_armv8-a").Module()
 
 	hasDep := func(m android.Module, wantDep android.Module) bool {
 		t.Helper()
@@ -190,7 +190,7 @@
 		"libf.so": nil,
 	})
 
-	shared := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_shared").Module().(*Module)
+	shared := ctx.ModuleForTests(t, "libtest", "android_arm64_armv8-a_shared").Module().(*Module)
 	assertString(t, shared.OutputFile().Path().Base(), "libtest.so")
 }
 
@@ -204,7 +204,7 @@
 		"libf.a": nil,
 	})
 
-	static := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_static").Module().(*Module)
+	static := ctx.ModuleForTests(t, "libtest", "android_arm64_armv8-a_static").Module().(*Module)
 	assertString(t, static.OutputFile().Path().Base(), "libf.a")
 }
 
@@ -227,10 +227,10 @@
 		"libf.so": nil,
 	})
 
-	shared := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_shared").Module().(*Module)
+	shared := ctx.ModuleForTests(t, "libtest", "android_arm64_armv8-a_shared").Module().(*Module)
 	assertString(t, shared.OutputFile().Path().Base(), "libtest.so")
 
-	static := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_static").Module().(*Module)
+	static := ctx.ModuleForTests(t, "libtest", "android_arm64_armv8-a_static").Module().(*Module)
 	assertString(t, static.OutputFile().Path().Base(), "libf.a")
 }
 
@@ -254,10 +254,10 @@
 		"libfoo.so": nil,
 	})
 
-	static := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Module().(*Module)
+	static := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_static").Module().(*Module)
 	assertString(t, static.OutputFile().Path().Base(), "libfoo.a")
 
-	shared := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module().(*Module)
+	shared := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared").Module().(*Module)
 	assertString(t, shared.OutputFile().Path().Base(), "libbar.so")
 }
 
@@ -275,7 +275,7 @@
 		"libfoo.so": nil,
 	})
 
-	shared := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module().(*Module)
+	shared := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared").Module().(*Module)
 	assertString(t, shared.OutputFile().Path().Base(), "libbar.so")
 }
 
@@ -312,7 +312,7 @@
 		"foo":       nil,
 	})
 
-	fooRule := ctx.ModuleForTests("foo", "linux_glibc_x86_64").Rule("Symlink")
+	fooRule := ctx.ModuleForTests(t, "foo", "linux_glibc_x86_64").Rule("Symlink")
 	assertString(t, fooRule.Output.String(), "out/soong/.intermediates/foo/linux_glibc_x86_64/foo")
 	assertString(t, fooRule.Args["fromPath"], "$$PWD/linux_glibc_x86_64/bin/foo")
 
@@ -355,16 +355,16 @@
 	// Without SANITIZE_TARGET.
 	ctx := testPrebuilt(t, bp, fs)
 
-	shared_rule := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_shared").Rule("android/soong/cc.strip")
+	shared_rule := ctx.ModuleForTests(t, "libtest", "android_arm64_armv8-a_shared").Rule("android/soong/cc.strip")
 	assertString(t, shared_rule.Input.String(), "libf.so")
 
-	static := ctx.ModuleForTests("libtest", "android_arm64_armv8-a_static").Module().(*Module)
+	static := ctx.ModuleForTests(t, "libtest", "android_arm64_armv8-a_static").Module().(*Module)
 	assertString(t, static.OutputFile().Path().Base(), "libf.a")
 
-	shared_rule2 := ctx.ModuleForTests("libtest_shared", "android_arm64_armv8-a_shared").Rule("android/soong/cc.strip")
+	shared_rule2 := ctx.ModuleForTests(t, "libtest_shared", "android_arm64_armv8-a_shared").Rule("android/soong/cc.strip")
 	assertString(t, shared_rule2.Input.String(), "libf.so")
 
-	static2 := ctx.ModuleForTests("libtest_static", "android_arm64_armv8-a_static").Module().(*Module)
+	static2 := ctx.ModuleForTests(t, "libtest_static", "android_arm64_armv8-a_static").Module().(*Module)
 	assertString(t, static2.OutputFile().Path().Base(), "libf.a")
 
 	// With SANITIZE_TARGET=hwaddress
@@ -374,16 +374,16 @@
 		}),
 	)
 
-	shared_rule = ctx.ModuleForTests("libtest", "android_arm64_armv8-a_shared_hwasan").Rule("android/soong/cc.strip")
+	shared_rule = ctx.ModuleForTests(t, "libtest", "android_arm64_armv8-a_shared_hwasan").Rule("android/soong/cc.strip")
 	assertString(t, shared_rule.Input.String(), "hwasan/libf.so")
 
-	static = ctx.ModuleForTests("libtest", "android_arm64_armv8-a_static_hwasan").Module().(*Module)
+	static = ctx.ModuleForTests(t, "libtest", "android_arm64_armv8-a_static_hwasan").Module().(*Module)
 	assertString(t, static.OutputFile().Path().Base(), "libf.hwasan.a")
 
-	shared_rule2 = ctx.ModuleForTests("libtest_shared", "android_arm64_armv8-a_shared_hwasan").Rule("android/soong/cc.strip")
+	shared_rule2 = ctx.ModuleForTests(t, "libtest_shared", "android_arm64_armv8-a_shared_hwasan").Rule("android/soong/cc.strip")
 	assertString(t, shared_rule2.Input.String(), "hwasan/libf.so")
 
-	static2 = ctx.ModuleForTests("libtest_static", "android_arm64_armv8-a_static_hwasan").Module().(*Module)
+	static2 = ctx.ModuleForTests(t, "libtest_static", "android_arm64_armv8-a_static_hwasan").Module().(*Module)
 	assertString(t, static2.OutputFile().Path().Base(), "libf.hwasan.a")
 }
 
@@ -394,7 +394,7 @@
 	srcs: [],
 }`
 	ctx := testPrebuilt(t, bp, map[string][]byte{})
-	mod := ctx.ModuleForTests("bintest", "android_arm64_armv8-a").Module().(*Module)
+	mod := ctx.ModuleForTests(t, "bintest", "android_arm64_armv8-a").Module().(*Module)
 	android.AssertBoolEquals(t, `expected no srcs to yield no output file`, false, mod.OutputFile().Valid())
 }
 
@@ -484,8 +484,8 @@
 			"libbar.so": nil,
 			"crtx.o":    nil,
 		}, preparer)
-		libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
-		expectedDependency := ctx.ModuleForTests(tc.expectedDependencyName, "android_arm64_armv8-a_shared").Module()
+		libfoo := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared").Module()
+		expectedDependency := ctx.ModuleForTests(t, tc.expectedDependencyName, "android_arm64_armv8-a_shared").Module()
 		android.AssertBoolEquals(t, fmt.Sprintf("expected dependency from %s to %s\n", libfoo.Name(), tc.expectedDependencyName), true, hasDep(ctx, libfoo, expectedDependency))
 		// check that LOCAL_SHARED_LIBRARIES contains libbar and not libbar.v<N>
 		entries := android.AndroidMkInfoForTest(t, ctx, libfoo).PrimaryInfo
@@ -493,7 +493,7 @@
 
 		// check installation rules
 		// the selected soong module should be exported to make
-		libbar := ctx.ModuleForTests(tc.expectedDependencyName, "android_arm64_armv8-a_shared").Module()
+		libbar := ctx.ModuleForTests(t, tc.expectedDependencyName, "android_arm64_armv8-a_shared").Module()
 		android.AssertBoolEquals(t, fmt.Sprintf("dependency %s should be exported to make\n", expectedDependency), true, !libbar.IsHideFromMake())
 
 		// check LOCAL_MODULE of the selected module name
@@ -585,8 +585,8 @@
 		if tc.expectedErr != "" {
 			return // the fixture will assert that the excepted err has been raised
 		}
-		libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
-		expectedDependency := ctx.ModuleForTests(tc.expectedDependencyName, "android_arm64_armv8-a_shared").Module()
+		libfoo := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared").Module()
+		expectedDependency := ctx.ModuleForTests(t, tc.expectedDependencyName, "android_arm64_armv8-a_shared").Module()
 		android.AssertBoolEquals(t, fmt.Sprintf("expected dependency from %s to %s\n", libfoo.Name(), tc.expectedDependencyName), true, hasDep(ctx, libfoo, expectedDependency))
 	}
 }
@@ -638,8 +638,8 @@
 		"libbar.so": nil,
 		"crtx.o":    nil,
 	}, preparer)
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
-	sourceLibBar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_static").Module()
+	libfoo := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared").Module()
+	sourceLibBar := ctx.ModuleForTests(t, "libbar", "android_arm64_armv8-a_static").Module()
 	// Even though the prebuilt is listed in apex_contributions, the prebuilt does not have a static variant.
 	// Therefore source of libbar should be used.
 	android.AssertBoolEquals(t, fmt.Sprintf("expected dependency from libfoo to source libbar"), true, hasDep(ctx, libfoo, sourceLibBar))
diff --git a/cc/proto_test.go b/cc/proto_test.go
index a905ea8..47b7e17 100644
--- a/cc/proto_test.go
+++ b/cc/proto_test.go
@@ -29,7 +29,7 @@
 			srcs: ["a.proto"],
 		}`)
 
-		proto := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Output("proto/a.pb.cc")
+		proto := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_shared").Output("proto/a.pb.cc")
 
 		if cmd := proto.RuleParams.Command; !strings.Contains(cmd, "--cpp_out=") {
 			t.Errorf("expected '--cpp_out' in %q", cmd)
@@ -53,8 +53,8 @@
 
 		buildOS := ctx.Config().BuildOS.String()
 
-		proto := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Output("proto/a.pb.cc")
-		foobar := ctx.ModuleForTests("protoc-gen-foobar", buildOS+"_x86_64")
+		proto := ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_shared").Output("proto/a.pb.cc")
+		foobar := ctx.ModuleForTests(t, "protoc-gen-foobar", buildOS+"_x86_64")
 
 		cmd := proto.RuleParams.Command
 		if w := "--foobar_out="; !strings.Contains(cmd, w) {
@@ -85,8 +85,8 @@
 
 		buildOS := ctx.Config().BuildOS.String()
 
-		proto := ctx.ModuleForTests("libgrpc", "android_arm_armv7-a-neon_shared").Output("proto/a.grpc.pb.cc")
-		grpcCppPlugin := ctx.ModuleForTests("protoc-gen-grpc-cpp-plugin", buildOS+"_x86_64")
+		proto := ctx.ModuleForTests(t, "libgrpc", "android_arm_armv7-a-neon_shared").Output("proto/a.grpc.pb.cc")
+		grpcCppPlugin := ctx.ModuleForTests(t, "protoc-gen-grpc-cpp-plugin", buildOS+"_x86_64")
 
 		cmd := proto.RuleParams.Command
 		if w := "--grpc-cpp-plugin_out="; !strings.Contains(cmd, w) {
diff --git a/cc/sabi.go b/cc/sabi.go
index bc61b6c..06ab6ec 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -137,7 +137,7 @@
 		if m.isImplementationForLLNDKPublic() {
 			result = append(result, llndkLsdumpTag)
 		}
-		if m.library.hasStubsVariants() {
+		if m.library.HasStubsVariants() {
 			result = append(result, apexLsdumpTag)
 		}
 		if headerAbiChecker.enabled() {
diff --git a/cc/sabi_test.go b/cc/sabi_test.go
index 6b8cc17..3d2a98c 100644
--- a/cc/sabi_test.go
+++ b/cc/sabi_test.go
@@ -48,16 +48,16 @@
 		PrepareForTestWithCcDefaultModules,
 	).RunTestWithBp(t, bp)
 
-	libsabiStatic := result.ModuleForTests("libsabi", "android_arm64_armv8-a_static_sabi")
+	libsabiStatic := result.ModuleForTests(t, "libsabi", "android_arm64_armv8-a_static_sabi")
 	sabiObjSDump := libsabiStatic.Output("obj/sabi.sdump")
 
-	libDirect := result.ModuleForTests("libdirect", "android_arm64_armv8-a_static_sabi")
+	libDirect := result.ModuleForTests(t, "libdirect", "android_arm64_armv8-a_static_sabi")
 	directObjSDump := libDirect.Output("obj/direct.sdump")
 
-	libTransitive := result.ModuleForTests("libtransitive", "android_arm64_armv8-a_static_sabi")
+	libTransitive := result.ModuleForTests(t, "libtransitive", "android_arm64_armv8-a_static_sabi")
 	transitiveObjSDump := libTransitive.Output("obj/transitive.sdump")
 
-	libsabiShared := result.ModuleForTests("libsabi", "android_arm64_armv8-a_shared")
+	libsabiShared := result.ModuleForTests(t, "libsabi", "android_arm64_armv8-a_shared")
 	sabiLink := libsabiShared.Rule("sAbiLink")
 
 	android.AssertStringListContains(t, "sabi link inputs", sabiLink.Inputs.Strings(), sabiObjSDump.Output.String())
diff --git a/cc/sanitize.go b/cc/sanitize.go
index d8d8c7a..f0b0308 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -79,7 +79,7 @@
 
 	minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined",
 		"-fno-sanitize-recover=integer,undefined"}
-	memtagStackCommonFlags = []string{"-march=armv8-a+memtag"}
+	memtagStackCommonFlags = []string{"-Xclang -target-feature -Xclang +mte"}
 	memtagStackLlvmFlags   = []string{"-dom-tree-reachability-max-bbs-to-explore=128"}
 
 	hostOnlySanitizeFlags   = []string{"-fno-sanitize-recover=all"}
@@ -1422,6 +1422,7 @@
 				sanitizers = append(sanitizers,
 					"bool",
 					"integer-divide-by-zero",
+					"object-size",
 					"return",
 					"returns-nonnull-attribute",
 					"shift-exponent",
@@ -1438,10 +1439,6 @@
 					//"shift-base",
 					//"signed-integer-overflow",
 				)
-
-				if mctx.Config().ReleaseBuildObjectSizeSanitizer() {
-					sanitizers = append(sanitizers, "object-size")
-				}
 			}
 			sanitizers = append(sanitizers, sanProps.Misc_undefined...)
 		}
@@ -1901,6 +1898,8 @@
 
 	ctx.SetOutputFiles(android.Paths{outputFile}, "")
 	txt.outputFile = outputFile
+
+	etc.SetCommonPrebuiltEtcInfo(ctx, txt)
 }
 
 func (txt *sanitizerLibrariesTxtModule) PrepareAndroidMKProviderInfo(config android.Config) *android.AndroidMkProviderInfo {
diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go
index a1cfb5c..543e808 100644
--- a/cc/sanitize_test.go
+++ b/cc/sanitize_test.go
@@ -222,31 +222,31 @@
 		staticAsanVariant := staticVariant + "_asan"
 
 		// The binaries, one with asan and one without
-		binWithAsan := result.ModuleForTests("bin_with_asan", asanVariant)
-		binNoAsan := result.ModuleForTests("bin_no_asan", variant)
+		binWithAsan := result.ModuleForTests(t, "bin_with_asan", asanVariant)
+		binNoAsan := result.ModuleForTests(t, "bin_no_asan", variant)
 
 		// Shared libraries that don't request asan
-		libShared := result.ModuleForTests("libshared", sharedVariant)
-		libTransitive := result.ModuleForTests("libtransitive", sharedVariant)
+		libShared := result.ModuleForTests(t, "libshared", sharedVariant)
+		libTransitive := result.ModuleForTests(t, "libtransitive", sharedVariant)
 
 		// Shared library that requests asan
-		libAsan := result.ModuleForTests("libasan", sharedAsanVariant)
+		libAsan := result.ModuleForTests(t, "libasan", sharedAsanVariant)
 
 		// Static library that uses an asan variant for bin_with_asan and a non-asan variant
 		// for bin_no_asan.
-		libStaticAsanVariant := result.ModuleForTests("libstatic", staticAsanVariant)
-		libStaticNoAsanVariant := result.ModuleForTests("libstatic", staticVariant)
+		libStaticAsanVariant := result.ModuleForTests(t, "libstatic", staticAsanVariant)
+		libStaticNoAsanVariant := result.ModuleForTests(t, "libstatic", staticVariant)
 
 		// Static library that never uses asan.
-		libNoAsan := result.ModuleForTests("libnoasan", staticVariant)
+		libNoAsan := result.ModuleForTests(t, "libnoasan", staticVariant)
 
 		// Static library that specifies asan
-		libStaticAsan := result.ModuleForTests("libstatic_asan", staticAsanVariant)
-		libStaticAsanNoAsanVariant := result.ModuleForTests("libstatic_asan", staticVariant)
+		libStaticAsan := result.ModuleForTests(t, "libstatic_asan", staticAsanVariant)
+		libStaticAsanNoAsanVariant := result.ModuleForTests(t, "libstatic_asan", staticVariant)
 
-		libAsanSharedRuntime := result.ModuleForTests("libclang_rt.asan", sharedVariant)
-		libAsanStaticRuntime := result.ModuleForTests("libclang_rt.asan.static", staticVariant)
-		libAsanStaticCxxRuntime := result.ModuleForTests("libclang_rt.asan_cxx.static", staticVariant)
+		libAsanSharedRuntime := result.ModuleForTests(t, "libclang_rt.asan", sharedVariant)
+		libAsanStaticRuntime := result.ModuleForTests(t, "libclang_rt.asan.static", staticVariant)
+		libAsanStaticCxxRuntime := result.ModuleForTests(t, "libclang_rt.asan_cxx.static", staticVariant)
 
 		expectSharedLinkDep(t, ctx, binWithAsan, libShared)
 		expectSharedLinkDep(t, ctx, binWithAsan, libAsan)
@@ -386,15 +386,15 @@
 		sharedTsanVariant := sharedVariant + "_tsan"
 
 		// The binaries, one with tsan and one without
-		binWithTsan := result.ModuleForTests("bin_with_tsan", tsanVariant)
-		binNoTsan := result.ModuleForTests("bin_no_tsan", variant)
+		binWithTsan := result.ModuleForTests(t, "bin_with_tsan", tsanVariant)
+		binNoTsan := result.ModuleForTests(t, "bin_no_tsan", variant)
 
 		// Shared libraries that don't request tsan
-		libShared := result.ModuleForTests("libshared", sharedVariant)
-		libTransitive := result.ModuleForTests("libtransitive", sharedVariant)
+		libShared := result.ModuleForTests(t, "libshared", sharedVariant)
+		libTransitive := result.ModuleForTests(t, "libtransitive", sharedVariant)
 
 		// Shared library that requests tsan
-		libTsan := result.ModuleForTests("libtsan", sharedTsanVariant)
+		libTsan := result.ModuleForTests(t, "libtsan", sharedTsanVariant)
 
 		expectSharedLinkDep(t, ctx, binWithTsan, libShared)
 		expectSharedLinkDep(t, ctx, binWithTsan, libTsan)
@@ -479,16 +479,16 @@
 		staticVariant := variant + "_static"
 
 		// The binaries, one with ubsan and one without
-		binWithUbsan := result.ModuleForTests("bin_with_ubsan", variant)
-		binNoUbsan := result.ModuleForTests("bin_no_ubsan", variant)
+		binWithUbsan := result.ModuleForTests(t, "bin_with_ubsan", variant)
+		binNoUbsan := result.ModuleForTests(t, "bin_no_ubsan", variant)
 
 		// Static libraries that don't request ubsan
-		libStatic := result.ModuleForTests("libstatic", staticVariant)
-		libTransitive := result.ModuleForTests("libtransitive", staticVariant)
+		libStatic := result.ModuleForTests(t, "libstatic", staticVariant)
+		libTransitive := result.ModuleForTests(t, "libtransitive", staticVariant)
 
-		libUbsan := result.ModuleForTests("libubsan", staticVariant)
+		libUbsan := result.ModuleForTests(t, "libubsan", staticVariant)
 
-		libUbsanMinimal := result.ModuleForTests("libclang_rt.ubsan_minimal", staticVariant)
+		libUbsanMinimal := result.ModuleForTests(t, "libclang_rt.ubsan_minimal", staticVariant)
 
 		expectStaticLinkDep(t, ctx, binWithUbsan, libStatic)
 		expectStaticLinkDep(t, ctx, binWithUbsan, libUbsan)
@@ -610,31 +610,31 @@
 		staticFuzzerVariant := staticVariant + "_fuzzer"
 
 		// The binaries, one with fuzzer and one without
-		binWithFuzzer := result.ModuleForTests("bin_with_fuzzer", fuzzerVariant)
-		binNoFuzzer := result.ModuleForTests("bin_no_fuzzer", variant)
+		binWithFuzzer := result.ModuleForTests(t, "bin_with_fuzzer", fuzzerVariant)
+		binNoFuzzer := result.ModuleForTests(t, "bin_no_fuzzer", variant)
 
 		// Shared libraries that don't request fuzzer
-		libShared := result.ModuleForTests("libshared", sharedVariant)
-		libTransitive := result.ModuleForTests("libtransitive", sharedVariant)
+		libShared := result.ModuleForTests(t, "libshared", sharedVariant)
+		libTransitive := result.ModuleForTests(t, "libtransitive", sharedVariant)
 
 		// Shared libraries that don't request fuzzer
-		libSharedFuzzer := result.ModuleForTests("libshared", sharedFuzzerVariant)
-		libTransitiveFuzzer := result.ModuleForTests("libtransitive", sharedFuzzerVariant)
+		libSharedFuzzer := result.ModuleForTests(t, "libshared", sharedFuzzerVariant)
+		libTransitiveFuzzer := result.ModuleForTests(t, "libtransitive", sharedFuzzerVariant)
 
 		// Shared library that requests fuzzer
-		libFuzzer := result.ModuleForTests("libfuzzer", sharedFuzzerVariant)
+		libFuzzer := result.ModuleForTests(t, "libfuzzer", sharedFuzzerVariant)
 
 		// Static library that uses an fuzzer variant for bin_with_fuzzer and a non-fuzzer variant
 		// for bin_no_fuzzer.
-		libStaticFuzzerVariant := result.ModuleForTests("libstatic", staticFuzzerVariant)
-		libStaticNoFuzzerVariant := result.ModuleForTests("libstatic", staticVariant)
+		libStaticFuzzerVariant := result.ModuleForTests(t, "libstatic", staticFuzzerVariant)
+		libStaticNoFuzzerVariant := result.ModuleForTests(t, "libstatic", staticVariant)
 
 		// Static library that never uses fuzzer.
-		libNoFuzzer := result.ModuleForTests("libnofuzzer", staticVariant)
+		libNoFuzzer := result.ModuleForTests(t, "libnofuzzer", staticVariant)
 
 		// Static library that specifies fuzzer
-		libStaticFuzzer := result.ModuleForTests("libstatic_fuzzer", staticFuzzerVariant)
-		libStaticFuzzerNoFuzzerVariant := result.ModuleForTests("libstatic_fuzzer", staticVariant)
+		libStaticFuzzer := result.ModuleForTests(t, "libstatic_fuzzer", staticFuzzerVariant)
+		libStaticFuzzerNoFuzzerVariant := result.ModuleForTests(t, "libstatic_fuzzer", staticVariant)
 
 		expectSharedLinkDep(t, ctx, binWithFuzzer, libSharedFuzzer)
 		expectSharedLinkDep(t, ctx, binWithFuzzer, libFuzzer)
@@ -781,16 +781,16 @@
 		staticVariant := variant + "_static"
 		sharedVariant := variant + "_shared"
 
-		minimalRuntime := result.ModuleForTests("libclang_rt.ubsan_minimal", staticVariant)
-		standaloneRuntime := result.ModuleForTests("libclang_rt.ubsan_standalone.static", staticVariant)
+		minimalRuntime := result.ModuleForTests(t, "libclang_rt.ubsan_minimal", staticVariant)
+		standaloneRuntime := result.ModuleForTests(t, "libclang_rt.ubsan_standalone.static", staticVariant)
 
 		// The binaries, one with ubsan and one without
-		binWithUbsan := result.ModuleForTests("bin_with_ubsan", variant)
-		binDependsUbsan := result.ModuleForTests("bin_depends_ubsan_static", variant)
-		libSharedUbsan := result.ModuleForTests("libsharedubsan", sharedVariant)
-		binDependsUbsanShared := result.ModuleForTests("bin_depends_ubsan_shared", variant)
-		binNoUbsan := result.ModuleForTests("bin_no_ubsan", variant)
-		staticBin := result.ModuleForTests("static_bin_with_ubsan_dep", variant)
+		binWithUbsan := result.ModuleForTests(t, "bin_with_ubsan", variant)
+		binDependsUbsan := result.ModuleForTests(t, "bin_depends_ubsan_static", variant)
+		libSharedUbsan := result.ModuleForTests(t, "libsharedubsan", sharedVariant)
+		binDependsUbsanShared := result.ModuleForTests(t, "bin_depends_ubsan_shared", variant)
+		binNoUbsan := result.ModuleForTests(t, "bin_no_ubsan", variant)
+		staticBin := result.ModuleForTests(t, "static_bin_with_ubsan_dep", variant)
 
 		android.AssertStringListContains(t, "missing libclang_rt.ubsan_minimal in bin_with_ubsan static libs",
 			strings.Split(binWithUbsan.Rule("ld").Args["libFlags"], " "),
@@ -979,67 +979,67 @@
 	).RunTest(t)
 	ctx := result.TestContext
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_no_override", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_async", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_sync", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_sync", variant), None)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_no_override", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_async", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_sync", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_sync", variant), None)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_no_override", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_disable", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_no_override", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_disable", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_sync", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_sync", variant), Async)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_no_override", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_disable", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_sync", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_sync", variant), Async)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_sync", variant), Sync)
 
 	// should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_no_override", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_async", variant), Sync)
 	// should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_no_override", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_sync", variant), Sync)
 }
 
 func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) {
@@ -1055,66 +1055,66 @@
 	).RunTest(t)
 	ctx := result.TestContext
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_no_override", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_async", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_sync", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_sync", variant), None)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_no_override", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_async", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_sync", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_sync", variant), None)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_no_override", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_disable", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_no_override", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_disable", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_sync", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_sync", variant), Async)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_no_override", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_disable", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_sync", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_sync", variant), Async)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_async", variant), Sync)
 	// should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_no_override", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_sync", variant), Sync)
 }
 
 func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) {
@@ -1131,66 +1131,66 @@
 	).RunTest(t)
 	ctx := result.TestContext
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_no_override", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_async", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_sync", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_sync", variant), None)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_no_override", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_async", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_sync", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_sync", variant), None)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_no_override", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_disable", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_sync", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_sync", variant), Async)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_no_override", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_disable", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_sync", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_sync", variant), Async)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_async", variant), Sync)
 	// should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_sync", variant), Sync)
 }
 
 func TestCfi(t *testing.T) {
@@ -1250,14 +1250,14 @@
 	cfi_suffix := "_cfi"
 	static_suffix := "_static"
 
-	sharedWithCfiLib := result.ModuleForTests("shared_with_cfi", buildOs+shared_suffix+cfi_suffix)
-	sharedNoCfiLib := result.ModuleForTests("shared_no_cfi", buildOs+shared_suffix)
-	staticWithCfiLib := result.ModuleForTests("static_dep_with_cfi", buildOs+static_suffix)
-	staticWithCfiLibCfiVariant := result.ModuleForTests("static_dep_with_cfi", buildOs+static_suffix+cfi_suffix)
-	staticNoCfiLib := result.ModuleForTests("static_dep_no_cfi", buildOs+static_suffix)
-	staticNoCfiLibCfiVariant := result.ModuleForTests("static_dep_no_cfi", buildOs+static_suffix+cfi_suffix)
-	sharedRdepNoCfi := result.ModuleForTests("shared_rdep_no_cfi", buildOs+shared_suffix)
-	staticDepWithCfi2Lib := result.ModuleForTests("static_dep_with_cfi_2", buildOs+static_suffix)
+	sharedWithCfiLib := result.ModuleForTests(t, "shared_with_cfi", buildOs+shared_suffix+cfi_suffix)
+	sharedNoCfiLib := result.ModuleForTests(t, "shared_no_cfi", buildOs+shared_suffix)
+	staticWithCfiLib := result.ModuleForTests(t, "static_dep_with_cfi", buildOs+static_suffix)
+	staticWithCfiLibCfiVariant := result.ModuleForTests(t, "static_dep_with_cfi", buildOs+static_suffix+cfi_suffix)
+	staticNoCfiLib := result.ModuleForTests(t, "static_dep_no_cfi", buildOs+static_suffix)
+	staticNoCfiLibCfiVariant := result.ModuleForTests(t, "static_dep_no_cfi", buildOs+static_suffix+cfi_suffix)
+	sharedRdepNoCfi := result.ModuleForTests(t, "shared_rdep_no_cfi", buildOs+shared_suffix)
+	staticDepWithCfi2Lib := result.ModuleForTests(t, "static_dep_with_cfi_2", buildOs+static_suffix)
 
 	// Confirm assumptions about propagation of CFI enablement
 	expectStaticLinkDep(t, ctx, sharedWithCfiLib, staticWithCfiLibCfiVariant)
diff --git a/cc/sdk_test.go b/cc/sdk_test.go
index 61925e3..664a7c6 100644
--- a/cc/sdk_test.go
+++ b/cc/sdk_test.go
@@ -81,16 +81,16 @@
 
 	ctx := testCc(t, bp)
 
-	libsdkNDK := ctx.ModuleForTests("libsdk", "android_arm64_armv8-a_sdk_shared")
-	libsdkPlatform := ctx.ModuleForTests("libsdk", "android_arm64_armv8-a_shared")
-	libsdkdepNDK := ctx.ModuleForTests("libsdkdep", "android_arm64_armv8-a_sdk_shared")
-	libsdkdepPlatform := ctx.ModuleForTests("libsdkdep", "android_arm64_armv8-a_shared")
-	libplatform := ctx.ModuleForTests("libplatform", "android_arm64_armv8-a_shared")
-	platformbinary := ctx.ModuleForTests("platformbinary", "android_arm64_armv8-a")
-	sdkbinary := ctx.ModuleForTests("sdkbinary", "android_arm64_armv8-a_sdk")
+	libsdkNDK := ctx.ModuleForTests(t, "libsdk", "android_arm64_armv8-a_sdk_shared")
+	libsdkPlatform := ctx.ModuleForTests(t, "libsdk", "android_arm64_armv8-a_shared")
+	libsdkdepNDK := ctx.ModuleForTests(t, "libsdkdep", "android_arm64_armv8-a_sdk_shared")
+	libsdkdepPlatform := ctx.ModuleForTests(t, "libsdkdep", "android_arm64_armv8-a_shared")
+	libplatform := ctx.ModuleForTests(t, "libplatform", "android_arm64_armv8-a_shared")
+	platformbinary := ctx.ModuleForTests(t, "platformbinary", "android_arm64_armv8-a")
+	sdkbinary := ctx.ModuleForTests(t, "sdkbinary", "android_arm64_armv8-a_sdk")
 
-	libcxxNDK := ctx.ModuleForTests("ndk_libc++_shared", "android_arm64_armv8-a_sdk_shared")
-	libcxxPlatform := ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared")
+	libcxxNDK := ctx.ModuleForTests(t, "ndk_libc++_shared", "android_arm64_armv8-a_sdk_shared")
+	libcxxPlatform := ctx.ModuleForTests(t, "libc++", "android_arm64_armv8-a_shared")
 
 	assertDep(t, libsdkNDK, libsdkdepNDK)
 	assertDep(t, libsdkPlatform, libsdkdepPlatform)
diff --git a/cc/strip.go b/cc/strip.go
index b1f34bb..42c9137 100644
--- a/cc/strip.go
+++ b/cc/strip.go
@@ -23,7 +23,6 @@
 // StripProperties defines the type of stripping applied to the module.
 type StripProperties struct {
 	Strip struct {
-		// none forces all stripping to be disabled.
 		// Device modules default to stripping enabled leaving mini debuginfo.
 		// Host modules default to stripping disabled, but can be enabled by setting any other
 		// strip boolean property.
@@ -52,7 +51,8 @@
 // NeedsStrip determines if stripping is required for a module.
 func (stripper *Stripper) NeedsStrip(actx android.ModuleContext) bool {
 	forceDisable := Bool(stripper.StripProperties.Strip.None)
-	defaultEnable := (!actx.Config().KatiEnabled() || actx.Device())
+	// Strip is enabled by default for device variants.
+	defaultEnable := actx.Device() || actx.Config().StripByDefault()
 	forceEnable := Bool(stripper.StripProperties.Strip.All) ||
 		Bool(stripper.StripProperties.Strip.Keep_symbols) ||
 		Bool(stripper.StripProperties.Strip.Keep_symbols_and_debug_frame)
diff --git a/cc/stub_library.go b/cc/stub_library.go
index e746a33..21ef139 100644
--- a/cc/stub_library.go
+++ b/cc/stub_library.go
@@ -26,22 +26,26 @@
 	android.RegisterParallelSingletonType("stublibraries", stubLibrariesSingleton)
 }
 
+func stubLibrariesSingleton() android.Singleton {
+	return &stubLibraries{}
+}
+
 type stubLibraries struct {
-	stubLibraryMap       map[string]bool
-	stubVendorLibraryMap map[string]bool
+	stubLibraries       []string
+	vendorStubLibraries []string
 
 	apiListCoverageXmlPaths []string
 }
 
 // Check if the module defines stub, or itself is stub
-func IsStubTarget(m *Module) bool {
-	return m.IsStubs() || m.HasStubsVariants()
+func IsStubTarget(info *LinkableInfo) bool {
+	return info != nil && (info.IsStubs || info.HasStubsVariants)
 }
 
 // Get target file name to be installed from this module
-func getInstalledFileName(ctx android.SingletonContext, m *Module) string {
+func getInstalledFileName(ctx android.SingletonContext, m android.ModuleProxy) string {
 	for _, ps := range android.OtherModuleProviderOrDefault(
-		ctx, m.Module(), android.InstallFilesProvider).PackagingSpecs {
+		ctx, m, android.InstallFilesProvider).PackagingSpecs {
 		if name := ps.FileName(); name != "" {
 			return name
 		}
@@ -51,36 +55,39 @@
 
 func (s *stubLibraries) GenerateBuildActions(ctx android.SingletonContext) {
 	// Visit all generated soong modules and store stub library file names.
-	ctx.VisitAllModules(func(module android.Module) {
-		if m, ok := module.(*Module); ok {
-			if IsStubTarget(m) {
-				if name := getInstalledFileName(ctx, m); name != "" {
-					s.stubLibraryMap[name] = true
-					if m.InVendor() {
-						s.stubVendorLibraryMap[name] = true
+	stubLibraryMap := make(map[string]bool)
+	vendorStubLibraryMap := make(map[string]bool)
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+		if linkableInfo, ok := android.OtherModuleProvider(ctx, module, LinkableInfoProvider); ok {
+			if IsStubTarget(linkableInfo) {
+				if name := getInstalledFileName(ctx, module); name != "" {
+					stubLibraryMap[name] = true
+					if linkableInfo.InVendor {
+						vendorStubLibraryMap[name] = true
 					}
 				}
 			}
-			if m.library != nil && android.IsModulePreferred(m) {
-				if p := m.library.getAPIListCoverageXMLPath().String(); p != "" {
+			if linkableInfo.CcLibraryInterface && android.IsModulePreferredProxy(ctx, module) {
+				if p := linkableInfo.APIListCoverageXMLPath.String(); p != "" {
 					s.apiListCoverageXmlPaths = append(s.apiListCoverageXmlPaths, p)
 				}
 			}
 		}
 	})
+	s.stubLibraries = android.SortedKeys(stubLibraryMap)
+	s.vendorStubLibraries = android.SortedKeys(vendorStubLibraryMap)
+
+	android.WriteFileRule(ctx, StubLibrariesFile(ctx), strings.Join(s.stubLibraries, " "))
 }
 
-func stubLibrariesSingleton() android.Singleton {
-	return &stubLibraries{
-		stubLibraryMap:       make(map[string]bool),
-		stubVendorLibraryMap: make(map[string]bool),
-	}
+func StubLibrariesFile(ctx android.PathContext) android.WritablePath {
+	return android.PathForIntermediates(ctx, "stub_libraries.txt")
 }
 
 func (s *stubLibraries) MakeVars(ctx android.MakeVarsContext) {
 	// Convert stub library file names into Makefile variable.
-	ctx.Strict("STUB_LIBRARIES", strings.Join(android.SortedKeys(s.stubLibraryMap), " "))
-	ctx.Strict("SOONG_STUB_VENDOR_LIBRARIES", strings.Join(android.SortedKeys(s.stubVendorLibraryMap), " "))
+	ctx.Strict("STUB_LIBRARIES", strings.Join(s.stubLibraries, " "))
+	ctx.Strict("SOONG_STUB_VENDOR_LIBRARIES", strings.Join(s.vendorStubLibraries, " "))
 
 	// Export the list of API XML files to Make.
 	sort.Strings(s.apiListCoverageXmlPaths)
diff --git a/cc/test.go b/cc/test.go
index ae73886..9c276b8 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -18,6 +18,7 @@
 	"path/filepath"
 	"strconv"
 
+	"github.com/google/blueprint/depset"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -29,8 +30,8 @@
 	// if set, build against the gtest library. Defaults to true.
 	Gtest *bool
 
-	// if set, use the isolated gtest runner. Defaults to true if gtest is also true and the arch is Windows, false
-	// otherwise.
+	// if set, use the isolated gtest runner. Defaults to false.
+	// Isolation is not supported on Windows.
 	Isolated *bool
 }
 
@@ -83,16 +84,21 @@
 	// the test
 	Data []string `android:"path,arch_variant"`
 
-	// Same as data, but adds depedencies on modules using the device's os variant, and common
+	// Same as data, but adds dependencies on modules using the device's os variant, and common
 	// architecture's variant. Can be useful to add device-built apps to the data of a host
 	// test.
 	Device_common_data []string `android:"path_device_common"`
 
-	// Same as data, but adds depedencies on modules using the device's os variant, and the device's
-	// first architecture's variant. Can be useful to add device-built apps to the data of a host
-	// test.
+	// Same as data, but adds dependencies on modules using the device's os variant, and the
+	// device's first architecture's variant. Can be useful to add device-built apps to the data
+	// of a host test.
 	Device_first_data []string `android:"path_device_first"`
 
+	// Same as data, but will add dependencies on modules using the host's os variation and
+	// the common arch variation. Useful for a device test that wants to depend on a host
+	// module, for example to include a custom Tradefed test runner.
+	Host_common_data []string `android:"path_host_common"`
+
 	// list of shared library modules that should be installed alongside the test
 	Data_libs []string `android:"arch_variant"`
 
@@ -128,6 +134,13 @@
 
 	// Install the test into a folder named for the module in all test suites.
 	Per_testcase_directory *bool
+
+	// Install the test's dependencies into a folder named standalone-libs relative to the
+	// test's installation path. ld-library-path will be set to this path in the test's
+	// auto-generated config. This way the dependencies can be used by the test without having
+	// to manually install them to the device. See more details in
+	// go/standalone-native-device-tests.
+	Standalone_test *bool
 }
 
 func init() {
@@ -198,8 +211,8 @@
 	return BoolDefault(test.LinkerProperties.Gtest, true)
 }
 
-func (test *testDecorator) isolated(ctx android.EarlyModuleContext) bool {
-	return BoolDefault(test.LinkerProperties.Isolated, false)
+func (test *testDecorator) isolated(ctx android.BaseModuleContext) bool {
+	return BoolDefault(test.LinkerProperties.Isolated, false) && !ctx.Windows()
 }
 
 // NOTE: Keep this in sync with cc/cc_test.bzl#gtest_copts
@@ -261,6 +274,12 @@
 	}
 }
 
+func (test *testDecorator) testSuiteInfo(ctx ModuleContext) {
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: test.InstallerProperties.Test_suites,
+	})
+}
+
 func NewTestInstaller() *baseInstaller {
 	return NewBaseInstaller("nativetest", "nativetest64", InstallInData)
 }
@@ -329,6 +348,10 @@
 
 }
 
+func (test *testBinary) testSuiteInfo(ctx ModuleContext) {
+	test.testDecorator.testSuiteInfo(ctx)
+}
+
 func (test *testBinary) installerProps() []interface{} {
 	return append(test.baseInstaller.installerProps(), test.testDecorator.installerProps()...)
 }
@@ -337,33 +360,34 @@
 	dataSrcPaths := android.PathsForModuleSrc(ctx, test.Properties.Data)
 	dataSrcPaths = append(dataSrcPaths, android.PathsForModuleSrc(ctx, test.Properties.Device_common_data)...)
 	dataSrcPaths = append(dataSrcPaths, android.PathsForModuleSrc(ctx, test.Properties.Device_first_data)...)
+	dataSrcPaths = append(dataSrcPaths, android.PathsForModuleSrc(ctx, test.Properties.Host_common_data)...)
 
 	for _, dataSrcPath := range dataSrcPaths {
 		test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
 	}
 
-	ctx.VisitDirectDepsWithTag(dataLibDepTag, func(dep android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(dataLibDepTag, func(dep android.ModuleProxy) {
 		depName := ctx.OtherModuleName(dep)
-		linkableDep, ok := dep.(LinkableInterface)
+		linkableDep, ok := android.OtherModuleProvider(ctx, dep, LinkableInfoProvider)
 		if !ok {
 			ctx.ModuleErrorf("data_lib %q is not a LinkableInterface module", depName)
 		}
-		if linkableDep.OutputFile().Valid() {
+		if linkableDep.OutputFile.Valid() {
 			test.data = append(test.data,
-				android.DataPath{SrcPath: linkableDep.OutputFile().Path(),
-					RelativeInstallPath: linkableDep.RelativeInstallPath()})
+				android.DataPath{SrcPath: linkableDep.OutputFile.Path(),
+					RelativeInstallPath: linkableDep.RelativeInstallPath})
 		}
 	})
-	ctx.VisitDirectDepsWithTag(dataBinDepTag, func(dep android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(dataBinDepTag, func(dep android.ModuleProxy) {
 		depName := ctx.OtherModuleName(dep)
-		linkableDep, ok := dep.(LinkableInterface)
+		linkableDep, ok := android.OtherModuleProvider(ctx, dep, LinkableInfoProvider)
 		if !ok {
 			ctx.ModuleErrorf("data_bin %q is not a LinkableInterface module", depName)
 		}
-		if linkableDep.OutputFile().Valid() {
+		if linkableDep.OutputFile.Valid() {
 			test.data = append(test.data,
-				android.DataPath{SrcPath: linkableDep.OutputFile().Path(),
-					RelativeInstallPath: linkableDep.RelativeInstallPath()})
+				android.DataPath{SrcPath: linkableDep.OutputFile.Path(),
+					RelativeInstallPath: linkableDep.RelativeInstallPath})
 		}
 	})
 
@@ -380,6 +404,7 @@
 		TestInstallBase:        testInstallBase,
 		DeviceTemplate:         "${NativeTestConfigTemplate}",
 		HostTemplate:           "${NativeHostTestConfigTemplate}",
+		StandaloneTest:         test.Properties.Standalone_test,
 	})
 
 	test.extraTestConfigs = android.PathsForModuleSrc(ctx, test.Properties.Test_options.Extra_test_configs)
@@ -397,8 +422,55 @@
 		test.Properties.Test_options.Unit_test = proptools.BoolPtr(true)
 	}
 
+	if !ctx.Config().KatiEnabled() { // TODO(spandandas): Remove the special case for kati
+		// Install the test config in testcases/ directory for atest.
+		c, ok := ctx.Module().(*Module)
+		if !ok {
+			ctx.ModuleErrorf("Not a cc_test module")
+		}
+		// Install configs in the root of $PRODUCT_OUT/testcases/$module
+		testCases := android.PathForModuleInPartitionInstall(ctx, "testcases", ctx.ModuleName()+c.SubName())
+		if ctx.PrimaryArch() {
+			if test.testConfig != nil {
+				ctx.InstallFile(testCases, ctx.ModuleName()+".config", test.testConfig)
+			}
+			dynamicConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "DynamicConfig.xml")
+			if dynamicConfig.Valid() {
+				ctx.InstallFile(testCases, ctx.ModuleName()+".dynamic", dynamicConfig.Path())
+			}
+			for _, extraTestConfig := range test.extraTestConfigs {
+				ctx.InstallFile(testCases, extraTestConfig.Base(), extraTestConfig)
+			}
+		}
+		// Install tests and data in arch specific subdir $PRODUCT_OUT/testcases/$module/$arch
+		testCases = testCases.Join(ctx, ctx.Target().Arch.ArchType.String())
+		ctx.InstallTestData(testCases, test.data)
+		ctx.InstallFile(testCases, file.Base(), file)
+	}
+
 	test.binaryDecorator.baseInstaller.installTestData(ctx, test.data)
 	test.binaryDecorator.baseInstaller.install(ctx, file)
+	if Bool(test.Properties.Standalone_test) {
+		packagingSpecsBuilder := depset.NewBuilder[android.PackagingSpec](depset.TOPOLOGICAL)
+
+		ctx.VisitDirectDeps(func(dep android.Module) {
+			deps := android.OtherModuleProviderOrDefault(ctx, dep, android.InstallFilesProvider)
+			packagingSpecsBuilder.Transitive(deps.TransitivePackagingSpecs)
+		})
+
+		for _, standaloneTestDep := range packagingSpecsBuilder.Build().ToList() {
+			if standaloneTestDep.ToGob().SrcPath == nil {
+				continue
+			}
+			if standaloneTestDep.SkipInstall() {
+				continue
+			}
+			if standaloneTestDep.Partition() == "data" {
+				continue
+			}
+			test.binaryDecorator.baseInstaller.installStandaloneTestDep(ctx, standaloneTestDep)
+		}
+	}
 }
 
 func getTestInstallBase(useVendor bool) string {
@@ -516,6 +588,10 @@
 	test.testDecorator.moduleInfoJSON(ctx, moduleInfoJSON)
 }
 
+func (test *testLibrary) testSuiteInfo(ctx ModuleContext) {
+	test.testDecorator.testSuiteInfo(ctx)
+}
+
 func (test *testLibrary) installerProps() []interface{} {
 	return append(test.baseInstaller.installerProps(), test.testDecorator.installerProps()...)
 }
@@ -633,6 +709,12 @@
 	}
 }
 
+func (benchmark *benchmarkDecorator) testSuiteInfo(ctx ModuleContext) {
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: benchmark.Properties.Test_suites,
+	})
+}
+
 func NewBenchmark(hod android.HostOrDeviceSupported) *Module {
 	module, binary := newBinary(hod)
 	module.multilib = android.MultilibBoth
diff --git a/cc/test_data_test.go b/cc/test_data_test.go
index a621166..2c03ca4 100644
--- a/cc/test_data_test.go
+++ b/cc/test_data_test.go
@@ -132,7 +132,7 @@
 			_, errs = ctx.PrepareBuildActions(config)
 			android.FailIfErrored(t, errs)
 
-			foo := ctx.ModuleForTests("foo", "")
+			foo := ctx.ModuleForTests(t, "foo", "")
 
 			got := foo.Module().(*testDataTest).data
 			if len(got) != len(test.data) {
diff --git a/cc/testing.go b/cc/testing.go
index 14a6b7a..69ae11d 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -193,7 +193,7 @@
 			},
 			apex_available: [
 				"//apex_available:platform",
-				"myapex"
+				"//apex_available:anyapex",
 			],
 			llndk: {
 				symbol_file: "libm.map.txt",
@@ -253,7 +253,7 @@
 			},
 			apex_available: [
 				"//apex_available:platform",
-				"myapex"
+				"//apex_available:anyapex",
 			],
 			llndk: {
 				symbol_file: "libdl.map.txt",
@@ -708,7 +708,7 @@
 
 func checkSnapshotIncludeExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string, include bool, fake bool) {
 	t.Helper()
-	mod := ctx.ModuleForTests(moduleName, variant)
+	mod := ctx.ModuleForTests(t, moduleName, variant)
 	outputFiles := mod.OutputFiles(ctx, t, "")
 	if len(outputFiles) != 1 {
 		t.Errorf("%q must have single output\n", moduleName)
@@ -750,9 +750,10 @@
 	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, true)
 }
 
-func GetOutputPaths(ctx *android.TestContext, variant string, moduleNames []string) (paths android.Paths) {
+func GetOutputPaths(t *testing.T, ctx *android.TestContext, variant string, moduleNames []string) (paths android.Paths) {
+	t.Helper()
 	for _, moduleName := range moduleNames {
-		module := ctx.ModuleForTests(moduleName, variant).Module().(*Module)
+		module := ctx.ModuleForTests(t, moduleName, variant).Module().(*Module)
 		output := module.outputFile.Path().RelativeToTop()
 		paths = append(paths, output)
 	}
diff --git a/cc/tidy.go b/cc/tidy.go
index 6481b95..bf273e9 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -211,7 +211,7 @@
 type tidyPhonySingleton struct{}
 
 // Given a final module, add its tidy/obj phony targets to tidy/objModulesInDirGroup.
-func collectTidyObjModuleTargets(ctx android.SingletonContext, module android.Module,
+func collectTidyObjModuleTargets(ctx android.SingletonContext, module android.ModuleProxy,
 	tidyModulesInDirGroup, objModulesInDirGroup map[string]map[string]android.Paths) {
 	allObjFileGroups := make(map[string]android.Paths)     // variant group name => obj file Paths
 	allTidyFileGroups := make(map[string]android.Paths)    // variant group name => tidy file Paths
@@ -220,10 +220,10 @@
 
 	// (1) Collect all obj/tidy files into OS-specific groups.
 	ctx.VisitAllModuleVariantProxies(module, func(variant android.ModuleProxy) {
-		osName := android.OtherModuleProviderOrDefault(ctx, variant, android.CommonModuleInfoKey).CompileTarget.Os.Name
+		osName := android.OtherModulePointerProviderOrDefault(ctx, variant, android.CommonModuleInfoProvider).Target.Os.Name
 		info := android.OtherModuleProviderOrDefault(ctx, variant, CcObjectInfoProvider)
-		addToOSGroup(osName, info.objFiles, allObjFileGroups, subsetObjFileGroups)
-		addToOSGroup(osName, info.tidyFiles, allTidyFileGroups, subsetTidyFileGroups)
+		addToOSGroup(osName, info.ObjFiles, allObjFileGroups, subsetObjFileGroups)
+		addToOSGroup(osName, info.TidyFiles, allTidyFileGroups, subsetTidyFileGroups)
 	})
 
 	// (2) Add an all-OS group, with "" or "subset" name, to include all os-specific phony targets.
@@ -253,7 +253,7 @@
 	objModulesInDirGroup := make(map[string]map[string]android.Paths)
 
 	// Collect tidy/obj targets from the 'final' modules.
-	ctx.VisitAllModules(func(module android.Module) {
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
 		if ctx.IsFinalModule(module) {
 			collectTidyObjModuleTargets(ctx, module, tidyModulesInDirGroup, objModulesInDirGroup)
 		}
@@ -268,7 +268,7 @@
 }
 
 // The name for an obj/tidy module variant group phony target is Name_group-obj/tidy,
-func objTidyModuleGroupName(module android.Module, group string, suffix string) string {
+func objTidyModuleGroupName(module android.ModuleProxy, group string, suffix string) string {
 	if group == "" {
 		return module.Name() + "-" + suffix
 	}
@@ -327,7 +327,7 @@
 }
 
 // Add an all-OS group, with groupName, to include all os-specific phony targets.
-func addAllOSGroup(ctx android.SingletonContext, module android.Module, phonyTargetGroups map[string]android.Paths, groupName string, objTidyName string) {
+func addAllOSGroup(ctx android.SingletonContext, module android.ModuleProxy, phonyTargetGroups map[string]android.Paths, groupName string, objTidyName string) {
 	if len(phonyTargetGroups) > 0 {
 		var targets android.Paths
 		for group, _ := range phonyTargetGroups {
@@ -338,7 +338,7 @@
 }
 
 // Create one phony targets for each group and add them to the targetGroups.
-func genObjTidyPhonyTargets(ctx android.SingletonContext, module android.Module, objTidyName string, fileGroups map[string]android.Paths, targetGroups map[string]android.Path) {
+func genObjTidyPhonyTargets(ctx android.SingletonContext, module android.ModuleProxy, objTidyName string, fileGroups map[string]android.Paths, targetGroups map[string]android.Path) {
 	for group, files := range fileGroups {
 		groupName := objTidyModuleGroupName(module, group, objTidyName)
 		ctx.Phony(groupName, files...)
diff --git a/cc/tidy_test.go b/cc/tidy_test.go
index 9481778..afc12b8 100644
--- a/cc/tidy_test.go
+++ b/cc/tidy_test.go
@@ -83,7 +83,7 @@
 		variant := "android_arm64_armv8-a_shared"
 		ctx := testCc(t, test.bp)
 		t.Run("caseTidyFlags", func(t *testing.T) {
-			flags := ctx.ModuleForTests(test.libName, variant).Rule("clangTidy").Args["tidyFlags"]
+			flags := ctx.ModuleForTests(t, test.libName, variant).Rule("clangTidy").Args["tidyFlags"]
 			for _, flag := range test.flags {
 				if !strings.Contains(flags, flag) {
 					t.Errorf("tidyFlags %v for %s does not contain %s.", flags, test.libName, flag)
@@ -143,7 +143,7 @@
 		variant := "android_arm64_armv8-a_shared"
 		for _, test := range testCases {
 			libName := fmt.Sprintf("libfoo_%d", test.libNumber)
-			flags := ctx.ModuleForTests(libName, variant).Rule("clangTidy").Args["tidyFlags"]
+			flags := ctx.ModuleForTests(t, libName, variant).Rule("clangTidy").Args["tidyFlags"]
 			splitFlags := strings.Split(flags, " ")
 			foundCheckFlag := false
 			for _, flag := range splitFlags {
@@ -231,7 +231,7 @@
 				checkLibraryRule := func(foo, variant, ruleName string) {
 					libName := fmt.Sprintf("lib%s_%d", foo, n)
 					tidyFile := "out/soong/.intermediates/" + libName + "/" + variant + "/obj/" + foo + ".tidy"
-					depFiles := ctx.ModuleForTests(libName, variant).Rule(ruleName).Validations.Strings()
+					depFiles := ctx.ModuleForTests(t, libName, variant).Rule(ruleName).Validations.Strings()
 					if test.needTidyFile[n] {
 						android.AssertStringListContains(t, libName+" needs .tidy file", depFiles, tidyFile)
 					} else {
@@ -262,7 +262,7 @@
 	ctx := android.GroupFixturePreparers(prepareForCcTest, android.FixtureMergeEnv(testEnv)).RunTestWithBp(t, bp)
 
 	t.Run("tidy should be only run for source code, not for generated code", func(t *testing.T) {
-		depFiles := ctx.ModuleForTests("libfoo", variant).Rule("ld").Validations.Strings()
+		depFiles := ctx.ModuleForTests(t, "libfoo", variant).Rule("ld").Validations.Strings()
 
 		tidyFileForCpp := "out/soong/.intermediates/libfoo/" + variant + "/obj/foo_src.tidy"
 
diff --git a/cc/vendor_public_library_test.go b/cc/vendor_public_library_test.go
index 7385f2b..797bb25 100644
--- a/cc/vendor_public_library_test.go
+++ b/cc/vendor_public_library_test.go
@@ -70,32 +70,32 @@
 
 	// test if header search paths are correctly added
 	// _static variant is used since _shared reuses *.o from the static variant
-	cc := ctx.ModuleForTests("libsystem", strings.Replace(coreVariant, "_shared", "_static", 1)).Rule("cc")
+	cc := ctx.ModuleForTests(t, "libsystem", strings.Replace(coreVariant, "_shared", "_static", 1)).Rule("cc")
 	cflags := cc.Args["cFlags"]
 	if !strings.Contains(cflags, "-Imy_include") {
 		t.Errorf("cflags for libsystem must contain -Imy_include, but was %#v.", cflags)
 	}
 
 	// test if libsystem is linked to the stub
-	ld := ctx.ModuleForTests("libsystem", coreVariant).Rule("ld")
+	ld := ctx.ModuleForTests(t, "libsystem", coreVariant).Rule("ld")
 	libflags := ld.Args["libFlags"]
-	stubPaths := GetOutputPaths(ctx, coreVariant, []string{"libvendorpublic"})
+	stubPaths := GetOutputPaths(t, ctx, coreVariant, []string{"libvendorpublic"})
 	if !strings.Contains(libflags, stubPaths[0].String()) {
 		t.Errorf("libflags for libsystem must contain %#v, but was %#v", stubPaths[0], libflags)
 	}
 
 	// test if libsystem is linked to the stub
-	ld = ctx.ModuleForTests("libproduct", productVariant).Rule("ld")
+	ld = ctx.ModuleForTests(t, "libproduct", productVariant).Rule("ld")
 	libflags = ld.Args["libFlags"]
-	stubPaths = GetOutputPaths(ctx, productVariant, []string{"libvendorpublic"})
+	stubPaths = GetOutputPaths(t, ctx, productVariant, []string{"libvendorpublic"})
 	if !strings.Contains(libflags, stubPaths[0].String()) {
 		t.Errorf("libflags for libproduct must contain %#v, but was %#v", stubPaths[0], libflags)
 	}
 
 	// test if libvendor is linked to the real shared lib
-	ld = ctx.ModuleForTests("libvendor", vendorVariant).Rule("ld")
+	ld = ctx.ModuleForTests(t, "libvendor", vendorVariant).Rule("ld")
 	libflags = ld.Args["libFlags"]
-	stubPaths = GetOutputPaths(ctx, vendorVariant, []string{"libvendorpublic"})
+	stubPaths = GetOutputPaths(t, ctx, vendorVariant, []string{"libvendorpublic"})
 
 	if !strings.Contains(libflags, stubPaths[0].String()) {
 		t.Errorf("libflags for libvendor must contain %#v, but was %#v", stubPaths[0], libflags)
diff --git a/ci_tests/Android.bp b/ci_tests/Android.bp
new file mode 100644
index 0000000..181ded4
--- /dev/null
+++ b/ci_tests/Android.bp
@@ -0,0 +1,21 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-ci-tests",
+    pkgPath: "android/soong/ci_tests",
+    deps: [
+        "blueprint",
+        "blueprint-proptools",
+        "soong",
+        "soong-android",
+    ],
+    srcs: [
+        "ci_test_package_zip.go",
+    ],
+    testSrcs: [
+    ],
+    pluginFor: ["soong_build"],
+    visibility: ["//visibility:public"],
+}
diff --git a/ci_tests/ci_test_package_zip.go b/ci_tests/ci_test_package_zip.go
new file mode 100644
index 0000000..4cadffd
--- /dev/null
+++ b/ci_tests/ci_test_package_zip.go
@@ -0,0 +1,287 @@
+// 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.
+
+package ci_tests
+
+import (
+	"fmt"
+	"path/filepath"
+	"strings"
+
+	"android/soong/android"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+func init() {
+	pctx.Import("android/soong/android")
+	registerTestPackageZipBuildComponents(android.InitRegistrationContext)
+}
+
+func registerTestPackageZipBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("test_package", TestPackageZipFactory)
+}
+
+type testPackageZip struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	properties CITestPackageProperties
+
+	output android.Path
+}
+
+type CITestPackageProperties struct {
+	// test modules will be added as dependencies using the device os and the common architecture's variant.
+	Tests proptools.Configurable[[]string] `android:"arch_variant"`
+	// test modules that will be added as dependencies based on the first supported arch variant and the device os variant
+	Device_first_tests proptools.Configurable[[]string] `android:"arch_variant"`
+	// test modules that will be added as dependencies based on both 32bit and 64bit arch variant and the device os variant
+	Device_both_tests proptools.Configurable[[]string] `android:"arch_variant"`
+	// test modules that will be added as dependencies based on host
+	Host_tests proptools.Configurable[[]string] `android:"arch_variant"`
+	// git-main only test modules. Will only be added as dependencies using the device os and the common architecture's variant if exists.
+	Tests_if_exist_common proptools.Configurable[[]string] `android:"arch_variant"`
+	// git-main only test modules. Will only be added as dependencies based on both 32bit and 64bit arch variant and the device os variant if exists.
+	Tests_if_exist_device_both proptools.Configurable[[]string] `android:"arch_variant"`
+}
+
+type testPackageZipDepTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var testPackageZipDepTag testPackageZipDepTagType
+
+var (
+	pctx = android.NewPackageContext("android/soong/ci_tests")
+	// test_package module type should only be used for the following modules.
+	// TODO: remove "_soong" from the module names inside when eliminating the corresponding make modules
+	moduleNamesAllowed = []string{"continuous_instrumentation_tests_soong", "continuous_instrumentation_metric_tests_soong", "continuous_native_tests_soong", "continuous_native_metric_tests_soong", "platform_tests"}
+)
+
+func (p *testPackageZip) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// adding tests property deps
+	for _, t := range p.properties.Tests.GetOrDefault(ctx, nil) {
+		ctx.AddVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), testPackageZipDepTag, t)
+	}
+
+	// adding device_first_tests property deps
+	for _, t := range p.properties.Device_first_tests.GetOrDefault(ctx, nil) {
+		ctx.AddVariationDependencies(ctx.Config().AndroidFirstDeviceTarget.Variations(), testPackageZipDepTag, t)
+	}
+
+	// adding device_both_tests property deps
+	p.addDeviceBothDeps(ctx, false)
+
+	// adding host_tests property deps
+	for _, t := range p.properties.Host_tests.GetOrDefault(ctx, nil) {
+		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), testPackageZipDepTag, t)
+	}
+
+	// adding Tests_if_exist_* property deps
+	for _, t := range p.properties.Tests_if_exist_common.GetOrDefault(ctx, nil) {
+		if ctx.OtherModuleExists(t) {
+			ctx.AddVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), testPackageZipDepTag, t)
+		}
+	}
+	p.addDeviceBothDeps(ctx, true)
+}
+
+func (p *testPackageZip) addDeviceBothDeps(ctx android.BottomUpMutatorContext, checkIfExist bool) {
+	android32TargetList := android.FirstTarget(ctx.Config().Targets[android.Android], "lib32")
+	android64TargetList := android.FirstTarget(ctx.Config().Targets[android.Android], "lib64")
+	if len(android32TargetList) > 0 {
+		maybeAndroid32Target := &android32TargetList[0]
+		if checkIfExist {
+			for _, t := range p.properties.Tests_if_exist_device_both.GetOrDefault(ctx, nil) {
+				if ctx.OtherModuleExists(t) {
+					ctx.AddFarVariationDependencies(maybeAndroid32Target.Variations(), testPackageZipDepTag, t)
+				}
+			}
+		} else {
+			ctx.AddFarVariationDependencies(maybeAndroid32Target.Variations(), testPackageZipDepTag, p.properties.Device_both_tests.GetOrDefault(ctx, nil)...)
+		}
+	}
+	if len(android64TargetList) > 0 {
+		maybeAndroid64Target := &android64TargetList[0]
+		if checkIfExist {
+			for _, t := range p.properties.Tests_if_exist_device_both.GetOrDefault(ctx, nil) {
+				if ctx.OtherModuleExists(t) {
+					ctx.AddFarVariationDependencies(maybeAndroid64Target.Variations(), testPackageZipDepTag, t)
+				}
+			}
+		} else {
+			ctx.AddFarVariationDependencies(maybeAndroid64Target.Variations(), testPackageZipDepTag, p.properties.Device_both_tests.GetOrDefault(ctx, nil)...)
+		}
+	}
+}
+
+func TestPackageZipFactory() android.Module {
+	module := &testPackageZip{}
+
+	module.AddProperties(&module.properties)
+
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+
+	return module
+}
+
+func (p *testPackageZip) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Never install this test package, it's for disting only
+	p.SkipInstall()
+
+	if !android.InList(ctx.ModuleName(), moduleNamesAllowed) {
+		ctx.ModuleErrorf("%s is not allowed to use module type test_package")
+	}
+
+	p.output = createOutput(ctx, pctx)
+
+	ctx.SetOutputFiles(android.Paths{p.output}, "")
+}
+
+func createOutput(ctx android.ModuleContext, pctx android.PackageContext) android.ModuleOutPath {
+	productOut := filepath.Join(ctx.Config().OutDir(), "target", "product", ctx.Config().DeviceName())
+	stagingDir := android.PathForModuleOut(ctx, "STAGING")
+	productVariables := ctx.Config().ProductVariables()
+	arch := proptools.String(productVariables.DeviceArch)
+	secondArch := proptools.String(productVariables.DeviceSecondaryArch)
+
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().Text("rm").Flag("-rf").Text(stagingDir.String())
+	builder.Command().Text("mkdir").Flag("-p").Output(stagingDir)
+	builder.Temporary(stagingDir)
+	ctx.WalkDeps(func(child, parent android.Module) bool {
+		if !child.Enabled(ctx) {
+			return false
+		}
+		if android.EqualModules(parent, ctx.Module()) && ctx.OtherModuleDependencyTag(child) == testPackageZipDepTag {
+			// handle direct deps
+			extendBuilderCommand(ctx, child, builder, stagingDir, productOut, arch, secondArch)
+			return true
+		} else if !android.EqualModules(parent, ctx.Module()) && ctx.OtherModuleDependencyTag(child) == android.RequiredDepTag {
+			// handle the "required" from deps
+			extendBuilderCommand(ctx, child, builder, stagingDir, productOut, arch, secondArch)
+			return true
+		} else {
+			return false
+		}
+	})
+
+	output := android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
+	builder.Command().
+		BuiltTool("soong_zip").
+		Flag("-o").Output(output).
+		Flag("-C").Text(stagingDir.String()).
+		Flag("-D").Text(stagingDir.String())
+	builder.Command().Text("rm").Flag("-rf").Text(stagingDir.String())
+	builder.Build("test_package", fmt.Sprintf("build test_package for %s", ctx.ModuleName()))
+	return output
+}
+
+func extendBuilderCommand(ctx android.ModuleContext, m android.Module, builder *android.RuleBuilder, stagingDir android.ModuleOutPath, productOut, arch, secondArch string) {
+	info, ok := android.OtherModuleProvider(ctx, m, android.ModuleInfoJSONProvider)
+	if !ok {
+		ctx.OtherModuleErrorf(m, "doesn't set ModuleInfoJSON provider")
+	} else if len(info) != 1 {
+		ctx.OtherModuleErrorf(m, "doesn't provide exactly one ModuleInfoJSON")
+	}
+
+	classes := info[0].GetClass()
+	if len(info[0].Class) != 1 {
+		ctx.OtherModuleErrorf(m, "doesn't have exactly one class in its ModuleInfoJSON")
+	}
+	class := strings.ToLower(classes[0])
+	if class == "apps" {
+		class = "app"
+	} else if class == "java_libraries" {
+		class = "framework"
+	}
+
+	installedFilesInfo, ok := android.OtherModuleProvider(ctx, m, android.InstallFilesProvider)
+	if !ok {
+		ctx.ModuleErrorf("Module %s doesn't set InstallFilesProvider", m.Name())
+	}
+
+	for _, spec := range installedFilesInfo.PackagingSpecs {
+		if spec.SrcPath() == nil {
+			// Probably a symlink
+			continue
+		}
+		installedFile := spec.FullInstallPath()
+
+		ext := installedFile.Ext()
+		// there are additional installed files for some app-class modules, we only need the .apk, .odex and .vdex files in the test package
+		excludeInstalledFile := ext != ".apk" && ext != ".odex" && ext != ".vdex"
+		if class == "app" && excludeInstalledFile {
+			continue
+		}
+		// only .jar files should be included for a framework dep
+		if class == "framework" && ext != ".jar" {
+			continue
+		}
+		name := removeFileExtension(installedFile.Base())
+		// some apks have other apk as installed files, these additional files shouldn't be included
+		isAppOrFramework := class == "app" || class == "framework"
+		if isAppOrFramework && name != ctx.OtherModuleName(m) {
+			continue
+		}
+
+		f := strings.TrimPrefix(installedFile.String(), productOut+"/")
+		if strings.HasPrefix(f, "out") {
+			continue
+		}
+		if strings.HasPrefix(f, "system/") {
+			f = strings.Replace(f, "system/", "DATA/", 1)
+		}
+		f = strings.ReplaceAll(f, filepath.Join("testcases", name, arch), filepath.Join("DATA", class, name))
+		f = strings.ReplaceAll(f, filepath.Join("testcases", name, secondArch), filepath.Join("DATA", class, name))
+		if strings.HasPrefix(f, "testcases") {
+			f = strings.Replace(f, "testcases", filepath.Join("DATA", class), 1)
+		}
+		if strings.HasPrefix(f, "data/") {
+			f = strings.Replace(f, "data/", "DATA/", 1)
+		}
+		f = strings.ReplaceAll(f, "DATA_other", "system_other")
+		f = strings.ReplaceAll(f, "system_other/DATA", "system_other/system")
+		dir := filepath.Dir(f)
+
+		tempOut := android.PathForModuleOut(ctx, "STAGING", f)
+		builder.Command().Text("mkdir").Flag("-p").Text(filepath.Join(stagingDir.String(), dir))
+		// Copy srcPath instead of installedFile because some rules like target-files.zip
+		// are non-hermetic and would be affected if we built the installed files.
+		builder.Command().Text("cp").Flag("-Rf").Input(spec.SrcPath()).Output(tempOut)
+		builder.Temporary(tempOut)
+	}
+}
+
+func removeFileExtension(filename string) string {
+	return strings.TrimSuffix(filename, filepath.Ext(filename))
+}
+
+// The only purpose of this method is to make sure we can build the module directly
+// without adding suffix "-soong"
+func (p *testPackageZip) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{
+		android.AndroidMkEntries{
+			Class:      "ETC",
+			OutputFile: android.OptionalPathForPath(p.output),
+			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+					entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+				}},
+		},
+	}
+}
diff --git a/cmd/find_input_delta/find_input_delta/main.go b/cmd/find_input_delta/find_input_delta/main.go
index a864584..65ef881 100644
--- a/cmd/find_input_delta/find_input_delta/main.go
+++ b/cmd/find_input_delta/find_input_delta/main.go
@@ -17,11 +17,13 @@
 import (
 	"flag"
 	"os"
-	"strings"
+	"regexp"
 
 	fid_lib "android/soong/cmd/find_input_delta/find_input_delta_lib"
 )
 
+var fileSepRegex = regexp.MustCompile("[^[:space:]]+")
+
 func main() {
 	var top string
 	var prior_state_file string
@@ -46,6 +48,8 @@
 	if target == "" {
 		panic("must specify --target")
 	}
+	// Drop any extra file names that arrived in `target`.
+	target = fileSepRegex.FindString(target)
 	if prior_state_file == "" {
 		prior_state_file = target + ".pc_state"
 	}
@@ -63,7 +67,7 @@
 		if err != nil {
 			panic(err)
 		}
-		inputs = append(inputs, strings.Split(string(data), "\n")...)
+		inputs = append(inputs, fileSepRegex.FindAllString(string(data), -1)...)
 	}
 
 	// Read the prior state
@@ -80,15 +84,16 @@
 		panic(err)
 	}
 
-	file_list := *fid_lib.CompareInternalState(prior_state, new_state, target)
+	file_list := fid_lib.CompareInternalState(prior_state, new_state, target)
 
 	if err = file_list.Format(os.Stdout, template); err != nil {
 		panic(err)
 	}
 
-	metrics_file := os.Getenv("SOONG_METRICS_AGGREGATION_FILE")
-	if metrics_file != "" {
-		if err = file_list.SendMetrics(metrics_file); err != nil {
+	metrics_dir := os.Getenv("SOONG_METRICS_AGGREGATION_DIR")
+	out_dir := os.Getenv("OUT_DIR")
+	if metrics_dir != "" {
+		if err = file_list.WriteMetrics(metrics_dir, out_dir); err != nil {
 			panic(err)
 		}
 	}
diff --git a/cmd/find_input_delta/find_input_delta_lib/file_list.go b/cmd/find_input_delta/find_input_delta_lib/file_list.go
index 01242a0..f1d588b 100644
--- a/cmd/find_input_delta/find_input_delta_lib/file_list.go
+++ b/cmd/find_input_delta/find_input_delta_lib/file_list.go
@@ -16,10 +16,11 @@
 
 import (
 	"fmt"
+	"hash/fnv"
 	"io"
 	"os"
 	"path/filepath"
-	"slices"
+	"strings"
 	"text/template"
 
 	fid_exp "android/soong/cmd/find_input_delta/find_input_delta_proto"
@@ -106,95 +107,75 @@
 	fl.ExtCountMap[ext].Changes += 1
 }
 
-func (fl FileList) ToProto() (*fid_exp.FileList, error) {
-	var count uint32
-	return fl.toProto(&count)
-}
-
-func (fl FileList) toProto(count *uint32) (*fid_exp.FileList, error) {
-	ret := &fid_exp.FileList{
-		Name: proto.String(fl.Name),
+// Write a SoongExecutionMetrics FileList proto to `dir`.
+//
+// Path
+// Prune any paths that
+// begin with `pruneDir` (usually ${OUT_DIR}).  The file is only written if any
+// non-pruned changes are present.
+func (fl *FileList) WriteMetrics(dir, pruneDir string) (err error) {
+	if dir == "" {
+		return fmt.Errorf("No directory given")
 	}
+	var needed bool
+
+	if !strings.HasSuffix(pruneDir, "/") {
+		pruneDir += "/"
+	}
+
+	// Hash the dir and `fl.Name` to simplify scanning the metrics
+	// aggregation directory.
+	h := fnv.New128()
+	h.Write([]byte(dir + " " + fl.Name + ".FileList"))
+	path := fmt.Sprintf("%x.pb", h.Sum([]byte{}))
+	path = filepath.Join(dir, path[0:2], path[2:])
+
+	var msg = &fid_exp.FileList{Name: proto.String(fl.Name)}
 	for _, a := range fl.Additions {
-		if *count >= MaxFilesRecorded {
-			break
+		if strings.HasPrefix(a, pruneDir) {
+			continue
 		}
-		ret.Additions = append(ret.Additions, a)
-		*count += 1
+		msg.Additions = append(msg.Additions, a)
+		needed = true
 	}
 	for _, ch := range fl.Changes {
-		if *count >= MaxFilesRecorded {
-			break
-		} else {
-			// Pre-increment to limit what the call adds.
-			*count += 1
-			change, err := ch.toProto(count)
-			if err != nil {
-				return nil, err
-			}
-			ret.Changes = append(ret.Changes, change)
+		if strings.HasPrefix(ch.Name, pruneDir) {
+			continue
 		}
+		msg.Changes = append(msg.Changes, ch.Name)
+		needed = true
 	}
 	for _, d := range fl.Deletions {
-		if *count >= MaxFilesRecorded {
-			break
+		if strings.HasPrefix(d, pruneDir) {
+			continue
 		}
-		ret.Deletions = append(ret.Deletions, d)
+		msg.Deletions = append(msg.Deletions, d)
+		needed = true
 	}
-	ret.TotalDelta = proto.Uint32(*count)
-	exts := []string{}
-	for k := range fl.ExtCountMap {
-		exts = append(exts, k)
+	if !needed {
+		return nil
 	}
-	slices.Sort(exts)
-	for _, k := range exts {
-		v := fl.ExtCountMap[k]
-		ret.Counts = append(ret.Counts, &fid_exp.FileCount{
-			Extension:     proto.String(k),
-			Additions:     proto.Uint32(v.Additions),
-			Deletions:     proto.Uint32(v.Deletions),
-			Modifications: proto.Uint32(v.Changes),
-		})
-	}
-	return ret, nil
-}
-
-func (fl FileList) SendMetrics(path string) error {
-	if path == "" {
-		return fmt.Errorf("No path given")
-	}
-	message, err := fl.ToProto()
-	if err != nil {
-		return err
-	}
-
-	// Marshal the message wrapped in SoongCombinedMetrics.
 	data := protowire.AppendVarint(
 		[]byte{},
 		protowire.EncodeTag(
 			protowire.Number(fid_exp.FieldNumbers_FIELD_NUMBERS_FILE_LIST),
 			protowire.BytesType))
-	size := uint64(proto.Size(message))
+	size := uint64(proto.Size(msg))
 	data = protowire.AppendVarint(data, size)
-	data, err = proto.MarshalOptions{UseCachedSize: true}.MarshalAppend(data, message)
+	data, err = proto.MarshalOptions{UseCachedSize: true}.MarshalAppend(data, msg)
 	if err != nil {
 		return err
 	}
 
-	out, err := os.Create(path)
+	err = os.MkdirAll(filepath.Dir(path), 0777)
 	if err != nil {
 		return err
 	}
-	defer func() {
-		if err := out.Close(); err != nil {
-			fmt.Fprintf(os.Stderr, "Failed to close %s: %v\n", path, err)
-		}
-	}()
-	_, err = out.Write(data)
-	return err
+
+	return os.WriteFile(path, data, 0644)
 }
 
-func (fl FileList) Format(wr io.Writer, format string) error {
+func (fl *FileList) Format(wr io.Writer, format string) error {
 	tmpl, err := template.New("filelist").Parse(format)
 	if err != nil {
 		return err
diff --git a/cmd/find_input_delta/find_input_delta_lib/internal_state.go b/cmd/find_input_delta/find_input_delta_lib/internal_state.go
index 2b8c395..0f88159 100644
--- a/cmd/find_input_delta/find_input_delta_lib/internal_state.go
+++ b/cmd/find_input_delta/find_input_delta_lib/internal_state.go
@@ -51,6 +51,9 @@
 	for _, input := range inputs {
 		stat, err := fs.Stat(fsys, input)
 		if err != nil {
+			if errors.Is(err, fs.ErrNotExist) {
+				continue
+			}
 			return ret, err
 		}
 		pci := &fid_proto.PartialCompileInput{
@@ -92,9 +95,14 @@
 	}
 	ret := []*fid_proto.PartialCompileInput{}
 	for _, v := range rc.File {
+		// Only include timestamp when there is no CRC.
+		timeNsec := proto.Int64(v.ModTime().UnixNano())
+		if v.CRC32 != 0 {
+			timeNsec = nil
+		}
 		pci := &fid_proto.PartialCompileInput{
 			Name:      proto.String(v.Name),
-			MtimeNsec: proto.Int64(v.ModTime().UnixNano()),
+			MtimeNsec: timeNsec,
 			Hash:      proto.String(fmt.Sprintf("%08x", v.CRC32)),
 		}
 		ret = append(ret, pci)
diff --git a/cmd/find_input_delta/find_input_delta_proto/file_list.pb.go b/cmd/find_input_delta/find_input_delta_proto/file_list.pb.go
index 745de2d..b6adc45 100644
--- a/cmd/find_input_delta/find_input_delta_proto/file_list.pb.go
+++ b/cmd/find_input_delta/find_input_delta_proto/file_list.pb.go
@@ -96,20 +96,14 @@
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	// The name of the file.
-	// In the outermost message, this is the name of the Ninja target.
-	// When used in `changes`, this is the name of the changed file.
+	// The name of the output file (Ninja target).
 	Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
 	// The added files.
 	Additions []string `protobuf:"bytes,2,rep,name=additions" json:"additions,omitempty"`
 	// The changed files.
-	Changes []*FileList `protobuf:"bytes,3,rep,name=changes" json:"changes,omitempty"`
+	Changes []string `protobuf:"bytes,3,rep,name=changes" json:"changes,omitempty"`
 	// The deleted files.
 	Deletions []string `protobuf:"bytes,4,rep,name=deletions" json:"deletions,omitempty"`
-	// Count of files added/changed/deleted.
-	TotalDelta *uint32 `protobuf:"varint,5,opt,name=total_delta,json=totalDelta" json:"total_delta,omitempty"`
-	// Counts by extension.
-	Counts []*FileCount `protobuf:"bytes,6,rep,name=counts" json:"counts,omitempty"`
 }
 
 func (x *FileList) Reset() {
@@ -158,7 +152,7 @@
 	return nil
 }
 
-func (x *FileList) GetChanges() []*FileList {
+func (x *FileList) GetChanges() []string {
 	if x != nil {
 		return x.Changes
 	}
@@ -172,135 +166,28 @@
 	return nil
 }
 
-func (x *FileList) GetTotalDelta() uint32 {
-	if x != nil && x.TotalDelta != nil {
-		return *x.TotalDelta
-	}
-	return 0
-}
-
-func (x *FileList) GetCounts() []*FileCount {
-	if x != nil {
-		return x.Counts
-	}
-	return nil
-}
-
-type FileCount struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// The file extension
-	Extension *string `protobuf:"bytes,1,opt,name=extension" json:"extension,omitempty"`
-	// Number of added files with this extension.
-	Additions *uint32 `protobuf:"varint,2,opt,name=additions" json:"additions,omitempty"`
-	// Number of modified files with this extension.
-	Modifications *uint32 `protobuf:"varint,3,opt,name=modifications" json:"modifications,omitempty"`
-	// Number of deleted files with this extension.
-	Deletions *uint32 `protobuf:"varint,4,opt,name=deletions" json:"deletions,omitempty"`
-}
-
-func (x *FileCount) Reset() {
-	*x = FileCount{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_file_list_proto_msgTypes[1]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *FileCount) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*FileCount) ProtoMessage() {}
-
-func (x *FileCount) ProtoReflect() protoreflect.Message {
-	mi := &file_file_list_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 FileCount.ProtoReflect.Descriptor instead.
-func (*FileCount) Descriptor() ([]byte, []int) {
-	return file_file_list_proto_rawDescGZIP(), []int{1}
-}
-
-func (x *FileCount) GetExtension() string {
-	if x != nil && x.Extension != nil {
-		return *x.Extension
-	}
-	return ""
-}
-
-func (x *FileCount) GetAdditions() uint32 {
-	if x != nil && x.Additions != nil {
-		return *x.Additions
-	}
-	return 0
-}
-
-func (x *FileCount) GetModifications() uint32 {
-	if x != nil && x.Modifications != nil {
-		return *x.Modifications
-	}
-	return 0
-}
-
-func (x *FileCount) GetDeletions() uint32 {
-	if x != nil && x.Deletions != nil {
-		return *x.Deletions
-	}
-	return 0
-}
-
 var File_file_list_proto protoreflect.FileDescriptor
 
 var file_file_list_proto_rawDesc = []byte{
 	0x0a, 0x0f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
 	0x6f, 0x12, 0x1e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x5f,
 	0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x22, 0x82, 0x02, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x12,
-	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
-	0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
-	0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-	0x12, 0x42, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
-	0x0b, 0x32, 0x28, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x66, 0x69, 0x6e, 0x64,
-	0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f,
-	0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x07, 0x63, 0x68, 0x61,
-	0x6e, 0x67, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e,
-	0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f,
-	0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x74,
-	0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x44, 0x65,
-	0x6c, 0x74, 0x61, 0x12, 0x41, 0x0a, 0x06, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20,
-	0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x66, 0x69,
-	0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x06,
-	0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x22, 0x8b, 0x01, 0x0a, 0x09, 0x46, 0x69, 0x6c, 0x65, 0x43,
-	0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
-	0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
-	0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
-	0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-	0x12, 0x24, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
-	0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63,
-	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69,
-	0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74,
-	0x69, 0x6f, 0x6e, 0x73, 0x2a, 0x4a, 0x0a, 0x0c, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4e, 0x75, 0x6d,
-	0x62, 0x65, 0x72, 0x73, 0x12, 0x1d, 0x0a, 0x19, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x4e, 0x55,
-	0x4d, 0x42, 0x45, 0x52, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45,
-	0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x4e, 0x55, 0x4d,
-	0x42, 0x45, 0x52, 0x53, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x01,
-	0x42, 0x3b, 0x5a, 0x39, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e,
-	0x67, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74,
-	0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75,
-	0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x6f, 0x22, 0x74, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x12, 0x0a,
+	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
+	0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02,
+	0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12,
+	0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09,
+	0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x6c,
+	0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x64, 0x65,
+	0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2a, 0x4a, 0x0a, 0x0c, 0x46, 0x69, 0x65, 0x6c, 0x64,
+	0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x1d, 0x0a, 0x19, 0x46, 0x49, 0x45, 0x4c, 0x44,
+	0x5f, 0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
+	0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f,
+	0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x53, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x4c, 0x49, 0x53,
+	0x54, 0x10, 0x01, 0x42, 0x3b, 0x5a, 0x39, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73,
+	0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e,
+	0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69,
+	0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 }
 
 var (
@@ -316,20 +203,17 @@
 }
 
 var file_file_list_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
-var file_file_list_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_file_list_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
 var file_file_list_proto_goTypes = []interface{}{
 	(FieldNumbers)(0), // 0: android.find_input_delta_proto.FieldNumbers
 	(*FileList)(nil),  // 1: android.find_input_delta_proto.FileList
-	(*FileCount)(nil), // 2: android.find_input_delta_proto.FileCount
 }
 var file_file_list_proto_depIdxs = []int32{
-	1, // 0: android.find_input_delta_proto.FileList.changes:type_name -> android.find_input_delta_proto.FileList
-	2, // 1: android.find_input_delta_proto.FileList.counts:type_name -> android.find_input_delta_proto.FileCount
-	2, // [2:2] is the sub-list for method output_type
-	2, // [2:2] is the sub-list for method input_type
-	2, // [2:2] is the sub-list for extension type_name
-	2, // [2:2] is the sub-list for extension extendee
-	0, // [0:2] is the sub-list for field type_name
+	0, // [0:0] is the sub-list for method output_type
+	0, // [0:0] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
 }
 
 func init() { file_file_list_proto_init() }
@@ -350,18 +234,6 @@
 				return nil
 			}
 		}
-		file_file_list_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*FileCount); 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{
@@ -369,7 +241,7 @@
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_file_list_proto_rawDesc,
 			NumEnums:      1,
-			NumMessages:   2,
+			NumMessages:   1,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/cmd/find_input_delta/find_input_delta_proto/file_list.proto b/cmd/find_input_delta/find_input_delta_proto/file_list.proto
index 7180358..5309d66 100644
--- a/cmd/find_input_delta/find_input_delta_proto/file_list.proto
+++ b/cmd/find_input_delta/find_input_delta_proto/file_list.proto
@@ -23,37 +23,15 @@
 }
 
 message FileList {
-  // The name of the file.
-  // In the outermost message, this is the name of the Ninja target.
-  // When used in `changes`, this is the name of the changed file.
+  // The name of the output file (Ninja target).
   optional string name = 1;
 
   // The added files.
   repeated string additions = 2;
 
   // The changed files.
-  repeated FileList changes = 3;
+  repeated string changes = 3;
 
   // The deleted files.
   repeated string deletions = 4;
-
-  // Count of files added/changed/deleted.
-  optional uint32 total_delta = 5;
-
-  // Counts by extension.
-  repeated FileCount counts = 6;
-}
-
-message FileCount {
-  // The file extension
-  optional string extension = 1;
-
-  // Number of added files with this extension.
-  optional uint32 additions = 2;
-
-  // Number of modified files with this extension.
-  optional uint32 modifications = 3;
-
-  // Number of deleted files with this extension.
-  optional uint32 deletions = 4;
 }
diff --git a/cmd/find_input_delta/find_input_delta_proto/regen.sh b/cmd/find_input_delta/find_input_delta_proto/regen.sh
old mode 100644
new mode 100755
diff --git a/cmd/find_input_delta/find_input_delta_proto_internal/regen.sh b/cmd/find_input_delta/find_input_delta_proto_internal/regen.sh
old mode 100644
new mode 100755
diff --git a/cmd/javac_wrapper/javac_wrapper.go b/cmd/javac_wrapper/javac_wrapper.go
index 4679906..49bcedf 100644
--- a/cmd/javac_wrapper/javac_wrapper.go
+++ b/cmd/javac_wrapper/javac_wrapper.go
@@ -200,6 +200,9 @@
 
 var warningFilters = []*regexp.Regexp{
 	regexp.MustCompile(`bootstrap class path not set in conjunction with -source`),
+	regexp.MustCompile(`source value 8 is obsolete and will be removed in a future release`),
+	regexp.MustCompile(`target value 8 is obsolete and will be removed in a future release`),
+	regexp.MustCompile(`To suppress warnings about obsolete options, use -Xlint:-options.`),
 }
 
 var filters = []*regexp.Regexp{
diff --git a/cmd/javac_wrapper/javac_wrapper_test.go b/cmd/javac_wrapper/javac_wrapper_test.go
index ad23001..5e26786 100644
--- a/cmd/javac_wrapper/javac_wrapper_test.go
+++ b/cmd/javac_wrapper/javac_wrapper_test.go
@@ -90,6 +90,15 @@
 `,
 		out: "\n\x1b[1m\x1b[35mwarning:\x1b[0m\x1b[1m foo\x1b[0m\n1 warning\n",
 	},
+	{
+		in: `
+warning: [options] source value 8 is obsolete and will be removed in a future release
+warning: [options] target value 8 is obsolete and will be removed in a future release
+warning: [options] To suppress warnings about obsolete options, use -Xlint:-options.
+3 warnings
+`,
+		out: "\n",
+	},
 }
 
 func TestJavacColorize(t *testing.T) {
diff --git a/cmd/kotlinc_incremental/Android.bp b/cmd/kotlinc_incremental/Android.bp
new file mode 100644
index 0000000..7816553
--- /dev/null
+++ b/cmd/kotlinc_incremental/Android.bp
@@ -0,0 +1,65 @@
+//
+// 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.
+//
+
+package {
+    default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
+    default_applicable_licenses: [
+        "Android-Apache-2.0",
+        "Kotlin_Incremental_license",
+    ],
+}
+
+license {
+    name: "Kotlin_Incremental_license",
+    visibility: [":__subpackages__"],
+    license_kinds: ["legacy_proprietary"],
+}
+
+java_library_host {
+    name: "kotlin-incremental-client-lib",
+    srcs: [
+        "src/com/**/*.kt",
+    ],
+    static_libs: [
+        "kotlin-compiler-embeddable",
+        "kotlin-compiler-runner",
+        "kotlin-daemon-client",
+    ],
+
+    plugins: [],
+
+    kotlincflags: [
+        "-Werror",
+    ],
+}
+
+java_binary_host {
+    name: "kotlin-incremental-client",
+    manifest: "kotlin-incremental-client.mf",
+    static_libs: ["kotlin-incremental-client-lib"],
+}
+
+java_test_host {
+    name: "kotlin-incremental-client-tests",
+    srcs: [
+        "tests/src/com/**/*.kt",
+    ],
+    static_libs: [
+        "kotlin-incremental-client-lib",
+        "junit",
+        "truth",
+    ],
+}
diff --git a/cmd/kotlinc_incremental/kotlin-incremental-client.mf b/cmd/kotlinc_incremental/kotlin-incremental-client.mf
new file mode 100644
index 0000000..b84c86a
--- /dev/null
+++ b/cmd/kotlinc_incremental/kotlin-incremental-client.mf
@@ -0,0 +1 @@
+Main-Class: com.android.kotlin.compiler.client.MainKt
diff --git a/cmd/kotlinc_incremental/src/com/android/kotlin/compiler/client/Main.kt b/cmd/kotlinc_incremental/src/com/android/kotlin/compiler/client/Main.kt
new file mode 100644
index 0000000..4938641
--- /dev/null
+++ b/cmd/kotlinc_incremental/src/com/android/kotlin/compiler/client/Main.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+package com.android.kotlin.compiler.client
+
+fun main(args: Array<String>) {
+  println("compiling")
+}
diff --git a/cmd/kotlinc_incremental/tests/src/com/android/kotlin/compiler/client/MainTest.kt b/cmd/kotlinc_incremental/tests/src/com/android/kotlin/compiler/client/MainTest.kt
new file mode 100644
index 0000000..3354aa4
--- /dev/null
+++ b/cmd/kotlinc_incremental/tests/src/com/android/kotlin/compiler/client/MainTest.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package com.android.kotlin.compiler.client
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class MainTest {
+    @Test
+    fun testMain() {
+        assertThat(true).isTrue()
+    }
+}
\ No newline at end of file
diff --git a/cmd/release_config/release_config/main.go b/cmd/release_config/release_config/main.go
index d06b2b7..7013d6b 100644
--- a/cmd/release_config/release_config/main.go
+++ b/cmd/release_config/release_config/main.go
@@ -95,7 +95,7 @@
 	if allMake {
 		// Write one makefile per release config, using the canonical release name.
 		for _, c := range configs.GetSortedReleaseConfigs() {
-			if c.Name != targetRelease {
+			if c.Name != targetRelease && !c.DisallowLunchUse {
 				makefilePath = filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.varmk", product, c.Name))
 				err = config.WriteMakefile(makefilePath, c.Name, configs)
 				if err != nil {
diff --git a/cmd/release_config/release_config_lib/flag_artifact.go b/cmd/release_config/release_config_lib/flag_artifact.go
index cb13fdc..f493e1e 100644
--- a/cmd/release_config/release_config_lib/flag_artifact.go
+++ b/cmd/release_config/release_config_lib/flag_artifact.go
@@ -189,7 +189,7 @@
 	if redacted {
 		fa.Redact()
 		flagValue.proto.Value = fa.Value
-		fmt.Printf("Redacting flag %s in %s\n", name, flagValue.path)
+		warnf("Redacting flag %s in %s\n", name, flagValue.path)
 	} else {
 		// If we are assigning a value, then the flag is no longer redacted.
 		fa.Redacted = false
diff --git a/cmd/release_config/release_config_lib/release_config.go b/cmd/release_config/release_config_lib/release_config.go
index 719ddc0..873f2fc 100644
--- a/cmd/release_config/release_config_lib/release_config.go
+++ b/cmd/release_config/release_config_lib/release_config.go
@@ -67,6 +67,9 @@
 	// overrides. Build flag value overrides are an error.
 	AconfigFlagsOnly bool
 
+	// True if this release config is not allowed as TARGET_RELEASE.
+	DisallowLunchUse bool
+
 	// Unmarshalled flag artifacts
 	FlagArtifacts FlagArtifacts
 
@@ -93,6 +96,11 @@
 
 // If true, this is a proper release config that can be used in "lunch".
 func (config *ReleaseConfig) isConfigListable() bool {
+	// Do not list disallowed release configs.
+	if config.DisallowLunchUse {
+		return false
+	}
+	// Logic based on ReleaseConfigType.
 	switch config.ReleaseConfigType {
 	case rc_proto.ReleaseConfigType_RELEASE_CONFIG:
 		return true
@@ -405,6 +413,7 @@
 		ValueDirectories:  valueDirectories,
 		PriorStages:       SortedMapKeys(config.PriorStagesMap),
 		ReleaseConfigType: config.ReleaseConfigType.Enum(),
+		DisallowLunchUse:  proto.Bool(config.DisallowLunchUse),
 	}
 
 	config.compileInProgress = false
@@ -481,6 +490,9 @@
 	}
 	// As it stands this list is not per-product, but conceptually it is, and will be.
 	data += fmt.Sprintf("ALL_RELEASE_CONFIGS_FOR_PRODUCT :=$= %s\n", strings.Join(configs.GetAllReleaseNames(), " "))
+	if config.DisallowLunchUse {
+		data += fmt.Sprintf("_disallow_lunch_use :=$= true\n")
+	}
 	data += fmt.Sprintf("_used_files := %s\n", strings.Join(config.GetSortedFileList(), " "))
 	data += fmt.Sprintf("_ALL_RELEASE_FLAGS :=$= %s\n", strings.Join(names, " "))
 	for _, pName := range pNames {
diff --git a/cmd/release_config/release_config_lib/release_configs.go b/cmd/release_config/release_config_lib/release_configs.go
index 4f621c7..dd98bca 100644
--- a/cmd/release_config/release_config_lib/release_configs.go
+++ b/cmd/release_config/release_config_lib/release_configs.go
@@ -314,6 +314,9 @@
 				}
 			}
 		}
+		if flagDeclaration.Namespace == nil {
+			return fmt.Errorf("Flag declaration %s has no namespace.", path)
+		}
 
 		m.FlagDeclarations = append(m.FlagDeclarations, *flagDeclaration)
 		name := *flagDeclaration.Name
@@ -378,6 +381,7 @@
 			return fmt.Errorf("%s mismatching ReleaseConfigType value %s", path, *releaseConfigType)
 		}
 		config.FilesUsedMap[path] = true
+		config.DisallowLunchUse = config.DisallowLunchUse || releaseConfigContribution.proto.GetDisallowLunchUse()
 		inheritNames := make(map[string]bool)
 		for _, inh := range config.InheritNames {
 			inheritNames[inh] = true
diff --git a/cmd/release_config/release_config_proto/build_flags_common.pb.go b/cmd/release_config/release_config_proto/build_flags_common.pb.go
index f8ad38f..7b52b07 100644
--- a/cmd/release_config/release_config_proto/build_flags_common.pb.go
+++ b/cmd/release_config/release_config_proto/build_flags_common.pb.go
@@ -15,7 +15,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.33.0
+// 	protoc-gen-go v1.30.0
 // 	protoc        v3.21.12
 // source: build_flags_common.proto
 
diff --git a/cmd/release_config/release_config_proto/build_flags_declarations.pb.go b/cmd/release_config/release_config_proto/build_flags_declarations.pb.go
index 1df246c..0029f99 100644
--- a/cmd/release_config/release_config_proto/build_flags_declarations.pb.go
+++ b/cmd/release_config/release_config_proto/build_flags_declarations.pb.go
@@ -15,7 +15,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.33.0
+// 	protoc-gen-go v1.30.0
 // 	protoc        v3.21.12
 // source: build_flags_declarations.proto
 
diff --git a/cmd/release_config/release_config_proto/build_flags_out.pb.go b/cmd/release_config/release_config_proto/build_flags_out.pb.go
index ec07415..21a37a9 100644
--- a/cmd/release_config/release_config_proto/build_flags_out.pb.go
+++ b/cmd/release_config/release_config_proto/build_flags_out.pb.go
@@ -15,7 +15,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.33.0
+// 	protoc-gen-go v1.30.0
 // 	protoc        v3.21.12
 // source: build_flags_out.proto
 
@@ -237,6 +237,10 @@
 	ValueDirectories []string `protobuf:"bytes,8,rep,name=value_directories,json=valueDirectories" json:"value_directories,omitempty"`
 	// The ReleaseConfigType of this release config.
 	ReleaseConfigType *ReleaseConfigType `protobuf:"varint,9,opt,name=release_config_type,json=releaseConfigType,enum=android.release_config_proto.ReleaseConfigType" json:"release_config_type,omitempty"`
+	// Whether to disallow this release config as TARGET_RELEASE.
+	// If true, this release config can only be inherited, it cannot be used
+	// directly in a build.
+	DisallowLunchUse *bool `protobuf:"varint,10,opt,name=disallow_lunch_use,json=disallowLunchUse" json:"disallow_lunch_use,omitempty"`
 }
 
 func (x *ReleaseConfigArtifact) Reset() {
@@ -334,6 +338,13 @@
 	return ReleaseConfigType_CONFIG_TYPE_UNSPECIFIED
 }
 
+func (x *ReleaseConfigArtifact) GetDisallowLunchUse() bool {
+	if x != nil && x.DisallowLunchUse != nil {
+		return *x.DisallowLunchUse
+	}
+	return false
+}
+
 type ReleaseConfigsArtifact struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -434,7 +445,7 @@
 	0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
 	0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x6c, 0x61, 0x67, 0x41, 0x72, 0x74, 0x69, 0x66,
 	0x61, 0x63, 0x74, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x0e, 0x66, 0x6c, 0x61, 0x67,
-	0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x22, 0xbb, 0x03, 0x0a, 0x15, 0x52,
+	0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x22, 0xe9, 0x03, 0x0a, 0x15, 0x52,
 	0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x1f, 0x0a, 0x0b, 0x6f, 0x74, 0x68, 0x65,
@@ -461,41 +472,44 @@
 	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, 0x54, 0x79, 0x70, 0x65, 0x52, 0x11, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f,
-	0x6e, 0x66, 0x69, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0e, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x61,
-	0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x22, 0xde, 0x03, 0x0a, 0x16, 0x52, 0x65, 0x6c,
-	0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66,
-	0x61, 0x63, 0x74, 0x12, 0x5a, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63,
-	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 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, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74,
-	0x52, 0x0d, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
-	0x67, 0x0a, 0x15, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65,
-	0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33,
-	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, 0x41, 0x72, 0x74, 0x69, 0x66,
-	0x61, 0x63, 0x74, 0x52, 0x13, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73,
-	0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x85, 0x01, 0x0a, 0x17, 0x72, 0x65, 0x6c,
-	0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6d, 0x61, 0x70, 0x73,
-	0x5f, 0x6d, 0x61, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4e, 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, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74,
-	0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61,
-	0x70, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x72, 0x65, 0x6c, 0x65,
-	0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x73, 0x4d, 0x61, 0x70,
-	0x1a, 0x77, 0x0a, 0x19, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x4d, 0x61, 0x70, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
-	0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
-	0x44, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e,
-	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, 0x4d, 0x61, 0x70, 0x52, 0x05,
-	0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 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,
+	0x6e, 0x66, 0x69, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x64, 0x69, 0x73, 0x61,
+	0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6c, 0x75, 0x6e, 0x63, 0x68, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x0a,
+	0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x64, 0x69, 0x73, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4c, 0x75,
+	0x6e, 0x63, 0x68, 0x55, 0x73, 0x65, 0x52, 0x0e, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x61, 0x72, 0x74,
+	0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x22, 0xde, 0x03, 0x0a, 0x16, 0x52, 0x65, 0x6c, 0x65, 0x61,
+	0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63,
+	0x74, 0x12, 0x5a, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 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, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x0d,
+	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x67, 0x0a,
+	0x15, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 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, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63,
+	0x74, 0x52, 0x13, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x85, 0x01, 0x0a, 0x17, 0x72, 0x65, 0x6c, 0x65, 0x61,
+	0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x5f, 0x6d,
+	0x61, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4e, 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, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x2e, 0x52,
+	0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x73,
+	0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
+	0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x73, 0x4d, 0x61, 0x70, 0x1a, 0x77,
+	0x0a, 0x19, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d,
+	0x61, 0x70, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,
+	0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x44, 0x0a,
+	0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 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, 0x4d, 0x61, 0x70, 0x52, 0x05, 0x76, 0x61,
+	0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 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 (
diff --git a/cmd/release_config/release_config_proto/build_flags_out.proto b/cmd/release_config/release_config_proto/build_flags_out.proto
index 55c848e..8b53464 100644
--- a/cmd/release_config/release_config_proto/build_flags_out.proto
+++ b/cmd/release_config/release_config_proto/build_flags_out.proto
@@ -99,6 +99,11 @@
 
   // The ReleaseConfigType of this release config.
   optional ReleaseConfigType release_config_type = 9;
+
+  // Whether to disallow this release config as TARGET_RELEASE.
+  // If true, this release config can only be inherited, it cannot be used
+  // directly in a build.
+  optional bool disallow_lunch_use = 10;
 }
 
 message ReleaseConfigsArtifact {
diff --git a/cmd/release_config/release_config_proto/build_flags_src.pb.go b/cmd/release_config/release_config_proto/build_flags_src.pb.go
index c48c323..9fc4ea4 100644
--- a/cmd/release_config/release_config_proto/build_flags_src.pb.go
+++ b/cmd/release_config/release_config_proto/build_flags_src.pb.go
@@ -15,7 +15,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.33.0
+// 	protoc-gen-go v1.30.0
 // 	protoc        v3.21.12
 // source: build_flags_src.proto
 
@@ -410,6 +410,10 @@
 	PriorStages []string `protobuf:"bytes,5,rep,name=prior_stages,json=priorStages" json:"prior_stages,omitempty"`
 	// The ReleaseConfigType of this release config.
 	ReleaseConfigType *ReleaseConfigType `protobuf:"varint,6,opt,name=release_config_type,json=releaseConfigType,enum=android.release_config_proto.ReleaseConfigType" json:"release_config_type,omitempty"`
+	// Whether to disallow this release config as TARGET_RELEASE.
+	// If true, this release config can only be inherited, it cannot be used
+	// directly in a build.
+	DisallowLunchUse *bool `protobuf:"varint,7,opt,name=disallow_lunch_use,json=disallowLunchUse" json:"disallow_lunch_use,omitempty"`
 }
 
 func (x *ReleaseConfig) Reset() {
@@ -486,6 +490,13 @@
 	return ReleaseConfigType_CONFIG_TYPE_UNSPECIFIED
 }
 
+func (x *ReleaseConfig) GetDisallowLunchUse() bool {
+	if x != nil && x.DisallowLunchUse != nil {
+		return *x.DisallowLunchUse
+	}
+	return false
+}
+
 // Any aliases.  These are used for continuous integration builder config.
 type ReleaseAlias struct {
 	state         protoimpl.MessageState
@@ -655,7 +666,7 @@
 	0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x56, 0x61,
 	0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x08, 0x72, 0x65,
 	0x64, 0x61, 0x63, 0x74, 0x65, 0x64, 0x18, 0xca, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72,
-	0x65, 0x64, 0x61, 0x63, 0x74, 0x65, 0x64, 0x22, 0x9f, 0x02, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x65,
+	0x65, 0x64, 0x61, 0x63, 0x74, 0x65, 0x64, 0x22, 0xcd, 0x02, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x65,
 	0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
 	0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a,
 	0x08, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52,
@@ -673,32 +684,35 @@
 	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, 0x54, 0x79, 0x70, 0x65, 0x52, 0x11, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43,
-	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x0a, 0x0c, 0x52, 0x65, 0x6c,
-	0x65, 0x61, 0x73, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
-	0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a,
-	0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74,
-	0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xa9, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73,
-	0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x12, 0x44, 0x0a, 0x07, 0x61, 0x6c,
-	0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 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, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73,
-	0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18,
-	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
-	0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f,
-	0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11,
-	0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72,
-	0x73, 0x2a, 0x78, 0x0a, 0x11, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66,
-	0x69, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47,
-	0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45,
-	0x44, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x4c, 0x45, 0x41, 0x53, 0x45, 0x5f, 0x43,
-	0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x45, 0x58, 0x50, 0x4c, 0x49,
-	0x43, 0x49, 0x54, 0x5f, 0x49, 0x4e, 0x48, 0x45, 0x52, 0x49, 0x54, 0x41, 0x4e, 0x43, 0x45, 0x5f,
-	0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x42, 0x55, 0x49, 0x4c,
-	0x44, 0x5f, 0x56, 0x41, 0x52, 0x49, 0x41, 0x4e, 0x54, 0x10, 0x03, 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,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x64, 0x69, 0x73,
+	0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6c, 0x75, 0x6e, 0x63, 0x68, 0x5f, 0x75, 0x73, 0x65, 0x18,
+	0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x64, 0x69, 0x73, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4c,
+	0x75, 0x6e, 0x63, 0x68, 0x55, 0x73, 0x65, 0x22, 0x3a, 0x0a, 0x0c, 0x52, 0x65, 0x6c, 0x65, 0x61,
+	0x73, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74,
+	0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72,
+	0x67, 0x65, 0x74, 0x22, 0xa9, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x12, 0x44, 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61,
+	0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 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,
+	0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x20,
+	0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+	0x12, 0x2d, 0x0a, 0x12, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74,
+	0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x64, 0x65,
+	0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x2a,
+	0x78, 0x0a, 0x11, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x54,
+	0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10,
+	0x00, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x4c, 0x45, 0x41, 0x53, 0x45, 0x5f, 0x43, 0x4f, 0x4e,
+	0x46, 0x49, 0x47, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x45, 0x58, 0x50, 0x4c, 0x49, 0x43, 0x49,
+	0x54, 0x5f, 0x49, 0x4e, 0x48, 0x45, 0x52, 0x49, 0x54, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x43, 0x4f,
+	0x4e, 0x46, 0x49, 0x47, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x5f,
+	0x56, 0x41, 0x52, 0x49, 0x41, 0x4e, 0x54, 0x10, 0x03, 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 (
diff --git a/cmd/release_config/release_config_proto/build_flags_src.proto b/cmd/release_config/release_config_proto/build_flags_src.proto
index e42ac31..2cc1a84 100644
--- a/cmd/release_config/release_config_proto/build_flags_src.proto
+++ b/cmd/release_config/release_config_proto/build_flags_src.proto
@@ -138,6 +138,11 @@
 
   // The ReleaseConfigType of this release config.
   optional ReleaseConfigType release_config_type = 6;
+
+  // Whether to disallow this release config as TARGET_RELEASE.
+  // If true, this release config can only be inherited, it cannot be used
+  // directly in a build.
+  optional bool disallow_lunch_use = 7;
 }
 
 // Any aliases.  These are used for continuous integration builder config.
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
index 54854f1..339e4ba 100644
--- a/cmd/release_config/release_config_proto/release_configs_contributions.pb.go
+++ b/cmd/release_config/release_config_proto/release_configs_contributions.pb.go
@@ -15,7 +15,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.33.0
+// 	protoc-gen-go v1.30.0
 // 	protoc        v3.21.12
 // source: release_configs_contributions.proto
 
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 6642023..cd4e9bd 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -64,6 +64,7 @@
 	flag.StringVar(&usedEnvFile, "used_env", "", "File containing used environment variables")
 	flag.StringVar(&cmdlineArgs.OutDir, "out", "", "the ninja builddir directory")
 	flag.StringVar(&cmdlineArgs.ModuleListFile, "l", "", "file that lists filepaths to parse")
+	flag.StringVar(&cmdlineArgs.KatiSuffix, "kati_suffix", "", "the suffix for kati and ninja files, so that different configurations don't clobber each other")
 
 	// Debug flags
 	flag.StringVar(&delveListen, "delve_listen", "", "Delve port to listen on for debugging")
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index c7134d7..584cc04 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -27,6 +27,7 @@
 
 	"android/soong/shared"
 	"android/soong/ui/build"
+	"android/soong/ui/execution_metrics"
 	"android/soong/ui/logger"
 	"android/soong/ui/metrics"
 	"android/soong/ui/signal"
@@ -170,14 +171,16 @@
 		stat.Finish()
 	})
 	criticalPath := status.NewCriticalPath()
+	emet := execution_metrics.NewExecutionMetrics(log)
 	buildCtx := build.Context{ContextImpl: &build.ContextImpl{
-		Context:      ctx,
-		Logger:       log,
-		Metrics:      met,
-		Tracer:       trace,
-		Writer:       output,
-		Status:       stat,
-		CriticalPath: criticalPath,
+		Context:          ctx,
+		Logger:           log,
+		Metrics:          met,
+		ExecutionMetrics: emet,
+		Tracer:           trace,
+		Writer:           output,
+		Status:           stat,
+		CriticalPath:     criticalPath,
 	}}
 
 	freshConfig := func() build.Config {
@@ -194,6 +197,7 @@
 	rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
 	soongBuildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_build_metrics.pb")
 	buildTraceFile := filepath.Join(logsDir, c.logsPrefix+"build.trace.gz")
+	executionMetricsFile := filepath.Join(logsDir, c.logsPrefix+"execution_metrics.pb")
 
 	metricsFiles := []string{
 		buildErrorFile,        // build error strings
@@ -204,9 +208,15 @@
 	}
 
 	defer func() {
+		emet.Finish(buildCtx)
 		stat.Finish()
 		criticalPath.WriteToMetrics(met)
 		met.Dump(soongMetricsFile)
+		emet.Dump(executionMetricsFile, args)
+		// If there are execution metrics, upload them.
+		if _, err := os.Stat(executionMetricsFile); err == nil {
+			metricsFiles = append(metricsFiles, executionMetricsFile)
+		}
 		if !config.SkipMetricsUpload() {
 			build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, metricsFiles...)
 		}
@@ -323,12 +333,12 @@
 
 	varName := flags.Arg(0)
 	if varName == "report_config" {
-		varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
+		varData, err := build.DumpMakeVars(ctx, config, nil, append(build.BannerVars, "PRODUCT_SOONG_ONLY"))
 		if err != nil {
 			ctx.Fatal(err)
 		}
 
-		fmt.Println(build.Banner(varData))
+		fmt.Println(build.Banner(config, varData))
 	} else {
 		varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
 		if err != nil {
@@ -390,7 +400,7 @@
 
 	if i := indexList("report_config", allVars); i != -1 {
 		allVars = append(allVars[:i], allVars[i+1:]...)
-		allVars = append(allVars, build.BannerVars...)
+		allVars = append(allVars, append(build.BannerVars, "PRODUCT_SOONG_ONLY")...)
 	}
 
 	if len(allVars) == 0 {
@@ -404,7 +414,7 @@
 
 	for _, name := range vars {
 		if name == "report_config" {
-			fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
+			fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(config, varData))
 		} else {
 			fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
 		}
@@ -461,20 +471,6 @@
 		description: "Build action: build from the top of the source tree.",
 		action:      build.BUILD_MODULES,
 	}, {
-		// This is redirecting to mma build command behaviour. Once it has soaked for a
-		// while, the build command is deleted from here once it has been removed from the
-		// envsetup.sh.
-		name:        "modules-in-a-dir-no-deps",
-		description: "Build action: builds all of the modules in the current directory without their dependencies.",
-		action:      build.BUILD_MODULES_IN_A_DIRECTORY,
-	}, {
-		// This is redirecting to mmma build command behaviour. Once it has soaked for a
-		// while, the build command is deleted from here once it has been removed from the
-		// envsetup.sh.
-		name:        "modules-in-dirs-no-deps",
-		description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
-		action:      build.BUILD_MODULES_IN_DIRECTORIES,
-	}, {
 		name:        "modules-in-a-dir",
 		description: "Build action: builds all of the modules in the current directory and their dependencies.",
 		action:      build.BUILD_MODULES_IN_A_DIRECTORY,
diff --git a/genrule/allowlists.go b/cmd/source_tree_size/Android.bp
similarity index 63%
copy from genrule/allowlists.go
copy to cmd/source_tree_size/Android.bp
index 45a7f72..97d29c6 100644
--- a/genrule/allowlists.go
+++ b/cmd/source_tree_size/Android.bp
@@ -1,4 +1,4 @@
-// Copyright 2023 Google Inc. All rights reserved.
+// 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.
@@ -12,11 +12,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package genrule
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
 
-var (
-	SandboxingDenyModuleList = []string{
-		// go/keep-sorted start
-		// go/keep-sorted end
-	}
-)
+blueprint_go_binary {
+    name: "source_tree_size",
+    srcs: [
+        "source_tree.pb.go",
+        "source_tree_size.go",
+    ],
+    deps: [
+        "golang-protobuf-runtime-protoimpl",
+    ],
+}
diff --git a/cmd/source_tree_size/regen.sh b/cmd/source_tree_size/regen.sh
new file mode 100755
index 0000000..b0f1c80
--- /dev/null
+++ b/cmd/source_tree_size/regen.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. source_tree.proto
+
diff --git a/cmd/source_tree_size/source_tree.pb.go b/cmd/source_tree_size/source_tree.pb.go
new file mode 100644
index 0000000..da8cba5
--- /dev/null
+++ b/cmd/source_tree_size/source_tree.pb.go
@@ -0,0 +1,230 @@
+// 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.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.30.0
+// 	protoc        v3.21.12
+// source: source_tree.proto
+
+package main
+
+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 SourceFile struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Path      *string `protobuf:"bytes,1,opt,name=path" json:"path,omitempty"`
+	SizeBytes *int32  `protobuf:"varint,2,opt,name=size_bytes,json=sizeBytes" json:"size_bytes,omitempty"`
+}
+
+func (x *SourceFile) Reset() {
+	*x = SourceFile{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_source_tree_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SourceFile) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SourceFile) ProtoMessage() {}
+
+func (x *SourceFile) ProtoReflect() protoreflect.Message {
+	mi := &file_source_tree_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 SourceFile.ProtoReflect.Descriptor instead.
+func (*SourceFile) Descriptor() ([]byte, []int) {
+	return file_source_tree_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *SourceFile) GetPath() string {
+	if x != nil && x.Path != nil {
+		return *x.Path
+	}
+	return ""
+}
+
+func (x *SourceFile) GetSizeBytes() int32 {
+	if x != nil && x.SizeBytes != nil {
+		return *x.SizeBytes
+	}
+	return 0
+}
+
+type SourceTree struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Files []*SourceFile `protobuf:"bytes,1,rep,name=files" json:"files,omitempty"`
+}
+
+func (x *SourceTree) Reset() {
+	*x = SourceTree{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_source_tree_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SourceTree) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SourceTree) ProtoMessage() {}
+
+func (x *SourceTree) ProtoReflect() protoreflect.Message {
+	mi := &file_source_tree_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 SourceTree.ProtoReflect.Descriptor instead.
+func (*SourceTree) Descriptor() ([]byte, []int) {
+	return file_source_tree_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *SourceTree) GetFiles() []*SourceFile {
+	if x != nil {
+		return x.Files
+	}
+	return nil
+}
+
+var File_source_tree_proto protoreflect.FileDescriptor
+
+var file_source_tree_proto_rawDesc = []byte{
+	0x0a, 0x11, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65,
+	0x73, 0x22, 0x3f, 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12,
+	0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70,
+	0x61, 0x74, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65,
+	0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74,
+	0x65, 0x73, 0x22, 0x3c, 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x72, 0x65, 0x65,
+	0x12, 0x2e, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
+	0x18, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x53,
+	0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73,
+	0x42, 0x08, 0x5a, 0x06, 0x2e, 0x2f, 0x6d, 0x61, 0x69, 0x6e,
+}
+
+var (
+	file_source_tree_proto_rawDescOnce sync.Once
+	file_source_tree_proto_rawDescData = file_source_tree_proto_rawDesc
+)
+
+func file_source_tree_proto_rawDescGZIP() []byte {
+	file_source_tree_proto_rawDescOnce.Do(func() {
+		file_source_tree_proto_rawDescData = protoimpl.X.CompressGZIP(file_source_tree_proto_rawDescData)
+	})
+	return file_source_tree_proto_rawDescData
+}
+
+var file_source_tree_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_source_tree_proto_goTypes = []interface{}{
+	(*SourceFile)(nil), // 0: source_files.SourceFile
+	(*SourceTree)(nil), // 1: source_files.SourceTree
+}
+var file_source_tree_proto_depIdxs = []int32{
+	0, // 0: source_files.SourceTree.files:type_name -> source_files.SourceFile
+	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_source_tree_proto_init() }
+func file_source_tree_proto_init() {
+	if File_source_tree_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_source_tree_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SourceFile); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_source_tree_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SourceTree); 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_source_tree_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_source_tree_proto_goTypes,
+		DependencyIndexes: file_source_tree_proto_depIdxs,
+		MessageInfos:      file_source_tree_proto_msgTypes,
+	}.Build()
+	File_source_tree_proto = out.File
+	file_source_tree_proto_rawDesc = nil
+	file_source_tree_proto_goTypes = nil
+	file_source_tree_proto_depIdxs = nil
+}
diff --git a/genrule/allowlists.go b/cmd/source_tree_size/source_tree.proto
similarity index 61%
copy from genrule/allowlists.go
copy to cmd/source_tree_size/source_tree.proto
index 45a7f72..1a5b89f 100644
--- a/genrule/allowlists.go
+++ b/cmd/source_tree_size/source_tree.proto
@@ -1,10 +1,10 @@
-// Copyright 2023 Google Inc. All rights reserved.
+// 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
+//   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,
@@ -12,11 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package genrule
+syntax = "proto2";
 
-var (
-	SandboxingDenyModuleList = []string{
-		// go/keep-sorted start
-		// go/keep-sorted end
-	}
-)
+package source_files;
+option go_package = "./main";
+
+message SourceFile {
+  optional string path = 1;
+  optional int32 size_bytes = 2;
+}
+
+message SourceTree {
+  repeated SourceFile files = 1;
+}
diff --git a/cmd/source_tree_size/source_tree_size.go b/cmd/source_tree_size/source_tree_size.go
new file mode 100644
index 0000000..8b2a4cf
--- /dev/null
+++ b/cmd/source_tree_size/source_tree_size.go
@@ -0,0 +1,126 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path"
+	"path/filepath"
+	"sort"
+	"strings"
+	"sync"
+
+	"google.golang.org/protobuf/proto"
+)
+
+var root string
+var repo string
+var outDir string
+var channel chan SourceFile
+var waiter sync.WaitGroup
+var sourceTree SourceTree
+
+func normalizeOutDir(outDir, root string) string {
+	if len(outDir) == 0 {
+		return ""
+	}
+	if filepath.IsAbs(outDir) {
+		if strings.HasPrefix(outDir, root) {
+			// Absolute path inside root, use it
+			return outDir
+		} else {
+			// Not inside root, we don't care about it
+			return ""
+		}
+	} else {
+		// Relative path inside root, make it absolute
+		return root + outDir
+	}
+}
+
+func walkDir(dir string) {
+	defer waiter.Done()
+
+	visit := func(path string, info os.FileInfo, err error) error {
+		name := info.Name()
+
+		// Repo git projects are symlinks.  A real directory called .git counts as checked in
+		// (and is very likely to be wasted space)
+		if info.Mode().Type()&os.ModeSymlink != 0 && name == ".git" {
+			return nil
+		}
+
+		// Skip .repo and out
+		if info.IsDir() && (path == repo || path == outDir) {
+			return filepath.SkipDir
+		}
+
+		if info.IsDir() && path != dir {
+			waiter.Add(1)
+			go walkDir(path)
+			return filepath.SkipDir
+		}
+
+		if !info.IsDir() {
+			sourcePath := strings.TrimPrefix(path, root)
+			file := SourceFile{
+				Path:      proto.String(sourcePath),
+				SizeBytes: proto.Int32(42),
+			}
+			channel <- file
+		}
+		return nil
+
+	}
+	filepath.Walk(dir, visit)
+}
+
+func main() {
+	var outputFile string
+	flag.StringVar(&outputFile, "o", "", "The file to write")
+	flag.StringVar(&outDir, "out_dir", "out", "The out directory")
+	flag.Parse()
+
+	if outputFile == "" {
+		fmt.Fprintf(os.Stderr, "source_tree_size: Missing argument: -o\n")
+		os.Exit(1)
+	}
+
+	root, _ = os.Getwd()
+	if root[len(root)-1] != '/' {
+		root += "/"
+	}
+
+	outDir = normalizeOutDir(outDir, root)
+	repo = path.Join(root, ".repo")
+
+	// The parallel scanning reduces the run time by about a minute
+	channel = make(chan SourceFile)
+	waiter.Add(1)
+	go walkDir(root)
+	go func() {
+		waiter.Wait()
+		close(channel)
+	}()
+	for sourceFile := range channel {
+		sourceTree.Files = append(sourceTree.Files, &sourceFile)
+	}
+
+	// Sort the results, for a stable output
+	sort.Slice(sourceTree.Files, func(i, j int) bool {
+		return *sourceTree.Files[i].Path < *sourceTree.Files[j].Path
+	})
+
+	// Flatten and write
+	buf, err := proto.Marshal(&sourceTree)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Couldn't marshal protobuf\n")
+		os.Exit(1)
+	}
+	err = ioutil.WriteFile(outputFile, buf, 0644)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error writing file: %v\n", err)
+		os.Exit(1)
+	}
+}
diff --git a/cmd/symbols_map/symbols_map.go b/cmd/symbols_map/symbols_map.go
index c56cf93..3955c8a 100644
--- a/cmd/symbols_map/symbols_map.go
+++ b/cmd/symbols_map/symbols_map.go
@@ -72,6 +72,7 @@
 
 	elfFile := flags.String("elf", "", "extract identifier from an elf file")
 	r8File := flags.String("r8", "", "extract identifier from an r8 dictionary")
+	locationFlag := flags.String("location", "", "an override for the value of the location field in the proto. If not specified, the filename will be used")
 	merge := flags.String("merge", "", "merge multiple identifier protos")
 
 	writeIfChanged := flags.Bool("write_if_changed", false, "only write output file if it is modified")
@@ -134,6 +135,10 @@
 		panic("shouldn't get here")
 	}
 
+	if *locationFlag != "" {
+		location = *locationFlag
+	}
+
 	mapping := symbols_map_proto.Mapping{
 		Identifier: proto.String(identifier),
 		Location:   proto.String(location),
diff --git a/cmd/symbols_map/symbols_map_proto/regen.sh b/cmd/symbols_map/symbols_map_proto/regen.sh
new file mode 100755
index 0000000..3c189d1
--- /dev/null
+++ b/cmd/symbols_map/symbols_map_proto/regen.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. symbols_map.proto
+
diff --git a/cmd/symbols_map/symbols_map_proto/symbols_map.pb.go b/cmd/symbols_map/symbols_map_proto/symbols_map.pb.go
index f9c0ce5..07f4b39 100644
--- a/cmd/symbols_map/symbols_map_proto/symbols_map.pb.go
+++ b/cmd/symbols_map/symbols_map_proto/symbols_map.pb.go
@@ -14,8 +14,8 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.27.1
-// 	protoc        v3.9.1
+// 	protoc-gen-go v1.30.0
+// 	protoc        v3.21.12
 // source: symbols_map.proto
 
 package symbols_map_proto
@@ -93,6 +93,68 @@
 	return file_symbols_map_proto_rawDescGZIP(), []int{0, 0}
 }
 
+// LocationType is the place where to look for the file with the given
+// identifier.
+type Mapping_LocationType int32
+
+const (
+	// ZIP denotes the file with the given identifier is in the distribuited
+	// symbols.zip or proguard_dict.zip files, or the local disc.
+	Mapping_ZIP Mapping_LocationType = 0
+	// AB denotes the file with the given identifier is in the AB artifacts but
+	// not in a symbols.zip or proguard_dict.zip.
+	Mapping_AB Mapping_LocationType = 1
+)
+
+// Enum value maps for Mapping_LocationType.
+var (
+	Mapping_LocationType_name = map[int32]string{
+		0: "ZIP",
+		1: "AB",
+	}
+	Mapping_LocationType_value = map[string]int32{
+		"ZIP": 0,
+		"AB":  1,
+	}
+)
+
+func (x Mapping_LocationType) Enum() *Mapping_LocationType {
+	p := new(Mapping_LocationType)
+	*p = x
+	return p
+}
+
+func (x Mapping_LocationType) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (Mapping_LocationType) Descriptor() protoreflect.EnumDescriptor {
+	return file_symbols_map_proto_enumTypes[1].Descriptor()
+}
+
+func (Mapping_LocationType) Type() protoreflect.EnumType {
+	return &file_symbols_map_proto_enumTypes[1]
+}
+
+func (x Mapping_LocationType) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *Mapping_LocationType) UnmarshalJSON(b []byte) error {
+	num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+	if err != nil {
+		return err
+	}
+	*x = Mapping_LocationType(num)
+	return nil
+}
+
+// Deprecated: Use Mapping_LocationType.Descriptor instead.
+func (Mapping_LocationType) EnumDescriptor() ([]byte, []int) {
+	return file_symbols_map_proto_rawDescGZIP(), []int{0, 1}
+}
+
 type Mapping struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -107,6 +169,9 @@
 	Location *string `protobuf:"bytes,2,opt,name=location" json:"location,omitempty"`
 	// type is the type of the mapping, either ELF or R8.
 	Type *Mapping_Type `protobuf:"varint,3,opt,name=type,enum=symbols_map.Mapping_Type" json:"type,omitempty"`
+	// location_type is the Location Type that dictates where to search for the
+	// file with the given identifier. Defaults to ZIP if not present.
+	LocationType *Mapping_LocationType `protobuf:"varint,4,opt,name=location_type,json=locationType,enum=symbols_map.Mapping_LocationType" json:"location_type,omitempty"`
 }
 
 func (x *Mapping) Reset() {
@@ -162,6 +227,13 @@
 	return Mapping_ELF
 }
 
+func (x *Mapping) GetLocationType() Mapping_LocationType {
+	if x != nil && x.LocationType != nil {
+		return *x.LocationType
+	}
+	return Mapping_ZIP
+}
+
 type Mappings struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -214,23 +286,29 @@
 var file_symbols_map_proto_rawDesc = []byte{
 	0x0a, 0x11, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x2e, 0x70, 0x72,
 	0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70,
-	0x22, 0x8d, 0x01, 0x0a, 0x07, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x1e, 0x0a, 0x0a,
+	0x22, 0xf6, 0x01, 0x0a, 0x07, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x1e, 0x0a, 0x0a,
 	0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
 	0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08,
 	0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
 	0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
 	0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73,
 	0x5f, 0x6d, 0x61, 0x70, 0x2e, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x54, 0x79, 0x70,
-	0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x17, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,
-	0x07, 0x0a, 0x03, 0x45, 0x4c, 0x46, 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x52, 0x38, 0x10, 0x01,
-	0x22, 0x3c, 0x0a, 0x08, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x30, 0x0a, 0x08,
-	0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14,
+	0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x46, 0x0a, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21,
 	0x2e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x2e, 0x4d, 0x61, 0x70,
-	0x70, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x31,
-	0x5a, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f,
-	0x63, 0x6d, 0x64, 0x2f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x2f,
-	0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x74,
-	0x6f,
+	0x70, 0x69, 0x6e, 0x67, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70,
+	0x65, 0x52, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x22,
+	0x17, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x45, 0x4c, 0x46, 0x10, 0x00,
+	0x12, 0x06, 0x0a, 0x02, 0x52, 0x38, 0x10, 0x01, 0x22, 0x1f, 0x0a, 0x0c, 0x4c, 0x6f, 0x63, 0x61,
+	0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x5a, 0x49, 0x50, 0x10,
+	0x00, 0x12, 0x06, 0x0a, 0x02, 0x41, 0x42, 0x10, 0x01, 0x22, 0x3c, 0x0a, 0x08, 0x4d, 0x61, 0x70,
+	0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x30, 0x0a, 0x08, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67,
+	0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c,
+	0x73, 0x5f, 0x6d, 0x61, 0x70, 0x2e, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x6d,
+	0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x31, 0x5a, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x73, 0x79, 0x6d,
+	0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x2f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73,
+	0x5f, 0x6d, 0x61, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 }
 
 var (
@@ -245,21 +323,23 @@
 	return file_symbols_map_proto_rawDescData
 }
 
-var file_symbols_map_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_symbols_map_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
 var file_symbols_map_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
 var file_symbols_map_proto_goTypes = []interface{}{
-	(Mapping_Type)(0), // 0: symbols_map.Mapping.Type
-	(*Mapping)(nil),   // 1: symbols_map.Mapping
-	(*Mappings)(nil),  // 2: symbols_map.Mappings
+	(Mapping_Type)(0),         // 0: symbols_map.Mapping.Type
+	(Mapping_LocationType)(0), // 1: symbols_map.Mapping.LocationType
+	(*Mapping)(nil),           // 2: symbols_map.Mapping
+	(*Mappings)(nil),          // 3: symbols_map.Mappings
 }
 var file_symbols_map_proto_depIdxs = []int32{
 	0, // 0: symbols_map.Mapping.type:type_name -> symbols_map.Mapping.Type
-	1, // 1: symbols_map.Mappings.mappings:type_name -> symbols_map.Mapping
-	2, // [2:2] is the sub-list for method output_type
-	2, // [2:2] is the sub-list for method input_type
-	2, // [2:2] is the sub-list for extension type_name
-	2, // [2:2] is the sub-list for extension extendee
-	0, // [0:2] is the sub-list for field type_name
+	1, // 1: symbols_map.Mapping.location_type:type_name -> symbols_map.Mapping.LocationType
+	2, // 2: symbols_map.Mappings.mappings:type_name -> symbols_map.Mapping
+	3, // [3:3] is the sub-list for method output_type
+	3, // [3:3] is the sub-list for method input_type
+	3, // [3:3] is the sub-list for extension type_name
+	3, // [3:3] is the sub-list for extension extendee
+	0, // [0:3] is the sub-list for field type_name
 }
 
 func init() { file_symbols_map_proto_init() }
@@ -298,7 +378,7 @@
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_symbols_map_proto_rawDesc,
-			NumEnums:      1,
+			NumEnums:      2,
 			NumMessages:   2,
 			NumExtensions: 0,
 			NumServices:   0,
diff --git a/cmd/symbols_map/symbols_map_proto/symbols_map.proto b/cmd/symbols_map/symbols_map_proto/symbols_map.proto
index a76d171..a52f75c 100644
--- a/cmd/symbols_map/symbols_map_proto/symbols_map.proto
+++ b/cmd/symbols_map/symbols_map_proto/symbols_map.proto
@@ -40,7 +40,7 @@
 
   // LocationType is the place where to look for the file with the given
   // identifier.
-  Enum LocationType {
+  enum LocationType {
     // ZIP denotes the file with the given identifier is in the distribuited
     // symbols.zip or proguard_dict.zip files, or the local disc.
     ZIP = 0;
@@ -56,4 +56,4 @@
 
 message Mappings {
   repeated Mapping mappings = 4;
-}
\ No newline at end of file
+}
diff --git a/compliance/Android.bp b/compliance/Android.bp
index 6662970..25f6f86 100644
--- a/compliance/Android.bp
+++ b/compliance/Android.bp
@@ -34,7 +34,41 @@
     name: "notice_xml_system",
     partition_name: "system",
     visibility: [
-        "//build/make/target/product/generic",
-        "//build/make/target/product/gsi",
+        "//visibility:any_system_partition",
     ],
 }
+
+notice_xml {
+    name: "notice_xml_system_ext",
+    partition_name: "system_ext",
+}
+
+notice_xml {
+    name: "notice_xml_system_dlkm",
+    partition_name: "system_dlkm",
+}
+
+notice_xml {
+    name: "notice_xml_product",
+    partition_name: "product",
+}
+
+notice_xml {
+    name: "notice_xml_odm",
+    partition_name: "odm",
+}
+
+notice_xml {
+    name: "notice_xml_odm_dlkm",
+    partition_name: "odm_dlkm",
+}
+
+notice_xml {
+    name: "notice_xml_vendor",
+    partition_name: "vendor",
+}
+
+notice_xml {
+    name: "notice_xml_vendor_dlkm",
+    partition_name: "vendor_dlkm",
+}
diff --git a/compliance/notice.go b/compliance/notice.go
index edd1b34..c5b0fbe 100644
--- a/compliance/notice.go
+++ b/compliance/notice.go
@@ -71,12 +71,17 @@
 }
 
 func (nx *NoticeXmlModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	prodVars := ctx.Config().ProductVariables()
+	buildFingerprintFile := android.PathForArbitraryOutput(ctx, "target", "product", android.String(prodVars.DeviceName), "build_fingerprint.txt")
+	implicits := []android.Path{buildFingerprintFile}
+
 	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,
+		Rule:      genNoticeXmlRule,
+		Input:     metadataDb,
+		Implicits: implicits,
+		Output:    output,
 		Args: map[string]string{
 			"productOut": filepath.Join(ctx.Config().OutDir(), "target", "product", ctx.Config().DeviceName()),
 			"soongOut":   ctx.Config().SoongOutDir(),
@@ -86,8 +91,10 @@
 
 	nx.outputFile = output.OutputPath
 
-	installPath := android.PathForModuleInPartitionInstall(ctx, nx.props.Partition_name, "etc")
-	ctx.PackageFile(installPath, "NOTICE.xml.gz", nx.outputFile)
+	if android.Bool(ctx.Config().ProductVariables().UseSoongNoticeXML) {
+		installPath := android.PathForModuleInPartitionInstall(ctx, nx.props.Partition_name, "etc")
+		ctx.InstallFile(installPath, "NOTICE.xml.gz", nx.outputFile)
+	}
 }
 
 func (nx *NoticeXmlModule) AndroidMkEntries() []android.AndroidMkEntries {
diff --git a/dexpreopt/Android.bp b/dexpreopt/Android.bp
index 679d066..dbc1922 100644
--- a/dexpreopt/Android.bp
+++ b/dexpreopt/Android.bp
@@ -9,6 +9,8 @@
         "class_loader_context.go",
         "config.go",
         "dexpreopt.go",
+        "dexpreopt_tools_zip.go",
+        "system_server_zip.go",
         "testing.go",
     ],
     testSrcs: [
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index af1d33d..7f50912 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -291,6 +291,11 @@
 	// For prebuilts, library should have the same name as the source module.
 	lib = android.RemoveOptionalPrebuiltPrefix(lib)
 
+	// Bootclasspath libraries should not be added to CLC.
+	if android.InList(lib, ctx.Config().BootJars()) {
+		return nil
+	}
+
 	devicePath := UnknownInstallLibraryPath
 	if installPath == nil {
 		if android.InList(lib, CompatUsesLibs) || android.InList(lib, OptionalCompatUsesLibs) {
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index e3804e5..af09dbc 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -139,6 +139,11 @@
 
 // Returns all jars that system_server loads.
 func (g *GlobalConfig) AllSystemServerJars(ctx android.PathContext) *android.ConfiguredJarList {
+	// dexpreopt does not initialize the soong config.
+	// Initialize the OncePer here.
+	if ctx.Config().OncePer == nil {
+		ctx.Config().OncePer = &android.OncePer{}
+	}
 	return ctx.Config().Once(allSystemServerJarsKey, func() interface{} {
 		res := g.AllPlatformSystemServerJars(ctx).AppendList(g.AllApexSystemServerJars(ctx))
 		return &res
@@ -464,8 +469,8 @@
 
 func (d dex2oatDependencyTag) AllowDisabledModuleDependencyProxy(
 	ctx android.OtherModuleProviderContext, target android.ModuleProxy) bool {
-	return android.OtherModuleProviderOrDefault(
-		ctx, target, android.CommonModuleInfoKey).ReplacedByPrebuilt
+	return android.OtherModulePointerProviderOrDefault(
+		ctx, target, android.CommonModuleInfoProvider).ReplacedByPrebuilt
 }
 
 // Dex2oatDepTag represents the dependency onto the dex2oatd module. It is added to any module that
@@ -507,36 +512,37 @@
 	// final variants, while prebuilt_postdeps needs to run before many of the
 	// PostDeps mutators, like the APEX mutators). Hence we need to dig out the
 	// prebuilt explicitly here instead.
-	var dex2oatModule android.Module
-	ctx.WalkDeps(func(child, parent android.Module) bool {
-		if parent == ctx.Module() && ctx.OtherModuleDependencyTag(child) == Dex2oatDepTag {
+	var dex2oatModule android.ModuleProxy
+	ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
+		prebuiltInfo, isPrebuilt := android.OtherModuleProvider(ctx, child, android.PrebuiltModuleInfoProvider)
+		if android.EqualModules(parent, ctx.Module()) && ctx.OtherModuleDependencyTag(child) == Dex2oatDepTag {
 			// Found the source module, or prebuilt module that has replaced the source.
 			dex2oatModule = child
-			if android.IsModulePrebuilt(child) {
+			if isPrebuilt {
 				return false // If it's the prebuilt we're done.
 			} else {
 				return true // Recurse to check if the source has a prebuilt dependency.
 			}
 		}
-		if parent == dex2oatModule && ctx.OtherModuleDependencyTag(child) == android.PrebuiltDepTag {
-			if p := android.GetEmbeddedPrebuilt(child); p != nil && p.UsePrebuilt() {
+		if android.EqualModules(parent, dex2oatModule) && ctx.OtherModuleDependencyTag(child) == android.PrebuiltDepTag {
+			if isPrebuilt && prebuiltInfo.UsePrebuilt {
 				dex2oatModule = child // Found a prebuilt that should be used.
 			}
 		}
 		return false
 	})
 
-	if dex2oatModule == nil {
+	if dex2oatModule.IsNil() {
 		// If this happens there's probably a missing call to AddToolDeps in DepsMutator.
 		panic(fmt.Sprintf("Failed to lookup %s dependency", dex2oatBin))
 	}
 
-	dex2oatPath := dex2oatModule.(android.HostToolProvider).HostToolPath()
-	if !dex2oatPath.Valid() {
+	dex2oatPath, ok := android.OtherModuleProvider(ctx, dex2oatModule, android.HostToolProviderInfoProvider)
+	if !ok || !dex2oatPath.HostToolPath.Valid() {
 		panic(fmt.Sprintf("Failed to find host tool path in %s", dex2oatModule))
 	}
 
-	return dex2oatPath.Path()
+	return dex2oatPath.HostToolPath.Path()
 }
 
 // createGlobalSoongConfig creates a GlobalSoongConfig from the current context.
@@ -716,8 +722,9 @@
 	} else if global.EnableUffdGc == "false" {
 		android.WriteFileRuleVerbatim(ctx, uffdGcFlag, "")
 	} else if global.EnableUffdGc == "default" {
-		// Generated by `build/make/core/Makefile`.
+		// Generated by build/make/core/Makefile, or the android_device module in soong-only builds.
 		kernelVersionFile := android.PathForOutput(ctx, "dexpreopt/kernel_version_for_uffd_gc.txt")
+
 		// Determine the UFFD GC flag by the kernel version file.
 		rule := android.NewRuleBuilder(pctx, ctx)
 		rule.Command().
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 7a39fa1..699a675 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -46,13 +46,14 @@
 
 const SystemPartition = "/system/"
 const SystemOtherPartition = "/system_other/"
+const SystemServerDexjarsDir = "system_server_dexjars"
 
 var DexpreoptRunningInSoong = false
 
 // GenerateDexpreoptRule generates a set of commands that will preopt a module based on a GlobalConfig and a
 // ModuleConfig.  The produced files and their install locations will be available through rule.Installs().
 func GenerateDexpreoptRule(ctx android.BuilderContext, globalSoong *GlobalSoongConfig,
-	global *GlobalConfig, module *ModuleConfig, productPackages android.Path, copyApexSystemServerJarDex bool) (
+	global *GlobalConfig, module *ModuleConfig, productPackages android.Path) (
 	rule *android.RuleBuilder, err error) {
 
 	defer func() {
@@ -83,7 +84,7 @@
 
 	if !dexpreoptDisabled(ctx, global, module) {
 		if valid, err := validateClassLoaderContext(module.ClassLoaderContexts); err != nil {
-			android.ReportPathErrorf(ctx, err.Error())
+			android.ReportPathErrorf(ctx, "%s", err.Error())
 		} else if valid {
 			fixClassLoaderContext(module.ClassLoaderContexts)
 
@@ -94,7 +95,7 @@
 
 			for archIdx, _ := range module.Archs {
 				dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage,
-					generateDM, productPackages, copyApexSystemServerJarDex)
+					generateDM, productPackages)
 			}
 		}
 	}
@@ -231,7 +232,7 @@
 
 func dexpreoptCommand(ctx android.BuilderContext, globalSoong *GlobalSoongConfig,
 	global *GlobalConfig, module *ModuleConfig, rule *android.RuleBuilder, archIdx int,
-	profile android.WritablePath, appImage bool, generateDM bool, productPackages android.Path, copyApexSystemServerJarDex bool) {
+	profile android.WritablePath, appImage bool, generateDM bool, productPackages android.Path) {
 
 	arch := module.Archs[archIdx]
 
@@ -279,25 +280,10 @@
 			clcTarget = append(clcTarget, GetSystemServerDexLocation(ctx, global, lib))
 		}
 
-		if DexpreoptRunningInSoong && copyApexSystemServerJarDex {
-			// Copy the system server jar to a predefined location where dex2oat will find it.
-			dexPathHost := SystemServerDexJarHostPath(ctx, module.Name)
-			rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String()))
-			rule.Command().Text("cp -f").Input(module.DexPath).Output(dexPathHost)
-		} else {
-			// For Make modules the copy rule is generated in the makefiles, not in dexpreopt.sh.
-			// This is necessary to expose the rule to Ninja, otherwise it has rules that depend on
-			// the jar (namely, dexpreopt commands for all subsequent system server jars that have
-			// this one in their class loader context), but no rule that creates it (because Ninja
-			// cannot see the rule in the generated dexpreopt.sh script).
-		}
-
 		clcHostString := "PCL[" + strings.Join(clcHost.Strings(), ":") + "]"
 		clcTargetString := "PCL[" + strings.Join(clcTarget, ":") + "]"
 
-		if systemServerClasspathJars.ContainsJar(module.Name) {
-			checkSystemServerOrder(ctx, jarIndex)
-		} else {
+		if !systemServerClasspathJars.ContainsJar(module.Name) {
 			// Standalone jars are loaded by separate class loaders with SYSTEMSERVERCLASSPATH as the
 			// parent.
 			clcHostString = "PCL[];" + clcHostString
@@ -581,34 +567,11 @@
 func SystemServerDexJarHostPath(ctx android.PathContext, jar string) android.OutputPath {
 	if DexpreoptRunningInSoong {
 		// Soong module, just use the default output directory $OUT/soong.
-		return android.PathForOutput(ctx, "system_server_dexjars", jar+".jar")
+		return android.PathForOutput(ctx, SystemServerDexjarsDir, jar+".jar")
 	} else {
 		// Make module, default output directory is $OUT (passed via the "null config" created
 		// by dexpreopt_gen). Append Soong subdirectory to match Soong module paths.
-		return android.PathForOutput(ctx, "soong", "system_server_dexjars", jar+".jar")
-	}
-}
-
-// Check the order of jars on the system server classpath and give a warning/error if a jar precedes
-// one of its dependencies. This is not an error, but a missed optimization, as dexpreopt won't
-// have the dependency jar in the class loader context, and it won't be able to resolve any
-// references to its classes and methods.
-func checkSystemServerOrder(ctx android.PathContext, jarIndex int) {
-	mctx, isModule := ctx.(android.ModuleContext)
-	if isModule {
-		config := GetGlobalConfig(ctx)
-		jars := config.AllSystemServerClasspathJars(ctx)
-		mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
-			depIndex := jars.IndexOfJar(dep.Name())
-			if jarIndex < depIndex && !config.BrokenSuboptimalOrderOfSystemServerJars {
-				jar := jars.Jar(jarIndex)
-				dep := jars.Jar(depIndex)
-				mctx.ModuleErrorf("non-optimal order of jars on the system server classpath:"+
-					" '%s' precedes its dependency '%s', so dexpreopt is unable to resolve any"+
-					" references from '%s' to '%s'.\n", jar, dep, jar, dep)
-			}
-			return true
-		})
+		return android.PathForOutput(ctx, "soong", SystemServerDexjarsDir, jar+".jar")
 	}
 }
 
diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
index 7512005..8033b48 100644
--- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
+++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
@@ -205,9 +205,8 @@
 			panic(err)
 		}
 	}
-	cpApexSscpServerJar := false // dexpreopt_gen operates on make modules, and since sscp libraries are in soong, this should be a noop
 	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(
-		ctx, globalSoong, global, module, android.PathForTesting(productPackagesPath), cpApexSscpServerJar)
+		ctx, globalSoong, global, module, android.PathForTesting(productPackagesPath))
 	if err != nil {
 		panic(err)
 	}
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 7b0f51f..500b2ff 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -15,9 +15,10 @@
 package dexpreopt
 
 import (
-	"android/soong/android"
 	"fmt"
 	"testing"
+
+	"android/soong/android"
 )
 
 func testSystemModuleConfig(ctx android.PathContext, name string) *ModuleConfig {
@@ -103,7 +104,7 @@
 	module := testSystemModuleConfig(ctx, "test")
 	productPackages := android.PathForTesting("product_packages.txt")
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -163,7 +164,7 @@
 	for _, test := range tests {
 		global.PatternsOnSystemOther = test.patterns
 		for _, mt := range test.moduleTests {
-			rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, mt.module, productPackages, true)
+			rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, mt.module, productPackages)
 			if err != nil {
 				t.Fatal(err)
 			}
@@ -198,7 +199,7 @@
 	global.ApexSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"com.android.apex1:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -210,15 +211,6 @@
 
 	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
 }
@@ -241,7 +233,7 @@
 	global.ApexSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"com.android.apex1:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -253,15 +245,6 @@
 
 	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
 }
@@ -277,7 +260,7 @@
 	global.StandaloneSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"platform:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -301,7 +284,7 @@
 	global.StandaloneSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"system_ext:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -325,7 +308,7 @@
 	global.ApexStandaloneSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"com.android.apex1:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -348,7 +331,7 @@
 
 	module.ProfileClassListing = android.OptionalPathForPath(android.PathForTesting("profile"))
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -394,7 +377,7 @@
 			result := preparers.RunTest(t)
 			ctx := result.TestContext
 
-			ctx.SingletonForTests("dexpreopt-soong-config").Output("out/soong/dexpreopt/uffd_gc_flag.txt")
+			ctx.SingletonForTests(t, "dexpreopt-soong-config").Output("out/soong/dexpreopt/uffd_gc_flag.txt")
 		})
 	}
 }
@@ -403,6 +386,7 @@
 	preparers := android.GroupFixturePreparers(
 		PrepareForTestWithFakeDex2oatd,
 		PrepareForTestWithDexpreoptConfig,
+		android.FixtureModifyConfig(android.SetKatiEnabledForTests),
 		FixtureSetEnableUffdGc("default"),
 	)
 
@@ -410,7 +394,7 @@
 	ctx := result.TestContext
 	config := ctx.Config()
 
-	rule := ctx.SingletonForTests("dexpreopt-soong-config").Rule("dexpreopt_uffd_gc_flag")
+	rule := ctx.SingletonForTests(t, "dexpreopt-soong-config").Rule("dexpreopt_uffd_gc_flag")
 
 	android.AssertStringDoesContain(t, "", rule.RuleParams.Command, "construct_uffd_gc_flag")
 	android.AssertStringPathsRelativeToTopEquals(t, "", config, []string{
diff --git a/dexpreopt/dexpreopt_tools_zip.go b/dexpreopt/dexpreopt_tools_zip.go
new file mode 100644
index 0000000..c7cf128
--- /dev/null
+++ b/dexpreopt/dexpreopt_tools_zip.go
@@ -0,0 +1,62 @@
+package dexpreopt
+
+import "android/soong/android"
+
+func init() {
+	android.InitRegistrationContext.RegisterSingletonType("dexpreopt_tools_zip_singleton", dexpreoptToolsZipSingletonFactory)
+}
+
+func dexpreoptToolsZipSingletonFactory() android.Singleton {
+	return &dexpreoptToolsZipSingleton{}
+}
+
+type dexpreoptToolsZipSingleton struct{}
+
+func (s *dexpreoptToolsZipSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	// The mac build doesn't build dex2oat, so create the zip file only if the build OS is linux.
+	if !ctx.Config().BuildOS.Linux() {
+		return
+	}
+	global := GetGlobalConfig(ctx)
+	if global.DisablePreopt {
+		return
+	}
+	config := GetCachedGlobalSoongConfig(ctx)
+	if config == nil {
+		return
+	}
+
+	deps := android.Paths{
+		ctx.Config().HostToolPath(ctx, "dexpreopt_gen"),
+		ctx.Config().HostToolPath(ctx, "dexdump"),
+		ctx.Config().HostToolPath(ctx, "oatdump"),
+		config.Profman,
+		config.Dex2oat,
+		config.Aapt,
+		config.SoongZip,
+		config.Zip2zip,
+		config.ManifestCheck,
+		config.ConstructContext,
+		config.UffdGcFlag,
+	}
+
+	out := android.PathForOutput(ctx, "dexpreopt_tools.zip")
+	builder := android.NewRuleBuilder(pctx, ctx)
+
+	cmd := builder.Command().BuiltTool("soong_zip").
+		Flag("-d").
+		FlagWithOutput("-o ", out).
+		Flag("-j")
+
+	for _, dep := range deps {
+		cmd.FlagWithInput("-f ", dep)
+	}
+
+	// This reads through a symlink to include the file it points to. This isn't great for
+	// build reproducibility, will need to be revisited later.
+	cmd.Textf("-f $(realpath %s)", config.Dex2oat)
+
+	builder.Build("dexpreopt_tools_zip", "building dexpreopt_tools.zip")
+
+	ctx.DistForGoal("droidcore", out)
+}
diff --git a/dexpreopt/system_server_zip.go b/dexpreopt/system_server_zip.go
new file mode 100644
index 0000000..cef847b
--- /dev/null
+++ b/dexpreopt/system_server_zip.go
@@ -0,0 +1,49 @@
+package dexpreopt
+
+import "android/soong/android"
+
+func init() {
+	android.InitRegistrationContext.RegisterSingletonType("system_server_zip_singleton", systemServerZipSingletonFactory)
+}
+
+func systemServerZipSingletonFactory() android.Singleton {
+	return &systemServerZipSingleton{}
+}
+
+type systemServerZipSingleton struct{}
+
+func (s *systemServerZipSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	global := GetGlobalConfig(ctx)
+	if global.DisablePreopt || global.OnlyPreoptArtBootImage {
+		return
+	}
+
+	systemServerDexjarsDir := android.PathForOutput(ctx, SystemServerDexjarsDir)
+
+	out := android.PathForOutput(ctx, "system_server.zip")
+	builder := android.NewRuleBuilder(pctx, ctx)
+	cmd := builder.Command().BuiltTool("soong_zip").
+		FlagWithOutput("-o ", out).
+		FlagWithArg("-C ", systemServerDexjarsDir.String())
+
+	for i := 0; i < global.SystemServerJars.Len(); i++ {
+		jar := global.SystemServerJars.Jar(i) + ".jar"
+		cmd.FlagWithInput("-f ", systemServerDexjarsDir.Join(ctx, jar))
+	}
+	for i := 0; i < global.StandaloneSystemServerJars.Len(); i++ {
+		jar := global.StandaloneSystemServerJars.Jar(i) + ".jar"
+		cmd.FlagWithInput("-f ", systemServerDexjarsDir.Join(ctx, jar))
+	}
+	for i := 0; i < global.ApexSystemServerJars.Len(); i++ {
+		jar := global.ApexSystemServerJars.Jar(i) + ".jar"
+		cmd.FlagWithInput("-f ", systemServerDexjarsDir.Join(ctx, jar))
+	}
+	for i := 0; i < global.ApexStandaloneSystemServerJars.Len(); i++ {
+		jar := global.ApexStandaloneSystemServerJars.Jar(i) + ".jar"
+		cmd.FlagWithInput("-f ", systemServerDexjarsDir.Join(ctx, jar))
+	}
+
+	builder.Build("system_server_zip", "building system_server.zip")
+
+	ctx.DistForGoal("droidcore", out)
+}
diff --git a/etc/avbpubkey.go b/etc/avbpubkey.go
index 3f998d4..dc242ce 100644
--- a/etc/avbpubkey.go
+++ b/etc/avbpubkey.go
@@ -51,6 +51,7 @@
 		Command: `${avbtool} extract_public_key --key ${in} --output ${out}.tmp` +
 			` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
 		CommandDeps: []string{"${avbtool}"},
+		Restat:      true,
 		Description: "Extracting system_other avb key",
 	})
 
diff --git a/etc/install_symlink_test.go b/etc/install_symlink_test.go
index c97d97c..dce6873 100644
--- a/etc/install_symlink_test.go
+++ b/etc/install_symlink_test.go
@@ -39,7 +39,7 @@
 		t.Fatalf("expected 1 variant, got %#v", foo_variants)
 	}
 
-	foo := result.ModuleForTests("foo", "android_common").Module()
+	foo := result.ModuleForTests(t, "foo", "android_common").Module()
 	androidMkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, foo)
 	if len(androidMkEntries) != 1 {
 		t.Fatalf("expected 1 androidmkentry, got %d", len(androidMkEntries))
@@ -70,7 +70,7 @@
 		t.Fatalf("expected 1 variant, got %#v", foo_variants)
 	}
 
-	foo := result.ModuleForTests("foo", "android_common").Module()
+	foo := result.ModuleForTests(t, "foo", "android_common").Module()
 	androidMkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, foo)
 	if len(androidMkEntries) != 1 {
 		t.Fatalf("expected 1 androidmkentry, got %d", len(androidMkEntries))
@@ -149,7 +149,7 @@
 	`)
 
 	buildOS := result.Config.BuildOS.String()
-	foo := result.ModuleForTests("foo", buildOS+"_common").Module()
+	foo := result.ModuleForTests(t, "foo", buildOS+"_common").Module()
 
 	androidMkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, foo)
 	if len(androidMkEntries) != 1 {
diff --git a/etc/otacerts_zip.go b/etc/otacerts_zip.go
index 8220cea..53ddc50 100644
--- a/etc/otacerts_zip.go
+++ b/etc/otacerts_zip.go
@@ -111,6 +111,10 @@
 	return proptools.StringDefault(m.properties.Filename, "otacerts.zip")
 }
 
+func (m *otacertsZipModule) onlyInRecovery() bool {
+	return m.ModuleBase.InstallInRecovery()
+}
+
 func (m *otacertsZipModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// Read .x509.pem file defined in PRODUCT_DEFAULT_DEV_CERTIFICATE or the default test key.
 	pem, _ := ctx.Config().DefaultAppCertificate(ctx)
@@ -136,7 +140,7 @@
 
 func (m *otacertsZipModule) AndroidMkEntries() []android.AndroidMkEntries {
 	nameSuffix := ""
-	if m.InRecovery() {
+	if m.InRecovery() && !m.onlyInRecovery() {
 		nameSuffix = ".recovery"
 	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 3d1e242..7820047 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -32,6 +32,7 @@
 	"path/filepath"
 	"strings"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -60,9 +61,11 @@
 	ctx.RegisterModuleType("prebuilt_usr_keychars", PrebuiltUserKeyCharsFactory)
 	ctx.RegisterModuleType("prebuilt_usr_idc", PrebuiltUserIdcFactory)
 	ctx.RegisterModuleType("prebuilt_usr_srec", PrebuiltUserSrecFactory)
+	ctx.RegisterModuleType("prebuilt_usr_odml", PrebuiltUserOdmlFactory)
 	ctx.RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
 	ctx.RegisterModuleType("prebuilt_overlay", PrebuiltOverlayFactory)
 	ctx.RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory)
+	ctx.RegisterModuleType("prebuilt_gpu", PrebuiltGPUFactory)
 	ctx.RegisterModuleType("prebuilt_dsp", PrebuiltDSPFactory)
 	ctx.RegisterModuleType("prebuilt_rfsa", PrebuiltRFSAFactory)
 	ctx.RegisterModuleType("prebuilt_renderscript_bitcode", PrebuiltRenderScriptBitcodeFactory)
@@ -71,12 +74,15 @@
 	ctx.RegisterModuleType("prebuilt_bin", PrebuiltBinaryFactory)
 	ctx.RegisterModuleType("prebuilt_wallpaper", PrebuiltWallpaperFactory)
 	ctx.RegisterModuleType("prebuilt_priv_app", PrebuiltPrivAppFactory)
+	ctx.RegisterModuleType("prebuilt_radio", PrebuiltRadioFactory)
 	ctx.RegisterModuleType("prebuilt_rfs", PrebuiltRfsFactory)
 	ctx.RegisterModuleType("prebuilt_framework", PrebuiltFrameworkFactory)
 	ctx.RegisterModuleType("prebuilt_res", PrebuiltResFactory)
+	ctx.RegisterModuleType("prebuilt_tee", PrebuiltTeeFactory)
 	ctx.RegisterModuleType("prebuilt_wlc_upt", PrebuiltWlcUptFactory)
 	ctx.RegisterModuleType("prebuilt_odm", PrebuiltOdmFactory)
 	ctx.RegisterModuleType("prebuilt_vendor_dlkm", PrebuiltVendorDlkmFactory)
+	ctx.RegisterModuleType("prebuilt_vendor_overlay", PrebuiltVendorOverlayFactory)
 	ctx.RegisterModuleType("prebuilt_bt_firmware", PrebuiltBtFirmwareFactory)
 	ctx.RegisterModuleType("prebuilt_tvservice", PrebuiltTvServiceFactory)
 	ctx.RegisterModuleType("prebuilt_optee", PrebuiltOpteeFactory)
@@ -91,6 +97,15 @@
 
 }
 
+type PrebuiltEtcInfo struct {
+	// Returns the base install directory, such as "etc", "usr/share".
+	BaseDir string
+	// Returns the sub install directory relative to BaseDir().
+	SubDir string
+}
+
+var PrebuiltEtcInfoProvider = blueprint.NewProvider[PrebuiltEtcInfo]()
+
 var PrepareForTestWithPrebuiltEtc = android.FixtureRegisterWithContext(RegisterPrebuiltEtcBuildComponents)
 
 type PrebuiltEtcProperties struct {
@@ -510,7 +525,28 @@
 		ip.addInstallRules(ctx)
 	}
 
+	p.updateModuleInfoJSON(ctx)
+
 	ctx.SetOutputFiles(p.outputFilePaths.Paths(), "")
+
+	SetCommonPrebuiltEtcInfo(ctx, p)
+}
+
+func SetCommonPrebuiltEtcInfo(ctx android.ModuleContext, p PrebuiltEtcModule) {
+	android.SetProvider(ctx, PrebuiltEtcInfoProvider, PrebuiltEtcInfo{
+		BaseDir: p.BaseDir(),
+		SubDir:  p.SubDir(),
+	})
+}
+
+func (p *PrebuiltEtc) updateModuleInfoJSON(ctx android.ModuleContext) {
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Class = []string{"ETC"}
+	if p.makeClass != "" {
+		moduleInfoJSON.Class = []string{p.makeClass}
+	}
+	moduleInfoJSON.SystemSharedLibs = []string{"none"}
+	moduleInfoJSON.Tags = []string{"optional"}
 }
 
 type installProperties struct {
@@ -782,6 +818,17 @@
 	return module
 }
 
+// prebuilt_usr_odml is for a prebuilt artifact that is installed in
+// <partition>/usr/odml/<sub_dir> directory.
+func PrebuiltUserOdmlFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "usr/odml")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	android.InitDefaultableModule(module)
+	return module
+}
+
 // prebuilt_font installs a font in <partition>/fonts directory.
 func PrebuiltFontFactory() android.Module {
 	module := &PrebuiltEtc{}
@@ -815,6 +862,15 @@
 	return module
 }
 
+// prebuilt_gpu is for a prebuilt artifact in <partition>/gpu directory.
+func PrebuiltGPUFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "gpu")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
+
 // prebuilt_dsp installs a DSP related file to <partition>/etc/dsp directory for system image.
 // If soc_specific property is set to true, the DSP related file is installed to the
 // vendor <partition>/dsp directory for vendor image.
@@ -855,6 +911,16 @@
 	return module
 }
 
+// prebuilt_tee installs files in <partition>/tee directory.
+func PrebuiltTeeFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "tee")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
 // prebuilt_media installs media files in <partition>/media directory.
 func PrebuiltMediaFactory() android.Module {
 	module := &PrebuiltEtc{}
@@ -905,6 +971,16 @@
 	return module
 }
 
+// prebuilt_radio installs files in <partition>/radio directory.
+func PrebuiltRadioFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "radio")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
 // prebuilt_rfs installs files in <partition>/rfs directory.
 func PrebuiltRfsFactory() android.Module {
 	module := &PrebuiltEtc{}
@@ -1015,6 +1091,16 @@
 	return module
 }
 
+// prebuilt_vendor_overlay is for a prebuilt artifact in <partition>/vendor_overlay directory.
+func PrebuiltVendorOverlayFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "vendor_overlay")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
 // prebuilt_sbin installs files in <partition>/sbin directory.
 func PrebuiltSbinFactory() android.Module {
 	module := &PrebuiltEtc{}
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index 0fd04d8..eb722b4 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -133,8 +133,8 @@
 	android.AssertStringEquals(t, "output file path", "different.name", p.outputFilePaths[1].Base())
 
 	expectedPaths := [...]string{
-		"out/soong/target/product/test_device/system/etc/foodir",
-		"out/soong/target/product/test_device/system/etc/bardir/extradir",
+		"out/target/product/test_device/system/etc/foodir",
+		"out/target/product/test_device/system/etc/bardir/extradir",
 	}
 	android.AssertPathRelativeToTopEquals(t, "install dir", expectedPaths[0], p.installDirPaths[0])
 	android.AssertPathRelativeToTopEquals(t, "install dir", expectedPaths[1], p.installDirPaths[1])
@@ -155,8 +155,8 @@
 	android.AssertStringEquals(t, "output file path", "different.name", p.outputFilePaths[1].Base())
 
 	expectedPaths := [...]string{
-		"out/soong/target/product/test_device/system/etc/somewhere/foodir",
-		"out/soong/target/product/test_device/system/etc/somewhere/bardir/extradir",
+		"out/target/product/test_device/system/etc/somewhere/foodir",
+		"out/target/product/test_device/system/etc/somewhere/bardir/extradir",
 	}
 	android.AssertPathRelativeToTopEquals(t, "install dir", expectedPaths[0], p.installDirPaths[0])
 	android.AssertPathRelativeToTopEquals(t, "install dir", expectedPaths[1], p.installDirPaths[1])
@@ -271,7 +271,7 @@
 	`)
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
-	expected := "out/soong/target/product/test_device/system/etc/bar"
+	expected := "out/target/product/test_device/system/etc/bar"
 	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
@@ -324,7 +324,7 @@
 	`)
 
 	android.AssertStringEquals(t, "expected error rule", "android/soong/android.Error",
-		result.ModuleForTests("foo.conf", "android_arm64_armv8-a").Output("foo.conf").Rule.String())
+		result.ModuleForTests(t, "foo.conf", "android_arm64_armv8-a").Output("foo.conf").Rule.String())
 }
 
 func TestPrebuiltRootInstallDirPath(t *testing.T) {
@@ -337,7 +337,7 @@
 	`)
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
-	expected := "out/soong/target/product/test_device/system"
+	expected := "out/target/product/test_device/system"
 	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
@@ -362,7 +362,7 @@
 	`)
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
-	expected := "out/soong/target/product/test_device/root/avb"
+	expected := "out/target/product/test_device/root/avb"
 	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
@@ -386,7 +386,7 @@
 	`)
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
-	expected := "out/soong/target/product/test_device/system/usr/share/bar"
+	expected := "out/target/product/test_device/system/usr/share/bar"
 	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
@@ -401,7 +401,7 @@
 
 	buildOS := result.Config.BuildOS.String()
 	p := result.Module("foo.conf", buildOS+"_common").(*PrebuiltEtc)
-	expected := filepath.Join("out/soong/host", result.Config.PrebuiltOS(), "usr", "share", "bar")
+	expected := filepath.Join("out/host", result.Config.PrebuiltOS(), "usr", "share", "bar")
 	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
@@ -415,7 +415,7 @@
 	`)
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
-	expected := "out/soong/target/product/test_device/system/usr/hyphen-data/bar"
+	expected := "out/target/product/test_device/system/usr/hyphen-data/bar"
 	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
@@ -429,7 +429,7 @@
 	`)
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
-	expected := "out/soong/target/product/test_device/system/usr/keylayout/bar"
+	expected := "out/target/product/test_device/system/usr/keylayout/bar"
 	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
@@ -443,7 +443,7 @@
 	`)
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
-	expected := "out/soong/target/product/test_device/system/usr/keychars/bar"
+	expected := "out/target/product/test_device/system/usr/keychars/bar"
 	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
@@ -457,7 +457,7 @@
 	`)
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
-	expected := "out/soong/target/product/test_device/system/usr/idc/bar"
+	expected := "out/target/product/test_device/system/usr/idc/bar"
 	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
@@ -470,7 +470,7 @@
 	`)
 
 	p := result.Module("foo.conf", "android_common").(*PrebuiltEtc)
-	expected := "out/soong/target/product/test_device/system/fonts"
+	expected := "out/target/product/test_device/system/fonts"
 	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
@@ -483,12 +483,12 @@
 	`)
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
-	expected := "out/soong/target/product/test_device/system/overlay"
+	expected := "out/target/product/test_device/system/overlay"
 	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltFirmwareDirPath(t *testing.T) {
-	targetPath := "out/soong/target/product/test_device"
+	targetPath := "out/target/product/test_device"
 	tests := []struct {
 		description  string
 		config       string
@@ -522,7 +522,7 @@
 }
 
 func TestPrebuiltDSPDirPath(t *testing.T) {
-	targetPath := "out/soong/target/product/test_device"
+	targetPath := "out/target/product/test_device"
 	tests := []struct {
 		description  string
 		config       string
@@ -556,7 +556,7 @@
 }
 
 func TestPrebuiltRFSADirPath(t *testing.T) {
-	targetPath := "out/soong/target/product/test_device"
+	targetPath := "out/target/product/test_device"
 	tests := []struct {
 		description  string
 		config       string
@@ -600,6 +600,6 @@
 	`)
 
 	p := result.Module("foo", "android_common").(*PrebuiltEtc)
-	expected := "out/soong/target/product/test_device/product/media/alarms"
+	expected := "out/target/product/test_device/product/media/alarms"
 	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
diff --git a/filesystem/Android.bp b/filesystem/Android.bp
index 127faa7..cb76df2 100644
--- a/filesystem/Android.bp
+++ b/filesystem/Android.bp
@@ -17,6 +17,7 @@
     srcs: [
         "aconfig_files.go",
         "android_device.go",
+        "android_device_product_out.go",
         "avb_add_hash_footer.go",
         "avb_gen_vbmeta_image.go",
         "bootimg.go",
@@ -27,6 +28,7 @@
         "raw_binary.go",
         "super_image.go",
         "system_image.go",
+        "system_other.go",
         "vbmeta.go",
         "testing.go",
     ],
diff --git a/filesystem/aconfig_files.go b/filesystem/aconfig_files.go
index c80ae03..20a1953 100644
--- a/filesystem/aconfig_files.go
+++ b/filesystem/aconfig_files.go
@@ -16,50 +16,127 @@
 
 import (
 	"android/soong/android"
+	"strconv"
+	"strings"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
-func (f *filesystem) buildAconfigFlagsFiles(ctx android.ModuleContext, builder *android.RuleBuilder, specs map[string]android.PackagingSpec, dir android.OutputPath) {
+func init() {
+	pctx.HostBinToolVariable("aconfig", "aconfig")
+}
+
+var (
+	aconfigCreateStorage = pctx.AndroidStaticRule("aconfig_create_storage", blueprint.RuleParams{
+		Command:     `$aconfig create-storage --container $container --file $fileType --out $out --cache $in --version $version`,
+		CommandDeps: []string{"$aconfig"},
+	}, "container", "fileType", "version")
+
+	subPartitionsInPartition = map[string][]string{
+		"system": {"system_ext", "product", "vendor"},
+		"vendor": {"odm"},
+	}
+)
+
+func (f *filesystem) buildAconfigFlagsFiles(
+	ctx android.ModuleContext,
+	builder *android.RuleBuilder,
+	specs map[string]android.PackagingSpec,
+	dir android.OutputPath,
+	fullInstallPaths *[]FullInstallPathInfo,
+) {
 	if !proptools.Bool(f.properties.Gen_aconfig_flags_pb) {
 		return
 	}
 
+	partition := f.PartitionType()
+	subPartitionsFound := map[string]bool{}
+	fullInstallPath := android.PathForModuleInPartitionInstall(ctx, partition)
+
+	for _, subPartition := range subPartitionsInPartition[partition] {
+		subPartitionsFound[subPartition] = false
+	}
+
 	var caches []android.Path
 	for _, ps := range specs {
 		caches = append(caches, ps.GetAconfigPaths()...)
+		for subPartition, found := range subPartitionsFound {
+			if !found && strings.HasPrefix(ps.RelPathInPackage(), subPartition+"/") {
+				subPartitionsFound[subPartition] = true
+				break
+			}
+		}
 	}
 	caches = android.SortedUniquePaths(caches)
 
-	installAconfigFlagsPath := dir.Join(ctx, "etc", "aconfig_flags.pb")
-	cmd := builder.Command().
-		BuiltTool("aconfig").
-		Text(" dump-cache --dedup --format protobuf --out").
-		Output(installAconfigFlagsPath).
-		Textf("--filter container:%s", f.PartitionType())
-	for _, cache := range caches {
-		cmd.FlagWithInput("--cache ", cache)
-	}
-	f.appendToEntry(ctx, installAconfigFlagsPath)
-
-	installAconfigStorageDir := dir.Join(ctx, "etc", "aconfig")
-	builder.Command().Text("mkdir -p").Text(installAconfigStorageDir.String())
-
-	generatePartitionAconfigStorageFile := func(fileType, fileName string) {
-		outputPath := installAconfigStorageDir.Join(ctx, fileName)
-		builder.Command().
+	buildAconfigFlagsFiles := func(container string, dir android.OutputPath, fullInstallPath android.InstallPath) {
+		aconfigFlagsPb := android.PathForModuleOut(ctx, "aconfig", container, "aconfig_flags.pb")
+		aconfigFlagsPbBuilder := android.NewRuleBuilder(pctx, ctx)
+		cmd := aconfigFlagsPbBuilder.Command().
 			BuiltTool("aconfig").
-			FlagWithArg("create-storage --container ", f.PartitionType()).
-			FlagWithArg("--file ", fileType).
-			FlagWithOutput("--out ", outputPath).
-			FlagWithArg("--cache ", installAconfigFlagsPath.String())
-		f.appendToEntry(ctx, outputPath)
+			Text(" dump-cache --dedup --format protobuf --out").
+			Output(aconfigFlagsPb).
+			Textf("--filter container:%s+state:ENABLED", container).
+			Textf("--filter container:%s+permission:READ_WRITE", container)
+		for _, cache := range caches {
+			cmd.FlagWithInput("--cache ", cache)
+		}
+		aconfigFlagsPbBuilder.Build(container+"_aconfig_flags_pb", "build aconfig_flags.pb")
+
+		installEtcDir := dir.Join(ctx, "etc")
+		installAconfigFlagsPath := installEtcDir.Join(ctx, "aconfig_flags.pb")
+		builder.Command().Text("mkdir -p ").Text(installEtcDir.String())
+		builder.Command().Text("cp").Input(aconfigFlagsPb).Text(installAconfigFlagsPath.String())
+		*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+			FullInstallPath: fullInstallPath.Join(ctx, "etc/aconfig_flags.pb"),
+			SourcePath:      aconfigFlagsPb,
+		})
+		f.appendToEntry(ctx, installAconfigFlagsPath)
+
+		// To enable fingerprint, we need to have v2 storage files. The default version is 1.
+		storageFilesVersion := 1
+		if ctx.Config().ReleaseFingerprintAconfigPackages() {
+			storageFilesVersion = 2
+		}
+
+		installAconfigStorageDir := installEtcDir.Join(ctx, "aconfig")
+		builder.Command().Text("mkdir -p").Text(installAconfigStorageDir.String())
+
+		generatePartitionAconfigStorageFile := func(fileType, fileName string) {
+			outPath := android.PathForModuleOut(ctx, "aconfig", container, fileName)
+			installPath := installAconfigStorageDir.Join(ctx, fileName)
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   aconfigCreateStorage,
+				Input:  aconfigFlagsPb,
+				Output: outPath,
+				Args: map[string]string{
+					"container": container,
+					"fileType":  fileType,
+					"version":   strconv.Itoa(storageFilesVersion),
+				},
+			})
+			builder.Command().
+				Text("cp").Input(outPath).Text(installPath.String())
+			*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+				SourcePath:      outPath,
+				FullInstallPath: fullInstallPath.Join(ctx, "etc/aconfig", fileName),
+			})
+			f.appendToEntry(ctx, installPath)
+		}
+
+		if ctx.Config().ReleaseCreateAconfigStorageFile() {
+			generatePartitionAconfigStorageFile("package_map", "package.map")
+			generatePartitionAconfigStorageFile("flag_map", "flag.map")
+			generatePartitionAconfigStorageFile("flag_val", "flag.val")
+			generatePartitionAconfigStorageFile("flag_info", "flag.info")
+		}
 	}
 
-	if ctx.Config().ReleaseCreateAconfigStorageFile() {
-		generatePartitionAconfigStorageFile("package_map", "package.map")
-		generatePartitionAconfigStorageFile("flag_map", "flag.map")
-		generatePartitionAconfigStorageFile("flag_val", "flag.val")
-		generatePartitionAconfigStorageFile("flag_info", "flag.info")
+	buildAconfigFlagsFiles(partition, dir, fullInstallPath)
+	for _, subPartition := range android.SortedKeys(subPartitionsFound) {
+		if subPartitionsFound[subPartition] {
+			buildAconfigFlagsFiles(subPartition, dir.Join(ctx, subPartition), fullInstallPath.Join(ctx, subPartition))
+		}
 	}
 }
diff --git a/filesystem/android_device.go b/filesystem/android_device.go
index ab1b96e..8b6ea49 100644
--- a/filesystem/android_device.go
+++ b/filesystem/android_device.go
@@ -15,49 +15,138 @@
 package filesystem
 
 import (
+	"cmp"
+	"fmt"
+	"path/filepath"
+	"slices"
+	"sort"
+	"strings"
+	"sync/atomic"
+
 	"android/soong/android"
+	"android/soong/java"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
+var proguardDictToProto = pctx.AndroidStaticRule("proguard_dict_to_proto", blueprint.RuleParams{
+	Command:     `${symbols_map} -r8 $in -location $location -write_if_changed $out`,
+	Restat:      true,
+	CommandDeps: []string{"${symbols_map}"},
+}, "location")
+
 type PartitionNameProperties struct {
-	// Name of the Boot_partition_name partition filesystem module
+	// Name of the super partition filesystem module
+	Super_partition_name *string
+	// Name of the boot partition filesystem module
 	Boot_partition_name *string
-	// Name of the System partition filesystem module
+	// Name of the vendor boot partition filesystem module
+	Vendor_boot_partition_name *string
+	// Name of the init boot partition filesystem module
+	Init_boot_partition_name *string
+	// Name of the system partition filesystem module
 	System_partition_name *string
-	// Name of the System_ext partition filesystem module
+	// Name of the system_ext partition filesystem module
 	System_ext_partition_name *string
-	// Name of the Product partition filesystem module
+	// Name of the product partition filesystem module
 	Product_partition_name *string
-	// Name of the Vendor partition filesystem module
+	// Name of the vendor partition filesystem module
 	Vendor_partition_name *string
-	// Name of the Odm partition filesystem module
+	// Name of the odm partition filesystem module
 	Odm_partition_name *string
+	// Name of the recovery partition filesystem module
+	Recovery_partition_name *string
 	// The vbmeta partition and its "chained" partitions
 	Vbmeta_partitions []string
-	// Name of the Userdata partition filesystem module
+	// Name of the userdata partition filesystem module
 	Userdata_partition_name *string
+	// Name of the system_dlkm partition filesystem module
+	System_dlkm_partition_name *string
+	// Name of the vendor_dlkm partition filesystem module
+	Vendor_dlkm_partition_name *string
+	// Name of the odm_dlkm partition filesystem module
+	Odm_dlkm_partition_name *string
+}
+
+type DeviceProperties struct {
+	// Path to the prebuilt bootloader that would be copied to PRODUCT_OUT
+	Bootloader *string `android:"path"`
+	// Path to android-info.txt file containing board specific info.
+	Android_info *string `android:"path"`
+	// If this is the "main" android_device target for the build, i.e. the one that gets built
+	// when running a plain `m` command. Currently, this is the autogenerated android_device module
+	// in soong-only builds, but in the future when we check in android_device modules, the main
+	// one will be determined based on the lunch product. TODO: Figure out how to make this
+	// blueprint:"mutated" and still set it from filesystem_creator
+	Main_device *bool
+
+	Ab_ota_updater            *bool
+	Ab_ota_partitions         []string
+	Ab_ota_keys               []string
+	Ab_ota_postinstall_config []string
+
+	Ramdisk_node_list      *string `android:"path"`
+	Releasetools_extension *string `android:"path"`
+	FastbootInfo           *string `android:"path"`
+
+	Partial_ota_update_partitions []string
+	Flash_block_size              *string
+	Bootloader_in_update_package  *bool
+
+	// The kernel version in the build. Will be verified against the actual kernel.
+	// If not provided, will attempt to extract it from the loose kernel or the kernel inside
+	// the boot image. The version is later used to decide whether or not to enable uffd_gc
+	// when dexpreopting apps. So setting this doesn't really do anything except enforce that the
+	// actual kernel version is as specified here.
+	Kernel_version *string
 }
 
 type androidDevice struct {
 	android.ModuleBase
 
 	partitionProps PartitionNameProperties
+
+	deviceProps DeviceProperties
+
+	allImagesZip android.Path
+
+	proguardDictZip             android.Path
+	proguardDictMapping         android.Path
+	proguardUsageZip            android.Path
+	kernelConfig                android.Path
+	kernelVersion               android.Path
+	miscInfo                    android.Path
+	rootDirForFsConfig          string
+	rootDirForFsConfigTimestamp android.Path
+	apkCertsInfo                android.Path
+	targetFilesZip              android.Path
+	updatePackage               android.Path
 }
 
 func AndroidDeviceFactory() android.Module {
 	module := &androidDevice{}
-	module.AddProperties(&module.partitionProps)
-	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	module.AddProperties(&module.partitionProps, &module.deviceProps)
+	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibFirst)
 	return module
 }
 
+var numMainAndroidDevicesOnceKey android.OnceKey = android.NewOnceKey("num_auto_generated_anroid_devices")
+
 type partitionDepTagType struct {
 	blueprint.BaseDependencyTag
 }
 
+type superPartitionDepTagType struct {
+	blueprint.BaseDependencyTag
+}
+type targetFilesMetadataDepTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var superPartitionDepTag superPartitionDepTagType
 var filesystemDepTag partitionDepTagType
+var targetFilesMetadataDepTag targetFilesMetadataDepTagType
 
 func (a *androidDevice) DepsMutator(ctx android.BottomUpMutatorContext) {
 	addDependencyIfDefined := func(dep *string) {
@@ -66,18 +155,1020 @@
 		}
 	}
 
+	if a.partitionProps.Super_partition_name != nil {
+		ctx.AddDependency(ctx.Module(), superPartitionDepTag, *a.partitionProps.Super_partition_name)
+	}
 	addDependencyIfDefined(a.partitionProps.Boot_partition_name)
+	addDependencyIfDefined(a.partitionProps.Init_boot_partition_name)
+	addDependencyIfDefined(a.partitionProps.Vendor_boot_partition_name)
 	addDependencyIfDefined(a.partitionProps.System_partition_name)
 	addDependencyIfDefined(a.partitionProps.System_ext_partition_name)
 	addDependencyIfDefined(a.partitionProps.Product_partition_name)
 	addDependencyIfDefined(a.partitionProps.Vendor_partition_name)
 	addDependencyIfDefined(a.partitionProps.Odm_partition_name)
 	addDependencyIfDefined(a.partitionProps.Userdata_partition_name)
+	addDependencyIfDefined(a.partitionProps.System_dlkm_partition_name)
+	addDependencyIfDefined(a.partitionProps.Vendor_dlkm_partition_name)
+	addDependencyIfDefined(a.partitionProps.Odm_dlkm_partition_name)
+	addDependencyIfDefined(a.partitionProps.Recovery_partition_name)
 	for _, vbmetaPartition := range a.partitionProps.Vbmeta_partitions {
 		ctx.AddDependency(ctx.Module(), filesystemDepTag, vbmetaPartition)
 	}
+	a.addDepsForTargetFilesMetadata(ctx)
+}
+
+func (a *androidDevice) addDepsForTargetFilesMetadata(ctx android.BottomUpMutatorContext) {
+	ctx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), targetFilesMetadataDepTag, "liblz4") // host variant
 }
 
 func (a *androidDevice) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if proptools.Bool(a.deviceProps.Main_device) {
+		numMainAndroidDevices := ctx.Config().Once(numMainAndroidDevicesOnceKey, func() interface{} {
+			return &atomic.Int32{}
+		}).(*atomic.Int32)
+		total := numMainAndroidDevices.Add(1)
+		if total > 1 {
+			// There should only be 1 main android_device module. That one will be
+			// made the default thing to build in soong-only builds.
+			ctx.ModuleErrorf("There cannot be more than 1 main android_device module")
+		}
+	}
 
+	allInstalledModules := a.allInstalledModules(ctx)
+
+	a.apkCertsInfo = a.buildApkCertsInfo(ctx, allInstalledModules)
+	a.kernelVersion, a.kernelConfig = a.extractKernelVersionAndConfigs(ctx)
+	a.miscInfo = a.addMiscInfo(ctx)
+	a.buildTargetFilesZip(ctx, allInstalledModules)
+	a.buildProguardZips(ctx, allInstalledModules)
+	a.buildUpdatePackage(ctx)
+
+	var deps []android.Path
+	if proptools.String(a.partitionProps.Super_partition_name) != "" {
+		superImage := ctx.GetDirectDepProxyWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag)
+		if info, ok := android.OtherModuleProvider(ctx, superImage, SuperImageProvider); ok {
+			assertUnset := func(prop *string, propName string) {
+				if prop != nil && *prop != "" {
+					ctx.PropertyErrorf(propName, "Cannot be set because it's already part of the super image")
+				}
+			}
+			for _, subPartitionType := range android.SortedKeys(info.SubImageInfo) {
+				switch subPartitionType {
+				case "system":
+					assertUnset(a.partitionProps.System_partition_name, "system_partition_name")
+				case "system_ext":
+					assertUnset(a.partitionProps.System_ext_partition_name, "system_ext_partition_name")
+				case "system_dlkm":
+					assertUnset(a.partitionProps.System_dlkm_partition_name, "system_dlkm_partition_name")
+				case "system_other":
+					// TODO
+				case "product":
+					assertUnset(a.partitionProps.Product_partition_name, "product_partition_name")
+				case "vendor":
+					assertUnset(a.partitionProps.Vendor_partition_name, "vendor_partition_name")
+				case "vendor_dlkm":
+					assertUnset(a.partitionProps.Vendor_dlkm_partition_name, "vendor_dlkm_partition_name")
+				case "odm":
+					assertUnset(a.partitionProps.Odm_partition_name, "odm_partition_name")
+				case "odm_dlkm":
+					assertUnset(a.partitionProps.Odm_dlkm_partition_name, "odm_dlkm_partition_name")
+				default:
+					ctx.ModuleErrorf("Unsupported sub-partition of super partition: %q", subPartitionType)
+				}
+			}
+
+			deps = append(deps, info.SuperImage)
+		} else {
+			ctx.ModuleErrorf("Expected super image dep to provide SuperImageProvider")
+		}
+	}
+	ctx.VisitDirectDepsProxyWithTag(filesystemDepTag, func(m android.ModuleProxy) {
+		imageOutput, ok := android.OtherModuleProvider(ctx, m, android.OutputFilesProvider)
+		if !ok {
+			ctx.ModuleErrorf("Partition module %s doesn't set OutputfilesProvider", m.Name())
+		}
+		if len(imageOutput.DefaultOutputFiles) != 1 {
+			ctx.ModuleErrorf("Partition module %s should provide exact 1 output file", m.Name())
+		}
+		deps = append(deps, imageOutput.DefaultOutputFiles[0])
+	})
+
+	allImagesZip := android.PathForModuleOut(ctx, "all_images.zip")
+	allImagesZipBuilder := android.NewRuleBuilder(pctx, ctx)
+	cmd := allImagesZipBuilder.Command().BuiltTool("soong_zip")
+	for _, dep := range deps {
+		cmd.FlagWithArg("-e ", dep.Base())
+		cmd.FlagWithInput("-f ", dep)
+	}
+	cmd.FlagWithOutput("-o ", allImagesZip)
+	allImagesZipBuilder.Build("soong_all_images_zip", "all_images.zip")
+	a.allImagesZip = allImagesZip
+
+	allImagesStamp := android.PathForModuleOut(ctx, "all_images_stamp")
+	var validations android.Paths
+	if !ctx.Config().KatiEnabled() && proptools.Bool(a.deviceProps.Main_device) {
+		// In soong-only builds, build this module by default.
+		// This is the analogue to this make code:
+		// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/main.mk;l=1396;drc=6595459cdd8164a6008335f6372c9f97b9094060
+		ctx.Phony("droidcore-unbundled", allImagesStamp)
+
+		deps = append(deps, a.copyFilesToProductOutForSoongOnly(ctx))
+	}
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.Touch,
+		Output:      allImagesStamp,
+		Implicits:   deps,
+		Validations: validations,
+	})
+
+	// Checkbuilding it causes soong to make a phony, so you can say `m <module name>`
+	ctx.CheckbuildFile(allImagesStamp)
+
+	a.setVbmetaPhonyTargets(ctx)
+
+	a.distFiles(ctx)
+
+	android.SetProvider(ctx, android.AndroidDeviceInfoProvider, android.AndroidDeviceInfo{
+		Main_device: android.Bool(a.deviceProps.Main_device),
+	})
+
+	if proptools.String(a.partitionProps.Super_partition_name) != "" {
+		buildComplianceMetadata(ctx, superPartitionDepTag, filesystemDepTag)
+	} else {
+		buildComplianceMetadata(ctx, filesystemDepTag)
+	}
+}
+
+func buildComplianceMetadata(ctx android.ModuleContext, tags ...blueprint.DependencyTag) {
+	// Collect metadata from deps
+	filesContained := make([]string, 0)
+	prebuiltFilesCopied := make([]string, 0)
+	for _, tag := range tags {
+		ctx.VisitDirectDepsProxyWithTag(tag, func(m android.ModuleProxy) {
+			if complianceMetadataInfo, ok := android.OtherModuleProvider(ctx, m, android.ComplianceMetadataProvider); ok {
+				filesContained = append(filesContained, complianceMetadataInfo.GetFilesContained()...)
+				prebuiltFilesCopied = append(prebuiltFilesCopied, complianceMetadataInfo.GetPrebuiltFilesCopied()...)
+			}
+		})
+	}
+	// Merge to module's ComplianceMetadataInfo
+	complianceMetadataInfo := ctx.ComplianceMetadataInfo()
+	filesContained = append(filesContained, complianceMetadataInfo.GetFilesContained()...)
+	sort.Strings(filesContained)
+	complianceMetadataInfo.SetFilesContained(filesContained)
+
+	prebuiltFilesCopied = append(prebuiltFilesCopied, complianceMetadataInfo.GetPrebuiltFilesCopied()...)
+	sort.Strings(prebuiltFilesCopied)
+	complianceMetadataInfo.SetPrebuiltFilesCopied(prebuiltFilesCopied)
+}
+
+// Returns a list of modules that are installed, which are collected from the dependency
+// filesystem and super_image modules.
+func (a *androidDevice) allInstalledModules(ctx android.ModuleContext) []android.Module {
+	fsInfoMap := a.getFsInfos(ctx)
+	allOwners := make(map[string][]string)
+	for _, partition := range android.SortedKeys(fsInfoMap) {
+		fsInfo := fsInfoMap[partition]
+		for _, owner := range fsInfo.Owners {
+			allOwners[owner.Name] = append(allOwners[owner.Name], owner.Variation)
+		}
+	}
+
+	ret := []android.Module{}
+	ctx.WalkDepsProxy(func(mod, _ android.ModuleProxy) bool {
+		if variations, ok := allOwners[ctx.OtherModuleName(mod)]; ok && android.InList(ctx.OtherModuleSubDir(mod), variations) {
+			ret = append(ret, mod)
+		}
+		return true
+	})
+
+	// Remove duplicates
+	ret = android.FirstUniqueFunc(ret, func(a, b android.Module) bool {
+		return a.String() == b.String()
+	})
+
+	// Sort the modules by their names and variants
+	slices.SortFunc(ret, func(a, b android.Module) int {
+		return cmp.Compare(a.String(), b.String())
+	})
+	return ret
+}
+
+func insertBeforeExtension(file, insertion string) string {
+	ext := filepath.Ext(file)
+	return strings.TrimSuffix(file, ext) + insertion + ext
+}
+
+func (a *androidDevice) distInstalledFiles(ctx android.ModuleContext) {
+	distInstalledFilesJsonAndTxt := func(installedFiles InstalledFilesStruct) {
+		if installedFiles.Json != nil {
+			ctx.DistForGoal("droidcore-unbundled", installedFiles.Json)
+		}
+		if installedFiles.Txt != nil {
+			ctx.DistForGoal("droidcore-unbundled", installedFiles.Txt)
+		}
+	}
+
+	fsInfoMap := a.getFsInfos(ctx)
+	for _, partition := range android.SortedKeys(fsInfoMap) {
+		// installed-files-*{.txt | .json} is not disted for userdata partition
+		if partition == "userdata" {
+			continue
+		}
+		fsInfo := fsInfoMap[partition]
+		for _, installedFiles := range fsInfo.InstalledFilesDepSet.ToList() {
+			distInstalledFilesJsonAndTxt(installedFiles)
+		}
+	}
+}
+
+func (a *androidDevice) distFiles(ctx android.ModuleContext) {
+	if !ctx.Config().KatiEnabled() && proptools.Bool(a.deviceProps.Main_device) {
+		a.distInstalledFiles(ctx)
+
+		namePrefix := ""
+		if ctx.Config().HasDeviceProduct() {
+			namePrefix = ctx.Config().DeviceProduct() + "-"
+		}
+		ctx.DistForGoalWithFilename("droidcore-unbundled", a.proguardDictZip, namePrefix+insertBeforeExtension(a.proguardDictZip.Base(), "-FILE_NAME_TAG_PLACEHOLDER"))
+		ctx.DistForGoalWithFilename("droidcore-unbundled", a.proguardDictMapping, namePrefix+insertBeforeExtension(a.proguardDictMapping.Base(), "-FILE_NAME_TAG_PLACEHOLDER"))
+		ctx.DistForGoalWithFilename("droidcore-unbundled", a.proguardUsageZip, namePrefix+insertBeforeExtension(a.proguardUsageZip.Base(), "-FILE_NAME_TAG_PLACEHOLDER"))
+
+		if a.deviceProps.Android_info != nil {
+			ctx.DistForGoal("droidcore-unbundled", android.PathForModuleSrc(ctx, *a.deviceProps.Android_info))
+		}
+		if a.miscInfo != nil {
+			ctx.DistForGoal("droidcore-unbundled", a.miscInfo)
+			if a.partitionProps.Super_partition_name != nil {
+				ctx.DistForGoalWithFilename("dist_files", a.miscInfo, "super_misc_info.txt")
+			}
+		}
+		if a.targetFilesZip != nil {
+			ctx.DistForGoalWithFilename("target-files-package", a.targetFilesZip, namePrefix+insertBeforeExtension(a.targetFilesZip.Base(), "-FILE_NAME_TAG_PLACEHOLDER"))
+		}
+		if a.updatePackage != nil {
+			ctx.DistForGoalWithFilename("updatepackage", a.updatePackage, namePrefix+insertBeforeExtension(a.updatePackage.Base(), "-FILE_NAME_TAG_PLACEHOLDER"))
+		}
+
+	}
+}
+
+func (a *androidDevice) MakeVars(_ android.MakeVarsModuleContext) []android.ModuleMakeVarsValue {
+	if proptools.Bool(a.deviceProps.Main_device) {
+		return []android.ModuleMakeVarsValue{{"SOONG_ONLY_ALL_IMAGES_ZIP", a.allImagesZip.String()}}
+	}
+	return nil
+}
+
+func (a *androidDevice) buildProguardZips(ctx android.ModuleContext, allInstalledModules []android.Module) {
+	dictZip := android.PathForModuleOut(ctx, "proguard-dict.zip")
+	dictZipBuilder := android.NewRuleBuilder(pctx, ctx)
+	dictZipCmd := dictZipBuilder.Command().BuiltTool("soong_zip").Flag("-d").FlagWithOutput("-o ", dictZip)
+
+	dictMapping := android.PathForModuleOut(ctx, "proguard-dict-mapping.textproto")
+	dictMappingBuilder := android.NewRuleBuilder(pctx, ctx)
+	dictMappingCmd := dictMappingBuilder.Command().BuiltTool("symbols_map").Flag("-merge").Output(dictMapping)
+
+	protosDir := android.PathForModuleOut(ctx, "proguard_mapping_protos")
+
+	usageZip := android.PathForModuleOut(ctx, "proguard-usage.zip")
+	usageZipBuilder := android.NewRuleBuilder(pctx, ctx)
+	usageZipCmd := usageZipBuilder.Command().BuiltTool("merge_zips").Output(usageZip)
+
+	for _, mod := range allInstalledModules {
+		if proguardInfo, ok := android.OtherModuleProvider(ctx, mod, java.ProguardProvider); ok {
+			// Maintain these out/target/common paths for backwards compatibility. They may be able
+			// to be changed if tools look up file locations from the protobuf, but I'm not
+			// exactly sure how that works.
+			dictionaryFakePath := fmt.Sprintf("out/target/common/obj/%s/%s_intermediates/proguard_dictionary", proguardInfo.Class, proguardInfo.ModuleName)
+			dictZipCmd.FlagWithArg("-e ", dictionaryFakePath)
+			dictZipCmd.FlagWithInput("-f ", proguardInfo.ProguardDictionary)
+			dictZipCmd.Textf("-e out/target/common/obj/%s/%s_intermediates/classes.jar", proguardInfo.Class, proguardInfo.ModuleName)
+			dictZipCmd.FlagWithInput("-f ", proguardInfo.ClassesJar)
+
+			protoFile := protosDir.Join(ctx, filepath.Dir(dictionaryFakePath), "proguard_dictionary.textproto")
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   proguardDictToProto,
+				Input:  proguardInfo.ProguardDictionary,
+				Output: protoFile,
+				Args: map[string]string{
+					"location": dictionaryFakePath,
+				},
+			})
+			dictMappingCmd.Input(protoFile)
+
+			usageZipCmd.Input(proguardInfo.ProguardUsageZip)
+		}
+	}
+
+	dictZipBuilder.Build("proguard_dict_zip", "Building proguard dictionary zip")
+	dictMappingBuilder.Build("proguard_dict_mapping_proto", "Building proguard mapping proto")
+	usageZipBuilder.Build("proguard_usage_zip", "Building proguard usage zip")
+
+	a.proguardDictZip = dictZip
+	a.proguardDictMapping = dictMapping
+	a.proguardUsageZip = usageZip
+}
+
+// Helper structs for target_files.zip creation
+type targetFilesZipCopy struct {
+	srcModule  *string
+	destSubdir string
+}
+
+type targetFilesystemZipCopy struct {
+	fsInfo     FilesystemInfo
+	destSubdir string
+}
+
+func (a *androidDevice) buildTargetFilesZip(ctx android.ModuleContext, allInstalledModules []android.Module) {
+	targetFilesDir := android.PathForModuleOut(ctx, "target_files_dir")
+	targetFilesZip := android.PathForModuleOut(ctx, "target_files.zip")
+
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().Textf("rm -rf %s", targetFilesDir.String())
+	builder.Command().Textf("mkdir -p %s", targetFilesDir.String())
+	toCopy := []targetFilesZipCopy{
+		targetFilesZipCopy{a.partitionProps.System_partition_name, "SYSTEM"},
+		targetFilesZipCopy{a.partitionProps.System_ext_partition_name, "SYSTEM_EXT"},
+		targetFilesZipCopy{a.partitionProps.Product_partition_name, "PRODUCT"},
+		targetFilesZipCopy{a.partitionProps.Vendor_partition_name, "VENDOR"},
+		targetFilesZipCopy{a.partitionProps.Odm_partition_name, "ODM"},
+		targetFilesZipCopy{a.partitionProps.System_dlkm_partition_name, "SYSTEM_DLKM"},
+		targetFilesZipCopy{a.partitionProps.Vendor_dlkm_partition_name, "VENDOR_DLKM"},
+		targetFilesZipCopy{a.partitionProps.Odm_dlkm_partition_name, "ODM_DLKM"},
+		targetFilesZipCopy{a.partitionProps.Init_boot_partition_name, "BOOT/RAMDISK"},
+		targetFilesZipCopy{a.partitionProps.Init_boot_partition_name, "INIT_BOOT/RAMDISK"},
+		targetFilesZipCopy{a.partitionProps.Vendor_boot_partition_name, "VENDOR_BOOT/RAMDISK"},
+	}
+
+	filesystemsToCopy := []targetFilesystemZipCopy{}
+	for _, zipCopy := range toCopy {
+		if zipCopy.srcModule == nil {
+			continue
+		}
+		filesystemsToCopy = append(
+			filesystemsToCopy,
+			targetFilesystemZipCopy{a.getFilesystemInfo(ctx, *zipCopy.srcModule), zipCopy.destSubdir},
+		)
+	}
+	// Get additional filesystems from super_partition dependency
+	if a.partitionProps.Super_partition_name != nil {
+		superPartition := ctx.GetDirectDepProxyWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag)
+		if info, ok := android.OtherModuleProvider(ctx, superPartition, SuperImageProvider); ok {
+			for _, partition := range android.SortedKeys(info.SubImageInfo) {
+				filesystemsToCopy = append(
+					filesystemsToCopy,
+					targetFilesystemZipCopy{info.SubImageInfo[partition], strings.ToUpper(partition)},
+				)
+			}
+		} else {
+			ctx.ModuleErrorf("Super partition %s does set SuperImageProvider\n", superPartition.Name())
+		}
+	}
+
+	for _, toCopy := range filesystemsToCopy {
+		rootDirString := toCopy.fsInfo.RootDir.String()
+		if toCopy.destSubdir == "SYSTEM" {
+			rootDirString = rootDirString + "/system"
+		}
+		builder.Command().Textf("mkdir -p %s/%s", targetFilesDir.String(), toCopy.destSubdir)
+		builder.Command().
+			BuiltTool("acp").
+			Textf("-rd %s/. %s/%s", rootDirString, targetFilesDir, toCopy.destSubdir).
+			Implicit(toCopy.fsInfo.Output) // so that the staging dir is built
+		for _, extraRootDir := range toCopy.fsInfo.ExtraRootDirs {
+			builder.Command().
+				BuiltTool("acp").
+				Textf("-rd %s/. %s/%s", extraRootDir, targetFilesDir, toCopy.destSubdir).
+				Implicit(toCopy.fsInfo.Output) // so that the staging dir is built
+		}
+
+		if toCopy.destSubdir == "SYSTEM" {
+			// Create the ROOT partition in target_files.zip
+			builder.Command().Textf("rsync --links --exclude=system/* %s/ -r %s/ROOT", toCopy.fsInfo.RootDir, targetFilesDir.String())
+			// Add a duplicate rule to assemble the ROOT/ directory in separate intermediates.
+			// The output timestamp will be an input to a separate fs_config call.
+			a.rootDirForFsConfig = android.PathForModuleOut(ctx, "root_dir_for_fs_config").String()
+			rootDirBuilder := android.NewRuleBuilder(pctx, ctx)
+			rootDirForFsConfigTimestamp := android.PathForModuleOut(ctx, "root_dir_for_fs_config.timestamp")
+			rootDirBuilder.Command().Textf("rsync --links --exclude=system/* %s/ -r %s", toCopy.fsInfo.RootDir, a.rootDirForFsConfig).
+				Implicit(toCopy.fsInfo.Output).
+				Text("&& touch").
+				Output(rootDirForFsConfigTimestamp)
+			rootDirBuilder.Build("assemble_root_dir_for_fs_config", "Assemble ROOT/ for fs_config")
+			a.rootDirForFsConfigTimestamp = rootDirForFsConfigTimestamp
+		}
+	}
+	// Copy cmdline, kernel etc. files of boot images
+	if a.partitionProps.Vendor_boot_partition_name != nil {
+		bootImg := ctx.GetDirectDepProxyWithTag(proptools.String(a.partitionProps.Vendor_boot_partition_name), filesystemDepTag)
+		bootImgInfo, _ := android.OtherModuleProvider(ctx, bootImg, BootimgInfoProvider)
+		builder.Command().Textf("echo %s > %s/VENDOR_BOOT/cmdline", proptools.ShellEscape(strings.Join(bootImgInfo.Cmdline, " ")), targetFilesDir)
+		builder.Command().Textf("echo %s > %s/VENDOR_BOOT/vendor_cmdline", proptools.ShellEscape(strings.Join(bootImgInfo.Cmdline, " ")), targetFilesDir)
+		if bootImgInfo.Dtb != nil {
+			builder.Command().Textf("cp ").Input(bootImgInfo.Dtb).Textf(" %s/VENDOR_BOOT/dtb", targetFilesDir)
+		}
+		if bootImgInfo.Bootconfig != nil {
+			builder.Command().Textf("cp ").Input(bootImgInfo.Bootconfig).Textf(" %s/VENDOR_BOOT/vendor_bootconfig", targetFilesDir)
+		}
+	}
+	if a.partitionProps.Boot_partition_name != nil {
+		bootImg := ctx.GetDirectDepProxyWithTag(proptools.String(a.partitionProps.Boot_partition_name), filesystemDepTag)
+		bootImgInfo, _ := android.OtherModuleProvider(ctx, bootImg, BootimgInfoProvider)
+		builder.Command().Textf("echo %s > %s/BOOT/cmdline", proptools.ShellEscape(strings.Join(bootImgInfo.Cmdline, " ")), targetFilesDir)
+		if bootImgInfo.Dtb != nil {
+			builder.Command().Textf("cp ").Input(bootImgInfo.Dtb).Textf(" %s/BOOT/dtb", targetFilesDir)
+		}
+		if bootImgInfo.Kernel != nil {
+			builder.Command().Textf("cp ").Input(bootImgInfo.Kernel).Textf(" %s/BOOT/kernel", targetFilesDir)
+			// Even though kernel is not used to build vendor_boot, copy the kernel to VENDOR_BOOT to match the behavior of make packaging.
+			builder.Command().Textf("cp ").Input(bootImgInfo.Kernel).Textf(" %s/VENDOR_BOOT/kernel", targetFilesDir)
+		}
+		if bootImgInfo.Bootconfig != nil {
+			builder.Command().Textf("cp ").Input(bootImgInfo.Bootconfig).Textf(" %s/BOOT/bootconfig", targetFilesDir)
+		}
+	}
+
+	if a.deviceProps.Android_info != nil {
+		builder.Command().Textf("mkdir -p %s/OTA", targetFilesDir)
+		builder.Command().Textf("cp ").Input(android.PathForModuleSrc(ctx, *a.deviceProps.Android_info)).Textf(" %s/OTA/android-info.txt", targetFilesDir)
+	}
+
+	a.copyImagesToTargetZip(ctx, builder, targetFilesDir)
+	a.copyMetadataToTargetZip(ctx, builder, targetFilesDir, allInstalledModules)
+
+	a.targetFilesZip = targetFilesZip
+	builder.Command().
+		BuiltTool("soong_zip").
+		Text("-d").
+		FlagWithOutput("-o ", targetFilesZip).
+		FlagWithArg("-C ", targetFilesDir.String()).
+		FlagWithArg("-D ", targetFilesDir.String()).
+		Text("-sha256")
+	builder.Build("target_files_"+ctx.ModuleName(), "Build target_files.zip")
+}
+
+func (a *androidDevice) copyImagesToTargetZip(ctx android.ModuleContext, builder *android.RuleBuilder, targetFilesDir android.WritablePath) {
+	// Create an IMAGES/ subdirectory
+	builder.Command().Textf("mkdir -p %s/IMAGES", targetFilesDir.String())
+	if a.deviceProps.Bootloader != nil {
+		builder.Command().Textf("cp ").Input(android.PathForModuleSrc(ctx, proptools.String(a.deviceProps.Bootloader))).Textf(" %s/IMAGES/bootloader", targetFilesDir.String())
+	}
+	// Copy the filesystem ,boot and vbmeta img files to IMAGES/
+	ctx.VisitDirectDepsProxyWithTag(filesystemDepTag, func(child android.ModuleProxy) {
+		if strings.Contains(child.Name(), "recovery") {
+			return // skip recovery.img to match the make packaging behavior
+		}
+		if info, ok := android.OtherModuleProvider(ctx, child, BootimgInfoProvider); ok {
+			// Check Boot img first so that the boot.img is copied and not its dep ramdisk.img
+			builder.Command().Textf("cp ").Input(info.Output).Textf(" %s/IMAGES/", targetFilesDir.String())
+		} else if info, ok := android.OtherModuleProvider(ctx, child, FilesystemProvider); ok {
+			builder.Command().Textf("cp ").Input(info.Output).Textf(" %s/IMAGES/", targetFilesDir.String())
+		} else if info, ok := android.OtherModuleProvider(ctx, child, vbmetaPartitionProvider); ok {
+			builder.Command().Textf("cp ").Input(info.Output).Textf(" %s/IMAGES/", targetFilesDir.String())
+		} else {
+			ctx.ModuleErrorf("Module %s does not provide an .img file output for target_files.zip", child.Name())
+		}
+	})
+
+	if a.partitionProps.Super_partition_name != nil {
+		superPartition := ctx.GetDirectDepProxyWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag)
+		if info, ok := android.OtherModuleProvider(ctx, superPartition, SuperImageProvider); ok {
+			for _, partition := range android.SortedKeys(info.SubImageInfo) {
+				if info.SubImageInfo[partition].OutputHermetic != nil {
+					builder.Command().Textf("cp ").Input(info.SubImageInfo[partition].OutputHermetic).Textf(" %s/IMAGES/", targetFilesDir.String())
+				}
+				if info.SubImageInfo[partition].MapFile != nil {
+					builder.Command().Textf("cp ").Input(info.SubImageInfo[partition].MapFile).Textf(" %s/IMAGES/", targetFilesDir.String())
+				}
+			}
+			// super_empty.img
+			if info.SuperEmptyImage != nil {
+				builder.Command().Textf("cp ").Input(info.SuperEmptyImage).Textf(" %s/IMAGES/", targetFilesDir.String())
+			}
+		} else {
+			ctx.ModuleErrorf("Super partition %s does set SuperImageProvider\n", superPartition.Name())
+		}
+	}
+}
+
+func (a *androidDevice) copyMetadataToTargetZip(ctx android.ModuleContext, builder *android.RuleBuilder, targetFilesDir android.WritablePath, allInstalledModules []android.Module) {
+	// Create a META/ subdirectory
+	builder.Command().Textf("mkdir -p %s/META", targetFilesDir.String())
+	if proptools.Bool(a.deviceProps.Ab_ota_updater) {
+		ctx.VisitDirectDepsProxyWithTag(targetFilesMetadataDepTag, func(child android.ModuleProxy) {
+			info, _ := android.OtherModuleProvider(ctx, child, android.OutputFilesProvider)
+			builder.Command().Textf("cp").Inputs(info.DefaultOutputFiles).Textf(" %s/META/", targetFilesDir.String())
+		})
+		builder.Command().Textf("cp").Input(android.PathForSource(ctx, "external/zucchini/version_info.h")).Textf(" %s/META/zucchini_config.txt", targetFilesDir.String())
+		builder.Command().Textf("cp").Input(android.PathForSource(ctx, "system/update_engine/update_engine.conf")).Textf(" %s/META/update_engine_config.txt", targetFilesDir.String())
+		if a.getFsInfos(ctx)["system"].ErofsCompressHints != nil {
+			builder.Command().Textf("cp").Input(a.getFsInfos(ctx)["system"].ErofsCompressHints).Textf(" %s/META/erofs_default_compress_hints.txt", targetFilesDir.String())
+		}
+		// ab_partitions.txt
+		abPartitionsSorted := android.SortedUniqueStrings(a.deviceProps.Ab_ota_partitions)
+		abPartitionsSortedString := proptools.ShellEscape(strings.Join(abPartitionsSorted, "\\n"))
+		builder.Command().Textf("echo -e").Flag(abPartitionsSortedString).Textf(" > %s/META/ab_partitions.txt", targetFilesDir.String())
+		// otakeys.txt
+		abOtaKeysSorted := android.SortedUniqueStrings(a.deviceProps.Ab_ota_keys)
+		abOtaKeysSortedString := proptools.ShellEscape(strings.Join(abOtaKeysSorted, "\\n"))
+		builder.Command().Textf("echo -e").Flag(abOtaKeysSortedString).Textf(" > %s/META/otakeys.txt", targetFilesDir.String())
+		// postinstall_config.txt
+		abOtaPostInstallConfigString := proptools.ShellEscape(strings.Join(a.deviceProps.Ab_ota_postinstall_config, "\\n"))
+		builder.Command().Textf("echo -e").Flag(abOtaPostInstallConfigString).Textf(" > %s/META/postinstall_config.txt", targetFilesDir.String())
+		// selinuxfc
+		if a.getFsInfos(ctx)["system"].SelinuxFc != nil {
+			builder.Command().Textf("cp").Input(a.getFsInfos(ctx)["system"].SelinuxFc).Textf(" %s/META/file_contexts.bin", targetFilesDir.String())
+		}
+	}
+	// Copy $partition_filesystem_config.txt
+	fsInfos := a.getFsInfos(ctx)
+	for _, partition := range android.SortedKeys(fsInfos) {
+		if fsInfos[partition].FilesystemConfig == nil {
+			continue
+		}
+		if android.InList(partition, []string{"userdata"}) {
+			continue
+		}
+		if partition != "vendor_ramdisk" {
+			// vendor_ramdisk will be handled separately.
+			builder.Command().Textf("cp").Input(fsInfos[partition].FilesystemConfig).Textf(" %s/META/%s", targetFilesDir.String(), a.filesystemConfigNameForTargetFiles(partition))
+		}
+		if partition == "ramdisk" {
+			// Create an additional copy at boot_filesystem_config.txt
+			builder.Command().Textf("cp").Input(fsInfos[partition].FilesystemConfig).Textf(" %s/META/boot_filesystem_config.txt", targetFilesDir.String())
+		}
+		if partition == "system" {
+			// Create root_filesystem_config from the assembled ROOT/ intermediates directory
+			a.generateFilesystemConfigForTargetFiles(ctx, builder, a.rootDirForFsConfigTimestamp, targetFilesDir.String(), a.rootDirForFsConfig, "root_filesystem_config.txt")
+		}
+		if partition == "vendor_ramdisk" {
+			// Create vendor_boot_filesystem_config from the assembled VENDOR_BOOT/RAMDISK intermediates directory
+			vendorRamdiskStagingDir := targetFilesDir.String() + "/VENDOR_BOOT/RAMDISK"
+			vendorRamdiskFsConfigOut := targetFilesDir.String() + "/META/vendor_boot_filesystem_config.txt"
+			fsConfigBin := ctx.Config().HostToolPath(ctx, "fs_config")
+			builder.Command().Textf(
+				`(cd %s; find . -type d | sed 's,$,/,'; find . \! -type d) | cut -c 3- | sort | sed 's,^,,' | %s -C -D %s -R \"\" > %s`,
+				vendorRamdiskStagingDir, fsConfigBin, vendorRamdiskStagingDir, vendorRamdiskFsConfigOut).
+				Implicit(fsConfigBin)
+		}
+	}
+	// Copy ramdisk_node_list
+	if ramdiskNodeList := android.PathForModuleSrc(ctx, proptools.String(a.deviceProps.Ramdisk_node_list)); ramdiskNodeList != nil {
+		builder.Command().Textf("cp").Input(ramdiskNodeList).Textf(" %s/META/", targetFilesDir.String())
+	}
+	// Copy releasetools.py
+	if releaseTools := android.PathForModuleSrc(ctx, proptools.String(a.deviceProps.Releasetools_extension)); releaseTools != nil {
+		builder.Command().Textf("cp").Input(releaseTools).Textf(" %s/META/", targetFilesDir.String())
+	}
+	// apexkeys.txt
+	var installedApexKeys []android.Path
+	for _, installedModule := range allInstalledModules {
+		if info, ok := android.OtherModuleProvider(ctx, installedModule, ApexKeyPathInfoProvider); ok {
+			installedApexKeys = append(installedApexKeys, info.ApexKeyPath)
+		}
+	}
+	installedApexKeys = android.SortedUniquePaths(installedApexKeys) // Sort by keypath to match make
+	builder.Command().Text("cat").Inputs(installedApexKeys).Textf(" >> %s/META/apexkeys.txt", targetFilesDir.String())
+	// apkcerts.txt
+	builder.Command().Textf("cp").Input(a.apkCertsInfo).Textf(" %s/META/", targetFilesDir.String())
+
+	// Copy fastboot-info.txt
+	if fastbootInfo := android.PathForModuleSrc(ctx, proptools.String(a.deviceProps.FastbootInfo)); fastbootInfo != nil {
+		// TODO (b/399788523): Autogenerate fastboot-info.txt if there is no source fastboot-info.txt
+		// https://cs.android.com/android/_/android/platform/build/+/80b9546f8f69e78b8fe1870e0e745d70fc18dfcd:core/Makefile;l=5831-5893;drc=077490384423dff9eac954da5c001c6f0be3fa6e;bpv=0;bpt=0
+		builder.Command().Textf("cp").Input(fastbootInfo).Textf(" %s/META/fastboot-info.txt", targetFilesDir.String())
+	}
+
+	// kernel_configs.txt and kernel_version.txt
+	if a.kernelConfig != nil {
+		builder.Command().Textf("cp").Input(a.kernelConfig).Textf(" %s/META/", targetFilesDir.String())
+	}
+	if a.kernelVersion != nil {
+		builder.Command().Textf("cp").Input(a.kernelVersion).Textf(" %s/META/", targetFilesDir.String())
+	}
+	// misc_info.txt
+	if a.miscInfo != nil {
+		builder.Command().Textf("cp").Input(a.miscInfo).Textf(" %s/META/", targetFilesDir.String())
+	}
+	// apex_info.pb, care_map.pb, vbmeta_digest.txt
+	a.addImgToTargetFiles(ctx, builder, targetFilesDir.String())
+
+	if a.partitionProps.Super_partition_name != nil {
+		superPartition := ctx.GetDirectDepProxyWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag)
+		if info, ok := android.OtherModuleProvider(ctx, superPartition, SuperImageProvider); ok {
+			// dynamic_partitions_info.txt
+			// TODO (b/390192334): Add `building_super_empty_partition=true`
+			builder.Command().Text("cp").Input(info.DynamicPartitionsInfo).Textf(" %s/META/", targetFilesDir.String())
+		} else {
+			ctx.ModuleErrorf("Super partition %s does set SuperImageProvider\n", superPartition.Name())
+		}
+	}
+
+}
+
+var (
+	// https://cs.android.com/android/_/android/platform/build/+/30f05352c3e6f4333c77d4af66c253572d3ea6c9:core/Makefile;l=2111-2120;drc=519f75666431ee2926e0ec8991c682b28a4c9521;bpv=1;bpt=0
+	defaultTargetRecoveryFstypeMountOptions = "ext4=max_batch_time=0,commit=1,data=ordered,barrier=1,errors=panic,nodelalloc"
+)
+
+// A partial implementation of make's $PRODUCT_OUT/misc_info.txt
+// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=5894?q=misc_info.txt%20f:build%2Fmake%2Fcore%2FMakefile&ss=android%2Fplatform%2Fsuperproject%2Fmain
+// This file is subsequently used by add_img_to_target_files to create additioanl metadata files like apex_info.pb
+// TODO (b/399788119): Complete the migration of misc_info.txt
+func (a *androidDevice) addMiscInfo(ctx android.ModuleContext) android.Path {
+	buildType := func() string {
+		if ctx.Config().Debuggable() {
+			return "userdebug"
+		} else if ctx.Config().Eng() {
+			return "eng"
+		} else {
+			return "user"
+		}
+	}
+	defaultAppCertificate := func() string {
+		pem, _ := ctx.Config().DefaultAppCertificate(ctx)
+		return strings.TrimSuffix(pem.String(), ".x509.pem")
+	}
+
+	builder := android.NewRuleBuilder(pctx, ctx)
+	miscInfo := android.PathForModuleOut(ctx, "misc_info.txt")
+	builder.Command().
+		Textf("rm -f %s", miscInfo).
+		Textf("&& echo recovery_api_version=%s >> %s", ctx.Config().VendorConfig("recovery").String("recovery_api_version"), miscInfo).
+		Textf("&& echo fstab_version=%s >> %s", ctx.Config().VendorConfig("recovery").String("recovery_fstab_version"), miscInfo).
+		Textf("&& echo build_type=%s >> %s", buildType(), miscInfo).
+		Textf("&& echo default_system_dev_certificate=%s >> %s", defaultAppCertificate(), miscInfo).
+		Textf("&& echo root_dir=%s >> %s", android.PathForModuleInPartitionInstall(ctx, "root"), miscInfo).
+		ImplicitOutput(miscInfo)
+	if len(ctx.Config().ExtraOtaRecoveryKeys()) > 0 {
+		builder.Command().Textf(`echo "extra_recovery_keys=%s" >> %s`, strings.Join(ctx.Config().ExtraOtaRecoveryKeys(), ""), miscInfo)
+	} else {
+		if a.partitionProps.Boot_partition_name != nil {
+			builder.Command().
+				Textf("echo mkbootimg_args='--header_version %s' >> %s", a.getBootimgHeaderVersion(ctx, a.partitionProps.Boot_partition_name), miscInfo).
+				// TODO: Use boot's header version for recovery for now since cuttlefish does not set `BOARD_RECOVERY_MKBOOTIMG_ARGS`
+				Textf(" && echo recovery_mkbootimg_args='--header_version %s' >> %s", a.getBootimgHeaderVersion(ctx, a.partitionProps.Boot_partition_name), miscInfo)
+		}
+		if a.partitionProps.Init_boot_partition_name != nil {
+			builder.Command().
+				Textf("echo mkbootimg_init_args='--header_version' %s >> %s", a.getBootimgHeaderVersion(ctx, a.partitionProps.Init_boot_partition_name), miscInfo)
+		}
+		builder.Command().
+			Textf("echo mkbootimg_version_args='--os_version %s --os_patch_level %s' >> %s", ctx.Config().PlatformVersionLastStable(), ctx.Config().PlatformSecurityPatch(), miscInfo).
+			Textf(" && echo multistage_support=1 >> %s", miscInfo).
+			Textf(" && echo blockimgdiff_versions=3,4 >> %s", miscInfo)
+	}
+	fsInfos := a.getFsInfos(ctx)
+	if _, ok := fsInfos["vendor"]; ok {
+		builder.Command().Textf("echo board_uses_vendorimage=true >> %s", miscInfo)
+	}
+	if fsInfos["system"].ErofsCompressHints != nil {
+		builder.Command().Textf("echo erofs_default_compress_hints=%s >> %s", fsInfos["system"].ErofsCompressHints, miscInfo)
+	}
+	if releaseTools := android.PathForModuleSrc(ctx, proptools.String(a.deviceProps.Releasetools_extension)); releaseTools != nil {
+		builder.Command().Textf("echo tool_extensions=%s >> %s", filepath.Dir(releaseTools.String()), miscInfo)
+	}
+	// ramdisk uses `compressed_cpio` fs_type
+	// https://cs.android.com/android/_/android/platform/build/+/30f05352c3e6f4333c77d4af66c253572d3ea6c9:core/Makefile;l=5923-5925;drc=519f75666431ee2926e0ec8991c682b28a4c9521;bpv=1;bpt=0
+	if _, ok := fsInfos["ramdisk"]; ok {
+		builder.Command().Textf("echo lz4_ramdisks=true >> %s", miscInfo)
+	}
+	// recovery_mount_options
+	// TODO: Add support for TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS which can be used to override the default
+	builder.Command().Textf("echo recovery_mount_options=%s >> %s", defaultTargetRecoveryFstypeMountOptions, miscInfo)
+
+	// vintf information
+	if proptools.Bool(ctx.Config().ProductVariables().Enforce_vintf_manifest) {
+		builder.Command().Textf("echo vintf_enforce=true >> %s", miscInfo)
+	}
+	if len(ctx.Config().DeviceManifestFiles()) > 0 {
+		builder.Command().Textf("echo vintf_include_empty_vendor_sku=true >> %s", miscInfo)
+	}
+
+	if a.partitionProps.Recovery_partition_name == nil {
+		builder.Command().Textf("echo no_recovery=true >> %s", miscInfo)
+	}
+	for _, partition := range android.SortedKeys(fsInfos) {
+		if fsInfos[partition].PropFileForMiscInfo != nil {
+			builder.Command().Text("cat").Input(fsInfos[partition].PropFileForMiscInfo).Textf(" >> %s", miscInfo)
+		}
+	}
+	if len(a.partitionProps.Vbmeta_partitions) > 0 {
+		builder.Command().
+			Textf("echo avb_enable=true >> %s", miscInfo).
+			Textf("&& echo avb_building_vbmeta_image=true >> %s", miscInfo).
+			Textf("&& echo avb_avbtool=avbtool >> %s", miscInfo)
+
+		var allChainedVbmetaPartitionTypes []string
+		for _, vbmetaPartitionName := range a.partitionProps.Vbmeta_partitions {
+			img := ctx.GetDirectDepProxyWithTag(vbmetaPartitionName, filesystemDepTag)
+			if provider, ok := android.OtherModuleProvider(ctx, img, vbmetaPartitionProvider); ok {
+				builder.Command().Text("cat").Input(provider.PropFileForMiscInfo).Textf(" >> %s", miscInfo)
+				if provider.FilesystemPartitionType != "" { // the top-level vbmeta.img
+					allChainedVbmetaPartitionTypes = append(allChainedVbmetaPartitionTypes, provider.FilesystemPartitionType)
+				}
+			} else {
+				ctx.ModuleErrorf("vbmeta dep %s does not set vbmetaPartitionProvider\n", vbmetaPartitionName)
+			}
+		}
+		// Determine the custom vbmeta partitions by removing system and vendor
+		customVbmetaPartitionTypes := android.RemoveListFromList(allChainedVbmetaPartitionTypes, []string{"system", "vendor"})
+		builder.Command().Textf("echo avb_custom_vbmeta_images_partition_list=%s >> %s",
+			strings.Join(android.SortedUniqueStrings(customVbmetaPartitionTypes), " "),
+			miscInfo,
+		)
+
+	}
+	if a.partitionProps.Boot_partition_name != nil {
+		builder.Command().Textf("echo boot_images=boot.img >> %s", miscInfo)
+	}
+
+	if a.partitionProps.Super_partition_name != nil {
+		superPartition := ctx.GetDirectDepProxyWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag)
+		if info, ok := android.OtherModuleProvider(ctx, superPartition, SuperImageProvider); ok {
+			// cat dynamic_partition_info.txt
+			builder.Command().Text("cat").Input(info.DynamicPartitionsInfo).Textf(" >> %s", miscInfo)
+			if info.AbUpdate {
+				builder.Command().Textf("echo ab_update=true >> %s", miscInfo)
+			}
+
+		} else {
+			ctx.ModuleErrorf("Super partition %s does set SuperImageProvider\n", superPartition.Name())
+		}
+	}
+	bootImgNames := []*string{
+		a.partitionProps.Boot_partition_name,
+		a.partitionProps.Init_boot_partition_name,
+		a.partitionProps.Vendor_boot_partition_name,
+	}
+	for _, bootImgName := range bootImgNames {
+		if bootImgName == nil {
+			continue
+		}
+
+		bootImg := ctx.GetDirectDepProxyWithTag(proptools.String(bootImgName), filesystemDepTag)
+		bootImgInfo, _ := android.OtherModuleProvider(ctx, bootImg, BootimgInfoProvider)
+		// cat avb_ metadata of the boot images
+		builder.Command().Text("cat").Input(bootImgInfo.PropFileForMiscInfo).Textf(" >> %s", miscInfo)
+	}
+
+	builder.Command().Textf("echo blocksize=%s >> %s", proptools.String(a.deviceProps.Flash_block_size), miscInfo)
+	if proptools.Bool(a.deviceProps.Bootloader_in_update_package) {
+		builder.Command().Textf("echo bootloader_in_update_package=true >> %s", miscInfo)
+	}
+	if len(a.deviceProps.Partial_ota_update_partitions) > 0 {
+		builder.Command().Textf("echo partial_ota_update_partitions_list=%s >> %s", strings.Join(a.deviceProps.Partial_ota_update_partitions, " "), miscInfo)
+	}
+
+	// Sort and dedup
+	builder.Command().Textf("sort -u %s -o %s", miscInfo, miscInfo)
+
+	builder.Build("misc_info", "Building misc_info")
+
+	return miscInfo
+}
+
+func (a *androidDevice) getBootimgHeaderVersion(ctx android.ModuleContext, bootImgName *string) string {
+	bootImg := ctx.GetDirectDepProxyWithTag(proptools.String(bootImgName), filesystemDepTag)
+	bootImgInfo, _ := android.OtherModuleProvider(ctx, bootImg, BootimgInfoProvider)
+	return bootImgInfo.HeaderVersion
+}
+
+// addImgToTargetFiles invokes `add_img_to_target_files` and creates the following files in META/
+// - apex_info.pb
+// - care_map.pb
+// - vbmeta_digest.txt
+func (a *androidDevice) addImgToTargetFiles(ctx android.ModuleContext, builder *android.RuleBuilder, targetFilesDir string) {
+	mkbootimg := ctx.Config().HostToolPath(ctx, "mkbootimg")
+	builder.Command().
+		Textf("PATH=%s:$PATH", ctx.Config().HostToolDir()).
+		Textf("MKBOOTIMG=%s", mkbootimg).
+		Implicit(mkbootimg).
+		BuiltTool("add_img_to_target_files").
+		Flag("-a -v -p").
+		Flag(ctx.Config().HostToolDir()).
+		Text(targetFilesDir)
+}
+
+func (a *androidDevice) buildUpdatePackage(ctx android.ModuleContext) {
+	var exclusions []string
+	fsInfos := a.getFsInfos(ctx)
+	// Exclude the partitions that are not supported by flashall
+	for _, partition := range android.SortedKeys(fsInfos) {
+		if fsInfos[partition].NoFlashall {
+			exclusions = append(exclusions, fmt.Sprintf("IMAGES/%s.img", partition))
+			exclusions = append(exclusions, fmt.Sprintf("IMAGES/%s.map", partition))
+		}
+	}
+
+	updatePackage := android.PathForModuleOut(ctx, "img.zip")
+	rule := android.NewRuleBuilder(pctx, ctx)
+
+	buildSuperImage := ctx.Config().HostToolPath(ctx, "build_super_image")
+	zip2zip := ctx.Config().HostToolPath(ctx, "zip2zip")
+
+	rule.Command().
+		BuiltTool("img_from_target_files").
+		Text("--additional IMAGES/VerifiedBootParams.textproto:VerifiedBootParams.textproto").
+		FlagForEachArg("--exclude ", exclusions).
+		FlagWithArg("--build_super_image ", buildSuperImage.String()).
+		Implicit(buildSuperImage).
+		Implicit(zip2zip).
+		Input(a.targetFilesZip).
+		Output(updatePackage)
+
+	rule.Build("updatepackage", "Building updatepackage")
+
+	a.updatePackage = updatePackage
+}
+
+type ApexKeyPathInfo struct {
+	ApexKeyPath android.Path
+}
+
+var ApexKeyPathInfoProvider = blueprint.NewProvider[ApexKeyPathInfo]()
+
+func (a *androidDevice) generateFilesystemConfigForTargetFiles(ctx android.ModuleContext, builder *android.RuleBuilder, stagingDirTimestamp android.Path, targetFilesDir, stagingDir, filename string) {
+	fsConfigOut := android.PathForModuleOut(ctx, filename)
+	ctx.Build(pctx, android.BuildParams{
+		Rule:     fsConfigRule,
+		Implicit: stagingDirTimestamp,
+		Output:   fsConfigOut,
+		Args: map[string]string{
+			"rootDir": stagingDir,
+			"prefix":  "",
+		},
+	})
+	builder.Command().Textf("cp").Input(fsConfigOut).Textf(" %s/META/", targetFilesDir)
+}
+
+// Filenames for the partition specific fs_config files.
+// Hardcode the ramdisk files to their boot image prefix
+func (a *androidDevice) filesystemConfigNameForTargetFiles(partition string) string {
+	name := partition + "_filesystem_config.txt"
+	if partition == "system" {
+		name = "filesystem_config.txt"
+	} else if partition == "ramdisk" {
+		name = "init_boot_filesystem_config.txt"
+	}
+	return name
+}
+
+func (a *androidDevice) getFilesystemInfo(ctx android.ModuleContext, depName string) FilesystemInfo {
+	fsMod := ctx.GetDirectDepProxyWithTag(depName, filesystemDepTag)
+	fsInfo, ok := android.OtherModuleProvider(ctx, fsMod, FilesystemProvider)
+	if !ok {
+		ctx.ModuleErrorf("Expected dependency %s to be a filesystem", depName)
+	}
+	return fsInfo
+}
+
+func (a *androidDevice) setVbmetaPhonyTargets(ctx android.ModuleContext) {
+	if !proptools.Bool(a.deviceProps.Main_device) {
+		return
+	}
+
+	if !ctx.Config().KatiEnabled() {
+		for _, vbmetaPartitionName := range a.partitionProps.Vbmeta_partitions {
+			img := ctx.GetDirectDepProxyWithTag(vbmetaPartitionName, filesystemDepTag)
+			if provider, ok := android.OtherModuleProvider(ctx, img, vbmetaPartitionProvider); ok {
+				// make generates `vbmetasystemimage` phony target instead of `vbmeta_systemimage` phony target.
+				partitionName := strings.ReplaceAll(provider.Name, "_", "")
+				ctx.Phony(fmt.Sprintf("%simage", partitionName), provider.Output)
+			}
+		}
+	}
+}
+
+func (a *androidDevice) getKernel(ctx android.ModuleContext) android.Path {
+	if a.partitionProps.Boot_partition_name != nil {
+		bootImg := ctx.GetDirectDepProxyWithTag(proptools.String(a.partitionProps.Boot_partition_name), filesystemDepTag)
+		bootImgInfo, _ := android.OtherModuleProvider(ctx, bootImg, BootimgInfoProvider)
+		return bootImgInfo.Kernel
+	}
+	return nil
+}
+
+// Gets the kernel version and configs from the actual kernel file itself. Roughly equivalent to
+// this make code: https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=5443;drc=c0b66fc59de069e06ce0ffd703d4d21613be30c6
+// However, it is a simplified version of that make code. Differences include:
+//   - Not handling BOARD_KERNEL_CONFIG_FILE because BOARD_KERNEL_CONFIG_FILE was never used.
+//   - Not unpacking the bootimage, as we should be able to just always export the kernel directly
+//     in the BootimgInfo. We don't currently support prebuilt boot images, but even if we add that
+//     in the future, it can be done in a prebuilt_bootimage module type that still exports the same
+//     BootimgInfo.
+//   - We don't print a warning and output '<unknown-kernel>' to kernel_version_for_uffd_gc.txt
+//     because we expect the kernel to always be present. If it's not, we will get an error that
+//     kernel_version_for_uffd_gc.txt doesn't exist. This may require later tweaking to the
+//     dexpreopt rules so that they don't attempt to access that file in builds that don't have
+//     a kernel.
+func (a *androidDevice) extractKernelVersionAndConfigs(ctx android.ModuleContext) (android.Path, android.Path) {
+	kernel := a.getKernel(ctx)
+	// If there's no kernel, don't create kernel version / kernel config files. Reverse dependencies
+	// on those files have to account for this, for example by disabling dexpreopt in unbundled
+	// builds.
+	if kernel == nil {
+		return nil, nil
+	}
+
+	lz4tool := ctx.Config().HostToolPath(ctx, "lz4")
+
+	extractedVersionFile := android.PathForModuleOut(ctx, "kernel_version.txt")
+	extractedConfigsFile := android.PathForModuleOut(ctx, "kernel_configs.txt")
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().BuiltTool("extract_kernel").
+		Flag("--tools lz4:"+lz4tool.String()).Implicit(lz4tool).
+		FlagWithInput("--input ", kernel).
+		FlagWithOutput("--output-release ", extractedVersionFile).
+		FlagWithOutput("--output-configs ", extractedConfigsFile).
+		Textf(`&& printf "\n" >> %s`, extractedVersionFile)
+
+	if specifiedVersion := proptools.String(a.deviceProps.Kernel_version); specifiedVersion != "" {
+		specifiedVersionFile := android.PathForModuleOut(ctx, "specified_kernel_version.txt")
+		android.WriteFileRule(ctx, specifiedVersionFile, specifiedVersion)
+		builder.Command().Text("diff -q").
+			Input(specifiedVersionFile).
+			Input(extractedVersionFile).
+			Textf(`|| (echo "Specified kernel version '$(cat %s)' does not match actual kernel version '$(cat %s)'"; exit 1)`, specifiedVersionFile, extractedVersionFile)
+	}
+
+	builder.Build("extract_kernel_info", "Extract kernel version and configs")
+
+	if proptools.Bool(a.deviceProps.Main_device) && !ctx.Config().KatiEnabled() {
+		if ctx.Config().EnableUffdGc() == "default" {
+			kernelVersionFile := android.PathForOutput(ctx, "dexpreopt/kernel_version_for_uffd_gc.txt")
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.CpIfChanged,
+				Input:  extractedVersionFile,
+				Output: kernelVersionFile,
+			})
+		}
+
+		ctx.DistForGoal("droid_targets", extractedVersionFile)
+	}
+
+	return extractedVersionFile, extractedConfigsFile
+}
+
+func (a *androidDevice) buildApkCertsInfo(ctx android.ModuleContext, allInstalledModules []android.Module) android.Path {
+	// TODO (spandandas): Add compressed
+	formatLine := func(cert java.Certificate, name, partition string) string {
+		pem := cert.AndroidMkString()
+		var key string
+		if cert.Key == nil {
+			key = ""
+		} else {
+			key = cert.Key.String()
+		}
+		return fmt.Sprintf(`name="%s" certificate="%s" private_key="%s" partition="%s"`, name, pem, key, partition)
+	}
+
+	apkCerts := []string{}
+	var apkCertsFiles android.Paths
+	for _, installedModule := range allInstalledModules {
+		partition := ""
+		if commonInfo, ok := android.OtherModuleProvider(ctx, installedModule, android.CommonModuleInfoProvider); ok {
+			partition = commonInfo.PartitionTag
+		} else {
+			ctx.ModuleErrorf("%s does not set CommonModuleInfoKey", installedModule.Name())
+		}
+		if info, ok := android.OtherModuleProvider(ctx, installedModule, java.AppInfoProvider); ok {
+			if info.AppSet {
+				apkCertsFiles = append(apkCertsFiles, info.ApkCertsFile)
+			} else {
+				apkCerts = append(apkCerts, formatLine(info.Certificate, info.InstallApkName+".apk", partition))
+			}
+		} else if info, ok := android.OtherModuleProvider(ctx, installedModule, java.AppInfosProvider); ok {
+			for _, certInfo := range info {
+				// Partition information of apk-in-apex is not exported to the legacy Make packaging system.
+				// Hardcode the partition to "system"
+				apkCerts = append(apkCerts, formatLine(certInfo.Certificate, certInfo.InstallApkName+".apk", "system"))
+			}
+		} else if info, ok := android.OtherModuleProvider(ctx, installedModule, java.RuntimeResourceOverlayInfoProvider); ok {
+			apkCerts = append(apkCerts, formatLine(info.Certificate, info.OutputFile.Base(), partition))
+		}
+	}
+	slices.Sort(apkCerts) // sort by name
+	fsInfos := a.getFsInfos(ctx)
+	if fsInfos["system"].HasFsverity {
+		defaultPem, defaultKey := ctx.Config().DefaultAppCertificate(ctx)
+		apkCerts = append(apkCerts, formatLine(java.Certificate{Pem: defaultPem, Key: defaultKey}, "BuildManifest.apk", "system"))
+		if info, ok := fsInfos["system_ext"]; ok && info.HasFsverity {
+			apkCerts = append(apkCerts, formatLine(java.Certificate{Pem: defaultPem, Key: defaultKey}, "BuildManifestSystemExt.apk", "system_ext"))
+		}
+	}
+
+	apkCertsInfoWithoutAppSets := android.PathForModuleOut(ctx, "apkcerts_without_app_sets.txt")
+	android.WriteFileRuleVerbatim(ctx, apkCertsInfoWithoutAppSets, strings.Join(apkCerts, "\n")+"\n")
+	apkCertsInfo := android.PathForModuleOut(ctx, "apkcerts.txt")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.Cat,
+		Description: "combine apkcerts.txt",
+		Output:      apkCertsInfo,
+		Inputs:      append(apkCertsFiles, apkCertsInfoWithoutAppSets),
+	})
+	return apkCertsInfo
 }
diff --git a/filesystem/android_device_product_out.go b/filesystem/android_device_product_out.go
new file mode 100644
index 0000000..aa06337
--- /dev/null
+++ b/filesystem/android_device_product_out.go
@@ -0,0 +1,260 @@
+// 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.
+
+package filesystem
+
+import (
+	"android/soong/android"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+var (
+	copyStagingDirRule = pctx.AndroidStaticRule("copy_staging_dir", blueprint.RuleParams{
+		Command: "rsync -a --checksum $dir/ $dest && touch $out",
+	}, "dir", "dest")
+)
+
+func (a *androidDevice) copyToProductOut(ctx android.ModuleContext, builder *android.RuleBuilder, src android.Path, dest string) {
+	destPath := android.PathForModuleInPartitionInstall(ctx, "").Join(ctx, dest)
+	builder.Command().Text("rsync").Flag("-a").Flag("--checksum").Input(src).Text(destPath.String())
+}
+
+func (a *androidDevice) copyFilesToProductOutForSoongOnly(ctx android.ModuleContext) android.Path {
+	filesystemInfos := a.getFsInfos(ctx)
+
+	var deps android.Paths
+	var depsNoImg android.Paths // subset of deps without any img files. used for sbom creation.
+
+	for _, partition := range android.SortedKeys(filesystemInfos) {
+		info := filesystemInfos[partition]
+		imgInstallPath := android.PathForModuleInPartitionInstall(ctx, "", partition+".img")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Cp,
+			Input:  info.Output,
+			Output: imgInstallPath,
+		})
+
+		// Make it so doing `m <moduleName>` or `m <partitionType>image` will copy the files to
+		// PRODUCT_OUT
+		if partition == "system_ext" {
+			partition = "systemext"
+		}
+		partition = partition + "image"
+		ctx.Phony(info.ModuleName, imgInstallPath)
+		ctx.Phony(partition, imgInstallPath)
+		for _, fip := range info.FullInstallPaths {
+			// TODO: Directories. But maybe they're not necessary? Adevice doesn't care
+			// about empty directories, still need to check if adb sync does.
+			if !fip.IsDir {
+				if !fip.RequiresFullInstall {
+					// Some modules set requires_full_install: false, which causes their staging
+					// directory file to not be installed. This is usually because the file appears
+					// in both PRODUCT_COPY_FILES and a soong module for the handwritten soong system
+					// image. In this case, that module's installed files would conflict with the
+					// PRODUCT_COPY_FILES. However, in soong-only builds, we don't automatically
+					// create rules for PRODUCT_COPY_FILES unless they're needed in the partition.
+					// So in that case, nothing is creating the installed path. Create them now
+					// if that's the case.
+					if fip.SymlinkTarget == "" {
+						ctx.Build(pctx, android.BuildParams{
+							Rule:   android.CpWithBash,
+							Input:  fip.SourcePath,
+							Output: fip.FullInstallPath,
+							Args: map[string]string{
+								// Preserve timestamps for adb sync, so that this installed file's
+								// timestamp matches the timestamp in the filesystem's intermediate
+								// staging dir
+								"cpFlags": "-p",
+							},
+						})
+					} else {
+						ctx.Build(pctx, android.BuildParams{
+							Rule:   android.SymlinkWithBash,
+							Output: fip.FullInstallPath,
+							Args: map[string]string{
+								"fromPath": fip.SymlinkTarget,
+							},
+						})
+					}
+				}
+				ctx.Phony(info.ModuleName, fip.FullInstallPath)
+				ctx.Phony(partition, fip.FullInstallPath)
+				deps = append(deps, fip.FullInstallPath)
+				depsNoImg = append(depsNoImg, fip.FullInstallPath)
+				ctx.Phony("sync_"+partition, fip.FullInstallPath)
+				ctx.Phony("sync", fip.FullInstallPath)
+			}
+		}
+
+		deps = append(deps, imgInstallPath)
+	}
+
+	a.createComplianceMetadataTimestamp(ctx, depsNoImg)
+
+	// List all individual files to be copied to PRODUCT_OUT here
+	if a.deviceProps.Bootloader != nil {
+		bootloaderInstallPath := android.PathForModuleInPartitionInstall(ctx, "", "bootloader")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Cp,
+			Input:  android.PathForModuleSrc(ctx, *a.deviceProps.Bootloader),
+			Output: bootloaderInstallPath,
+		})
+		deps = append(deps, bootloaderInstallPath)
+	}
+
+	copyBootImg := func(prop *string, type_ string) {
+		if proptools.String(prop) != "" {
+			partition := ctx.GetDirectDepWithTag(*prop, filesystemDepTag)
+			if info, ok := android.OtherModuleProvider(ctx, partition, BootimgInfoProvider); ok {
+				installPath := android.PathForModuleInPartitionInstall(ctx, "", type_+".img")
+				ctx.Build(pctx, android.BuildParams{
+					Rule:   android.Cp,
+					Input:  info.Output,
+					Output: installPath,
+				})
+				deps = append(deps, installPath)
+			} else {
+				ctx.ModuleErrorf("%s does not set BootimgInfo\n", *prop)
+			}
+		}
+	}
+
+	copyBootImg(a.partitionProps.Init_boot_partition_name, "init_boot")
+	copyBootImg(a.partitionProps.Boot_partition_name, "boot")
+	copyBootImg(a.partitionProps.Vendor_boot_partition_name, "vendor_boot")
+
+	for _, vbmetaModName := range a.partitionProps.Vbmeta_partitions {
+		partition := ctx.GetDirectDepWithTag(vbmetaModName, filesystemDepTag)
+		if info, ok := android.OtherModuleProvider(ctx, partition, vbmetaPartitionProvider); ok {
+			installPath := android.PathForModuleInPartitionInstall(ctx, "", info.Name+".img")
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.Cp,
+				Input:  info.Output,
+				Output: installPath,
+			})
+			deps = append(deps, installPath)
+		} else {
+			ctx.ModuleErrorf("%s does not set vbmetaPartitionProvider\n", vbmetaModName)
+		}
+	}
+
+	if proptools.String(a.partitionProps.Super_partition_name) != "" {
+		partition := ctx.GetDirectDepWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag)
+		if info, ok := android.OtherModuleProvider(ctx, partition, SuperImageProvider); ok {
+			installPath := android.PathForModuleInPartitionInstall(ctx, "", "super.img")
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.Cp,
+				Input:  info.SuperImage,
+				Output: installPath,
+			})
+			deps = append(deps, installPath)
+		} else {
+			ctx.ModuleErrorf("%s does not set SuperImageProvider\n", *a.partitionProps.Super_partition_name)
+		}
+	}
+
+	if proptools.String(a.deviceProps.Android_info) != "" {
+		installPath := android.PathForModuleInPartitionInstall(ctx, "", "android-info.txt")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Cp,
+			Input:  android.PathForModuleSrc(ctx, *a.deviceProps.Android_info),
+			Output: installPath,
+		})
+		deps = append(deps, installPath)
+	}
+
+	copyToProductOutTimestamp := android.PathForModuleOut(ctx, "product_out_copy_timestamp")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:      android.Touch,
+		Output:    copyToProductOutTimestamp,
+		Implicits: deps,
+	})
+
+	emptyFile := android.PathForModuleOut(ctx, "empty_file")
+	android.WriteFileRule(ctx, emptyFile, "")
+
+	// TODO: We don't have these tests building in soong yet. Add phonies for them so that CI builds
+	// that try to build them don't error out.
+	ctx.Phony("continuous_instrumentation_tests", emptyFile)
+	ctx.Phony("continuous_native_tests", emptyFile)
+	ctx.Phony("device-tests", emptyFile)
+	ctx.Phony("device-platinum-tests", emptyFile)
+	ctx.Phony("platform_tests", emptyFile)
+
+	return copyToProductOutTimestamp
+}
+
+// createComplianceMetadataTimestampForSoongOnly creates a timestamp file in m --soong-only
+// this timestamp file depends on installed files of the main `android_device`.
+// Any changes to installed files of the main `android_device` will retrigger SBOM generation
+func (a *androidDevice) createComplianceMetadataTimestamp(ctx android.ModuleContext, installedFiles android.Paths) {
+	ctx.Build(pctx, android.BuildParams{
+		Rule:      android.Touch,
+		Implicits: installedFiles,
+		Output:    android.PathForOutput(ctx, "compliance-metadata", ctx.Config().DeviceProduct(), "installed_files.stamp"),
+	})
+}
+
+// Returns a mapping from partition type -> FilesystemInfo. This includes filesystems that are
+// nested inside of other partitions, such as the partitions inside super.img, or ramdisk inside
+// of boot.
+func (a *androidDevice) getFsInfos(ctx android.ModuleContext) map[string]FilesystemInfo {
+	type propToType struct {
+		prop *string
+		ty   string
+	}
+
+	filesystemInfos := make(map[string]FilesystemInfo)
+
+	partitionDefinitions := []propToType{
+		propToType{a.partitionProps.System_partition_name, "system"},
+		propToType{a.partitionProps.System_ext_partition_name, "system_ext"},
+		propToType{a.partitionProps.Product_partition_name, "product"},
+		propToType{a.partitionProps.Vendor_partition_name, "vendor"},
+		propToType{a.partitionProps.Odm_partition_name, "odm"},
+		propToType{a.partitionProps.Recovery_partition_name, "recovery"},
+		propToType{a.partitionProps.System_dlkm_partition_name, "system_dlkm"},
+		propToType{a.partitionProps.Vendor_dlkm_partition_name, "vendor_dlkm"},
+		propToType{a.partitionProps.Odm_dlkm_partition_name, "odm_dlkm"},
+		propToType{a.partitionProps.Userdata_partition_name, "userdata"},
+		// filesystemInfo from init_boot and vendor_boot actually are re-exports of the ramdisk
+		// images inside of them
+		propToType{a.partitionProps.Init_boot_partition_name, "ramdisk"},
+		propToType{a.partitionProps.Vendor_boot_partition_name, "vendor_ramdisk"},
+	}
+	for _, partitionDefinition := range partitionDefinitions {
+		if proptools.String(partitionDefinition.prop) != "" {
+			partition := ctx.GetDirectDepWithTag(*partitionDefinition.prop, filesystemDepTag)
+			if info, ok := android.OtherModuleProvider(ctx, partition, FilesystemProvider); ok {
+				filesystemInfos[partitionDefinition.ty] = info
+			} else {
+				ctx.ModuleErrorf("Super partition %s does not set FilesystemProvider\n", partition.Name())
+			}
+		}
+	}
+	if a.partitionProps.Super_partition_name != nil {
+		superPartition := ctx.GetDirectDepWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag)
+		if info, ok := android.OtherModuleProvider(ctx, superPartition, SuperImageProvider); ok {
+			for partition := range info.SubImageInfo {
+				filesystemInfos[partition] = info.SubImageInfo[partition]
+			}
+		} else {
+			ctx.ModuleErrorf("Super partition %s does not set SuperImageProvider\n", superPartition.Name())
+		}
+	}
+
+	return filesystemInfos
+}
diff --git a/filesystem/avb_add_hash_footer.go b/filesystem/avb_add_hash_footer.go
index 9d4ba3e..c776012 100644
--- a/filesystem/avb_add_hash_footer.go
+++ b/filesystem/avb_add_hash_footer.go
@@ -46,7 +46,7 @@
 
 type avbAddHashFooterProperties struct {
 	// Source file of this image. Can reference a genrule type module with the ":module" syntax.
-	Src *string `android:"path,arch_variant"`
+	Src proptools.Configurable[string] `android:"path,arch_variant,replace_instead_of_append"`
 
 	// Set the name of the output. Defaults to <module_name>.img.
 	Filename *string
@@ -70,7 +70,7 @@
 	Props []avbProp
 
 	// The index used to prevent rollback of the image on device.
-	Rollback_index *int64
+	Rollback_index proptools.Configurable[int64] `android:"replace_instead_of_append"`
 
 	// Include descriptors from images
 	Include_descriptors_from_images []string `android:"path,arch_variant"`
@@ -91,12 +91,13 @@
 
 func (a *avbAddHashFooter) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	builder := android.NewRuleBuilder(pctx, ctx)
+	src := a.properties.Src.GetOrDefault(ctx, "")
 
-	if a.properties.Src == nil {
+	if src == "" {
 		ctx.PropertyErrorf("src", "missing source file")
 		return
 	}
-	input := android.PathForModuleSrc(ctx, proptools.String(a.properties.Src))
+	input := android.PathForModuleSrc(ctx, src)
 	output := android.PathForModuleOut(ctx, a.installFileName())
 	builder.Command().Text("cp").Input(input).Output(output)
 
@@ -133,8 +134,9 @@
 		addAvbProp(ctx, cmd, prop)
 	}
 
-	if a.properties.Rollback_index != nil {
-		rollbackIndex := proptools.Int(a.properties.Rollback_index)
+	rollbackIndex := a.properties.Rollback_index.Get(ctx)
+	if rollbackIndex.IsPresent() {
+		rollbackIndex := rollbackIndex.Get()
 		if rollbackIndex < 0 {
 			ctx.PropertyErrorf("rollback_index", "Rollback index must be non-negative")
 		}
@@ -148,6 +150,8 @@
 	a.installDir = android.PathForModuleInstall(ctx, "etc")
 	ctx.InstallFile(a.installDir, a.installFileName(), output)
 	a.output = output
+
+	setCommonFilesystemInfo(ctx, a)
 }
 
 func addAvbProp(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, prop avbProp) {
@@ -207,6 +211,9 @@
 
 // Implements android.SourceFileProducer
 func (a *avbAddHashFooter) Srcs() android.Paths {
+	if a.output == nil {
+		return nil
+	}
 	return append(android.Paths{}, a.output)
 }
 
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index 0ffec26..485eae4 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -99,6 +99,9 @@
 	// The index used to prevent rollback of the image on device.
 	Avb_rollback_index *int64
 
+	// Rollback index location of this image. Must be 0, 1, 2, etc.
+	Avb_rollback_index_location *int64
+
 	// The security patch passed to as the com.android.build.<type>.security_patch avb property.
 	// Replacement for the make variables BOOT_SECURITY_PATCH / INIT_BOOT_SECURITY_PATCH.
 	Security_patch *string
@@ -197,12 +200,9 @@
 		ctx.PropertyErrorf("kernel_prebuilt", "boot partition must have kernel")
 		return
 	}
-	var kernel android.Path
-	if kernelProp != "" {
-		kernel = android.PathForModuleSrc(ctx, kernelProp)
-	}
 
-	unsignedOutput := b.buildBootImage(ctx, kernel)
+	kernelPath := b.getKernelPath(ctx)
+	unsignedOutput := b.buildBootImage(ctx, kernelPath)
 
 	output := unsignedOutput
 	if proptools.Bool(b.properties.Use_avb) {
@@ -213,7 +213,7 @@
 		case "default":
 			output = b.signImage(ctx, unsignedOutput)
 		case "make_legacy":
-			output = b.addAvbFooter(ctx, unsignedOutput, kernel)
+			output = b.addAvbFooter(ctx, unsignedOutput, kernelPath)
 		default:
 			ctx.PropertyErrorf("avb_mode", `Unknown value for avb_mode, expected "default" or "make_legacy", got: %q`, *b.properties.Avb_mode)
 		}
@@ -224,6 +224,103 @@
 
 	ctx.SetOutputFiles([]android.Path{output}, "")
 	b.output = output
+
+	// Set the Filesystem info of the ramdisk dependency.
+	// `android_device` will use this info to package `target_files.zip`
+	if ramdisk := proptools.String(b.properties.Ramdisk_module); ramdisk != "" {
+		ramdiskModule := ctx.GetDirectDepWithTag(ramdisk, bootimgRamdiskDep)
+		fsInfo, _ := android.OtherModuleProvider(ctx, ramdiskModule, FilesystemProvider)
+		android.SetProvider(ctx, FilesystemProvider, fsInfo)
+	} else {
+		setCommonFilesystemInfo(ctx, b)
+	}
+
+	// Set BootimgInfo for building target_files.zip
+	dtbPath := b.getDtbPath(ctx)
+	android.SetProvider(ctx, BootimgInfoProvider, BootimgInfo{
+		Cmdline:             b.properties.Cmdline,
+		Kernel:              kernelPath,
+		Dtb:                 dtbPath,
+		Bootconfig:          b.getBootconfigPath(ctx),
+		Output:              output,
+		PropFileForMiscInfo: b.buildPropFileForMiscInfo(ctx),
+		HeaderVersion:       proptools.String(b.properties.Header_version),
+	})
+
+	extractedPublicKey := android.PathForModuleOut(ctx, b.partitionName()+".avbpubkey")
+	if b.properties.Avb_private_key != nil {
+		key := android.PathForModuleSrc(ctx, proptools.String(b.properties.Avb_private_key))
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   extractPublicKeyRule,
+			Input:  key,
+			Output: extractedPublicKey,
+		})
+	}
+	var ril int
+	if b.properties.Avb_rollback_index_location != nil {
+		ril = proptools.Int(b.properties.Avb_rollback_index_location)
+	}
+
+	android.SetProvider(ctx, vbmetaPartitionProvider, vbmetaPartitionInfo{
+		Name:                  b.bootImageType.String(),
+		RollbackIndexLocation: ril,
+		PublicKey:             extractedPublicKey,
+		Output:                output,
+	})
+
+	// Dump compliance metadata
+	complianceMetadataInfo := ctx.ComplianceMetadataInfo()
+	prebuiltFilesCopied := make([]string, 0)
+	if kernelPath != nil {
+		prebuiltFilesCopied = append(prebuiltFilesCopied, kernelPath.String()+":kernel")
+	}
+	if dtbPath != nil {
+		prebuiltFilesCopied = append(prebuiltFilesCopied, dtbPath.String()+":dtb.img")
+	}
+	complianceMetadataInfo.SetPrebuiltFilesCopied(prebuiltFilesCopied)
+
+	if ramdisk := proptools.String(b.properties.Ramdisk_module); ramdisk != "" {
+		buildComplianceMetadata(ctx, bootimgRamdiskDep)
+	}
+}
+
+var BootimgInfoProvider = blueprint.NewProvider[BootimgInfo]()
+
+type BootimgInfo struct {
+	Cmdline             []string
+	Kernel              android.Path
+	Dtb                 android.Path
+	Bootconfig          android.Path
+	Output              android.Path
+	PropFileForMiscInfo android.Path
+	HeaderVersion       string
+}
+
+func (b *bootimg) getKernelPath(ctx android.ModuleContext) android.Path {
+	var kernelPath android.Path
+	kernelName := proptools.String(b.properties.Kernel_prebuilt)
+	if kernelName != "" {
+		kernelPath = android.PathForModuleSrc(ctx, kernelName)
+	}
+	return kernelPath
+}
+
+func (b *bootimg) getDtbPath(ctx android.ModuleContext) android.Path {
+	var dtbPath android.Path
+	dtbName := proptools.String(b.properties.Dtb_prebuilt)
+	if dtbName != "" {
+		dtbPath = android.PathForModuleSrc(ctx, dtbName)
+	}
+	return dtbPath
+}
+
+func (b *bootimg) getBootconfigPath(ctx android.ModuleContext) android.Path {
+	var bootconfigPath android.Path
+	bootconfigName := proptools.String(b.properties.Bootconfig)
+	if bootconfigName != "" {
+		bootconfigPath = android.PathForModuleSrc(ctx, bootconfigName)
+	}
+	return bootconfigPath
 }
 
 func (b *bootimg) buildBootImage(ctx android.ModuleContext, kernel android.Path) android.Path {
@@ -242,10 +339,8 @@
 		cmd.FlagWithArg("--os_patch_level ", ctx.Config().PlatformSecurityPatch())
 	}
 
-	dtbName := proptools.String(b.properties.Dtb_prebuilt)
-	if dtbName != "" {
-		dtb := android.PathForModuleSrc(ctx, dtbName)
-		cmd.FlagWithInput("--dtb ", dtb)
+	if b.getDtbPath(ctx) != nil {
+		cmd.FlagWithInput("--dtb ", b.getDtbPath(ctx))
 	}
 
 	cmdline := strings.Join(b.properties.Cmdline, " ")
@@ -391,15 +486,6 @@
 	return output
 }
 
-// Calculates avb_salt from some input for deterministic output.
-func (b *bootimg) salt() string {
-	var input []string
-	input = append(input, b.properties.Cmdline...)
-	input = append(input, proptools.StringDefault(b.properties.Partition_name, b.Name()))
-	input = append(input, proptools.String(b.properties.Header_version))
-	return sha1sum(input)
-}
-
 func (b *bootimg) buildPropFile(ctx android.ModuleContext) (android.Path, android.Paths) {
 	var sb strings.Builder
 	var deps android.Paths
@@ -420,13 +506,71 @@
 	addStr("avb_add_hash_footer_args", "") // TODO(jiyong): add --rollback_index
 	partitionName := proptools.StringDefault(b.properties.Partition_name, b.Name())
 	addStr("partition_name", partitionName)
-	addStr("avb_salt", b.salt())
 
 	propFile := android.PathForModuleOut(ctx, "prop")
 	android.WriteFileRule(ctx, propFile, sb.String())
 	return propFile, deps
 }
 
+func (b *bootimg) getAvbHashFooterArgs(ctx android.ModuleContext) string {
+	ret := ""
+	if !b.bootImageType.isVendorBoot() {
+		ret += "--prop " + fmt.Sprintf("com.android.build.%s.os_version:%s", b.bootImageType.String(), ctx.Config().PlatformVersionLastStable())
+	}
+
+	fingerprintFile := ctx.Config().BuildFingerprintFile(ctx)
+	ret += " --prop " + fmt.Sprintf("com.android.build.%s.fingerprint:{CONTENTS_OF:%s}", b.bootImageType.String(), fingerprintFile.String())
+
+	if b.properties.Security_patch != nil {
+		ret += " --prop " + fmt.Sprintf("com.android.build.%s.security_patch:%s", b.bootImageType.String(), *b.properties.Security_patch)
+	}
+
+	if b.properties.Avb_rollback_index != nil {
+		ret += " --rollback_index " + strconv.FormatInt(*b.properties.Avb_rollback_index, 10)
+	}
+	return strings.TrimSpace(ret)
+}
+
+func (b *bootimg) buildPropFileForMiscInfo(ctx android.ModuleContext) android.Path {
+	var sb strings.Builder
+	addStr := func(name string, value string) {
+		fmt.Fprintf(&sb, "%s=%s\n", name, value)
+	}
+
+	bootImgType := proptools.String(b.properties.Boot_image_type)
+	addStr("avb_"+bootImgType+"_add_hash_footer_args", b.getAvbHashFooterArgs(ctx))
+	if ramdisk := proptools.String(b.properties.Ramdisk_module); ramdisk != "" {
+		ramdiskModule := ctx.GetDirectDepWithTag(ramdisk, bootimgRamdiskDep)
+		fsInfo, _ := android.OtherModuleProvider(ctx, ramdiskModule, FilesystemProvider)
+		if fsInfo.HasOrIsRecovery {
+			// Create a dup entry for recovery
+			addStr("avb_recovery_add_hash_footer_args", strings.ReplaceAll(b.getAvbHashFooterArgs(ctx), bootImgType, "recovery"))
+		}
+	}
+	if b.properties.Avb_private_key != nil {
+		addStr("avb_"+bootImgType+"_algorithm", proptools.StringDefault(b.properties.Avb_algorithm, "SHA256_RSA4096"))
+		addStr("avb_"+bootImgType+"_key_path", android.PathForModuleSrc(ctx, proptools.String(b.properties.Avb_private_key)).String())
+		addStr("avb_"+bootImgType+"_rollback_index_location", strconv.Itoa(proptools.Int(b.properties.Avb_rollback_index_location)))
+	}
+	if b.properties.Partition_size != nil {
+		addStr(bootImgType+"_size", strconv.FormatInt(*b.properties.Partition_size, 10))
+	}
+	if bootImgType != "boot" {
+		addStr(bootImgType, "true")
+	}
+
+	propFilePreProcessing := android.PathForModuleOut(ctx, "prop_for_misc_info_pre_processing")
+	android.WriteFileRuleVerbatim(ctx, propFilePreProcessing, sb.String())
+	propFile := android.PathForModuleOut(ctx, "prop_file_for_misc_info")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   textFileProcessorRule,
+		Input:  propFilePreProcessing,
+		Output: propFile,
+	})
+
+	return propFile
+}
+
 var _ android.AndroidMkEntriesProvider = (*bootimg)(nil)
 
 // Implements android.AndroidMkEntriesProvider
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index b9cb076..c3c3835 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -20,6 +20,7 @@
 	"io"
 	"path/filepath"
 	"slices"
+	"sort"
 	"strconv"
 	"strings"
 
@@ -29,12 +30,18 @@
 	"android/soong/linkerconfig"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/depset"
 	"github.com/google/blueprint/proptools"
 )
 
+var pctx = android.NewPackageContext("android/soong/filesystem")
+
 func init() {
 	registerBuildComponents(android.InitRegistrationContext)
 	registerMutators(android.InitRegistrationContext)
+	pctx.HostBinToolVariable("fileslist", "fileslist")
+	pctx.HostBinToolVariable("fs_config", "fs_config")
+	pctx.HostBinToolVariable("symbols_map", "symbols_map")
 }
 
 func registerBuildComponents(ctx android.RegistrationContext) {
@@ -53,11 +60,28 @@
 	})
 }
 
-// Remember to add referenced files to implicits!
-var textFileProcessorRule = pctx.AndroidStaticRule("text_file_processing", blueprint.RuleParams{
-	Command:     "build/soong/scripts/text_file_processor.py $in $out",
-	CommandDeps: []string{"build/soong/scripts/text_file_processor.py"},
-})
+var (
+	// Remember to add referenced files to implicits!
+	textFileProcessorRule = pctx.AndroidStaticRule("text_file_processing", blueprint.RuleParams{
+		Command:     "build/soong/scripts/text_file_processor.py $in $out",
+		CommandDeps: []string{"build/soong/scripts/text_file_processor.py"},
+	})
+
+	// Remember to add the output image file as an implicit dependency!
+	installedFilesJsonRule = pctx.AndroidStaticRule("installed_files_json", blueprint.RuleParams{
+		Command:     `${fileslist} ${rootDir} > ${out}`,
+		CommandDeps: []string{"${fileslist}"},
+	}, "rootDir")
+
+	installedFilesTxtRule = pctx.AndroidStaticRule("installed_files_txt", blueprint.RuleParams{
+		Command:     `build/make/tools/fileslist_util.py -c ${in} > ${out}`,
+		CommandDeps: []string{"build/make/tools/fileslist_util.py"},
+	})
+	fsConfigRule = pctx.AndroidStaticRule("fs_config_rule", blueprint.RuleParams{
+		Command:     `(cd ${rootDir}; find . -type d | sed 's,$$,/,'; find . \! -type d) | cut -c 3- | sort | sed 's,^,${prefix},' | ${fs_config} -C -D ${rootDir} -R "${prefix}" > ${out}`,
+		CommandDeps: []string{"${fs_config}"},
+	}, "rootDir", "prefix")
+)
 
 type filesystem struct {
 	android.ModuleBase
@@ -75,10 +99,12 @@
 	entries []string
 
 	filesystemBuilder filesystemBuilder
+
+	selinuxFc android.Path
 }
 
 type filesystemBuilder interface {
-	BuildLinkerConfigFile(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath)
+	BuildLinkerConfigFile(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath, fullInstallPaths *[]FullInstallPathInfo)
 	// Function that filters PackagingSpec in PackagingBase.GatherPackagingSpecs()
 	FilterPackagingSpec(spec android.PackagingSpec) bool
 	// Function that modifies PackagingSpec in PackagingBase.GatherPackagingSpecs() to customize.
@@ -98,6 +124,14 @@
 	Name   *string
 }
 
+// CopyWithNamePrefix returns a new [SymlinkDefinition] with prefix added to Name.
+func (s *SymlinkDefinition) CopyWithNamePrefix(prefix string) SymlinkDefinition {
+	return SymlinkDefinition{
+		Target: s.Target,
+		Name:   proptools.StringPtr(filepath.Join(prefix, proptools.String(s.Name))),
+	}
+}
+
 type FilesystemProperties struct {
 	// When set to true, sign the image with avbtool. Default is false.
 	Use_avb *bool
@@ -110,15 +144,21 @@
 	Avb_algorithm *string
 
 	// Hash algorithm used for avbtool (for descriptors). This is passed as hash_algorithm to
-	// avbtool. Default used by avbtool is sha1.
+	// avbtool. Default is sha256.
 	Avb_hash_algorithm *string
 
+	// The security patch passed to as the com.android.build.<type>.security_patch avb property.
+	Security_patch *string
+
 	// Whether or not to use forward-error-correction codes when signing with AVB. Defaults to true.
 	Use_fec *bool
 
 	// The index used to prevent rollback of the image. Only used if use_avb is true.
 	Rollback_index *int64
 
+	// Rollback index location of this image. Must be 1, 2, 3, etc.
+	Rollback_index_location *int64
+
 	// Name of the partition stored in vbmeta desc. Defaults to the name of this module.
 	Partition_name *string
 
@@ -144,6 +184,11 @@
 	// Directories to be created under root. e.g. /dev, /proc, etc.
 	Dirs proptools.Configurable[[]string]
 
+	// List of filesystem modules to include in creating the partition. The root directory of
+	// the provided filesystem modules are included in creating the partition.
+	// This is only supported for cpio and compressed cpio filesystem types.
+	Include_files_of []string
+
 	// Symbolic links to be created under root with "ln -sf <target> <name>".
 	Symlinks []SymlinkDefinition
 
@@ -157,12 +202,6 @@
 	// Mount point for this image. Default is "/"
 	Mount_point *string
 
-	// If set to the name of a partition ("system", "vendor", etc), this filesystem module
-	// will also include the contents of the make-built staging directories. If any soong
-	// modules would be installed to the same location as a make module, they will overwrite
-	// the make version.
-	Include_make_built_files string
-
 	// When set, builds etc/event-log-tags file by merging logtags from all dependencies.
 	// Default is false
 	Build_logtags *bool
@@ -194,6 +233,26 @@
 
 	// Additional dependencies used for building android products
 	Android_filesystem_deps AndroidFilesystemDeps
+
+	// Name of the output. Default is $(module_name).img
+	Stem *string
+
+	// The size of the partition on the device. It will be a build error if this built partition
+	// image exceeds this size.
+	Partition_size *int64
+
+	// Whether to format f2fs and ext4 in a way that supports casefolding
+	Support_casefolding *bool
+
+	// Whether to format f2fs and ext4 in a way that supports project quotas
+	Support_project_quota *bool
+
+	// Whether to enable per-file compression in f2fs
+	Enable_compression *bool
+
+	// Whether this partition is not supported by flashall.
+	// If true, this partition will not be included in the `updatedpackage` dist artifact.
+	No_flashall *bool
 }
 
 type AndroidFilesystemDeps struct {
@@ -270,6 +329,8 @@
 
 var interPartitionDependencyTag = interPartitionDepTag{}
 
+var interPartitionInstallDependencyTag = interPartitionDepTag{}
+
 var _ android.ExcludeFromVisibilityEnforcementTag = (*depTagWithVisibilityEnforcementBypass)(nil)
 
 func (t depTagWithVisibilityEnforcementBypass) ExcludeFromVisibilityEnforcement() {}
@@ -298,6 +359,9 @@
 	if f.properties.Android_filesystem_deps.System_ext != nil {
 		ctx.AddDependency(ctx.Module(), interPartitionDependencyTag, proptools.String(f.properties.Android_filesystem_deps.System_ext))
 	}
+	for _, partition := range f.properties.Include_files_of {
+		ctx.AddDependency(ctx.Module(), interPartitionInstallDependencyTag, partition)
+	}
 }
 
 type fsType int
@@ -315,13 +379,132 @@
 	return fs == unknown
 }
 
+// Type string that build_image.py accepts.
+func (t fsType) String() string {
+	switch t {
+	// TODO(372522486): add more types like f2fs, erofs, etc.
+	case ext4Type:
+		return "ext4"
+	case erofsType:
+		return "erofs"
+	case f2fsType:
+		return "f2fs"
+	}
+	panic(fmt.Errorf("unsupported fs type %d", t))
+}
+
+type InstalledFilesStruct struct {
+	Txt  android.Path
+	Json android.Path
+}
+
+type InstalledModuleInfo struct {
+	Name      string
+	Variation string
+}
+
 type FilesystemInfo struct {
+	// The built filesystem image
+	Output android.Path
+	// Returns the output file that is signed by avbtool. If this module is not signed, returns
+	// nil.
+	SignedOutputPath android.Path
+	// An additional hermetic filesystem image.
+	// e.g. this will contain inodes with pinned timestamps.
+	// This will be copied to target_files.zip
+	OutputHermetic android.Path
 	// A text file containing the list of paths installed on the partition.
 	FileListFile android.Path
+	// The root staging directory used to build the output filesystem. If consuming this, make sure
+	// to add a dependency on the Output file, as you cannot add dependencies on directories
+	// in ninja.
+	RootDir android.Path
+	// Extra root directories that are also built into the partition. Currently only used for
+	// including the recovery partition files into the vendor_boot image.
+	ExtraRootDirs android.Paths
+	// The rebased staging directory used to build the output filesystem. If consuming this, make
+	// sure to add a dependency on the Output file, as you cannot add dependencies on directories
+	// in ninja. In many cases this is the same as RootDir, only in the system partition is it
+	// different. There, it points to the "system" sub-directory of RootDir.
+	RebasedDir android.Path
+	// A text file with block data of the .img file
+	// This is an implicit output of `build_image`
+	MapFile android.Path
+	// Name of the module that produced this FilesystemInfo origionally. (though it may be
+	// re-exported by super images or boot images)
+	ModuleName string
+	// The property file generated by this module and passed to build_image.
+	// It's exported here so that system_other can reuse system's property file.
+	BuildImagePropFile android.Path
+	// Paths to all the tools referenced inside of the build image property file.
+	BuildImagePropFileDeps android.Paths
+	// Packaging specs to be installed on the system_other image, for the initial boot's dexpreopt.
+	SpecsForSystemOther map[string]android.PackagingSpec
+
+	FullInstallPaths []FullInstallPathInfo
+
+	// Installed files dep set of this module and its dependency filesystem modules
+	InstalledFilesDepSet depset.DepSet[InstalledFilesStruct]
+
+	// Path to compress hints file for erofs filesystems
+	// This will be nil for other fileystems like ext4
+	ErofsCompressHints android.Path
+
+	SelinuxFc android.Path
+
+	FilesystemConfig android.Path
+
+	Owners []InstalledModuleInfo
+
+	HasFsverity bool
+
+	PropFileForMiscInfo android.Path
+
+	// Additional avb and partition size information.
+	// `system_other` will use this information of `system` dep for misc_info.txt processing.
+	PartitionSize    *int64
+	UseAvb           bool
+	AvbAlgorithm     string
+	AvbHashAlgorithm string
+	AvbKey           android.Path
+	PartitionName    string
+	NoFlashall       bool
+	// HasOrIsRecovery returns true for recovery and for ramdisks with a recovery partition.
+	HasOrIsRecovery bool
+}
+
+// FullInstallPathInfo contains information about the "full install" paths of all the files
+// inside this partition. The full install paths are the files installed in
+// out/target/product/<device>/<partition>. This is essentially legacy behavior, maintained for
+// tools like adb sync and adevice, but we should update them to query the build system for the
+// installed files no matter where they are.
+type FullInstallPathInfo struct {
+	// RequiresFullInstall tells us if the origional module did the install to FullInstallPath
+	// already. If it's false, the android_device module needs to emit the install rule.
+	RequiresFullInstall bool
+	// The "full install" paths for the files in this filesystem. This is the paths in the
+	// out/target/product/<device>/<partition> folder. They're not used by this filesystem,
+	// but can be depended on by the top-level android_device module to cause the staging
+	// directories to be built.
+	FullInstallPath android.InstallPath
+
+	// The file that's copied to FullInstallPath. May be nil if SymlinkTarget is set or IsDir is
+	// true.
+	SourcePath android.Path
+
+	// The target of the symlink, if this file is a symlink.
+	SymlinkTarget string
+
+	// If this file is a directory. Only used for empty directories, which are mostly mount points.
+	IsDir bool
 }
 
 var FilesystemProvider = blueprint.NewProvider[FilesystemInfo]()
 
+type FilesystemDefaultsInfo struct{}
+
+var FilesystemDefaultsInfoProvider = blueprint.NewProvider[FilesystemDefaultsInfo]()
+
 func GetFsTypeFromString(ctx android.EarlyModuleContext, typeStr string) fsType {
 	switch typeStr {
 	case "ext4":
@@ -349,7 +532,7 @@
 }
 
 func (f *filesystem) installFileName() string {
-	return f.BaseModuleName() + ".img"
+	return proptools.StringDefault(f.properties.Stem, f.BaseModuleName()+".img")
 }
 
 func (f *filesystem) partitionName() string {
@@ -362,6 +545,13 @@
 	if ps.SkipInstall() {
 		return false
 	}
+	// "apex" is a fake partition used to install files in out/target/product/<device>/apex/.
+	// Don't include these files in the partition. We should also look into removing the following
+	// TODO to check the PackagingSpec's partition against this filesystem's partition for all
+	// modules, not just autogenerated ones, which will fix this as well.
+	if ps.Partition() == "apex" {
+		return false
+	}
 	if proptools.Bool(f.properties.Is_auto_generated) { // TODO (spandandas): Remove this.
 		pt := f.PartitionType()
 		return ps.Partition() == pt || strings.HasPrefix(ps.Partition(), pt+"/")
@@ -384,20 +574,104 @@
 	}
 }
 
-var pctx = android.NewPackageContext("android/soong/filesystem")
+func buildInstalledFiles(ctx android.ModuleContext, partition string, rootDir android.Path, image android.Path) InstalledFilesStruct {
+	fileName := "installed-files"
+	if len(partition) > 0 {
+		fileName += fmt.Sprintf("-%s", partition)
+	}
+	txt := android.PathForModuleOut(ctx, fmt.Sprintf("%s.txt", fileName))
+	json := android.PathForModuleOut(ctx, fmt.Sprintf("%s.json", fileName))
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        installedFilesJsonRule,
+		Implicit:    image,
+		Output:      json,
+		Description: "Installed file list json",
+		Args: map[string]string{
+			"rootDir": rootDir.String(),
+		},
+	})
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        installedFilesTxtRule,
+		Input:       json,
+		Output:      txt,
+		Description: "Installed file list txt",
+	})
+
+	return InstalledFilesStruct{
+		Txt:  txt,
+		Json: json,
+	}
+}
 
 func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	validatePartitionType(ctx, f)
 	if f.filesystemBuilder.ShouldUseVintfFragmentModuleOnly() {
 		f.validateVintfFragments(ctx)
 	}
+
+	if len(f.properties.Include_files_of) > 0 && !android.InList(f.fsType(ctx), []fsType{compressedCpioType, cpioType}) {
+		ctx.PropertyErrorf("include_files_of", "include_files_of is only supported for cpio and compressed cpio filesystem types.")
+	}
+
+	rootDir := android.PathForModuleOut(ctx, f.rootDirString()).OutputPath
+	rebasedDir := rootDir
+	if f.properties.Base_dir != nil {
+		rebasedDir = rootDir.Join(ctx, *f.properties.Base_dir)
+	}
+	builder := android.NewRuleBuilder(pctx, ctx)
+
+	// Wipe the root dir to get rid of leftover files from prior builds
+	builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir)
+	specs := f.gatherFilteredPackagingSpecs(ctx)
+
+	var fullInstallPaths []FullInstallPathInfo
+	for _, specRel := range android.SortedKeys(specs) {
+		spec := specs[specRel]
+		fullInstallPaths = append(fullInstallPaths, FullInstallPathInfo{
+			FullInstallPath:     spec.FullInstallPath(),
+			RequiresFullInstall: spec.RequiresFullInstall(),
+			SourcePath:          spec.SrcPath(),
+			SymlinkTarget:       spec.ToGob().SymlinkTarget,
+		})
+	}
+
+	f.entries = f.copyPackagingSpecs(ctx, builder, specs, rootDir, rebasedDir)
+	f.buildNonDepsFiles(ctx, builder, rootDir, rebasedDir, &fullInstallPaths)
+	f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir, &fullInstallPaths)
+	f.buildEventLogtagsFile(ctx, builder, rebasedDir, &fullInstallPaths)
+	f.buildAconfigFlagsFiles(ctx, builder, specs, rebasedDir, &fullInstallPaths)
+	f.filesystemBuilder.BuildLinkerConfigFile(ctx, builder, rebasedDir, &fullInstallPaths)
+	// Assemeble the staging dir and output a timestamp
+	builder.Command().Text("touch").Output(f.fileystemStagingDirTimestamp(ctx))
+	builder.Build("assemble_filesystem_staging_dir", fmt.Sprintf("Assemble filesystem staging dir %s", f.BaseModuleName()))
+
+	// Create a new rule builder for build_image
+	builder = android.NewRuleBuilder(pctx, ctx)
+	var mapFile android.Path
+	var outputHermetic android.WritablePath
+	var buildImagePropFile android.Path
+	var buildImagePropFileDeps android.Paths
+	var extraRootDirs android.Paths
+	var propFileForMiscInfo android.Path
 	switch f.fsType(ctx) {
 	case ext4Type, erofsType, f2fsType:
-		f.output = f.buildImageUsingBuildImage(ctx)
+		buildImagePropFile, buildImagePropFileDeps = f.buildPropFile(ctx)
+		propFileForMiscInfo = f.buildPropFileForMiscInfo(ctx)
+		output := android.PathForModuleOut(ctx, f.installFileName())
+		f.buildImageUsingBuildImage(ctx, builder, buildImageParams{rootDir, buildImagePropFile, buildImagePropFileDeps, output})
+		f.output = output
+		// Create the hermetic img file using a separate rule builder so that it can be built independently
+		hermeticBuilder := android.NewRuleBuilder(pctx, ctx)
+		outputHermetic = android.PathForModuleOut(ctx, "for_target_files", f.installFileName())
+		propFileHermetic := f.propFileForHermeticImg(ctx, hermeticBuilder, buildImagePropFile)
+		f.buildImageUsingBuildImage(ctx, hermeticBuilder, buildImageParams{rootDir, propFileHermetic, buildImagePropFileDeps, outputHermetic})
+		mapFile = f.getMapFile(ctx)
 	case compressedCpioType:
-		f.output = f.buildCpioImage(ctx, true)
+		f.output, extraRootDirs = f.buildCpioImage(ctx, builder, rootDir, true)
 	case cpioType:
-		f.output = f.buildCpioImage(ctx, false)
+		f.output, extraRootDirs = f.buildCpioImage(ctx, builder, rootDir, false)
 	default:
 		return
 	}
@@ -406,17 +680,146 @@
 	ctx.InstallFile(f.installDir, f.installFileName(), f.output)
 	ctx.SetOutputFiles([]android.Path{f.output}, "")
 
+	if f.partitionName() == "recovery" {
+		rootDir = rootDir.Join(ctx, "root")
+	}
+
 	fileListFile := android.PathForModuleOut(ctx, "fileList")
 	android.WriteFileRule(ctx, fileListFile, f.installedFilesList())
 
-	android.SetProvider(ctx, FilesystemProvider, FilesystemInfo{
-		FileListFile: fileListFile,
+	var partitionNameForInstalledFiles string
+	switch f.partitionName() {
+	case "system":
+		partitionNameForInstalledFiles = ""
+	case "vendor_ramdisk":
+		partitionNameForInstalledFiles = "vendor-ramdisk"
+	default:
+		partitionNameForInstalledFiles = f.partitionName()
+	}
+
+	var erofsCompressHints android.Path
+	if f.properties.Erofs.Compress_hints != nil {
+		erofsCompressHints = android.PathForModuleSrc(ctx, *f.properties.Erofs.Compress_hints)
+	}
+
+	fsInfo := FilesystemInfo{
+		Output:                 f.OutputPath(),
+		SignedOutputPath:       f.SignedOutputPath(),
+		OutputHermetic:         outputHermetic,
+		FileListFile:           fileListFile,
+		RootDir:                rootDir,
+		ExtraRootDirs:          extraRootDirs,
+		RebasedDir:             rebasedDir,
+		MapFile:                mapFile,
+		ModuleName:             ctx.ModuleName(),
+		BuildImagePropFile:     buildImagePropFile,
+		BuildImagePropFileDeps: buildImagePropFileDeps,
+		SpecsForSystemOther:    f.systemOtherFiles(ctx),
+		FullInstallPaths:       fullInstallPaths,
+		InstalledFilesDepSet: depset.New(
+			depset.POSTORDER,
+			[]InstalledFilesStruct{buildInstalledFiles(ctx, partitionNameForInstalledFiles, rootDir, f.output)},
+			includeFilesInstalledFiles(ctx),
+		),
+		ErofsCompressHints:  erofsCompressHints,
+		SelinuxFc:           f.selinuxFc,
+		FilesystemConfig:    f.generateFilesystemConfig(ctx, rootDir, rebasedDir),
+		Owners:              f.gatherOwners(specs),
+		HasFsverity:         f.properties.Fsverity.Inputs.GetOrDefault(ctx, nil) != nil,
+		PropFileForMiscInfo: propFileForMiscInfo,
+		PartitionSize:       f.properties.Partition_size,
+		PartitionName:       f.partitionName(),
+		HasOrIsRecovery:     f.hasOrIsRecovery(ctx),
+		NoFlashall:          proptools.Bool(f.properties.No_flashall),
+	}
+	if proptools.Bool(f.properties.Use_avb) {
+		fsInfo.UseAvb = true
+		fsInfo.AvbAlgorithm = proptools.StringDefault(f.properties.Avb_algorithm, "SHA256_RSA4096")
+		fsInfo.AvbHashAlgorithm = proptools.StringDefault(f.properties.Avb_hash_algorithm, "sha256")
+		if f.properties.Avb_private_key != nil {
+			fsInfo.AvbKey = android.PathForModuleSrc(ctx, *f.properties.Avb_private_key)
+		}
+	}
+
+	android.SetProvider(ctx, FilesystemProvider, fsInfo)
+
+	android.SetProvider(ctx, android.PartitionTypeInfoProvider, android.PartitionTypeInfo{
+		PartitionType: f.PartitionType(),
 	})
+
 	f.fileListFile = fileListFile
 
 	if proptools.Bool(f.properties.Unchecked_module) {
 		ctx.UncheckedModule()
 	}
+
+	f.setVbmetaPartitionProvider(ctx)
+
+	// Dump metadata that can not be done in android/compliance-metadata.go
+	complianceMetadataInfo := ctx.ComplianceMetadataInfo()
+	filesContained := make([]string, 0, len(fullInstallPaths))
+	for _, file := range fullInstallPaths {
+		filesContained = append(filesContained, file.FullInstallPath.String())
+	}
+	complianceMetadataInfo.SetFilesContained(filesContained)
+}
+
+func (f *filesystem) fileystemStagingDirTimestamp(ctx android.ModuleContext) android.WritablePath {
+	return android.PathForModuleOut(ctx, "staging_dir.timestamp")
+}
+
+func (f *filesystem) generateFilesystemConfig(ctx android.ModuleContext, rootDir android.Path, rebasedDir android.Path) android.Path {
+	rootDirString := rootDir.String()
+	prefix := f.partitionName() + "/"
+	if f.partitionName() == "system" {
+		rootDirString = rebasedDir.String()
+	}
+	if f.partitionName() == "ramdisk" || f.partitionName() == "recovery" {
+		// Hardcoded to match make behavior.
+		// https://cs.android.com/android/_/android/platform/build/+/2a0ef42a432d4da00201e8eb7697dcaa68fd2389:core/Makefile;l=6957-6962;drc=9ea8ad9232cef4d0a24d70133b1b9d2ce2defe5f;bpv=1;bpt=0
+		prefix = ""
+	}
+	out := android.PathForModuleOut(ctx, "filesystem_config.txt")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   fsConfigRule,
+		Input:  f.fileystemStagingDirTimestamp(ctx), // assemble the staging directory
+		Output: out,
+		Args: map[string]string{
+			"rootDir": rootDirString,
+			"prefix":  prefix,
+		},
+	})
+	return out
+}
+
+func (f *filesystem) setVbmetaPartitionProvider(ctx android.ModuleContext) {
+	var extractedPublicKey android.ModuleOutPath
+	if f.properties.Avb_private_key != nil {
+		key := android.PathForModuleSrc(ctx, proptools.String(f.properties.Avb_private_key))
+		extractedPublicKey = android.PathForModuleOut(ctx, f.partitionName()+".avbpubkey")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   extractPublicKeyRule,
+			Input:  key,
+			Output: extractedPublicKey,
+		})
+	}
+
+	var ril int
+	if f.properties.Rollback_index_location != nil {
+		ril = proptools.Int(f.properties.Rollback_index_location)
+	}
+
+	android.SetProvider(ctx, vbmetaPartitionProvider, vbmetaPartitionInfo{
+		Name:                  f.partitionName(),
+		RollbackIndexLocation: ril,
+		PublicKey:             extractedPublicKey,
+		Output:                f.output,
+	})
+}
+
+func (f *filesystem) getMapFile(ctx android.ModuleContext) android.WritablePath {
+	// create the filepath by replacing the extension of the corresponding img file
+	return android.PathForModuleOut(ctx, f.installFileName()).ReplaceExtension(ctx, "map")
 }
 
 func (f *filesystem) validateVintfFragments(ctx android.ModuleContext) {
@@ -457,7 +860,7 @@
 }
 
 func (f *filesystem) appendToEntry(ctx android.ModuleContext, installedFile android.Path) {
-	partitionBaseDir := android.PathForModuleOut(ctx, "root", proptools.String(f.properties.Base_dir)).String() + "/"
+	partitionBaseDir := android.PathForModuleOut(ctx, f.rootDirString(), proptools.String(f.properties.Base_dir)).String() + "/"
 
 	relPath, inTargetPartition := strings.CutPrefix(installedFile.String(), partitionBaseDir)
 	if inTargetPartition {
@@ -477,12 +880,13 @@
 		ctx.PropertyErrorf("partition_type", "partition_type must be one of %s, found: %s", validPartitions, p.PartitionType())
 	}
 
-	ctx.VisitDirectDepsWithTag(android.DefaultsDepTag, func(m android.Module) {
-		if fdm, ok := m.(*filesystemDefaults); ok {
-			if p.PartitionType() != fdm.PartitionType() {
+	ctx.VisitDirectDepsProxyWithTag(android.DefaultsDepTag, func(m android.ModuleProxy) {
+		if _, ok := android.OtherModuleProvider(ctx, m, FilesystemDefaultsInfoProvider); ok {
+			partitionInfo := android.OtherModuleProviderOrDefault(ctx, m, android.PartitionTypeInfoProvider)
+			if p.PartitionType() != partitionInfo.PartitionType {
 				ctx.PropertyErrorf("partition_type",
 					"%s doesn't match with the partition type %s of the filesystem default module %s",
-					p.PartitionType(), fdm.PartitionType(), m.Name())
+					p.PartitionType(), partitionInfo.PartitionType, m.Name())
 			}
 		}
 	})
@@ -490,11 +894,36 @@
 
 // Copy extra files/dirs that are not from the `deps` property to `rootDir`, checking for conflicts with files
 // already in `rootDir`.
-func (f *filesystem) buildNonDepsFiles(ctx android.ModuleContext, builder *android.RuleBuilder, rootDir android.OutputPath) {
+func (f *filesystem) buildNonDepsFiles(
+	ctx android.ModuleContext,
+	builder *android.RuleBuilder,
+	rootDir android.OutputPath,
+	rebasedDir android.OutputPath,
+	fullInstallPaths *[]FullInstallPathInfo,
+) {
+	rebasedPrefix, err := filepath.Rel(rootDir.String(), rebasedDir.String())
+	if err != nil || strings.HasPrefix(rebasedPrefix, "../") {
+		panic("rebasedDir could not be made relative to rootDir")
+	}
+	if !strings.HasSuffix(rebasedPrefix, "/") {
+		rebasedPrefix += "/"
+	}
+	if rebasedPrefix == "./" {
+		rebasedPrefix = ""
+	}
+
 	// create dirs and symlinks
 	for _, dir := range f.properties.Dirs.GetOrDefault(ctx, nil) {
 		// OutputPath.Join verifies dir
 		builder.Command().Text("mkdir -p").Text(rootDir.Join(ctx, dir).String())
+		// Only add the fullInstallPath logic for files in the rebased dir. The root dir
+		// is harder to install to.
+		if strings.HasPrefix(dir, rebasedPrefix) {
+			*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+				FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), strings.TrimPrefix(dir, rebasedPrefix)),
+				IsDir:           true,
+			})
+		}
 	}
 
 	for _, symlink := range f.properties.Symlinks {
@@ -517,6 +946,31 @@
 		builder.Command().Text("mkdir -p").Text(filepath.Dir(dst.String()))
 		builder.Command().Text("ln -sf").Text(proptools.ShellEscape(target)).Text(dst.String())
 		f.appendToEntry(ctx, dst)
+		// Add the fullInstallPath logic for files in the rebased dir, and for non-rebased files in "system" partition
+		// the fullInstallPath is changed to "root" which aligns to the behavior in Make.
+		if f.PartitionType() == "system" {
+			installPath := android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), strings.TrimPrefix(name, rebasedPrefix))
+			if !strings.HasPrefix(name, rebasedPrefix) {
+				installPath = android.PathForModuleInPartitionInstall(ctx, "root", name)
+			}
+			*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+				FullInstallPath: installPath,
+				SymlinkTarget:   target,
+			})
+		} else {
+			if strings.HasPrefix(name, rebasedPrefix) {
+				*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+					FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), strings.TrimPrefix(name, rebasedPrefix)),
+					SymlinkTarget:   target,
+				})
+			}
+		}
+	}
+
+	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2835;drc=b186569ef00ff2f2a1fab28aedc75ebc32bcd67b
+	if f.partitionName() == "recovery" {
+		builder.Command().Text("mkdir -p").Text(rootDir.Join(ctx, "root/linkerconfig").String())
+		builder.Command().Text("touch").Text(rootDir.Join(ctx, "root/linkerconfig/ld.config.txt").String())
 	}
 }
 
@@ -536,46 +990,35 @@
 	dirsToSpecs[rootDir] = rootDirSpecs
 	dirsToSpecs[rebasedDir] = rebasedDirSpecs
 
-	return f.CopySpecsToDirs(ctx, builder, dirsToSpecs)
+	// Preserve timestamps for adb sync, so that this staging dir file matches the timestamp in the
+	// out/target/product staging directory.
+	return f.CopySpecsToDirs(ctx, builder, dirsToSpecs, true)
 }
 
-func (f *filesystem) copyFilesToProductOut(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) {
-	if f.Name() != ctx.Config().SoongDefinedSystemImage() {
-		return
-	}
-	installPath := android.PathForModuleInPartitionInstall(ctx, f.partitionName())
-	builder.Command().Textf("cp -prf %s/* %s", rebasedDir, installPath)
+func (f *filesystem) rootDirString() string {
+	return f.partitionName()
 }
 
-func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) android.Path {
-	rootDir := android.PathForModuleOut(ctx, "root").OutputPath
-	rebasedDir := rootDir
-	if f.properties.Base_dir != nil {
-		rebasedDir = rootDir.Join(ctx, *f.properties.Base_dir)
-	}
-	builder := android.NewRuleBuilder(pctx, ctx)
-	// Wipe the root dir to get rid of leftover files from prior builds
-	builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir)
-	specs := f.gatherFilteredPackagingSpecs(ctx)
-	f.entries = f.copyPackagingSpecs(ctx, builder, specs, rootDir, rebasedDir)
+type buildImageParams struct {
+	// inputs
+	rootDir  android.OutputPath
+	propFile android.Path
+	toolDeps android.Paths
+	// outputs
+	output android.WritablePath
+}
 
-	f.buildNonDepsFiles(ctx, builder, rootDir)
-	f.addMakeBuiltFiles(ctx, builder, rootDir)
-	f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir)
-	f.buildEventLogtagsFile(ctx, builder, rebasedDir)
-	f.buildAconfigFlagsFiles(ctx, builder, specs, rebasedDir)
-	f.filesystemBuilder.BuildLinkerConfigFile(ctx, builder, rebasedDir)
-	f.copyFilesToProductOut(ctx, builder, rebasedDir)
-
+func (f *filesystem) buildImageUsingBuildImage(
+	ctx android.ModuleContext,
+	builder *android.RuleBuilder,
+	params buildImageParams) {
 	// run host_init_verifier
 	// Ideally we should have a concept of pluggable linters that verify the generated image.
 	// While such concept is not implement this will do.
 	// TODO(b/263574231): substitute with pluggable linter.
 	builder.Command().
 		BuiltTool("host_init_verifier").
-		FlagWithArg("--out_system=", rootDir.String()+"/system")
-
-	propFile, toolDeps := f.buildPropFile(ctx)
+		FlagWithArg("--out_system=", params.rootDir.String()+"/system")
 
 	// Most of the time, if build_image were to call a host tool, it accepts the path to the
 	// host tool in a field in the prop file. However, it doesn't have that option for fec, which
@@ -583,21 +1026,32 @@
 	fec := ctx.Config().HostToolPath(ctx, "fec")
 	pathToolDirs := []string{filepath.Dir(fec.String())}
 
-	output := android.PathForModuleOut(ctx, f.installFileName())
 	builder.Command().
 		Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
 		BuiltTool("build_image").
-		Text(rootDir.String()). // input directory
-		Input(propFile).
-		Implicits(toolDeps).
+		Text(params.rootDir.String()). // input directory
+		Input(params.propFile).
+		Implicits(params.toolDeps).
 		Implicit(fec).
-		Output(output).
-		Text(rootDir.String()) // directory where to find fs_config_files|dirs
+		Implicit(f.fileystemStagingDirTimestamp(ctx)). // assemble the staging directory
+		Output(params.output).
+		Text(params.rootDir.String()) // directory where to find fs_config_files|dirs
+
+	if f.properties.Partition_size != nil {
+		assertMaxImageSize(builder, params.output, *f.properties.Partition_size, false)
+	}
 
 	// rootDir is not deleted. Might be useful for quick inspection.
-	builder.Build("build_filesystem_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
+	builder.Build("build_"+params.output.String(), fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
+}
 
-	return output
+func (f *filesystem) propFileForHermeticImg(ctx android.ModuleContext, builder *android.RuleBuilder, inputPropFile android.Path) android.Path {
+	propFilePinnedTimestamp := android.PathForModuleOut(ctx, "for_target_files", "prop")
+	builder.Command().Textf("cat").Input(inputPropFile).Flag(">").Output(propFilePinnedTimestamp).
+		Textf(" && echo use_fixed_timestamp=true >> %s", propFilePinnedTimestamp).
+		Textf(" && echo block_list=%s >> %s", f.getMapFile(ctx).String(), propFilePinnedTimestamp) // mapfile will be an implicit output
+	builder.Command().Text("touch").Output(f.getMapFile(ctx))
+	return propFilePinnedTimestamp
 }
 
 func (f *filesystem) buildFileContexts(ctx android.ModuleContext) android.Path {
@@ -610,40 +1064,18 @@
 	return fcBin
 }
 
-// Calculates avb_salt from entry list (sorted) for deterministic output.
-func (f *filesystem) salt() string {
-	return sha1sum(f.entries)
-}
-
 func (f *filesystem) buildPropFile(ctx android.ModuleContext) (android.Path, android.Paths) {
 	var deps android.Paths
-	var propFileString strings.Builder
+	var lines []string
 	addStr := func(name string, value string) {
-		propFileString.WriteString(name)
-		propFileString.WriteRune('=')
-		propFileString.WriteString(value)
-		propFileString.WriteRune('\n')
+		lines = append(lines, fmt.Sprintf("%s=%s", name, value))
 	}
 	addPath := func(name string, path android.Path) {
 		addStr(name, path.String())
 		deps = append(deps, path)
 	}
 
-	// Type string that build_image.py accepts.
-	fsTypeStr := func(t fsType) string {
-		switch t {
-		// TODO(372522486): add more types like f2fs, erofs, etc.
-		case ext4Type:
-			return "ext4"
-		case erofsType:
-			return "erofs"
-		case f2fsType:
-			return "f2fs"
-		}
-		panic(fmt.Errorf("unsupported fs type %v", t))
-	}
-
-	addStr("fs_type", fsTypeStr(f.fsType(ctx)))
+	addStr("fs_type", f.fsType(ctx).String())
 	addStr("mount_point", proptools.StringDefault(f.properties.Mount_point, "/"))
 	addStr("use_dynamic_partition_size", "true")
 	addPath("ext_mkuserimg", ctx.Config().HostToolPath(ctx, "mkuserimg_mke2fs"))
@@ -662,51 +1094,35 @@
 			addPath("avb_key_path", key)
 		}
 		addStr("partition_name", f.partitionName())
-		avb_add_hashtree_footer_args := ""
-		if !proptools.BoolDefault(f.properties.Use_fec, true) {
-			avb_add_hashtree_footer_args += " --do_not_generate_fec"
-		}
-		if hashAlgorithm := proptools.String(f.properties.Avb_hash_algorithm); hashAlgorithm != "" {
-			avb_add_hashtree_footer_args += " --hash_algorithm " + hashAlgorithm
-		}
-		if f.properties.Rollback_index != nil {
-			rollbackIndex := proptools.Int(f.properties.Rollback_index)
-			if rollbackIndex < 0 {
-				ctx.PropertyErrorf("rollback_index", "Rollback index must be non-negative")
-			}
-			avb_add_hashtree_footer_args += " --rollback_index " + strconv.Itoa(rollbackIndex)
-		}
-		avb_add_hashtree_footer_args += fmt.Sprintf(" --prop com.android.build.%s.os_version:%s", f.partitionName(), ctx.Config().PlatformVersionLastStable())
-		avb_add_hashtree_footer_args += fmt.Sprintf(" --prop com.android.build.%s.fingerprint:{CONTENTS_OF:%s}", f.partitionName(), ctx.Config().BuildFingerprintFile(ctx))
-		avb_add_hashtree_footer_args += fmt.Sprintf(" --prop com.android.build.%s.security_patch:%s", f.partitionName(), ctx.Config().PlatformSecurityPatch())
-		addStr("avb_add_hashtree_footer_args", avb_add_hashtree_footer_args)
-		addStr("avb_salt", f.salt())
+		addStr("avb_add_hashtree_footer_args", f.getAvbAddHashtreeFooterArgs(ctx))
 	}
 
 	if f.properties.File_contexts != nil && f.properties.Precompiled_file_contexts != nil {
 		ctx.ModuleErrorf("file_contexts and precompiled_file_contexts cannot both be set")
 	} else if f.properties.File_contexts != nil {
-		addPath("selinux_fc", f.buildFileContexts(ctx))
+		f.selinuxFc = f.buildFileContexts(ctx)
 	} else if f.properties.Precompiled_file_contexts != nil {
-		src := android.PathForModuleSrc(ctx, *f.properties.Precompiled_file_contexts)
-		if src != nil {
-			addPath("selinux_fc", src)
-		}
+		f.selinuxFc = android.PathForModuleSrc(ctx, *f.properties.Precompiled_file_contexts)
+	}
+	if f.selinuxFc != nil {
+		addPath("selinux_fc", f.selinuxFc)
 	}
 	if timestamp := proptools.String(f.properties.Fake_timestamp); timestamp != "" {
 		addStr("timestamp", timestamp)
+	} else if ctx.Config().Getenv("USE_FIXED_TIMESTAMP_IMG_FILES") == "true" {
+		addStr("use_fixed_timestamp", "true")
 	}
+
 	if uuid := proptools.String(f.properties.Uuid); uuid != "" {
 		addStr("uuid", uuid)
 		addStr("hash_seed", uuid)
 	}
 
-	// TODO(b/381120092): This should only be added if none of the size-related properties are set,
-	// but currently soong built partitions don't have size properties. Make code:
-	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2262;drc=39cd33701c9278db0e7e481a090605f428d5b12d
-	// Make uses system_disable_sparse but disable_sparse has the same effect, and we shouldn't need
-	// to qualify it because each partition gets its own property file built.
-	addStr("disable_sparse", "true")
+	// Disable sparse only when partition size is not defined. disable_sparse has the same
+	// effect as <partition name>_disable_sparse.
+	if f.properties.Partition_size == nil {
+		addStr("disable_sparse", "true")
+	}
 
 	fst := f.fsType(ctx)
 	switch fst {
@@ -727,20 +1143,135 @@
 			addStr("f2fs_sparse_flag", "-S")
 		}
 	}
-	f.checkFsTypePropertyError(ctx, fst, fsTypeStr(fst))
+	f.checkFsTypePropertyError(ctx, fst, fst.String())
+
+	if f.properties.Partition_size != nil {
+		addStr("partition_size", strconv.FormatInt(*f.properties.Partition_size, 10))
+	}
+
+	if proptools.BoolDefault(f.properties.Support_casefolding, false) {
+		addStr("needs_casefold", "1")
+	}
+
+	if proptools.BoolDefault(f.properties.Support_project_quota, false) {
+		addStr("needs_projid", "1")
+	}
+
+	if proptools.BoolDefault(f.properties.Enable_compression, false) {
+		addStr("needs_compress", "1")
+	}
+
+	sort.Strings(lines)
 
 	propFilePreProcessing := android.PathForModuleOut(ctx, "prop_pre_processing")
-	android.WriteFileRuleVerbatim(ctx, propFilePreProcessing, propFileString.String())
+	android.WriteFileRule(ctx, propFilePreProcessing, strings.Join(lines, "\n"))
 	propFile := android.PathForModuleOut(ctx, "prop")
 	ctx.Build(pctx, android.BuildParams{
-		Rule:     textFileProcessorRule,
-		Input:    propFilePreProcessing,
-		Output:   propFile,
-		Implicit: ctx.Config().BuildFingerprintFile(ctx),
+		Rule:   textFileProcessorRule,
+		Input:  propFilePreProcessing,
+		Output: propFile,
 	})
 	return propFile, deps
 }
 
+func (f *filesystem) buildPropFileForMiscInfo(ctx android.ModuleContext) android.Path {
+	var lines []string
+	addStr := func(name string, value string) {
+		lines = append(lines, fmt.Sprintf("%s=%s", name, value))
+	}
+
+	addStr("use_dynamic_partition_size", "true")
+	addStr("ext_mkuserimg", "mkuserimg_mke2fs")
+
+	addStr("building_"+f.partitionName()+"_image", "true")
+	addStr(f.partitionName()+"_fs_type", f.fsType(ctx).String())
+
+	if proptools.Bool(f.properties.Use_avb) {
+		addStr("avb_"+f.partitionName()+"_hashtree_enable", "true")
+		addStr("avb_"+f.partitionName()+"_add_hashtree_footer_args", strings.TrimSpace(f.getAvbAddHashtreeFooterArgs(ctx)))
+	}
+
+	if f.selinuxFc != nil {
+		addStr(f.partitionName()+"_selinux_fc", f.selinuxFc.String())
+	}
+
+	// Disable sparse only when partition size is not defined. disable_sparse has the same
+	// effect as <partition name>_disable_sparse.
+	if f.properties.Partition_size == nil {
+		addStr(f.partitionName()+"_disable_sparse", "true")
+	} else if f.partitionName() == "userdata" {
+		// Add userdata's partition size to misc_info.txt.
+		// userdata has been special-cased to make the make packaging misc_info.txt implementation
+		addStr("userdata_size", strconv.FormatInt(*f.properties.Partition_size, 10))
+	}
+
+	fst := f.fsType(ctx)
+	switch fst {
+	case erofsType:
+		// Add erofs properties
+		addStr("erofs_default_compressor", proptools.StringDefault(f.properties.Erofs.Compressor, "lz4hc,9"))
+		if proptools.BoolDefault(f.properties.Erofs.Sparse, true) {
+			// https://source.corp.google.com/h/googleplex-android/platform/build/+/88b1c67239ca545b11580237242774b411f2fed9:core/Makefile;l=2292;bpv=1;bpt=0;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b
+			addStr("erofs_sparse_flag", "-s")
+		}
+	case f2fsType:
+		if proptools.BoolDefault(f.properties.F2fs.Sparse, true) {
+			// https://source.corp.google.com/h/googleplex-android/platform/build/+/88b1c67239ca545b11580237242774b411f2fed9:core/Makefile;l=2294;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b;bpv=1;bpt=0
+			addStr("f2fs_sparse_flag", "-S")
+		}
+	}
+
+	if proptools.BoolDefault(f.properties.Support_casefolding, false) {
+		addStr("needs_casefold", "1")
+	}
+
+	if proptools.BoolDefault(f.properties.Support_project_quota, false) {
+		addStr("needs_projid", "1")
+	}
+
+	if proptools.BoolDefault(f.properties.Enable_compression, false) {
+		addStr("needs_compress", "1")
+	}
+
+	sort.Strings(lines)
+
+	propFilePreProcessing := android.PathForModuleOut(ctx, "prop_misc_info_pre_processing")
+	android.WriteFileRule(ctx, propFilePreProcessing, strings.Join(lines, "\n"))
+	propFile := android.PathForModuleOut(ctx, "prop_file_for_misc_info")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   textFileProcessorRule,
+		Input:  propFilePreProcessing,
+		Output: propFile,
+	})
+
+	return propFile
+}
+
+func (f *filesystem) getAvbAddHashtreeFooterArgs(ctx android.ModuleContext) string {
+	avb_add_hashtree_footer_args := ""
+	if !proptools.BoolDefault(f.properties.Use_fec, true) {
+		avb_add_hashtree_footer_args += " --do_not_generate_fec"
+	}
+	hashAlgorithm := proptools.StringDefault(f.properties.Avb_hash_algorithm, "sha256")
+	avb_add_hashtree_footer_args += " --hash_algorithm " + hashAlgorithm
+	if f.properties.Rollback_index != nil {
+		rollbackIndex := proptools.Int(f.properties.Rollback_index)
+		if rollbackIndex < 0 {
+			ctx.PropertyErrorf("rollback_index", "Rollback index must be non-negative")
+		}
+		avb_add_hashtree_footer_args += " --rollback_index " + strconv.Itoa(rollbackIndex)
+	}
+	avb_add_hashtree_footer_args += fmt.Sprintf(" --prop com.android.build.%s.os_version:%s", f.partitionName(), ctx.Config().PlatformVersionLastStable())
+	// We're not going to add BuildFingerPrintFile as a dep. If it changed, it's likely because
+	// the build number changed, and we don't want to trigger rebuilds solely based on the build
+	// number.
+	avb_add_hashtree_footer_args += fmt.Sprintf(" --prop com.android.build.%s.fingerprint:{CONTENTS_OF:%s}", f.partitionName(), ctx.Config().BuildFingerprintFile(ctx))
+	if f.properties.Security_patch != nil && proptools.String(f.properties.Security_patch) != "" {
+		avb_add_hashtree_footer_args += fmt.Sprintf(" --prop com.android.build.%s.security_patch:%s", f.partitionName(), proptools.String(f.properties.Security_patch))
+	}
+	return avb_add_hashtree_footer_args
+}
+
 // This method checks if there is any property set for the fstype(s) other than
 // the current fstype.
 func (f *filesystem) checkFsTypePropertyError(ctx android.ModuleContext, t fsType, fs string) {
@@ -761,7 +1292,47 @@
 	}
 }
 
-func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool) android.Path {
+func includeFilesRootDir(ctx android.ModuleContext) (rootDirs android.Paths, partitions android.Paths) {
+	ctx.VisitDirectDepsWithTag(interPartitionInstallDependencyTag, func(m android.Module) {
+		if fsProvider, ok := android.OtherModuleProvider(ctx, m, FilesystemProvider); ok {
+			rootDirs = append(rootDirs, fsProvider.RootDir)
+			partitions = append(partitions, fsProvider.Output)
+		} else {
+			ctx.PropertyErrorf("include_files_of", "only filesystem modules can be listed in "+
+				"include_files_of but %s is not a filesystem module", m.Name())
+		}
+	})
+	return rootDirs, partitions
+}
+
+func includeFilesInstalledFiles(ctx android.ModuleContext) (ret []depset.DepSet[InstalledFilesStruct]) {
+	ctx.VisitDirectDepsWithTag(interPartitionInstallDependencyTag, func(m android.Module) {
+		if fsProvider, ok := android.OtherModuleProvider(ctx, m, FilesystemProvider); ok {
+			ret = append(ret, fsProvider.InstalledFilesDepSet)
+		}
+	})
+	return
+}
+
+func (f *filesystem) hasOrIsRecovery(ctx android.ModuleContext) bool {
+	if f.partitionName() == "recovery" {
+		return true
+	}
+	ret := false
+	ctx.VisitDirectDepsWithTag(interPartitionInstallDependencyTag, func(m android.Module) {
+		if fsProvider, ok := android.OtherModuleProvider(ctx, m, FilesystemProvider); ok && fsProvider.PartitionName == "recovery" {
+			ret = true
+		}
+	})
+	return ret
+}
+
+func (f *filesystem) buildCpioImage(
+	ctx android.ModuleContext,
+	builder *android.RuleBuilder,
+	rootDir android.OutputPath,
+	compressed bool,
+) (android.Path, android.Paths) {
 	if proptools.Bool(f.properties.Use_avb) {
 		ctx.PropertyErrorf("use_avb", "signing compresed cpio image using avbtool is not supported."+
 			"Consider adding this to bootimg module and signing the entire boot image.")
@@ -771,32 +1342,19 @@
 		ctx.PropertyErrorf("file_contexts", "file_contexts is not supported for compressed cpio image.")
 	}
 
-	if f.properties.Include_make_built_files != "" {
-		ctx.PropertyErrorf("include_make_built_files", "include_make_built_files is not supported for compressed cpio image.")
-	}
-
-	rootDir := android.PathForModuleOut(ctx, "root").OutputPath
-	rebasedDir := rootDir
-	if f.properties.Base_dir != nil {
-		rebasedDir = rootDir.Join(ctx, *f.properties.Base_dir)
-	}
-	builder := android.NewRuleBuilder(pctx, ctx)
-	// Wipe the root dir to get rid of leftover files from prior builds
-	builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir)
-	specs := f.gatherFilteredPackagingSpecs(ctx)
-	f.entries = f.copyPackagingSpecs(ctx, builder, specs, rootDir, rebasedDir)
-
-	f.buildNonDepsFiles(ctx, builder, rootDir)
-	f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir)
-	f.buildEventLogtagsFile(ctx, builder, rebasedDir)
-	f.buildAconfigFlagsFiles(ctx, builder, specs, rebasedDir)
-	f.filesystemBuilder.BuildLinkerConfigFile(ctx, builder, rebasedDir)
-	f.copyFilesToProductOut(ctx, builder, rebasedDir)
+	rootDirs, partitions := includeFilesRootDir(ctx)
 
 	output := android.PathForModuleOut(ctx, f.installFileName())
 	cmd := builder.Command().
 		BuiltTool("mkbootfs").
+		Implicit(f.fileystemStagingDirTimestamp(ctx)).
 		Text(rootDir.String()) // input directory
+
+	for i := range len(rootDirs) {
+		cmd.Text(rootDirs[i].String())
+	}
+	cmd.Implicits(partitions)
+
 	if nodeList := f.properties.Dev_nodes_description_file; nodeList != nil {
 		cmd.FlagWithInput("-n ", android.PathForModuleSrc(ctx, proptools.String(nodeList)))
 	}
@@ -814,7 +1372,7 @@
 	// rootDir is not deleted. Might be useful for quick inspection.
 	builder.Build("build_cpio_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
 
-	return output
+	return output, rootDirs
 }
 
 var validPartitions = []string{
@@ -834,68 +1392,49 @@
 	"recovery",
 }
 
-func (f *filesystem) addMakeBuiltFiles(ctx android.ModuleContext, builder *android.RuleBuilder, rootDir android.Path) {
-	partition := f.properties.Include_make_built_files
-	if partition == "" {
-		return
-	}
-	if !slices.Contains(validPartitions, partition) {
-		ctx.PropertyErrorf("include_make_built_files", "Expected one of %#v, found %q", validPartitions, partition)
-		return
-	}
-	stampFile := fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/staging_dir.stamp", ctx.Config().DeviceName(), partition)
-	fileListFile := fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/file_list.txt", ctx.Config().DeviceName(), partition)
-	stagingDir := fmt.Sprintf("target/product/%s/%s", ctx.Config().DeviceName(), partition)
-
-	builder.Command().BuiltTool("merge_directories").
-		Implicit(android.PathForArbitraryOutput(ctx, stampFile)).
-		Text("--ignore-duplicates").
-		FlagWithInput("--file-list", android.PathForArbitraryOutput(ctx, fileListFile)).
-		Text(rootDir.String()).
-		Text(android.PathForArbitraryOutput(ctx, stagingDir).String())
-}
-
-func (f *filesystem) buildEventLogtagsFile(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) {
+func (f *filesystem) buildEventLogtagsFile(
+	ctx android.ModuleContext,
+	builder *android.RuleBuilder,
+	rebasedDir android.OutputPath,
+	fullInstallPaths *[]FullInstallPathInfo,
+) {
 	if !proptools.Bool(f.properties.Build_logtags) {
 		return
 	}
 
-	logtagsFilePaths := make(map[string]bool)
-	ctx.WalkDeps(func(child, parent android.Module) bool {
-		if logtagsInfo, ok := android.OtherModuleProvider(ctx, child, android.LogtagsProviderKey); ok {
-			for _, path := range logtagsInfo.Logtags {
-				logtagsFilePaths[path.String()] = true
-			}
-		}
-		return true
-	})
-
-	if len(logtagsFilePaths) == 0 {
-		return
-	}
-
 	etcPath := rebasedDir.Join(ctx, "etc")
 	eventLogtagsPath := etcPath.Join(ctx, "event-log-tags")
 	builder.Command().Text("mkdir").Flag("-p").Text(etcPath.String())
-	cmd := builder.Command().BuiltTool("merge-event-log-tags").
-		FlagWithArg("-o ", eventLogtagsPath.String()).
-		FlagWithInput("-m ", android.MergedLogtagsPath(ctx))
+	builder.Command().Text("cp").Input(android.MergedLogtagsPath(ctx)).Text(eventLogtagsPath.String())
 
-	for _, path := range android.SortedKeys(logtagsFilePaths) {
-		cmd.Text(path)
-	}
+	*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+		FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), "etc", "event-log-tags"),
+		SourcePath:      android.MergedLogtagsPath(ctx),
+	})
 
 	f.appendToEntry(ctx, eventLogtagsPath)
 }
 
-func (f *filesystem) BuildLinkerConfigFile(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) {
+func (f *filesystem) BuildLinkerConfigFile(
+	ctx android.ModuleContext,
+	builder *android.RuleBuilder,
+	rebasedDir android.OutputPath,
+	fullInstallPaths *[]FullInstallPathInfo,
+) {
 	if !proptools.Bool(f.properties.Linker_config.Gen_linker_config) {
 		return
 	}
 
 	provideModules, _ := f.getLibsForLinkerConfig(ctx)
+	intermediateOutput := android.PathForModuleOut(ctx, "linker.config.pb")
+	linkerconfig.BuildLinkerConfig(ctx, android.PathsForModuleSrc(ctx, f.properties.Linker_config.Linker_config_srcs), provideModules, nil, intermediateOutput)
 	output := rebasedDir.Join(ctx, "etc", "linker.config.pb")
-	linkerconfig.BuildLinkerConfig(ctx, builder, android.PathsForModuleSrc(ctx, f.properties.Linker_config.Linker_config_srcs), provideModules, nil, output)
+	builder.Command().Text("cp").Input(intermediateOutput).Output(output)
+
+	*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+		FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), "etc", "linker.config.pb"),
+		SourcePath:      intermediateOutput,
+	})
 
 	f.appendToEntry(ctx, output)
 }
@@ -959,8 +1498,33 @@
 // Note that "apex" module installs its contents to "apex"(fake partition) as well
 // for symbol lookup by imitating "activated" paths.
 func (f *filesystem) gatherFilteredPackagingSpecs(ctx android.ModuleContext) map[string]android.PackagingSpec {
-	specs := f.PackagingBase.GatherPackagingSpecsWithFilterAndModifier(ctx, f.filesystemBuilder.FilterPackagingSpec, f.filesystemBuilder.ModifyPackagingSpec)
-	return specs
+	return f.PackagingBase.GatherPackagingSpecsWithFilterAndModifier(ctx, f.filesystemBuilder.FilterPackagingSpec, f.filesystemBuilder.ModifyPackagingSpec)
+}
+
+func (f *filesystem) gatherOwners(specs map[string]android.PackagingSpec) []InstalledModuleInfo {
+	var owners []InstalledModuleInfo
+	for _, p := range android.SortedKeys(specs) {
+		spec := specs[p]
+		owners = append(owners, InstalledModuleInfo{
+			Name:      spec.Owner(),
+			Variation: spec.Variation(),
+		})
+	}
+	return owners
+}
+
+// Dexpreopt files are installed to system_other. Collect the packaingSpecs for the dexpreopt files
+// from this partition to export to the system_other partition later.
+func (f *filesystem) systemOtherFiles(ctx android.ModuleContext) map[string]android.PackagingSpec {
+	filter := func(spec android.PackagingSpec) bool {
+		// For some reason system_other packaging specs don't set the partition field.
+		return strings.HasPrefix(spec.RelPathInPackage(), "system_other/")
+	}
+	modifier := func(spec *android.PackagingSpec) {
+		spec.SetRelPathInPackage(strings.TrimPrefix(spec.RelPathInPackage(), "system_other/"))
+		spec.SetPartition("system_other")
+	}
+	return f.PackagingBase.GatherPackagingSpecsWithFilterAndModifier(ctx, filter, modifier)
 }
 
 func sha1sum(values []string) string {
@@ -1005,6 +1569,10 @@
 
 func (f *filesystemDefaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	validatePartitionType(ctx, f)
+	android.SetProvider(ctx, FilesystemDefaultsInfoProvider, FilesystemDefaultsInfo{})
+	android.SetProvider(ctx, android.PartitionTypeInfoProvider, android.PartitionTypeInfo{
+		PartitionType: f.PartitionType(),
+	})
 }
 
 // getLibsForLinkerConfig returns
@@ -1014,13 +1582,16 @@
 // `linkerconfig.BuildLinkerConfig` will convert these two to a linker.config.pb for the filesystem
 // (1) will be added to --provideLibs if they are C libraries with a stable interface (has stubs)
 // (2) will be added to --requireLibs if they are C libraries with a stable interface (has stubs)
-func (f *filesystem) getLibsForLinkerConfig(ctx android.ModuleContext) ([]android.Module, []android.Module) {
+func (f *filesystem) getLibsForLinkerConfig(ctx android.ModuleContext) ([]android.ModuleProxy, []android.ModuleProxy) {
 	// we need "Module"s for packaging items
-	modulesInPackageByModule := make(map[android.Module]bool)
+	modulesInPackageByModule := make(map[android.ModuleProxy]bool)
 	modulesInPackageByName := make(map[string]bool)
 
 	deps := f.gatherFilteredPackagingSpecs(ctx)
-	ctx.WalkDeps(func(child, parent android.Module) bool {
+	ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
+		if !android.OtherModulePointerProviderOrDefault(ctx, child, android.CommonModuleInfoProvider).Enabled {
+			return false
+		}
 		for _, ps := range android.OtherModuleProviderOrDefault(
 			ctx, child, android.InstallFilesProvider).PackagingSpecs {
 			if _, ok := deps[ps.RelPathInPackage()]; ok && ps.Partition() == f.PartitionType() {
@@ -1032,13 +1603,16 @@
 		return true
 	})
 
-	provideModules := make([]android.Module, 0, len(modulesInPackageByModule))
+	provideModules := make([]android.ModuleProxy, 0, len(modulesInPackageByModule))
 	for mod := range modulesInPackageByModule {
 		provideModules = append(provideModules, mod)
 	}
 
-	var requireModules []android.Module
-	ctx.WalkDeps(func(child, parent android.Module) bool {
+	var requireModules []android.ModuleProxy
+	ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
+		if !android.OtherModulePointerProviderOrDefault(ctx, child, android.CommonModuleInfoProvider).Enabled {
+			return false
+		}
 		_, parentInPackage := modulesInPackageByModule[parent]
 		_, childInPackageName := modulesInPackageByName[child.Name()]
 
@@ -1085,6 +1659,12 @@
 	}
 	thisPartition := f.PartitionType()
 	if thisPartition != "vendor" && thisPartition != "product" {
+		if f.properties.Android_filesystem_deps.System != nil {
+			ctx.PropertyErrorf("android_filesystem_deps.system", "only vendor or product partitions can use android_filesystem_deps")
+		}
+		if f.properties.Android_filesystem_deps.System_ext != nil {
+			ctx.PropertyErrorf("android_filesystem_deps.system_ext", "only vendor or product partitions can use android_filesystem_deps")
+		}
 		return
 	}
 	ctx.WalkDeps(func(child, parent android.Module) bool {
@@ -1101,3 +1681,17 @@
 		return true
 	})
 }
+
+func (f *filesystem) MakeVars(ctx android.MakeVarsModuleContext) []android.ModuleMakeVarsValue {
+	if f.Name() == ctx.Config().SoongDefinedSystemImage() {
+		return []android.ModuleMakeVarsValue{{"SOONG_DEFINED_SYSTEM_IMAGE_PATH", f.output.String()}}
+	}
+	return nil
+}
+
+func setCommonFilesystemInfo(ctx android.ModuleContext, m Filesystem) {
+	android.SetProvider(ctx, FilesystemProvider, FilesystemInfo{
+		Output:           m.OutputPath(),
+		SignedOutputPath: m.SignedOutputPath(),
+	})
+}
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index 0ed3870..e57e45c 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -16,7 +16,6 @@
 
 import (
 	"os"
-	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -119,9 +118,9 @@
 	`)
 
 	// produces "myfilesystem.img"
-	result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img")
+	result.ModuleForTests(t, "myfilesystem", "android_common").Output("myfilesystem.img")
 
-	fs := result.ModuleForTests("myfilesystem", "android_common").Module().(*filesystem)
+	fs := result.ModuleForTests(t, "myfilesystem", "android_common").Module().(*filesystem)
 	expected := []string{
 		"app/myapp/myapp.apk",
 		"bin/foo",
@@ -137,22 +136,6 @@
 	}
 }
 
-func TestIncludeMakeBuiltFiles(t *testing.T) {
-	result := fixture.RunTestWithBp(t, `
-		android_filesystem {
-			name: "myfilesystem",
-			include_make_built_files: "system",
-		}
-	`)
-
-	output := result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img")
-
-	stampFile := "out/target/product/test_device/obj/PACKAGING/system_intermediates/staging_dir.stamp"
-	fileListFile := "out/target/product/test_device/obj/PACKAGING/system_intermediates/file_list.txt"
-	android.AssertStringListContains(t, "deps of filesystem must include the staging dir stamp file", output.Implicits.Strings(), stampFile)
-	android.AssertStringListContains(t, "deps of filesystem must include the staging dir file list", output.Implicits.Strings(), fileListFile)
-}
-
 func TestFileSystemFillsLinkerConfigWithStubLibs(t *testing.T) {
 	result := fixture.RunTestWithBp(t, `
 		android_system_image {
@@ -180,12 +163,10 @@
 		}
 	`)
 
-	module := result.ModuleForTests("myfilesystem", "android_common")
-	output := module.Output("out/soong/.intermediates/myfilesystem/android_common/root/system/etc/linker.config.pb")
+	module := result.ModuleForTests(t, "myfilesystem", "android_common")
+	output := module.Output("out/soong/.intermediates/myfilesystem/android_common/linker.config.pb")
 
-	fullCommand := output.RuleParams.Command
-	startIndex := strings.Index(fullCommand, "conv_linker_config")
-	linkerConfigCommand := fullCommand[startIndex:]
+	linkerConfigCommand := output.RuleParams.Command
 
 	android.AssertStringDoesContain(t, "linker.config.pb should have libfoo",
 		linkerConfigCommand, "libfoo.so")
@@ -243,7 +224,7 @@
 		}
 	`)
 
-	module := result.ModuleForTests("myfilesystem", "android_common").Module().(*systemImage)
+	module := result.ModuleForTests(t, "myfilesystem", "android_common").Module().(*systemImage)
 	android.AssertDeepEquals(t, "entries should have foo and not bar", []string{"components/foo", "etc/linker.config.pb"}, module.entries)
 }
 
@@ -255,7 +236,7 @@
 			partition_name: "input_partition_name",
 			salt: "2222",
 		}`)
-	cmd := result.ModuleForTests("input_hashdesc", "android_arm64_armv8-a").Rule("avbGenVbmetaImage").RuleParams.Command
+	cmd := result.ModuleForTests(t, "input_hashdesc", "android_arm64_armv8-a").Rule("avbGenVbmetaImage").RuleParams.Command
 	android.AssertStringDoesContain(t, "Can't find correct --partition_name argument",
 		cmd, "--partition_name input_partition_name")
 	android.AssertStringDoesContain(t, "Can't find --do_not_append_vbmeta_image",
@@ -295,7 +276,7 @@
 			include_descriptors_from_images: ["input_hashdesc"],
 		}
 	`)
-	cmd := result.ModuleForTests("myfooter", "android_arm64_armv8-a").Rule("avbAddHashFooter").RuleParams.Command
+	cmd := result.ModuleForTests(t, "myfooter", "android_arm64_armv8-a").Rule("avbAddHashFooter").RuleParams.Command
 	android.AssertStringDoesContain(t, "Can't find correct --partition_name argument",
 		cmd, "--partition_name mypartition")
 	android.AssertStringDoesContain(t, "Can't find correct --key argument",
@@ -350,8 +331,8 @@
 		}
 	`)
 
-	filesystem := result.ModuleForTests("myfilesystem", "android_common_cov")
-	inputs := filesystem.Output("myfilesystem.img").Implicits
+	filesystem := result.ModuleForTests(t, "myfilesystem", "android_common_cov")
+	inputs := filesystem.Output("staging_dir.timestamp").Implicits
 	android.AssertStringListContains(t, "filesystem should have libfoo(cov)",
 		inputs.Strings(),
 		"out/soong/.intermediates/libfoo/android_arm64_armv8-a_shared_cov/libfoo.so")
@@ -359,8 +340,8 @@
 		inputs.Strings(),
 		"out/soong/.intermediates/libbar/android_arm64_armv8-a_shared_cov/libbar.so")
 
-	filesystemOutput := filesystem.Output("myfilesystem.img").Output
-	prebuiltInput := result.ModuleForTests("prebuilt", "android_arm64_armv8-a").Rule("Cp").Input
+	filesystemOutput := filesystem.OutputFiles(result.TestContext, t, "")[0]
+	prebuiltInput := result.ModuleForTests(t, "prebuilt", "android_arm64_armv8-a").Rule("Cp").Input
 	if filesystemOutput != prebuiltInput {
 		t.Error("prebuilt should use cov variant of filesystem")
 	}
@@ -422,7 +403,7 @@
 		}
 	`)
 
-	fs := result.ModuleForTests("system", "android_common").Module().(*systemImage)
+	fs := result.ModuleForTests(t, "system", "android_common").Module().(*systemImage)
 	expected := []string{
 		"bin/foo",
 		"lib/libbar.so",
@@ -502,7 +483,7 @@
 		}
 	`)
 
-	fs := result.ModuleForTests("fs", "android_common").Module().(*filesystem)
+	fs := result.ModuleForTests(t, "fs", "android_common").Module().(*filesystem)
 	expected := []string{
 		"bin/foo",
 		"lib64/libbar.so",
@@ -565,7 +546,7 @@
 		},
 	}
 	for _, c := range testcases {
-		fs := result.ModuleForTests(c.fsName, "android_common").Module().(*filesystem)
+		fs := result.ModuleForTests(t, c.fsName, "android_common").Module().(*filesystem)
 		for _, e := range c.expected {
 			android.AssertStringListContains(t, "missing entry", fs.entries, e)
 		}
@@ -592,7 +573,7 @@
 		}
 	`)
 
-	partition := result.ModuleForTests("erofs_partition", "android_common")
+	partition := result.ModuleForTests(t, "erofs_partition", "android_common")
 	buildImageConfig := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("prop_pre_processing"))
 	android.AssertStringDoesContain(t, "erofs fs type", buildImageConfig, "fs_type=erofs")
 	android.AssertStringDoesContain(t, "erofs fs type compress algorithm", buildImageConfig, "erofs_default_compressor=lz4hc,9")
@@ -608,7 +589,7 @@
 		}
 	`)
 
-	partition := result.ModuleForTests("f2fs_partition", "android_common")
+	partition := result.ModuleForTests(t, "f2fs_partition", "android_common")
 	buildImageConfig := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("prop_pre_processing"))
 	android.AssertStringDoesContain(t, "f2fs fs type", buildImageConfig, "fs_type=f2fs")
 	android.AssertStringDoesContain(t, "f2fs fs type sparse", buildImageConfig, "f2fs_sparse_flag=-S")
@@ -650,7 +631,7 @@
 		}
 	`)
 
-	partition := result.ModuleForTests("myfilesystem", "android_common")
+	partition := result.ModuleForTests(t, "myfilesystem", "android_common")
 	fileList := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("fileList"))
 	android.AssertDeepEquals(t, "filesystem with dependencies on different partition", "bin/binfoo\n", fileList)
 }
@@ -669,7 +650,7 @@
 		}
 	`)
 
-	partition := result.ModuleForTests("myfilesystem", "android_common")
+	partition := result.ModuleForTests(t, "myfilesystem", "android_common")
 	fileList := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("fileList"))
 	android.AssertDeepEquals(t, "cc_library listed in deps",
 		"lib64/bootstrap/libc.so\nlib64/bootstrap/libdl.so\nlib64/bootstrap/libm.so\nlib64/libc++.so\nlib64/libc.so\nlib64/libdl.so\nlib64/libfoo.so\nlib64/libm.so\n",
@@ -706,7 +687,7 @@
 }
 	`)
 
-	partition := result.ModuleForTests("myfilesystem", "android_common")
+	partition := result.ModuleForTests(t, "myfilesystem", "android_common")
 	fileList := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("fileList"))
 	android.AssertDeepEquals(t, "Shared library dep of overridden binary should not be installed",
 		"bin/binfoo1\nlib64/bootstrap/libc.so\nlib64/bootstrap/libdl.so\nlib64/bootstrap/libm.so\nlib64/libc++.so\nlib64/libc.so\nlib64/libdl.so\nlib64/libfoo2.so\nlib64/libm.so\n",
@@ -735,33 +716,67 @@
 }
 	`)
 
-	linkerConfigCmd := result.ModuleForTests("myfilesystem", "android_common").Rule("build_filesystem_image").RuleParams.Command
+	linkerConfigCmd := result.ModuleForTests(t, "myfilesystem", "android_common").Output("out/soong/.intermediates/myfilesystem/android_common/linker.config.pb").RuleParams.Command
 	android.AssertStringDoesContain(t, "Could not find linker.config.json file in cmd", linkerConfigCmd, "conv_linker_config proto --force -s linker.config.json")
 	android.AssertStringDoesContain(t, "Could not find stub in `provideLibs`", linkerConfigCmd, "--key provideLibs --value libfoo_has_stubs.so")
 }
 
 // override_android_* modules implicitly override their base module.
 // If both of these are listed in `deps`, the base module should not be installed.
+// Also, required deps should be updated too.
 func TestOverrideModulesInDeps(t *testing.T) {
 	result := fixture.RunTestWithBp(t, `
-		android_filesystem {
-			name: "myfilesystem",
-			deps: ["myapp", "myoverrideapp"],
+		cc_library_shared {
+			name: "libfoo",
+			stl: "none",
+			system_shared_libs: [],
 		}
-
+		cc_library_shared {
+			name: "libbar",
+			stl: "none",
+			system_shared_libs: [],
+		}
+		phony {
+			name: "myapp_phony",
+			required: ["myapp"],
+		}
+		phony {
+			name: "myoverrideapp_phony",
+			required: ["myoverrideapp"],
+		}
 		android_app {
 			name: "myapp",
 			platform_apis: true,
+			required: ["libfoo"],
 		}
 		override_android_app {
 			name: "myoverrideapp",
 			base: "myapp",
+			required: ["libbar"],
+		}
+		android_filesystem {
+			name: "myfilesystem",
+			deps: ["myapp"],
+		}
+		android_filesystem {
+			name: "myfilesystem_overridden",
+			deps: ["myapp", "myoverrideapp"],
+		}
+		android_filesystem {
+			name: "myfilesystem_overridden_indirect",
+			deps: ["myapp_phony", "myoverrideapp_phony"],
 		}
 	`)
 
-	partition := result.ModuleForTests("myfilesystem", "android_common")
+	partition := result.ModuleForTests(t, "myfilesystem", "android_common")
 	fileList := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("fileList"))
-	android.AssertStringEquals(t, "filesystem with override app", "app/myoverrideapp/myoverrideapp.apk\n", fileList)
+	android.AssertStringEquals(t, "filesystem without override app", "app/myapp/myapp.apk\nlib64/libfoo.so\n", fileList)
+
+	for _, overridden := range []string{"myfilesystem_overridden", "myfilesystem_overridden_indirect"} {
+		overriddenPartition := result.ModuleForTests(t, overridden, "android_common")
+		overriddenFileList := android.ContentFromFileRuleForTests(t, result.TestContext, overriddenPartition.Output("fileList"))
+		android.AssertStringEquals(t, "filesystem with "+overridden, "app/myoverrideapp/myoverrideapp.apk\nlib64/libbar.so\n", overriddenFileList)
+	}
 }
 
 func TestRamdiskPartitionSetsDevNodes(t *testing.T) {
diff --git a/filesystem/fsverity_metadata.go b/filesystem/fsverity_metadata.go
index b9a4026..89da318 100644
--- a/filesystem/fsverity_metadata.go
+++ b/filesystem/fsverity_metadata.go
@@ -21,9 +21,27 @@
 
 	"android/soong/android"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
+func init() {
+	pctx.HostBinToolVariable("fsverity_metadata_generator", "fsverity_metadata_generator")
+	pctx.HostBinToolVariable("fsverity_manifest_generator", "fsverity_manifest_generator")
+	pctx.HostBinToolVariable("fsverity", "fsverity")
+}
+
+var (
+	buildFsverityMeta = pctx.AndroidStaticRule("build_fsverity_meta", blueprint.RuleParams{
+		Command:     `$fsverity_metadata_generator --fsverity-path $fsverity --signature none --hash-alg sha256 --output $out $in`,
+		CommandDeps: []string{"$fsverity_metadata_generator", "$fsverity"},
+	})
+	buildFsverityManifest = pctx.AndroidStaticRule("build_fsverity_manifest", blueprint.RuleParams{
+		Command:     `$fsverity_manifest_generator --fsverity-path $fsverity --output $out @$in`,
+		CommandDeps: []string{"$fsverity_manifest_generator", "$fsverity"},
+	})
+)
+
 type fsverityProperties struct {
 	// Patterns of files for fsverity metadata generation.  For each matched file, a .fsv_meta file
 	// will be generated and included to the filesystem image.
@@ -35,16 +53,67 @@
 	Libs proptools.Configurable[[]string] `android:"path"`
 }
 
-func (f *filesystem) writeManifestGeneratorListFile(ctx android.ModuleContext, outputPath android.WritablePath, matchedSpecs []android.PackagingSpec, rebasedDir android.OutputPath) {
-	var buf strings.Builder
-	for _, spec := range matchedSpecs {
-		buf.WriteString(rebasedDir.Join(ctx, spec.RelPathInPackage()).String())
-		buf.WriteRune('\n')
-	}
-	android.WriteFileRuleVerbatim(ctx, outputPath, buf.String())
+// Mapping of a given fsverity file, which may be a real file or a symlink, and the on-device
+// path it should have relative to the filesystem root.
+type fsveritySrcDest struct {
+	src  android.Path
+	dest string
 }
 
-func (f *filesystem) buildFsverityMetadataFiles(ctx android.ModuleContext, builder *android.RuleBuilder, specs map[string]android.PackagingSpec, rootDir android.OutputPath, rebasedDir android.OutputPath) {
+func (f *filesystem) writeManifestGeneratorListFile(
+	ctx android.ModuleContext,
+	outputPath android.WritablePath,
+	matchedFiles []fsveritySrcDest,
+	rootDir android.OutputPath,
+	rebasedDir android.OutputPath,
+) []android.Path {
+	prefix, err := filepath.Rel(rootDir.String(), rebasedDir.String())
+	if err != nil {
+		panic("rebasedDir should be relative to rootDir")
+	}
+	if prefix == "." {
+		prefix = ""
+	}
+	if f.PartitionType() == "system_ext" {
+		// Use the equivalent of $PRODUCT_OUT as the base dir.
+		// This ensures that the paths in build_manifest.pb contain on-device paths
+		// e.g. system_ext/framework/javalib.jar
+		// and not framework/javalib.jar.
+		//
+		// Although base-dir is outside the rootdir provided for packaging, this action
+		// is hermetic since it uses `manifestGeneratorListPath` to filter the files to be written to build_manifest.pb
+		prefix = "system_ext"
+	}
+
+	var deps []android.Path
+	var buf strings.Builder
+	for _, spec := range matchedFiles {
+		src := spec.src.String()
+		dst := filepath.Join(prefix, spec.dest)
+		if strings.Contains(src, ",") {
+			ctx.ModuleErrorf("Path cannot contain a comma: %s", src)
+		}
+		if strings.Contains(dst, ",") {
+			ctx.ModuleErrorf("Path cannot contain a comma: %s", dst)
+		}
+		buf.WriteString(src)
+		buf.WriteString(",")
+		buf.WriteString(dst)
+		buf.WriteString("\n")
+		deps = append(deps, spec.src)
+	}
+	android.WriteFileRuleVerbatim(ctx, outputPath, buf.String())
+	return deps
+}
+
+func (f *filesystem) buildFsverityMetadataFiles(
+	ctx android.ModuleContext,
+	builder *android.RuleBuilder,
+	specs map[string]android.PackagingSpec,
+	rootDir android.OutputPath,
+	rebasedDir android.OutputPath,
+	fullInstallPaths *[]FullInstallPathInfo,
+) {
 	match := func(path string) bool {
 		for _, pattern := range f.properties.Fsverity.Inputs.GetOrDefault(ctx, nil) {
 			if matched, err := filepath.Match(pattern, path); matched {
@@ -57,53 +126,98 @@
 		return false
 	}
 
-	var matchedSpecs []android.PackagingSpec
+	var matchedFiles []android.PackagingSpec
+	var matchedSymlinks []android.PackagingSpec
 	for _, relPath := range android.SortedKeys(specs) {
 		if match(relPath) {
-			matchedSpecs = append(matchedSpecs, specs[relPath])
+			spec := specs[relPath]
+			if spec.SrcPath() != nil {
+				matchedFiles = append(matchedFiles, spec)
+			} else if spec.SymlinkTarget() != "" {
+				matchedSymlinks = append(matchedSymlinks, spec)
+			} else {
+				ctx.ModuleErrorf("Expected a file or symlink for fsverity packaging spec")
+			}
 		}
 	}
 
-	if len(matchedSpecs) == 0 {
+	if len(matchedFiles) == 0 && len(matchedSymlinks) == 0 {
 		return
 	}
 
-	fsverityPath := ctx.Config().HostToolPath(ctx, "fsverity")
-
 	// STEP 1: generate .fsv_meta
-	var sb strings.Builder
-	sb.WriteString("set -e\n")
-	for _, spec := range matchedSpecs {
+	var fsverityFileSpecs []fsveritySrcDest
+	for _, spec := range matchedFiles {
+		rel := spec.RelPathInPackage() + ".fsv_meta"
+		outPath := android.PathForModuleOut(ctx, "fsverity/meta_files", rel)
+		destPath := rebasedDir.Join(ctx, rel)
 		// srcPath is copied by CopySpecsToDir()
-		srcPath := rebasedDir.Join(ctx, spec.RelPathInPackage())
-		destPath := rebasedDir.Join(ctx, spec.RelPathInPackage()+".fsv_meta")
-		builder.Command().
-			BuiltTool("fsverity_metadata_generator").
-			FlagWithInput("--fsverity-path ", fsverityPath).
-			FlagWithArg("--signature ", "none").
-			FlagWithArg("--hash-alg ", "sha256").
-			FlagWithArg("--output ", destPath.String()).
-			Text(srcPath.String())
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   buildFsverityMeta,
+			Input:  spec.SrcPath(),
+			Output: outPath,
+		})
+		builder.Command().Textf("cp").Input(outPath).Output(destPath)
 		f.appendToEntry(ctx, destPath)
+		*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+			SourcePath:      destPath,
+			FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), rel),
+		})
+		fsverityFileSpecs = append(fsverityFileSpecs, fsveritySrcDest{
+			src:  spec.SrcPath(),
+			dest: spec.RelPathInPackage(),
+		})
+	}
+	for _, spec := range matchedSymlinks {
+		rel := spec.RelPathInPackage() + ".fsv_meta"
+		outPath := android.PathForModuleOut(ctx, "fsverity/meta_files", rel)
+		destPath := rebasedDir.Join(ctx, rel)
+		target := spec.SymlinkTarget() + ".fsv_meta"
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Symlink,
+			Output: outPath,
+			Args: map[string]string{
+				"fromPath": target,
+			},
+		})
+		builder.Command().
+			Textf("cp").
+			Flag(ctx.Config().CpPreserveSymlinksFlags()).
+			Input(outPath).
+			Output(destPath)
+		f.appendToEntry(ctx, destPath)
+		*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+			SymlinkTarget:   target,
+			FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), rel),
+		})
+		// The fsverity manifest tool needs to actually look at the symlink. But symlink
+		// packagingSpecs are not actually created on disk, at least until the staging dir is
+		// built for the partition. Create a fake one now so the tool can see it.
+		realizedSymlink := android.PathForModuleOut(ctx, "fsverity/realized_symlinks", spec.RelPathInPackage())
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Symlink,
+			Output: realizedSymlink,
+			Args: map[string]string{
+				"fromPath": spec.SymlinkTarget(),
+			},
+		})
+		fsverityFileSpecs = append(fsverityFileSpecs, fsveritySrcDest{
+			src:  realizedSymlink,
+			dest: spec.RelPathInPackage(),
+		})
 	}
 
 	// STEP 2: generate signed BuildManifest.apk
 	// STEP 2-1: generate build_manifest.pb
-	manifestGeneratorListPath := android.PathForModuleOut(ctx, "fsverity_manifest.list")
-	f.writeManifestGeneratorListFile(ctx, manifestGeneratorListPath, matchedSpecs, rebasedDir)
-	assetsPath := android.PathForModuleOut(ctx, "fsverity_manifest/assets")
-	manifestPbPath := assetsPath.Join(ctx, "build_manifest.pb")
-	builder.Command().Text("rm -rf " + assetsPath.String())
-	builder.Command().Text("mkdir -p " + assetsPath.String())
-	builder.Command().
-		BuiltTool("fsverity_manifest_generator").
-		FlagWithInput("--fsverity-path ", fsverityPath).
-		FlagWithArg("--base-dir ", rootDir.String()).
-		FlagWithArg("--output ", manifestPbPath.String()).
-		FlagWithInput("@", manifestGeneratorListPath)
-
-	f.appendToEntry(ctx, manifestPbPath)
-	f.appendToEntry(ctx, manifestGeneratorListPath)
+	manifestGeneratorListPath := android.PathForModuleOut(ctx, "fsverity/fsverity_manifest.list")
+	manifestDeps := f.writeManifestGeneratorListFile(ctx, manifestGeneratorListPath, fsverityFileSpecs, rootDir, rebasedDir)
+	manifestPbPath := android.PathForModuleOut(ctx, "fsverity/build_manifest.pb")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:      buildFsverityManifest,
+		Input:     manifestGeneratorListPath,
+		Implicits: manifestDeps,
+		Output:    manifestPbPath,
+	})
 
 	// STEP 2-2: generate BuildManifest.apk (unsigned)
 	apkNameSuffix := ""
@@ -111,8 +225,8 @@
 		//https://source.corp.google.com/h/googleplex-android/platform/build/+/e392d2b486c2d4187b20a72b1c67cc737ecbcca5:core/Makefile;l=3410;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b;bpv=1;bpt=0
 		apkNameSuffix = "SystemExt"
 	}
-	apkPath := rebasedDir.Join(ctx, "etc", "security", "fsverity", fmt.Sprintf("BuildManifest%s.apk", apkNameSuffix))
-	idsigPath := rebasedDir.Join(ctx, "etc", "security", "fsverity", fmt.Sprintf("BuildManifest%s.apk.idsig", apkNameSuffix))
+	apkPath := android.PathForModuleOut(ctx, "fsverity", fmt.Sprintf("BuildManifest%s.apk", apkNameSuffix))
+	idsigPath := android.PathForModuleOut(ctx, "fsverity", fmt.Sprintf("BuildManifest%s.apk.idsig", apkNameSuffix))
 	manifestTemplatePath := android.PathForSource(ctx, "system/security/fsverity/AndroidManifest.xml")
 	libs := android.PathsForModuleSrc(ctx, f.properties.Fsverity.Libs.GetOrDefault(ctx, nil))
 
@@ -121,11 +235,23 @@
 		minSdkVersion = ctx.Config().PlatformSdkVersion().String()
 	}
 
-	unsignedApkCommand := builder.Command().
+	apkBuilder := android.NewRuleBuilder(pctx, ctx)
+
+	// aapt2 doesn't support adding individual asset files. Create a temp directory to hold asset
+	// files and pass it to aapt2.
+	tmpAssetDir := android.PathForModuleOut(ctx, "fsverity/tmp_asset_dir")
+	stagedManifestPbPath := tmpAssetDir.Join(ctx, "build_manifest.pb")
+	apkBuilder.Command().
+		Text("rm -rf").Text(tmpAssetDir.String()).
+		Text("&&").
+		Text("mkdir -p").Text(tmpAssetDir.String())
+	apkBuilder.Command().Text("cp").Input(manifestPbPath).Output(stagedManifestPbPath)
+
+	unsignedApkCommand := apkBuilder.Command().
 		BuiltTool("aapt2").
 		Text("link").
 		FlagWithOutput("-o ", apkPath).
-		FlagWithArg("-A ", assetsPath.String())
+		FlagWithArg("-A ", tmpAssetDir.String()).Implicit(stagedManifestPbPath)
 	for _, lib := range libs {
 		unsignedApkCommand.FlagWithInput("-I ", lib)
 	}
@@ -136,17 +262,35 @@
 		FlagWithInput("--manifest ", manifestTemplatePath).
 		Text(" --rename-manifest-package com.android.security.fsverity_metadata." + f.partitionName())
 
-	f.appendToEntry(ctx, apkPath)
-
 	// STEP 2-3: sign BuildManifest.apk
 	pemPath, keyPath := ctx.Config().DefaultAppCertificate(ctx)
-	builder.Command().
+	apkBuilder.Command().
 		BuiltTool("apksigner").
 		Text("sign").
 		FlagWithArg("--in ", apkPath.String()).
 		FlagWithInput("--cert ", pemPath).
 		FlagWithInput("--key ", keyPath).
 		ImplicitOutput(idsigPath)
+	apkBuilder.Build(fmt.Sprintf("%s_fsverity_apk", ctx.ModuleName()), "build fsverity apk")
 
-	f.appendToEntry(ctx, idsigPath)
+	// STEP 2-4: Install the apk into the staging directory
+	installedApkPath := rebasedDir.Join(ctx, "etc", "security", "fsverity", fmt.Sprintf("BuildManifest%s.apk", apkNameSuffix))
+	installedIdsigPath := rebasedDir.Join(ctx, "etc", "security", "fsverity", fmt.Sprintf("BuildManifest%s.apk.idsig", apkNameSuffix))
+	builder.Command().Text("mkdir -p").Text(filepath.Dir(installedApkPath.String()))
+	builder.Command().Text("cp").Input(apkPath).Text(installedApkPath.String())
+	builder.Command().Text("cp").Input(idsigPath).Text(installedIdsigPath.String())
+
+	*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+		SourcePath:      apkPath,
+		FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), fmt.Sprintf("etc/security/fsverity/BuildManifest%s.apk", apkNameSuffix)),
+	})
+
+	f.appendToEntry(ctx, installedApkPath)
+
+	*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+		SourcePath:      idsigPath,
+		FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), fmt.Sprintf("etc/security/fsverity/BuildManifest%s.apk.idsig", apkNameSuffix)),
+	})
+
+	f.appendToEntry(ctx, installedIdsigPath)
 }
diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go
index d0888a9..1fd2e76 100644
--- a/filesystem/logical_partition.go
+++ b/filesystem/logical_partition.go
@@ -198,6 +198,8 @@
 
 	ctx.SetOutputFiles([]android.Path{output}, "")
 	l.output = output
+
+	setCommonFilesystemInfo(ctx, l)
 }
 
 // Add a rule that converts the filesystem for the given partition to the given rule builder. The
diff --git a/filesystem/raw_binary.go b/filesystem/raw_binary.go
index 707fba0..6ca155a 100644
--- a/filesystem/raw_binary.go
+++ b/filesystem/raw_binary.go
@@ -88,6 +88,8 @@
 
 	ctx.SetOutputFiles([]android.Path{outputFile}, "")
 	r.output = outputFile
+
+	setCommonFilesystemInfo(ctx, r)
 }
 
 var _ android.AndroidMkEntriesProvider = (*rawBinary)(nil)
diff --git a/filesystem/super_image.go b/filesystem/super_image.go
index 0f8f614..cf7e125 100644
--- a/filesystem/super_image.go
+++ b/filesystem/super_image.go
@@ -17,10 +17,13 @@
 import (
 	"fmt"
 	"path/filepath"
+	"regexp"
+	"slices"
 	"strconv"
 	"strings"
 
 	"android/soong/android"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -49,16 +52,36 @@
 	Ab_update *bool
 	// whether dynamic partitions is enabled on devices that were launched without this support
 	Retrofit *bool
-	// whether virtual A/B seamless update is enabled
-	Virtual_ab *bool
-	// whether retrofitting virtual A/B seamless update is enabled
-	Virtual_ab_retrofit *bool
 	// whether the output is a sparse image
 	Sparse *bool
 	// information about how partitions within the super partition are grouped together
 	Partition_groups []PartitionGroupsInfo
+	// Name of the system_other partition filesystem module. This module will be installed to
+	// the "b" slot of the system partition in a/b partition builds.
+	System_other_partition *string
 	// whether dynamic partitions is used
 	Use_dynamic_partitions *bool
+	Virtual_ab             struct {
+		// whether virtual A/B seamless update is enabled
+		Enable *bool
+		// whether retrofitting virtual A/B seamless update is enabled
+		Retrofit *bool
+		// If set, device uses virtual A/B Compression
+		Compression *bool
+		// This value controls the compression algorithm used for VABC.
+		// Valid options are defined in system/core/fs_mgr/libsnapshot/cow_writer.cpp
+		// e.g. "none", "gz", "brotli"
+		Compression_method *string
+		// Specifies maximum bytes to be compressed at once during ota. Options: 4096, 8192, 16384, 32768, 65536, 131072, 262144.
+		Compression_factor *int64
+		// Specifies COW version to be used by update_engine and libsnapshot. If this value is not
+		// specified we default to COW version 2 in update_engine for backwards compatibility
+		Cow_version *int64
+	}
+	// Whether the super image will be disted in the update package
+	Super_image_in_update_package *bool
+	// Whether a super_empty.img should be created
+	Create_super_empty *bool
 }
 
 type PartitionGroupsInfo struct {
@@ -88,6 +111,23 @@
 	Odm_dlkm_partition *string
 }
 
+type SuperImageInfo struct {
+	// The built super.img file, which contains the sub-partitions
+	SuperImage android.Path
+
+	// Mapping from the sub-partition type to its re-exported FileSystemInfo providers from the
+	// sub-partitions.
+	SubImageInfo map[string]FilesystemInfo
+
+	DynamicPartitionsInfo android.Path
+
+	SuperEmptyImage android.Path
+
+	AbUpdate bool
+}
+
+var SuperImageProvider = blueprint.NewProvider[SuperImageInfo]()
+
 func SuperImageFactory() android.Module {
 	module := &superImage{}
 	module.AddProperties(&module.properties, &module.partitionProps)
@@ -99,12 +139,18 @@
 	blueprint.BaseDependencyTag
 }
 
-var superImageDepTag superImageDepTagType
+var subImageDepTag superImageDepTagType
+
+type systemOtherDepTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var systemOtherDepTag systemOtherDepTagType
 
 func (s *superImage) DepsMutator(ctx android.BottomUpMutatorContext) {
 	addDependencyIfDefined := func(dep *string) {
 		if dep != nil {
-			ctx.AddDependency(ctx.Module(), superImageDepTag, proptools.String(dep))
+			ctx.AddDependency(ctx.Module(), subImageDepTag, proptools.String(dep))
 		}
 	}
 
@@ -117,10 +163,13 @@
 	addDependencyIfDefined(s.partitionProps.Vendor_dlkm_partition)
 	addDependencyIfDefined(s.partitionProps.Odm_partition)
 	addDependencyIfDefined(s.partitionProps.Odm_dlkm_partition)
+	if s.properties.System_other_partition != nil {
+		ctx.AddDependency(ctx.Module(), systemOtherDepTag, *s.properties.System_other_partition)
+	}
 }
 
 func (s *superImage) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	miscInfo, deps := s.buildMiscInfo(ctx)
+	miscInfo, deps, subImageInfos := s.buildMiscInfo(ctx, false)
 	builder := android.NewRuleBuilder(pctx, ctx)
 	output := android.PathForModuleOut(ctx, s.installFileName())
 	lpMake := ctx.Config().HostToolPath(ctx, "lpmake")
@@ -133,104 +182,232 @@
 		Implicits(deps).
 		Output(output)
 	builder.Build("build_super_image", fmt.Sprintf("Creating super image %s", s.BaseModuleName()))
+	var superEmptyImage android.WritablePath
+	if proptools.Bool(s.properties.Create_super_empty) {
+		superEmptyImageBuilder := android.NewRuleBuilder(pctx, ctx)
+		superEmptyImage = android.PathForModuleOut(ctx, "super_empty.img")
+		superEmptyMiscInfo, superEmptyDeps, _ := s.buildMiscInfo(ctx, true)
+		if superEmptyDeps != nil {
+			ctx.ModuleErrorf("TODO: Handle additional deps when building super_empty.img")
+		}
+		superEmptyImageBuilder.Command().Textf("PATH=%s:\\$PATH", lpMakeDir).
+			BuiltTool("build_super_image").
+			Text("-v").
+			Input(superEmptyMiscInfo).
+			Implicit(lpMake).
+			Output(superEmptyImage)
+		superEmptyImageBuilder.Build("build_super_empty_image", fmt.Sprintf("Creating super empty image %s", s.BaseModuleName()))
+	}
+	android.SetProvider(ctx, SuperImageProvider, SuperImageInfo{
+		SuperImage:            output,
+		SubImageInfo:          subImageInfos,
+		DynamicPartitionsInfo: s.generateDynamicPartitionsInfo(ctx),
+		SuperEmptyImage:       superEmptyImage,
+		AbUpdate:              proptools.Bool(s.properties.Ab_update),
+	})
 	ctx.SetOutputFiles([]android.Path{output}, "")
+	ctx.CheckbuildFile(output)
+
+	buildComplianceMetadata(ctx, subImageDepTag)
 }
 
 func (s *superImage) installFileName() string {
-	return s.BaseModuleName() + ".img"
+	return "super.img"
 }
 
-func (s *superImage) buildMiscInfo(ctx android.ModuleContext) (android.Path, android.Paths) {
+func (s *superImage) buildMiscInfo(ctx android.ModuleContext, superEmpty bool) (android.Path, android.Paths, map[string]FilesystemInfo) {
 	var miscInfoString strings.Builder
+	partitionList := s.dumpDynamicPartitionInfo(ctx, &miscInfoString)
 	addStr := func(name string, value string) {
 		miscInfoString.WriteString(name)
 		miscInfoString.WriteRune('=')
 		miscInfoString.WriteString(value)
 		miscInfoString.WriteRune('\n')
 	}
-
-	addStr("use_dynamic_partitions", strconv.FormatBool(proptools.Bool(s.properties.Use_dynamic_partitions)))
-	addStr("dynamic_partition_retrofit", strconv.FormatBool(proptools.Bool(s.properties.Retrofit)))
-	addStr("lpmake", "lpmake")
-	addStr("super_metadata_device", proptools.String(s.properties.Metadata_device))
-	if len(s.properties.Block_devices) > 0 {
-		addStr("super_block_devices", strings.Join(s.properties.Block_devices, " "))
-	}
-	addStr("super_super_device_size", strconv.Itoa(proptools.Int(s.properties.Size)))
-	var groups, partitionList []string
-	for _, groupInfo := range s.properties.Partition_groups {
-		groups = append(groups, groupInfo.Name)
-		partitionList = append(partitionList, groupInfo.PartitionList...)
-		addStr("super_"+groupInfo.Name+"_group_size", groupInfo.GroupSize)
-		addStr("super_"+groupInfo.Name+"_partition_list", strings.Join(groupInfo.PartitionList, " "))
-	}
-	addStr("super_partition_groups", strings.Join(groups, " "))
-	addStr("dynamic_partition_list", strings.Join(partitionList, " "))
-
-	addStr("virtual_ab", strconv.FormatBool(proptools.Bool(s.properties.Virtual_ab)))
-	addStr("virtual_ab_retrofit", strconv.FormatBool(proptools.Bool(s.properties.Virtual_ab_retrofit)))
 	addStr("ab_update", strconv.FormatBool(proptools.Bool(s.properties.Ab_update)))
-	addStr("build_non_sparse_super_partition", strconv.FormatBool(!proptools.Bool(s.properties.Sparse)))
+	if superEmpty {
+		miscInfo := android.PathForModuleOut(ctx, "misc_info_super_empty.txt")
+		android.WriteFileRule(ctx, miscInfo, miscInfoString.String())
+		return miscInfo, nil, nil
+	}
 
-	partitionToImagePath := make(map[string]string)
-	nameToPartition := make(map[string]string)
-	var systemOtherPartitionNameNeeded string
-	addEntryToPartitionToName := func(p string, s *string) {
-		if proptools.String(s) != "" {
-			nameToPartition[*s] = p
+	subImageInfo := make(map[string]FilesystemInfo)
+	var deps android.Paths
+
+	missingPartitionErrorMessage := ""
+	handleSubPartition := func(partitionType string, name *string) {
+		if proptools.String(name) == "" {
+			missingPartitionErrorMessage += fmt.Sprintf("%s image listed in partition groups, but its module was not specified. ", partitionType)
+			return
 		}
+		mod := ctx.GetDirectDepWithTag(*name, subImageDepTag)
+		if mod == nil {
+			ctx.ModuleErrorf("Could not get dep %q", *name)
+			return
+		}
+		info, ok := android.OtherModuleProvider(ctx, mod, FilesystemProvider)
+		if !ok {
+			ctx.ModuleErrorf("Expected dep %q to provide FilesystemInfo", *name)
+			return
+		}
+		addStr(partitionType+"_image", info.Output.String())
+		deps = append(deps, info.Output)
+		if _, ok := subImageInfo[partitionType]; ok {
+			ctx.ModuleErrorf("Already set subimageInfo for %q", partitionType)
+		}
+		subImageInfo[partitionType] = info
 	}
 
 	// Build partitionToImagePath, because system partition may need system_other
 	// partition image path
 	for _, p := range partitionList {
-		if _, ok := nameToPartition[p]; ok {
-			continue
-		}
 		switch p {
 		case "system":
-			addEntryToPartitionToName(p, s.partitionProps.System_partition)
-			systemOtherPartitionNameNeeded = proptools.String(s.partitionProps.System_other_partition)
+			handleSubPartition("system", s.partitionProps.System_partition)
 		case "system_dlkm":
-			addEntryToPartitionToName(p, s.partitionProps.System_dlkm_partition)
+			handleSubPartition("system_dlkm", s.partitionProps.System_dlkm_partition)
 		case "system_ext":
-			addEntryToPartitionToName(p, s.partitionProps.System_ext_partition)
+			handleSubPartition("system_ext", s.partitionProps.System_ext_partition)
 		case "product":
-			addEntryToPartitionToName(p, s.partitionProps.Product_partition)
+			handleSubPartition("product", s.partitionProps.Product_partition)
 		case "vendor":
-			addEntryToPartitionToName(p, s.partitionProps.Vendor_partition)
+			handleSubPartition("vendor", s.partitionProps.Vendor_partition)
 		case "vendor_dlkm":
-			addEntryToPartitionToName(p, s.partitionProps.Vendor_dlkm_partition)
+			handleSubPartition("vendor_dlkm", s.partitionProps.Vendor_dlkm_partition)
 		case "odm":
-			addEntryToPartitionToName(p, s.partitionProps.Odm_partition)
+			handleSubPartition("odm", s.partitionProps.Odm_partition)
 		case "odm_dlkm":
-			addEntryToPartitionToName(p, s.partitionProps.Odm_dlkm_partition)
+			handleSubPartition("odm_dlkm", s.partitionProps.Odm_dlkm_partition)
 		default:
-			ctx.ModuleErrorf("current partition %s not a super image supported partition", p)
+			ctx.ModuleErrorf("partition %q is not a super image supported partition", p)
 		}
 	}
 
-	var deps android.Paths
-	ctx.VisitDirectDeps(func(m android.Module) {
-		if p, ok := nameToPartition[m.Name()]; ok {
-			if output, ok := android.OtherModuleProvider(ctx, m, android.OutputFilesProvider); ok {
-				partitionToImagePath[p] = output.DefaultOutputFiles[0].String()
-				deps = append(deps, output.DefaultOutputFiles[0])
-			}
-		} else if systemOtherPartitionNameNeeded != "" && m.Name() == systemOtherPartitionNameNeeded {
-			if output, ok := android.OtherModuleProvider(ctx, m, android.OutputFilesProvider); ok {
-				partitionToImagePath["system_other"] = output.DefaultOutputFiles[0].String()
-				// TODO: add system_other to deps after it can be generated
-				// deps = append(deps, output.DefaultOutputFiles[0])
-			}
+	if s.properties.System_other_partition != nil {
+		if !slices.Contains(partitionList, "system") {
+			ctx.PropertyErrorf("system_other_partition", "Must have a system partition to use a system_other partition")
 		}
-	})
+		systemOther := ctx.GetDirectDepProxyWithTag(*s.properties.System_other_partition, systemOtherDepTag)
+		systemOtherFiles := android.OutputFilesForModule(ctx, systemOther, "")
+		if len(systemOtherFiles) != 1 {
+			ctx.PropertyErrorf("system_other_partition", "Expected 1 output file from module %q", *&s.properties.System_other_partition)
+		} else {
+			handleSubPartition("system_other", s.partitionProps.System_other_partition)
+		}
+	}
 
-	for _, p := range android.SortedKeys(partitionToImagePath) {
-		addStr(p+"_image", partitionToImagePath[p])
+	// Delay the error message until execution time because on aosp-main-future-without-vendor,
+	// BUILDING_VENDOR_IMAGE is false so we don't get the vendor image, but it's still listed in
+	// BOARD_GOOGLE_DYNAMIC_PARTITIONS_PARTITION_LIST.
+	missingPartitionErrorMessageFile := android.PathForModuleOut(ctx, "missing_partition_error.txt")
+	if missingPartitionErrorMessage != "" {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.ErrorRule,
+			Output: missingPartitionErrorMessageFile,
+			Args: map[string]string{
+				"error": missingPartitionErrorMessage,
+			},
+		})
+	} else {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Touch,
+			Output: missingPartitionErrorMessageFile,
+		})
 	}
 
 	miscInfo := android.PathForModuleOut(ctx, "misc_info.txt")
-	android.WriteFileRule(ctx, miscInfo, miscInfoString.String())
-	return miscInfo, deps
+	android.WriteFileRule(ctx, miscInfo, miscInfoString.String(), missingPartitionErrorMessageFile)
+	return miscInfo, deps, subImageInfo
+}
+
+func (s *superImage) dumpDynamicPartitionInfo(ctx android.ModuleContext, sb *strings.Builder) []string {
+	addStr := func(name string, value string) {
+		sb.WriteString(name)
+		sb.WriteRune('=')
+		sb.WriteString(value)
+		sb.WriteRune('\n')
+	}
+
+	addStr("use_dynamic_partitions", strconv.FormatBool(proptools.Bool(s.properties.Use_dynamic_partitions)))
+	if proptools.Bool(s.properties.Retrofit) {
+		addStr("dynamic_partition_retrofit", "true")
+	}
+	addStr("lpmake", "lpmake")
+	addStr("build_super_partition", "true")
+	if proptools.Bool(s.properties.Create_super_empty) {
+		addStr("build_super_empty_partition", "true")
+	}
+	addStr("super_metadata_device", proptools.String(s.properties.Metadata_device))
+	if len(s.properties.Block_devices) > 0 {
+		addStr("super_block_devices", strings.Join(s.properties.Block_devices, " "))
+	}
+	// TODO: In make, there's more complicated logic than just this surrounding super_*_device_size
+	addStr("super_super_device_size", strconv.Itoa(proptools.Int(s.properties.Size)))
+	var groups, partitionList []string
+	for _, groupInfo := range s.properties.Partition_groups {
+		groups = append(groups, groupInfo.Name)
+		partitionList = append(partitionList, groupInfo.PartitionList...)
+	}
+	addStr("dynamic_partition_list", strings.Join(android.SortedUniqueStrings(partitionList), " "))
+	addStr("super_partition_groups", strings.Join(groups, " "))
+	initialPartitionListLen := len(partitionList)
+	partitionList = android.SortedUniqueStrings(partitionList)
+	if len(partitionList) != initialPartitionListLen {
+		ctx.ModuleErrorf("Duplicate partitions found in the partition_groups property")
+	}
+	// Add Partition group info after adding `super_partition_groups` and `dynamic_partition_list`
+	for _, groupInfo := range s.properties.Partition_groups {
+		addStr("super_"+groupInfo.Name+"_group_size", groupInfo.GroupSize)
+		addStr("super_"+groupInfo.Name+"_partition_list", strings.Join(groupInfo.PartitionList, " "))
+	}
+
+	if proptools.Bool(s.properties.Super_image_in_update_package) {
+		addStr("super_image_in_update_package", "true")
+	}
+	addStr("super_partition_size", strconv.Itoa(proptools.Int(s.properties.Size)))
+
+	if proptools.Bool(s.properties.Virtual_ab.Enable) {
+		addStr("virtual_ab", "true")
+		if proptools.Bool(s.properties.Virtual_ab.Retrofit) {
+			addStr("virtual_ab_retrofit", "true")
+		}
+		addStr("virtual_ab_compression", strconv.FormatBool(proptools.Bool(s.properties.Virtual_ab.Compression)))
+		if s.properties.Virtual_ab.Compression_method != nil {
+			matched, _ := regexp.MatchString("^[a-zA-Z0-9_-]+$", *s.properties.Virtual_ab.Compression_method)
+			if !matched {
+				ctx.PropertyErrorf("virtual_ab.compression_method", "compression_method cannot have special characters")
+			}
+			addStr("virtual_ab_compression_method", *s.properties.Virtual_ab.Compression_method)
+		}
+		if s.properties.Virtual_ab.Cow_version != nil {
+			addStr("virtual_ab_cow_version", strconv.FormatInt(*s.properties.Virtual_ab.Cow_version, 10))
+		}
+		if s.properties.Virtual_ab.Compression_factor != nil {
+			addStr("virtual_ab_compression_factor", strconv.FormatInt(*s.properties.Virtual_ab.Compression_factor, 10))
+		}
+
+	} else {
+		if s.properties.Virtual_ab.Retrofit != nil {
+			ctx.PropertyErrorf("virtual_ab.retrofit", "This property cannot be set when virtual_ab is disabled")
+		}
+		if s.properties.Virtual_ab.Compression != nil {
+			ctx.PropertyErrorf("virtual_ab.compression", "This property cannot be set when virtual_ab is disabled")
+		}
+		if s.properties.Virtual_ab.Compression_method != nil {
+			ctx.PropertyErrorf("virtual_ab.compression_method", "This property cannot be set when virtual_ab is disabled")
+		}
+		if s.properties.Virtual_ab.Compression_factor != nil {
+			ctx.PropertyErrorf("virtual_ab.compression_factor", "This property cannot be set when virtual_ab is disabled")
+		}
+	}
+
+	return partitionList
+}
+
+func (s *superImage) generateDynamicPartitionsInfo(ctx android.ModuleContext) android.Path {
+	var contents strings.Builder
+	s.dumpDynamicPartitionInfo(ctx, &contents)
+	dynamicPartitionsInfo := android.PathForModuleOut(ctx, "dynamic_partitions_info.txt")
+	android.WriteFileRuleVerbatim(ctx, dynamicPartitionsInfo, contents.String())
+	return dynamicPartitionsInfo
 }
diff --git a/filesystem/system_image.go b/filesystem/system_image.go
index 60a5133..cc9093f 100644
--- a/filesystem/system_image.go
+++ b/filesystem/system_image.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"android/soong/cc"
 	"android/soong/linkerconfig"
 
 	"strings"
@@ -43,14 +44,65 @@
 	return s.filesystem.properties
 }
 
-func (s *systemImage) BuildLinkerConfigFile(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) {
+func (s *systemImage) BuildLinkerConfigFile(
+	ctx android.ModuleContext,
+	builder *android.RuleBuilder,
+	rebasedDir android.OutputPath,
+	fullInstallPaths *[]FullInstallPathInfo,
+) {
 	if !proptools.Bool(s.filesystem.properties.Linker_config.Gen_linker_config) {
 		return
 	}
 
-	provideModules, requireModules := s.getLibsForLinkerConfig(ctx)
 	output := rebasedDir.Join(ctx, "etc", "linker.config.pb")
-	linkerconfig.BuildLinkerConfig(ctx, builder, android.PathsForModuleSrc(ctx, s.filesystem.properties.Linker_config.Linker_config_srcs), provideModules, requireModules, output)
+	if s.filesystem.properties.Linker_config.Linker_config_srcs != nil {
+		provideModules, requireModules := s.getLibsForLinkerConfig(ctx)
+		intermediateOutput := android.PathForModuleOut(ctx, "linker.config.pb")
+		linkerconfig.BuildLinkerConfig(ctx, android.PathsForModuleSrc(ctx, s.filesystem.properties.Linker_config.Linker_config_srcs), provideModules, requireModules, intermediateOutput)
+		builder.Command().Text("cp").Input(intermediateOutput).Output(output)
+
+		*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+			FullInstallPath: android.PathForModuleInPartitionInstall(ctx, s.PartitionType(), "etc", "linker.config.pb"),
+			SourcePath:      intermediateOutput,
+		})
+	} else {
+		// TODO: This branch is the logic that make uses for the linker config file, which is
+		// different than linkerconfig.BuildLinkerConfig used above. Keeping both branches for now
+		// because microdroid uses the other method and is in theory happy with it. But we should
+		// consider deduping them.
+		stubLibraries := cc.StubLibrariesFile(ctx)
+		llndkMovedToApexLibraries := cc.MovedToApexLlndkLibrariesFile(ctx)
+		outputStep1 := android.PathForModuleOut(ctx, "linker.config.pb.step1")
+		builder.Command().
+			BuiltTool("conv_linker_config").
+			Text("proto --force").
+			FlagWithInput("-s ", android.PathForSource(ctx, "system/core/rootdir/etc/linker.config.json")).
+			FlagWithOutput("-o ", outputStep1)
+		builder.Temporary(outputStep1)
+		builder.Command().
+			BuiltTool("conv_linker_config").
+			Text("systemprovide").
+			FlagWithInput("--source ", outputStep1).
+			FlagWithArg("--output ", output.String()).
+			Textf(`--value "$(cat %s)"`, stubLibraries).
+			Implicit(stubLibraries).
+			FlagWithArg("--system ", rebasedDir.String())
+		builder.Command().
+			BuiltTool("conv_linker_config").
+			Text("append").
+			FlagWithArg("--source ", output.String()).
+			FlagWithOutput("--output ", output).
+			FlagWithArg("--key ", "requireLibs").
+			Textf(`--value "$(cat %s)"`, llndkMovedToApexLibraries).
+			Implicit(llndkMovedToApexLibraries)
+		// TODO: Make also supports adding an extra append command with PRODUCT_EXTRA_STUB_LIBRARIES,
+		// but that variable appears to have no usages.
+
+		*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+			FullInstallPath: android.PathForModuleInPartitionInstall(ctx, s.PartitionType(), "etc", "linker.config.pb"),
+			SourcePath:      output,
+		})
+	}
 
 	s.appendToEntry(ctx, output)
 }
diff --git a/filesystem/system_other.go b/filesystem/system_other.go
new file mode 100644
index 0000000..32a6cc7
--- /dev/null
+++ b/filesystem/system_other.go
@@ -0,0 +1,266 @@
+// 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 filesystem
+
+import (
+	"android/soong/android"
+	"fmt"
+	"path/filepath"
+	"sort"
+	"strings"
+	"time"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+var (
+	systemOtherPropFileTweaks = pctx.AndroidStaticRule("system_other_prop_file_tweaks", blueprint.RuleParams{
+		Command: `rm -rf $out && sed -e 's@^mount_point=/$$@mount_point=system_other@g' -e 's@^partition_name=system$$@partition_name=system_other@g' $in > $out`,
+	})
+)
+
+type SystemOtherImageProperties struct {
+	// The system_other image always requires a reference to the system image. The system_other
+	// partition gets built into the system partition's "b" slot in a/b partition builds. Thus, it
+	// copies most of its configuration from the system image, such as filesystem type, avb signing
+	// info, etc. Including it here does not automatically mean that it will pick up the system
+	// image's dexpropt files, it must also be listed in Preinstall_dexpreopt_files_from for that.
+	System_image *string
+
+	// This system_other partition will include all the dexpreopt files from the apps on these
+	// partitions.
+	Preinstall_dexpreopt_files_from []string
+}
+
+type systemOtherImage struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+	properties SystemOtherImageProperties
+}
+
+// The system_other image is the default contents of the "b" slot of the system image.
+// It contains the dexpreopt files of all the apps on the device, for a faster first boot.
+// Afterwards, at runtime, it will be used as a regular b slot for OTA updates, and the initial
+// dexpreopt files will be deleted.
+func SystemOtherImageFactory() android.Module {
+	module := &systemOtherImage{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+type systemImageDeptag struct {
+	blueprint.BaseDependencyTag
+}
+
+var systemImageDependencyTag = systemImageDeptag{}
+
+type dexpreoptDeptag struct {
+	blueprint.BaseDependencyTag
+}
+
+var dexpreoptDependencyTag = dexpreoptDeptag{}
+
+func (m *systemOtherImage) DepsMutator(ctx android.BottomUpMutatorContext) {
+	if proptools.String(m.properties.System_image) == "" {
+		ctx.ModuleErrorf("system_image property must be set")
+		return
+	}
+	ctx.AddDependency(ctx.Module(), systemImageDependencyTag, *m.properties.System_image)
+	ctx.AddDependency(ctx.Module(), dexpreoptDependencyTag, m.properties.Preinstall_dexpreopt_files_from...)
+}
+
+func (m *systemOtherImage) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	systemImage := ctx.GetDirectDepProxyWithTag(*m.properties.System_image, systemImageDependencyTag)
+	systemInfo, ok := android.OtherModuleProvider(ctx, systemImage, FilesystemProvider)
+	if !ok {
+		ctx.PropertyErrorf("system_image", "Expected system_image module to provide FilesystemProvider")
+		return
+	}
+
+	output := android.PathForModuleOut(ctx, "system_other.img")
+	stagingDir := android.PathForModuleOut(ctx, "staging_dir")
+	stagingDirTimestamp := android.PathForModuleOut(ctx, "staging_dir.timestamp")
+
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().Textf("rm -rf %s && mkdir -p %s", stagingDir, stagingDir)
+
+	specs := make(map[string]android.PackagingSpec)
+	for _, otherPartition := range m.properties.Preinstall_dexpreopt_files_from {
+		dexModule := ctx.GetDirectDepProxyWithTag(otherPartition, dexpreoptDependencyTag)
+		fsInfo, ok := android.OtherModuleProvider(ctx, dexModule, FilesystemProvider)
+		if !ok {
+			ctx.PropertyErrorf("preinstall_dexpreopt_files_from", "Expected module %q to provide FilesystemProvider", otherPartition)
+			return
+		}
+		// Merge all the packaging specs into 1 map
+		for k := range fsInfo.SpecsForSystemOther {
+			if _, ok := specs[k]; ok {
+				ctx.ModuleErrorf("Packaging spec %s given by two different partitions", k)
+				continue
+			}
+			specs[k] = fsInfo.SpecsForSystemOther[k]
+		}
+	}
+
+	// TOOD: CopySpecsToDir only exists on PackagingBase, but doesn't use any fields from it. Clean this up.
+	(&android.PackagingBase{}).CopySpecsToDir(ctx, builder, specs, stagingDir)
+
+	fullInstallPaths := []string{}
+	if len(m.properties.Preinstall_dexpreopt_files_from) > 0 {
+		builder.Command().Textf("touch %s", filepath.Join(stagingDir.String(), "system-other-odex-marker"))
+		installPath := android.PathForModuleInPartitionInstall(ctx, "system_other", "system-other-odex-marker")
+		fullInstallPaths = append(fullInstallPaths, installPath.String())
+	}
+	builder.Command().Textf("touch").Output(stagingDirTimestamp)
+	builder.Build("assemble_filesystem_staging_dir", "Assemble filesystem staging dir")
+
+	// Most of the time, if build_image were to call a host tool, it accepts the path to the
+	// host tool in a field in the prop file. However, it doesn't have that option for fec, which
+	// it expects to just be on the PATH. Add fec to the PATH.
+	fec := ctx.Config().HostToolPath(ctx, "fec")
+	pathToolDirs := []string{filepath.Dir(fec.String())}
+
+	// In make, the exact same prop file is used for both system and system_other. However, I
+	// believe make goes through a different build_image code path that is based on the name of
+	// the output file. So it sees the output file is named system_other.img and makes some changes.
+	// We don't use that codepath, so make the changes manually to the prop file.
+	propFile := android.PathForModuleOut(ctx, "prop")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   systemOtherPropFileTweaks,
+		Input:  systemInfo.BuildImagePropFile,
+		Output: propFile,
+	})
+
+	builder = android.NewRuleBuilder(pctx, ctx)
+	builder.Command().
+		Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
+		BuiltTool("build_image").
+		Text(stagingDir.String()). // input directory
+		Input(propFile).
+		Implicits(systemInfo.BuildImagePropFileDeps).
+		Implicit(fec).
+		Implicit(stagingDirTimestamp).
+		Output(output).
+		Text(stagingDir.String())
+
+	builder.Build("build_system_other", "build system other")
+
+	// Create a hermetic system_other.img with pinned timestamps
+	builder = android.NewRuleBuilder(pctx, ctx)
+	outputHermetic := android.PathForModuleOut(ctx, "for_target_files", "system_other.img")
+	outputHermeticPropFile := m.propFileForHermeticImg(ctx, builder, propFile)
+	builder.Command().
+		Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
+		BuiltTool("build_image").
+		Text(stagingDir.String()). // input directory
+		Input(outputHermeticPropFile).
+		Implicits(systemInfo.BuildImagePropFileDeps).
+		Implicit(fec).
+		Implicit(stagingDirTimestamp).
+		Output(outputHermetic).
+		Text(stagingDir.String())
+
+	builder.Build("build_system_other_hermetic", "build system other")
+
+	fsInfo := FilesystemInfo{
+		Output:              output,
+		OutputHermetic:      outputHermetic,
+		RootDir:             stagingDir,
+		FilesystemConfig:    m.generateFilesystemConfig(ctx, stagingDir, stagingDirTimestamp),
+		PropFileForMiscInfo: m.buildPropFileForMiscInfo(ctx),
+	}
+
+	android.SetProvider(ctx, FilesystemProvider, fsInfo)
+
+	ctx.SetOutputFiles(android.Paths{output}, "")
+	ctx.CheckbuildFile(output)
+
+	// Dump compliance metadata
+	complianceMetadataInfo := ctx.ComplianceMetadataInfo()
+	complianceMetadataInfo.SetFilesContained(fullInstallPaths)
+}
+
+func (s *systemOtherImage) generateFilesystemConfig(ctx android.ModuleContext, stagingDir, stagingDirTimestamp android.Path) android.Path {
+	out := android.PathForModuleOut(ctx, "filesystem_config.txt")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   fsConfigRule,
+		Input:  stagingDirTimestamp, // assemble the staging directory
+		Output: out,
+		Args: map[string]string{
+			"rootDir": stagingDir.String(),
+			"prefix":  "system/",
+		},
+	})
+	return out
+}
+
+func (f *systemOtherImage) propFileForHermeticImg(ctx android.ModuleContext, builder *android.RuleBuilder, inputPropFile android.Path) android.Path {
+	propFilePinnedTimestamp := android.PathForModuleOut(ctx, "for_target_files", "prop")
+	builder.Command().Textf("cat").Input(inputPropFile).Flag(">").Output(propFilePinnedTimestamp).
+		Textf(" && echo use_fixed_timestamp=true >> %s", propFilePinnedTimestamp)
+	return propFilePinnedTimestamp
+}
+
+func (f *systemOtherImage) buildPropFileForMiscInfo(ctx android.ModuleContext) android.Path {
+	var lines []string
+	addStr := func(name string, value string) {
+		lines = append(lines, fmt.Sprintf("%s=%s", name, value))
+	}
+
+	addStr("building_system_other_image", "true")
+
+	systemImage := ctx.GetDirectDepProxyWithTag(*f.properties.System_image, systemImageDependencyTag)
+	systemInfo, ok := android.OtherModuleProvider(ctx, systemImage, FilesystemProvider)
+	if !ok {
+		ctx.PropertyErrorf("system_image", "Expected system_image module to provide FilesystemProvider")
+		return nil
+	}
+	if systemInfo.PartitionSize == nil {
+		addStr("system_other_disable_sparse", "true")
+	}
+	if systemInfo.UseAvb {
+		addStr("avb_system_other_hashtree_enable", "true")
+		addStr("avb_system_other_algorithm", systemInfo.AvbAlgorithm)
+		footerArgs := fmt.Sprintf("--hash_algorithm %s", systemInfo.AvbHashAlgorithm)
+		if rollbackIndex, err := f.avbRollbackIndex(ctx); err == nil {
+			footerArgs += fmt.Sprintf(" --rollback_index %d", rollbackIndex)
+		} else {
+			ctx.ModuleErrorf("Could not determine rollback_index %s\n", err)
+		}
+		addStr("avb_system_other_add_hashtree_footer_args", footerArgs)
+		if systemInfo.AvbKey != nil {
+			addStr("avb_system_other_key_path", systemInfo.AvbKey.String())
+		}
+	}
+
+	sort.Strings(lines)
+
+	propFile := android.PathForModuleOut(ctx, "prop_file")
+	android.WriteFileRule(ctx, propFile, strings.Join(lines, "\n"))
+	return propFile
+}
+
+// Use the default: PlatformSecurityPatch
+// TODO: Get this value from vbmeta_system
+func (f *systemOtherImage) avbRollbackIndex(ctx android.ModuleContext) (int64, error) {
+	t, err := time.Parse(time.DateOnly, ctx.Config().PlatformSecurityPatch())
+	if err != nil {
+		return -1, err
+	}
+	return t.Unix(), err
+}
diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go
index 6a47859..e7a39be 100644
--- a/filesystem/vbmeta.go
+++ b/filesystem/vbmeta.go
@@ -16,7 +16,10 @@
 
 import (
 	"fmt"
+	"sort"
 	"strconv"
+	"strings"
+	"time"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -52,6 +55,10 @@
 	// Name of the partition stored in vbmeta desc. Defaults to the name of this module.
 	Partition_name *string
 
+	// Type of the `android_filesystem` for which the vbmeta.img is created.
+	// Examples are system, vendor, product.
+	Filesystem_partition_type *string
+
 	// Set the name of the output. Defaults to <module_name>.img.
 	Stem *string
 
@@ -97,7 +104,7 @@
 	// Name of the chained partition
 	Name *string
 
-	// Rollback index location of the chained partition. Must be 0, 1, 2, etc. Default is the
+	// Rollback index location of the chained partition. Must be 1, 2, 3, etc. Default is the
 	// index of this partition in the list + 1.
 	Rollback_index_location *int64
 
@@ -115,12 +122,22 @@
 	// Name of the partition
 	Name string
 
+	// Partition type of the correspdonding android_filesystem.
+	FilesystemPartitionType string
+
 	// Rollback index location, non-negative int
 	RollbackIndexLocation int
 
 	// The path to the public key of the private key used to sign this partition. Derived from
 	// the private key.
 	PublicKey android.Path
+
+	// The output of the vbmeta module
+	Output android.Path
+
+	// Information about the vbmeta partition that will be added to misc_info.txt
+	// created by android_device
+	PropFileForMiscInfo android.Path
 }
 
 var vbmetaPartitionProvider = blueprint.NewProvider[vbmetaPartitionInfo]()
@@ -145,8 +162,8 @@
 var vbmetaChainedPartitionDep = chainedPartitionDep{}
 
 func (v *vbmeta) DepsMutator(ctx android.BottomUpMutatorContext) {
-	ctx.AddDependency(ctx.Module(), vbmetaPartitionDep, v.properties.Partitions.GetOrDefault(ctx, nil)...)
-	ctx.AddDependency(ctx.Module(), vbmetaChainedPartitionDep, v.properties.Chained_partitions...)
+	ctx.AddVariationDependencies(ctx.Config().AndroidFirstDeviceTarget.Variations(), vbmetaPartitionDep, v.properties.Partitions.GetOrDefault(ctx, nil)...)
+	ctx.AddVariationDependencies(ctx.Config().AndroidFirstDeviceTarget.Variations(), vbmetaChainedPartitionDep, v.properties.Chained_partitions...)
 }
 
 func (v *vbmeta) installFileName() string {
@@ -170,13 +187,14 @@
 	algorithm := proptools.StringDefault(v.properties.Algorithm, "SHA256_RSA4096")
 	cmd.FlagWithArg("--algorithm ", algorithm)
 
+	cmd.FlagWithArg("--padding_size ", "4096")
+
 	cmd.FlagWithArg("--rollback_index ", v.rollbackIndexCommand(ctx))
 	ril := proptools.IntDefault(v.properties.Rollback_index_location, 0)
 	if ril < 0 {
 		ctx.PropertyErrorf("rollback_index_location", "must be 0, 1, 2, ...")
 		return
 	}
-	cmd.FlagWithArg("--rollback_index_location ", strconv.Itoa(ril))
 
 	for _, avb_prop := range v.properties.Avb_properties {
 		key := proptools.String(avb_prop.Key)
@@ -221,8 +239,8 @@
 		}
 
 		ril := info.RollbackIndexLocation
-		if ril < 0 {
-			ctx.PropertyErrorf("chained_partitions", "rollback index location must be 0, 1, 2, ...")
+		if ril < 1 {
+			ctx.PropertyErrorf("chained_partitions", "rollback index location must be 1, 2, 3, ...")
 			continue
 		} else if seenRils[ril] {
 			ctx.PropertyErrorf("chained_partitions", "Multiple chained partitions with the same rollback index location %d", ril)
@@ -237,13 +255,13 @@
 	for _, cpm := range v.properties.Chained_partition_metadata {
 		name := proptools.String(cpm.Name)
 		if name == "" {
-			ctx.PropertyErrorf("chained_partitions", "name must be specified")
+			ctx.PropertyErrorf("chained_partition_metadata", "name must be specified")
 			continue
 		}
 
-		ril := proptools.IntDefault(cpm.Rollback_index_location, -1)
-		if ril < 0 {
-			ctx.PropertyErrorf("chained_partition_metadata", "rollback index location must be 0, 1, 2, ...")
+		ril := proptools.IntDefault(cpm.Rollback_index_location, 0)
+		if ril < 1 {
+			ctx.PropertyErrorf("chained_partition_metadata", "rollback index location must be 1, 2, 3, ...")
 			continue
 		} else if seenRils[ril] {
 			ctx.PropertyErrorf("chained_partition_metadata", "Multiple chained partitions with the same rollback index location %d", ril)
@@ -294,13 +312,53 @@
 	})
 
 	android.SetProvider(ctx, vbmetaPartitionProvider, vbmetaPartitionInfo{
-		Name:                  v.partitionName(),
-		RollbackIndexLocation: ril,
-		PublicKey:             extractedPublicKey,
+		Name:                    v.partitionName(),
+		FilesystemPartitionType: proptools.String(v.properties.Filesystem_partition_type),
+		RollbackIndexLocation:   ril,
+		PublicKey:               extractedPublicKey,
+		Output:                  output,
+		PropFileForMiscInfo:     v.buildPropFileForMiscInfo(ctx),
 	})
 
 	ctx.SetOutputFiles([]android.Path{output}, "")
 	v.output = output
+
+	setCommonFilesystemInfo(ctx, v)
+}
+
+func (v *vbmeta) buildPropFileForMiscInfo(ctx android.ModuleContext) android.Path {
+	var lines []string
+	addStr := func(name string, value string) {
+		lines = append(lines, fmt.Sprintf("%s=%s", name, value))
+	}
+
+	addStr(fmt.Sprintf("avb_%s_algorithm", v.partitionName()), proptools.StringDefault(v.properties.Algorithm, "SHA256_RSA4096"))
+	if v.properties.Private_key != nil {
+		addStr(fmt.Sprintf("avb_%s_key_path", v.partitionName()), android.PathForModuleSrc(ctx, proptools.String(v.properties.Private_key)).String())
+	}
+	if v.properties.Rollback_index_location != nil {
+		addStr(fmt.Sprintf("avb_%s_rollback_index_location", v.partitionName()), strconv.FormatInt(*v.properties.Rollback_index_location, 10))
+	}
+
+	var partitionDepNames []string
+	ctx.VisitDirectDepsProxyWithTag(vbmetaPartitionDep, func(child android.ModuleProxy) {
+		if info, ok := android.OtherModuleProvider(ctx, child, vbmetaPartitionProvider); ok {
+			partitionDepNames = append(partitionDepNames, info.Name)
+		} else {
+			ctx.ModuleErrorf("vbmeta dep %s does not set vbmetaPartitionProvider\n", child)
+		}
+	})
+	if v.partitionName() != "vbmeta" { // skip for vbmeta to match Make's misc_info.txt
+		addStr(fmt.Sprintf("avb_%s", v.partitionName()), strings.Join(android.SortedUniqueStrings(partitionDepNames), " "))
+	}
+
+	addStr(fmt.Sprintf("avb_%s_args", v.partitionName()), fmt.Sprintf("--padding_size 4096 --rollback_index %s", v.rollbackIndexString(ctx)))
+
+	sort.Strings(lines)
+
+	propFile := android.PathForModuleOut(ctx, "prop_file_for_misc_info")
+	android.WriteFileRule(ctx, propFile, strings.Join(lines, "\n"))
+	return propFile
 }
 
 // Returns the embedded shell command that prints the rollback index
@@ -313,6 +371,17 @@
 	}
 }
 
+// Similar to rollbackIndexCommand, but guarantees that the rollback index is
+// always computed during Soong analysis, even if v.properties.Rollback_index is nil
+func (v *vbmeta) rollbackIndexString(ctx android.ModuleContext) string {
+	if v.properties.Rollback_index != nil {
+		return fmt.Sprintf("%d", *v.properties.Rollback_index)
+	} else {
+		t, _ := time.Parse(time.DateOnly, ctx.Config().PlatformSecurityPatch())
+		return fmt.Sprintf("%d", t.Unix())
+	}
+}
+
 var _ android.AndroidMkProviderInfoProducer = (*vbmeta)(nil)
 
 func (v *vbmeta) PrepareAndroidMKProviderInfo(config android.Config) *android.AndroidMkProviderInfo {
diff --git a/fsgen/Android.bp b/fsgen/Android.bp
index 365d954..1b828c5 100644
--- a/fsgen/Android.bp
+++ b/fsgen/Android.bp
@@ -14,6 +14,7 @@
     ],
     srcs: [
         "boot_imgs.go",
+        "config.go",
         "filesystem_creator.go",
         "fsgen_mutators.go",
         "prebuilt_etc_modules_gen.go",
diff --git a/fsgen/boot_imgs.go b/fsgen/boot_imgs.go
index 4e80720..0ba0a90 100644
--- a/fsgen/boot_imgs.go
+++ b/fsgen/boot_imgs.go
@@ -69,22 +69,27 @@
 	ctx.CreateModule(
 		filesystem.BootimgFactory,
 		&filesystem.BootimgProperties{
-			Kernel_prebuilt:    proptools.StringPtr(":" + kernelFilegroupName),
-			Header_version:     proptools.StringPtr(partitionVariables.BoardBootHeaderVersion),
-			Partition_size:     partitionSize,
-			Use_avb:            avbInfo.avbEnable,
-			Avb_mode:           avbInfo.avbMode,
-			Avb_private_key:    avbInfo.avbkeyFilegroup,
-			Avb_rollback_index: avbInfo.avbRollbackIndex,
-			Avb_algorithm:      avbInfo.avbAlgorithm,
-			Security_patch:     securityPatch,
-			Dtb_prebuilt:       dtbPrebuilt,
-			Cmdline:            cmdline,
+			Boot_image_type:             proptools.StringPtr("boot"),
+			Kernel_prebuilt:             proptools.StringPtr(":" + kernelFilegroupName),
+			Header_version:              proptools.StringPtr(partitionVariables.BoardBootHeaderVersion),
+			Partition_size:              partitionSize,
+			Use_avb:                     avbInfo.avbEnable,
+			Avb_mode:                    avbInfo.avbMode,
+			Avb_private_key:             avbInfo.avbkeyFilegroup,
+			Avb_rollback_index:          avbInfo.avbRollbackIndex,
+			Avb_rollback_index_location: avbInfo.avbRollbackIndexLocation,
+			Avb_algorithm:               avbInfo.avbAlgorithm,
+			Security_patch:              securityPatch,
+			Dtb_prebuilt:                dtbPrebuilt,
+			Cmdline:                     cmdline,
+			Stem:                        proptools.StringPtr("boot.img"),
 		},
 		&struct {
-			Name *string
+			Name       *string
+			Visibility []string
 		}{
-			Name: proptools.StringPtr(bootImageName),
+			Name:       proptools.StringPtr(bootImageName),
+			Visibility: []string{"//visibility:public"},
 		},
 	)
 	return true
@@ -109,25 +114,39 @@
 		vendorBootConfigImg = proptools.StringPtr(":" + name)
 	}
 
+	var partitionSize *int64
+	if partitionVariables.BoardVendorBootimagePartitionSize != "" {
+		// Base of zero will allow base 10 or base 16 if starting with 0x
+		parsed, err := strconv.ParseInt(partitionVariables.BoardVendorBootimagePartitionSize, 0, 64)
+		if err != nil {
+			ctx.ModuleErrorf("BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE must be an int, got %s", partitionVariables.BoardVendorBootimagePartitionSize)
+		}
+		partitionSize = &parsed
+	}
+
 	ctx.CreateModule(
 		filesystem.BootimgFactory,
 		&filesystem.BootimgProperties{
-			Boot_image_type:    proptools.StringPtr("vendor_boot"),
-			Ramdisk_module:     proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_ramdisk")),
-			Header_version:     proptools.StringPtr(partitionVariables.BoardBootHeaderVersion),
-			Use_avb:            avbInfo.avbEnable,
-			Avb_mode:           avbInfo.avbMode,
-			Avb_private_key:    avbInfo.avbkeyFilegroup,
-			Avb_rollback_index: avbInfo.avbRollbackIndex,
-			Avb_algorithm:      avbInfo.avbAlgorithm,
-			Dtb_prebuilt:       dtbPrebuilt,
-			Cmdline:            cmdline,
-			Bootconfig:         vendorBootConfigImg,
+			Boot_image_type:             proptools.StringPtr("vendor_boot"),
+			Ramdisk_module:              proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_ramdisk")),
+			Header_version:              proptools.StringPtr(partitionVariables.BoardBootHeaderVersion),
+			Partition_size:              partitionSize,
+			Use_avb:                     avbInfo.avbEnable,
+			Avb_mode:                    avbInfo.avbMode,
+			Avb_private_key:             avbInfo.avbkeyFilegroup,
+			Avb_rollback_index:          avbInfo.avbRollbackIndex,
+			Avb_rollback_index_location: avbInfo.avbRollbackIndexLocation,
+			Dtb_prebuilt:                dtbPrebuilt,
+			Cmdline:                     cmdline,
+			Bootconfig:                  vendorBootConfigImg,
+			Stem:                        proptools.StringPtr("vendor_boot.img"),
 		},
 		&struct {
-			Name *string
+			Name       *string
+			Visibility []string
 		}{
-			Name: proptools.StringPtr(bootImageName),
+			Name:       proptools.StringPtr(bootImageName),
+			Visibility: []string{"//visibility:public"},
 		},
 	)
 	return true
@@ -160,21 +179,25 @@
 	ctx.CreateModule(
 		filesystem.BootimgFactory,
 		&filesystem.BootimgProperties{
-			Boot_image_type:    proptools.StringPtr("init_boot"),
-			Ramdisk_module:     proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "ramdisk")),
-			Header_version:     proptools.StringPtr(partitionVariables.BoardBootHeaderVersion),
-			Security_patch:     securityPatch,
-			Partition_size:     partitionSize,
-			Use_avb:            avbInfo.avbEnable,
-			Avb_mode:           avbInfo.avbMode,
-			Avb_private_key:    avbInfo.avbkeyFilegroup,
-			Avb_rollback_index: avbInfo.avbRollbackIndex,
-			Avb_algorithm:      avbInfo.avbAlgorithm,
+			Boot_image_type:             proptools.StringPtr("init_boot"),
+			Ramdisk_module:              proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "ramdisk")),
+			Header_version:              proptools.StringPtr(partitionVariables.BoardBootHeaderVersion),
+			Security_patch:              securityPatch,
+			Partition_size:              partitionSize,
+			Use_avb:                     avbInfo.avbEnable,
+			Avb_mode:                    avbInfo.avbMode,
+			Avb_private_key:             avbInfo.avbkeyFilegroup,
+			Avb_rollback_index:          avbInfo.avbRollbackIndex,
+			Avb_rollback_index_location: avbInfo.avbRollbackIndexLocation,
+			Avb_algorithm:               avbInfo.avbAlgorithm,
+			Stem:                        proptools.StringPtr("init_boot.img"),
 		},
 		&struct {
-			Name *string
+			Name       *string
+			Visibility []string
 		}{
-			Name: proptools.StringPtr(bootImageName),
+			Name:       proptools.StringPtr(bootImageName),
+			Visibility: []string{"//visibility:public"},
 		},
 	)
 	return true
diff --git a/fsgen/config.go b/fsgen/config.go
new file mode 100644
index 0000000..a217600
--- /dev/null
+++ b/fsgen/config.go
@@ -0,0 +1,141 @@
+// 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/filesystem"
+
+	"github.com/google/blueprint/proptools"
+)
+
+var (
+	// Most of the symlinks and directories listed here originate from create_root_structure.mk,
+	// but the handwritten generic system image also recreates them:
+	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/target/product/generic/Android.bp;l=33;drc=db08311f1b6ef6cb0a4fbcc6263b89849360ce04
+	// TODO(b/377734331): only generate the symlinks if the relevant partitions exist
+	commonSymlinksFromRoot = []filesystem.SymlinkDefinition{
+		{
+			Target: proptools.StringPtr("/system/bin/init"),
+			Name:   proptools.StringPtr("init"),
+		},
+		{
+			Target: proptools.StringPtr("/system/etc"),
+			Name:   proptools.StringPtr("etc"),
+		},
+		{
+			Target: proptools.StringPtr("/system/bin"),
+			Name:   proptools.StringPtr("bin"),
+		},
+		{
+			Target: proptools.StringPtr("/data/user_de/0/com.android.shell/files/bugreports"),
+			Name:   proptools.StringPtr("bugreports"),
+		},
+		{
+			Target: proptools.StringPtr("/sys/kernel/debug"),
+			Name:   proptools.StringPtr("d"),
+		},
+		{
+			Target: proptools.StringPtr("/product/etc/security/adb_keys"),
+			Name:   proptools.StringPtr("adb_keys"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/app"),
+			Name:   proptools.StringPtr("odm/app"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/bin"),
+			Name:   proptools.StringPtr("odm/bin"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/etc"),
+			Name:   proptools.StringPtr("odm/etc"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/firmware"),
+			Name:   proptools.StringPtr("odm/firmware"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/framework"),
+			Name:   proptools.StringPtr("odm/framework"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/lib"),
+			Name:   proptools.StringPtr("odm/lib"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/lib64"),
+			Name:   proptools.StringPtr("odm/lib64"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/overlay"),
+			Name:   proptools.StringPtr("odm/overlay"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/priv-app"),
+			Name:   proptools.StringPtr("odm/priv-app"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/usr"),
+			Name:   proptools.StringPtr("odm/usr"),
+		},
+		// For Treble Generic System Image (GSI), system-as-root GSI needs to work on
+		// both devices with and without /odm_dlkm partition. Those symlinks are for
+		// devices without /odm_dlkm partition. For devices with /odm_dlkm
+		// partition, mount odm_dlkm.img under /odm_dlkm will hide those symlinks.
+		// Note that /odm_dlkm/lib is omitted because odm DLKMs should be accessed
+		// via /odm/lib/modules directly. All of this also applies to the vendor_dlkm symlink
+		{
+			Target: proptools.StringPtr("/odm/odm_dlkm/etc"),
+			Name:   proptools.StringPtr("odm_dlkm/etc"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/vendor_dlkm/etc"),
+			Name:   proptools.StringPtr("vendor_dlkm/etc"),
+		},
+	}
+
+	// Common directories between partitions that may be listed as `Dirs` property in the
+	// filesystem module.
+	commonPartitionDirs = []string{
+		// From generic_rootdirs in build/make/target/product/generic/Android.bp
+		"apex",
+		"bootstrap-apex",
+		"config",
+		"data",
+		"data_mirror",
+		"debug_ramdisk",
+		"dev",
+		"linkerconfig",
+		"metadata",
+		"mnt",
+		"odm",
+		"odm_dlkm",
+		"oem",
+		"postinstall",
+		"proc",
+		"second_stage_resources",
+		"storage",
+		"sys",
+		"system",
+		"system_dlkm",
+		"tmp",
+		"vendor",
+		"vendor_dlkm",
+
+		// from android_rootdirs in build/make/target/product/generic/Android.bp
+		"system_ext",
+		"product",
+	}
+)
diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
index 11dfac0..14aa062 100644
--- a/fsgen/filesystem_creator.go
+++ b/fsgen/filesystem_creator.go
@@ -42,8 +42,85 @@
 	ctx.PreDepsMutators(RegisterCollectFileSystemDepsMutators)
 }
 
+type generatedPartitionData struct {
+	partitionType string
+	moduleName    string
+	// supported is true if the module was created successfully, false if there was some problem
+	// and the module couldn't be created.
+	supported   bool
+	handwritten bool
+}
+
+type allGeneratedPartitionData []generatedPartitionData
+
+func (d allGeneratedPartitionData) moduleNames() []string {
+	var result []string
+	for _, data := range d {
+		if data.supported {
+			result = append(result, data.moduleName)
+		}
+	}
+	return result
+}
+
+func (d allGeneratedPartitionData) types() []string {
+	var result []string
+	for _, data := range d {
+		if data.supported {
+			result = append(result, data.partitionType)
+		}
+	}
+	return result
+}
+
+func (d allGeneratedPartitionData) unsupportedTypes() []string {
+	var result []string
+	for _, data := range d {
+		if !data.supported {
+			result = append(result, data.partitionType)
+		}
+	}
+	return result
+}
+
+func (d allGeneratedPartitionData) names() []string {
+	var result []string
+	for _, data := range d {
+		if data.supported {
+			result = append(result, data.moduleName)
+		}
+	}
+	return result
+}
+
+func (d allGeneratedPartitionData) nameForType(ty string) string {
+	for _, data := range d {
+		if data.supported && data.partitionType == ty {
+			return data.moduleName
+		}
+	}
+	return ""
+}
+
+func (d allGeneratedPartitionData) typeForName(name string) string {
+	for _, data := range d {
+		if data.supported && data.moduleName == name {
+			return data.partitionType
+		}
+	}
+	return ""
+}
+
+func (d allGeneratedPartitionData) isHandwritten(name string) bool {
+	for _, data := range d {
+		if data.supported && data.moduleName == name {
+			return data.handwritten
+		}
+	}
+	return false
+}
+
 type filesystemCreatorProps struct {
-	Generated_partition_types   []string `blueprint:"mutated"`
 	Unsupported_partition_types []string `blueprint:"mutated"`
 
 	Vbmeta_module_names    []string `blueprint:"mutated"`
@@ -78,56 +155,74 @@
 	return module
 }
 
-func generatedPartitions(ctx android.LoadHookContext) []string {
+func generatedPartitions(ctx android.EarlyModuleContext) allGeneratedPartitionData {
 	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
-	generatedPartitions := []string{"system"}
+
+	var result allGeneratedPartitionData
+	addGenerated := func(ty string) {
+		result = append(result, generatedPartitionData{
+			partitionType: ty,
+			moduleName:    generatedModuleNameForPartition(ctx.Config(), ty),
+			supported:     true,
+		})
+	}
+
+	if ctx.Config().UseSoongSystemImage() {
+		if ctx.Config().SoongDefinedSystemImage() == "" {
+			panic("PRODUCT_SOONG_DEFINED_SYSTEM_IMAGE must be set if USE_SOONG_DEFINED_SYSTEM_IMAGE is true")
+		}
+		result = append(result, generatedPartitionData{
+			partitionType: "system",
+			moduleName:    ctx.Config().SoongDefinedSystemImage(),
+			supported:     true,
+			handwritten:   true,
+		})
+	} else {
+		addGenerated("system")
+	}
 	if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
-		generatedPartitions = append(generatedPartitions, "system_ext")
+		addGenerated("system_ext")
 	}
 	if ctx.DeviceConfig().BuildingVendorImage() && ctx.DeviceConfig().VendorPath() == "vendor" {
-		generatedPartitions = append(generatedPartitions, "vendor")
+		addGenerated("vendor")
 	}
 	if ctx.DeviceConfig().BuildingProductImage() && ctx.DeviceConfig().ProductPath() == "product" {
-		generatedPartitions = append(generatedPartitions, "product")
+		addGenerated("product")
 	}
 	if ctx.DeviceConfig().BuildingOdmImage() && ctx.DeviceConfig().OdmPath() == "odm" {
-		generatedPartitions = append(generatedPartitions, "odm")
+		addGenerated("odm")
 	}
 	if ctx.DeviceConfig().BuildingUserdataImage() && ctx.DeviceConfig().UserdataPath() == "data" {
-		generatedPartitions = append(generatedPartitions, "userdata")
+		addGenerated("userdata")
 	}
 	if partitionVars.BuildingSystemDlkmImage {
-		generatedPartitions = append(generatedPartitions, "system_dlkm")
+		addGenerated("system_dlkm")
 	}
 	if partitionVars.BuildingVendorDlkmImage {
-		generatedPartitions = append(generatedPartitions, "vendor_dlkm")
+		addGenerated("vendor_dlkm")
 	}
 	if partitionVars.BuildingOdmDlkmImage {
-		generatedPartitions = append(generatedPartitions, "odm_dlkm")
+		addGenerated("odm_dlkm")
 	}
 	if partitionVars.BuildingRamdiskImage {
-		generatedPartitions = append(generatedPartitions, "ramdisk")
+		addGenerated("ramdisk")
 	}
 	if buildingVendorBootImage(partitionVars) {
-		generatedPartitions = append(generatedPartitions, "vendor_ramdisk")
+		addGenerated("vendor_ramdisk")
 	}
 	if ctx.DeviceConfig().BuildingRecoveryImage() && ctx.DeviceConfig().RecoveryPath() == "recovery" {
-		generatedPartitions = append(generatedPartitions, "recovery")
+		addGenerated("recovery")
 	}
-	return generatedPartitions
+	return result
 }
 
 func (f *filesystemCreator) createInternalModules(ctx android.LoadHookContext) {
-	soongGeneratedPartitions := generatedPartitions(ctx)
-	finalSoongGeneratedPartitions := make([]string, 0, len(soongGeneratedPartitions))
-	for _, partitionType := range soongGeneratedPartitions {
-		if f.createPartition(ctx, partitionType) {
-			f.properties.Generated_partition_types = append(f.properties.Generated_partition_types, partitionType)
-			finalSoongGeneratedPartitions = append(finalSoongGeneratedPartitions, partitionType)
-		} else {
-			f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, partitionType)
-		}
+	partitions := generatedPartitions(ctx)
+	for i := range partitions {
+		f.createPartition(ctx, partitions, &partitions[i])
 	}
+	// Create android_info.prop
+	f.createAndroidInfo(ctx)
 
 	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
 	dtbImg := createDtbImgFilegroup(ctx)
@@ -154,18 +249,37 @@
 		}
 	}
 
-	for _, x := range createVbmetaPartitions(ctx, finalSoongGeneratedPartitions) {
+	var systemOtherImageName string
+	if buildingSystemOtherImage(partitionVars) {
+		systemModule := partitions.nameForType("system")
+		systemOtherImageName = generatedModuleNameForPartition(ctx.Config(), "system_other")
+		ctx.CreateModule(
+			filesystem.SystemOtherImageFactory,
+			&filesystem.SystemOtherImageProperties{
+				System_image:                    &systemModule,
+				Preinstall_dexpreopt_files_from: partitions.moduleNames(),
+			},
+			&struct {
+				Name *string
+			}{
+				Name: proptools.StringPtr(systemOtherImageName),
+			},
+		)
+	}
+
+	for _, x := range f.createVbmetaPartitions(ctx, partitions) {
 		f.properties.Vbmeta_module_names = append(f.properties.Vbmeta_module_names, x.moduleName)
 		f.properties.Vbmeta_partition_names = append(f.properties.Vbmeta_partition_names, x.partitionName)
 	}
 
+	var superImageSubpartitions []string
 	if buildingSuperImage(partitionVars) {
-		createSuperImage(ctx, finalSoongGeneratedPartitions, partitionVars)
-		f.properties.Super_image = ":" + generatedModuleName(ctx.Config(), "super")
+		superImageSubpartitions = createSuperImage(ctx, partitions, partitionVars, systemOtherImageName)
+		f.properties.Super_image = ":" + generatedModuleNameForPartition(ctx.Config(), "super")
 	}
 
-	ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions = finalSoongGeneratedPartitions
-	f.createDeviceModule(ctx, finalSoongGeneratedPartitions, f.properties.Vbmeta_module_names)
+	ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions = partitions
+	f.createDeviceModule(ctx, partitions, f.properties.Vbmeta_module_names, superImageSubpartitions)
 }
 
 func generatedModuleName(cfg android.Config, suffix string) string {
@@ -180,10 +294,77 @@
 	return generatedModuleName(cfg, fmt.Sprintf("%s_image", partitionType))
 }
 
+func buildingSystemOtherImage(partitionVars android.PartitionVariables) bool {
+	// TODO: Recreate this logic from make instead of just depending on the final result variable:
+	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/board_config.mk;l=429;drc=15a0df840e7093f65518003ab80cf24a3d9e8e6a
+	return partitionVars.BuildingSystemOtherImage
+}
+
+func (f *filesystemCreator) createBootloaderFilegroup(ctx android.LoadHookContext) (string, bool) {
+	bootloaderPath := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.PrebuiltBootloader
+	if len(bootloaderPath) == 0 {
+		return "", false
+	}
+
+	bootloaderFilegroupName := generatedModuleName(ctx.Config(), "bootloader")
+	filegroupProps := &struct {
+		Name       *string
+		Srcs       []string
+		Visibility []string
+	}{
+		Name:       proptools.StringPtr(bootloaderFilegroupName),
+		Srcs:       []string{bootloaderPath},
+		Visibility: []string{"//visibility:public"},
+	}
+	ctx.CreateModuleInDirectory(android.FileGroupFactory, ".", filegroupProps)
+	return bootloaderFilegroupName, true
+}
+
+func (f *filesystemCreator) createReleaseToolsFilegroup(ctx android.LoadHookContext) (string, bool) {
+	releaseToolsDir := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.ReleaseToolsExtensionDir
+	if releaseToolsDir == "" {
+		return "", false
+	}
+
+	releaseToolsFilegroupName := generatedModuleName(ctx.Config(), "releasetools")
+	filegroupProps := &struct {
+		Name       *string
+		Srcs       []string
+		Visibility []string
+	}{
+		Name:       proptools.StringPtr(releaseToolsFilegroupName),
+		Srcs:       []string{"releasetools.py"},
+		Visibility: []string{"//visibility:public"},
+	}
+	ctx.CreateModuleInDirectory(android.FileGroupFactory, releaseToolsDir, filegroupProps)
+	return releaseToolsFilegroupName, true
+}
+
+func (f *filesystemCreator) createFastbootInfoFilegroup(ctx android.LoadHookContext) (string, bool) {
+	fastbootInfoFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BoardFastbootInfoFile
+	if fastbootInfoFile == "" {
+		return "", false
+	}
+
+	fastbootInfoFilegroupName := generatedModuleName(ctx.Config(), "fastboot")
+	filegroupProps := &struct {
+		Name       *string
+		Srcs       []string
+		Visibility []string
+	}{
+		Name:       proptools.StringPtr(fastbootInfoFilegroupName),
+		Srcs:       []string{fastbootInfoFile},
+		Visibility: []string{"//visibility:public"},
+	}
+	ctx.CreateModuleInDirectory(android.FileGroupFactory, ".", filegroupProps)
+	return fastbootInfoFilegroupName, true
+}
+
 func (f *filesystemCreator) createDeviceModule(
 	ctx android.LoadHookContext,
-	generatedPartitionTypes []string,
+	partitions allGeneratedPartitionData,
 	vbmetaPartitions []string,
+	superImageSubPartitions []string,
 ) {
 	baseProps := &struct {
 		Name *string
@@ -193,30 +374,78 @@
 
 	// Currently, only the system and system_ext partition module is created.
 	partitionProps := &filesystem.PartitionNameProperties{}
-	if android.InList("system", generatedPartitionTypes) {
-		partitionProps.System_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
+	if f.properties.Super_image != "" {
+		partitionProps.Super_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "super"))
 	}
-	if android.InList("system_ext", generatedPartitionTypes) {
-		partitionProps.System_ext_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
+	if modName := partitions.nameForType("system"); modName != "" && !android.InList("system", superImageSubPartitions) {
+		partitionProps.System_partition_name = proptools.StringPtr(modName)
 	}
-	if android.InList("vendor", generatedPartitionTypes) {
-		partitionProps.Vendor_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor"))
+	if modName := partitions.nameForType("system_ext"); modName != "" && !android.InList("system_ext", superImageSubPartitions) {
+		partitionProps.System_ext_partition_name = proptools.StringPtr(modName)
 	}
-	if android.InList("product", generatedPartitionTypes) {
-		partitionProps.Product_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "product"))
+	if modName := partitions.nameForType("vendor"); modName != "" && !android.InList("vendor", superImageSubPartitions) {
+		partitionProps.Vendor_partition_name = proptools.StringPtr(modName)
 	}
-	if android.InList("odm", generatedPartitionTypes) {
-		partitionProps.Odm_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "odm"))
+	if modName := partitions.nameForType("product"); modName != "" && !android.InList("product", superImageSubPartitions) {
+		partitionProps.Product_partition_name = proptools.StringPtr(modName)
 	}
-	if android.InList("userdata", f.properties.Generated_partition_types) {
-		partitionProps.Userdata_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "userdata"))
+	if modName := partitions.nameForType("odm"); modName != "" && !android.InList("odm", superImageSubPartitions) {
+		partitionProps.Odm_partition_name = proptools.StringPtr(modName)
+	}
+	if modName := partitions.nameForType("userdata"); modName != "" {
+		partitionProps.Userdata_partition_name = proptools.StringPtr(modName)
+	}
+	if modName := partitions.nameForType("recovery"); modName != "" && !ctx.DeviceConfig().BoardMoveRecoveryResourcesToVendorBoot() {
+		partitionProps.Recovery_partition_name = proptools.StringPtr(modName)
+	}
+	if modName := partitions.nameForType("system_dlkm"); modName != "" && !android.InList("system_dlkm", superImageSubPartitions) {
+		partitionProps.System_dlkm_partition_name = proptools.StringPtr(modName)
+	}
+	if modName := partitions.nameForType("vendor_dlkm"); modName != "" && !android.InList("vendor_dlkm", superImageSubPartitions) {
+		partitionProps.Vendor_dlkm_partition_name = proptools.StringPtr(modName)
+	}
+	if modName := partitions.nameForType("odm_dlkm"); modName != "" && !android.InList("odm_dlkm", superImageSubPartitions) {
+		partitionProps.Odm_dlkm_partition_name = proptools.StringPtr(modName)
+	}
+	if f.properties.Boot_image != "" {
+		partitionProps.Boot_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "boot"))
+	}
+	if f.properties.Vendor_boot_image != "" {
+		partitionProps.Vendor_boot_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_boot"))
+	}
+	if f.properties.Init_boot_image != "" {
+		partitionProps.Init_boot_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "init_boot"))
 	}
 	partitionProps.Vbmeta_partitions = vbmetaPartitions
 
-	ctx.CreateModule(filesystem.AndroidDeviceFactory, baseProps, partitionProps)
+	deviceProps := &filesystem.DeviceProperties{
+		Main_device:                   proptools.BoolPtr(true),
+		Ab_ota_updater:                proptools.BoolPtr(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.AbOtaUpdater),
+		Ab_ota_partitions:             ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.AbOtaPartitions,
+		Ab_ota_postinstall_config:     ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.AbOtaPostInstallConfig,
+		Ramdisk_node_list:             proptools.StringPtr(":ramdisk_node_list"),
+		Android_info:                  proptools.StringPtr(":" + generatedModuleName(ctx.Config(), "android_info.prop{.txt}")),
+		Kernel_version:                ctx.Config().ProductVariables().BoardKernelVersion,
+		Partial_ota_update_partitions: ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BoardPartialOtaUpdatePartitionsList,
+		Flash_block_size:              proptools.StringPtr(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BoardFlashBlockSize),
+		Bootloader_in_update_package:  proptools.BoolPtr(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BootloaderInUpdatePackage),
+	}
+
+	if bootloader, ok := f.createBootloaderFilegroup(ctx); ok {
+		deviceProps.Bootloader = proptools.StringPtr(":" + bootloader)
+	}
+	if releaseTools, ok := f.createReleaseToolsFilegroup(ctx); ok {
+		deviceProps.Releasetools_extension = proptools.StringPtr(":" + releaseTools)
+	}
+	if fastbootInfo, ok := f.createFastbootInfoFilegroup(ctx); ok {
+		deviceProps.FastbootInfo = proptools.StringPtr(":" + fastbootInfo)
+	}
+
+	ctx.CreateModule(filesystem.AndroidDeviceFactory, baseProps, partitionProps, deviceProps)
 }
 
-func partitionSpecificFsProps(ctx android.EarlyModuleContext, fsProps *filesystem.FilesystemProperties, partitionVars android.PartitionVariables, partitionType string) {
+func partitionSpecificFsProps(ctx android.EarlyModuleContext, partitions allGeneratedPartitionData, fsProps *filesystem.FilesystemProperties, partitionType string) {
+	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
 	switch partitionType {
 	case "system":
 		fsProps.Build_logtags = proptools.BoolPtr(true)
@@ -235,146 +464,39 @@
 			})
 			fsProps.Fsverity.Libs = proptools.NewSimpleConfigurable([]string{":framework-res{.export-package.apk}"})
 		}
-		// Most of the symlinks and directories listed here originate from create_root_structure.mk,
-		// but the handwritten generic system image also recreates them:
-		// https://cs.android.com/android/platform/superproject/main/+/main:build/make/target/product/generic/Android.bp;l=33;drc=db08311f1b6ef6cb0a4fbcc6263b89849360ce04
-		// TODO(b/377734331): only generate the symlinks if the relevant partitions exist
-		fsProps.Symlinks = []filesystem.SymlinkDefinition{
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/system/bin/init"),
-				Name:   proptools.StringPtr("init"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/system/etc"),
-				Name:   proptools.StringPtr("etc"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/system/bin"),
-				Name:   proptools.StringPtr("bin"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/data/user_de/0/com.android.shell/files/bugreports"),
-				Name:   proptools.StringPtr("bugreports"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/sys/kernel/debug"),
-				Name:   proptools.StringPtr("d"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/storage/self/primary"),
-				Name:   proptools.StringPtr("sdcard"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/product/etc/security/adb_keys"),
-				Name:   proptools.StringPtr("adb_keys"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/app"),
-				Name:   proptools.StringPtr("odm/app"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/bin"),
-				Name:   proptools.StringPtr("odm/bin"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/etc"),
-				Name:   proptools.StringPtr("odm/etc"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/firmware"),
-				Name:   proptools.StringPtr("odm/firmware"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/framework"),
-				Name:   proptools.StringPtr("odm/framework"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/lib"),
-				Name:   proptools.StringPtr("odm/lib"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/lib64"),
-				Name:   proptools.StringPtr("odm/lib64"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/overlay"),
-				Name:   proptools.StringPtr("odm/overlay"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/priv-app"),
-				Name:   proptools.StringPtr("odm/priv-app"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/usr"),
-				Name:   proptools.StringPtr("odm/usr"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/product"),
-				Name:   proptools.StringPtr("system/product"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/system_ext"),
-				Name:   proptools.StringPtr("system/system_ext"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor"),
-				Name:   proptools.StringPtr("system/vendor"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/system_dlkm/lib/modules"),
-				Name:   proptools.StringPtr("system/lib/modules"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/data/cache"),
-				Name:   proptools.StringPtr("cache"),
-			},
-			// For Treble Generic System Image (GSI), system-as-root GSI needs to work on
-			// both devices with and without /odm_dlkm partition. Those symlinks are for
-			// devices without /odm_dlkm partition. For devices with /odm_dlkm
-			// partition, mount odm_dlkm.img under /odm_dlkm will hide those symlinks.
-			// Note that /odm_dlkm/lib is omitted because odm DLKMs should be accessed
-			// via /odm/lib/modules directly. All of this also applies to the vendor_dlkm symlink
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/odm/odm_dlkm/etc"),
-				Name:   proptools.StringPtr("odm_dlkm/etc"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/vendor_dlkm/etc"),
-				Name:   proptools.StringPtr("vendor_dlkm/etc"),
-			},
-		}
+		fsProps.Symlinks = commonSymlinksFromRoot
+		fsProps.Symlinks = append(fsProps.Symlinks,
+			[]filesystem.SymlinkDefinition{
+				{
+					Target: proptools.StringPtr("/data/cache"),
+					Name:   proptools.StringPtr("cache"),
+				},
+				{
+					Target: proptools.StringPtr("/storage/self/primary"),
+					Name:   proptools.StringPtr("sdcard"),
+				},
+				{
+					Target: proptools.StringPtr("/system_dlkm/lib/modules"),
+					Name:   proptools.StringPtr("system/lib/modules"),
+				},
+				{
+					Target: proptools.StringPtr("/product"),
+					Name:   proptools.StringPtr("system/product"),
+				},
+				{
+					Target: proptools.StringPtr("/system_ext"),
+					Name:   proptools.StringPtr("system/system_ext"),
+				},
+				{
+					Target: proptools.StringPtr("/vendor"),
+					Name:   proptools.StringPtr("system/vendor"),
+				},
+			}...,
+		)
 		fsProps.Base_dir = proptools.StringPtr("system")
-		fsProps.Dirs = proptools.NewSimpleConfigurable([]string{
-			// From generic_rootdirs in build/make/target/product/generic/Android.bp
-			"acct",
-			"apex",
-			"bootstrap-apex",
-			"config",
-			"data",
-			"data_mirror",
-			"debug_ramdisk",
-			"dev",
-			"linkerconfig",
-			"metadata",
-			"mnt",
-			"odm",
-			"odm_dlkm",
-			"oem",
-			"postinstall",
-			"proc",
-			"second_stage_resources",
-			"storage",
-			"sys",
-			"system",
-			"system_dlkm",
-			"tmp",
-			"vendor",
-			"vendor_dlkm",
-
-			// from android_rootdirs in build/make/target/product/generic/Android.bp
-			"system_ext",
-			"product",
-		})
+		fsProps.Dirs = proptools.NewSimpleConfigurable(commonPartitionDirs)
+		fsProps.Security_patch = proptools.StringPtr(ctx.Config().PlatformSecurityPatch())
+		fsProps.Stem = proptools.StringPtr("system.img")
 	case "system_ext":
 		if partitionVars.ProductFsverityGenerateMetadata {
 			fsProps.Fsverity.Inputs = proptools.NewSimpleConfigurable([]string{
@@ -384,12 +506,17 @@
 			})
 			fsProps.Fsverity.Libs = proptools.NewSimpleConfigurable([]string{":framework-res{.export-package.apk}"})
 		}
+		fsProps.Security_patch = proptools.StringPtr(ctx.Config().PlatformSecurityPatch())
+		fsProps.Stem = proptools.StringPtr("system_ext.img")
+		fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
 	case "product":
 		fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
-		fsProps.Android_filesystem_deps.System = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
-		if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
-			fsProps.Android_filesystem_deps.System_ext = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
+		fsProps.Android_filesystem_deps.System = proptools.StringPtr(partitions.nameForType("system"))
+		if systemExtName := partitions.nameForType("system_ext"); systemExtName != "" {
+			fsProps.Android_filesystem_deps.System_ext = proptools.StringPtr(systemExtName)
 		}
+		fsProps.Security_patch = proptools.StringPtr(ctx.Config().PlatformSecurityPatch())
+		fsProps.Stem = proptools.StringPtr("product.img")
 	case "vendor":
 		fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
 		fsProps.Symlinks = []filesystem.SymlinkDefinition{
@@ -402,10 +529,12 @@
 				Name:   proptools.StringPtr("lib/modules"),
 			},
 		}
-		fsProps.Android_filesystem_deps.System = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
-		if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
-			fsProps.Android_filesystem_deps.System_ext = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
+		fsProps.Android_filesystem_deps.System = proptools.StringPtr(partitions.nameForType("system"))
+		if systemExtName := partitions.nameForType("system_ext"); systemExtName != "" {
+			fsProps.Android_filesystem_deps.System_ext = proptools.StringPtr(systemExtName)
 		}
+		fsProps.Security_patch = proptools.StringPtr(partitionVars.VendorSecurityPatch)
+		fsProps.Stem = proptools.StringPtr("vendor.img")
 	case "odm":
 		fsProps.Symlinks = []filesystem.SymlinkDefinition{
 			filesystem.SymlinkDefinition{
@@ -413,8 +542,36 @@
 				Name:   proptools.StringPtr("lib/modules"),
 			},
 		}
+		fsProps.Security_patch = proptools.StringPtr(partitionVars.OdmSecurityPatch)
+		fsProps.Stem = proptools.StringPtr("odm.img")
 	case "userdata":
-		fsProps.Base_dir = proptools.StringPtr("data")
+		fsProps.Stem = proptools.StringPtr("userdata.img")
+		if vars, ok := partitionVars.PartitionQualifiedVariables["userdata"]; ok {
+			parsed, err := strconv.ParseInt(vars.BoardPartitionSize, 10, 64)
+			if err != nil {
+				panic(fmt.Sprintf("Partition size must be an int, got %s", vars.BoardPartitionSize))
+			}
+			fsProps.Partition_size = &parsed
+			// Disable avb for userdata partition
+			fsProps.Use_avb = nil
+		}
+		// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2265;drc=7f50a123045520f2c5e18e9eb4e83f92244a1459
+		if s, err := strconv.ParseBool(partitionVars.ProductFsCasefold); err == nil {
+			fsProps.Support_casefolding = proptools.BoolPtr(s)
+		} else if len(partitionVars.ProductFsCasefold) > 0 {
+			ctx.ModuleErrorf("Unrecognized PRODUCT_FS_CASEFOLD value %s", partitionVars.ProductFsCasefold)
+		}
+		if s, err := strconv.ParseBool(partitionVars.ProductQuotaProjid); err == nil {
+			fsProps.Support_project_quota = proptools.BoolPtr(s)
+		} else if len(partitionVars.ProductQuotaProjid) > 0 {
+			ctx.ModuleErrorf("Unrecognized PRODUCT_QUOTA_PROJID value %s", partitionVars.ProductQuotaProjid)
+		}
+		if s, err := strconv.ParseBool(partitionVars.ProductFsCompression); err == nil {
+			fsProps.Enable_compression = proptools.BoolPtr(s)
+		} else if len(partitionVars.ProductFsCompression) > 0 {
+			ctx.ModuleErrorf("Unrecognized PRODUCT_FS_COMPRESSION value %s", partitionVars.ProductFsCompression)
+		}
+
 	case "ramdisk":
 		// Following the logic in https://cs.android.com/android/platform/superproject/main/+/c3c5063df32748a8806ce5da5dd0db158eab9ad9:build/make/core/Makefile;l=1307
 		fsProps.Dirs = android.NewSimpleConfigurable([]string{
@@ -437,23 +594,37 @@
 				"first_stage_ramdisk/sys",
 			})
 		}
+		fsProps.Stem = proptools.StringPtr("ramdisk.img")
 	case "recovery":
-		// Following https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2826;drc=ad7cfb56010cb22c3aa0e70cf71c804352553526
-		fsProps.Dirs = android.NewSimpleConfigurable([]string{
+		dirs := append(commonPartitionDirs, []string{
 			"sdcard",
-			"tmp",
-		})
-		fsProps.Symlinks = []filesystem.SymlinkDefinition{
-			{
-				Target: proptools.StringPtr("/system/bin/init"),
-				Name:   proptools.StringPtr("init"),
-			},
-			{
-				Target: proptools.StringPtr("prop.default"),
-				Name:   proptools.StringPtr("default.prop"),
-			},
+		}...)
+
+		dirsWithRoot := make([]string, len(dirs))
+		for i, dir := range dirs {
+			dirsWithRoot[i] = filepath.Join("root", dir)
 		}
-		fsProps.Base_dir = proptools.StringPtr("recovery")
+
+		fsProps.Dirs = proptools.NewSimpleConfigurable(dirsWithRoot)
+		fsProps.Symlinks = symlinksWithNamePrefix(append(commonSymlinksFromRoot, filesystem.SymlinkDefinition{
+			Target: proptools.StringPtr("prop.default"),
+			Name:   proptools.StringPtr("default.prop"),
+		}), "root")
+		fsProps.Stem = proptools.StringPtr("recovery.img")
+	case "system_dlkm":
+		fsProps.Security_patch = proptools.StringPtr(partitionVars.SystemDlkmSecurityPatch)
+		fsProps.Stem = proptools.StringPtr("system_dlkm.img")
+	case "vendor_dlkm":
+		fsProps.Security_patch = proptools.StringPtr(partitionVars.VendorDlkmSecurityPatch)
+		fsProps.Stem = proptools.StringPtr("vendor_dlkm.img")
+	case "odm_dlkm":
+		fsProps.Security_patch = proptools.StringPtr(partitionVars.OdmDlkmSecurityPatch)
+		fsProps.Stem = proptools.StringPtr("odm_dlkm.img")
+	case "vendor_ramdisk":
+		if recoveryName := partitions.nameForType("recovery"); recoveryName != "" {
+			fsProps.Include_files_of = []string{recoveryName}
+		}
+		fsProps.Stem = proptools.StringPtr("vendor_ramdisk.img")
 	}
 }
 
@@ -465,16 +636,22 @@
 	}
 )
 
-// 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 := generateBaseProps(proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), partitionType)))
-
-	fsProps, supported := generateFsProps(ctx, partitionType)
-	if !supported {
-		return false
+// Creates a soong module to build the given partition.
+func (f *filesystemCreator) createPartition(ctx android.LoadHookContext, partitions allGeneratedPartitionData, partition *generatedPartitionData) {
+	// Nextgen team's handwritten soong system image, don't need to create anything ourselves
+	if partition.partitionType == "system" && ctx.Config().UseSoongSystemImage() {
+		return
 	}
 
+	baseProps := generateBaseProps(proptools.StringPtr(partition.moduleName))
+
+	fsProps, supported := generateFsProps(ctx, partitions, partition.partitionType)
+	if !supported {
+		partition.supported = false
+		return
+	}
+
+	partitionType := partition.partitionType
 	if partitionType == "vendor" || partitionType == "product" || partitionType == "system" {
 		fsProps.Linker_config.Gen_linker_config = proptools.BoolPtr(true)
 		if partitionType != "system" {
@@ -498,7 +675,6 @@
 	if partitionType == "vendor" {
 		f.createVendorBuildProp(ctx)
 	}
-	return true
 }
 
 // Creates filegroups for the files specified in BOARD_(partition_)AVB_KEY_PATH
@@ -586,8 +762,10 @@
 		Load_by_default      *bool
 		Blocklist_file       *string
 		Options_file         *string
+		Strip_debug_symbols  *bool
 	}{
-		Name: proptools.StringPtr(name),
+		Name:                proptools.StringPtr(name),
+		Strip_debug_symbols: proptools.BoolPtr(false),
 	}
 	switch partitionType {
 	case "system_dlkm":
@@ -644,8 +822,8 @@
 	(*fsGenState.fsDeps[partitionType])[name] = defaultDepCandidateProps(ctx.Config())
 }
 
-// Create a build_prop and android_info module. This will be used to create /vendor/build.prop
-func (f *filesystemCreator) createVendorBuildProp(ctx android.LoadHookContext) {
+// Create an android_info module. This will be used to create /vendor/build.prop
+func (f *filesystemCreator) createAndroidInfo(ctx android.LoadHookContext) {
 	// Create a android_info for vendor
 	// The board info files might be in a directory outside the root soong namespace, so create
 	// the module in "."
@@ -654,9 +832,11 @@
 		Name                  *string
 		Board_info_files      []string
 		Bootloader_board_name *string
+		Stem                  *string
 	}{
-		Name:             proptools.StringPtr(generatedModuleName(ctx.Config(), "android-info.prop")),
+		Name:             proptools.StringPtr(generatedModuleName(ctx.Config(), "android_info.prop")),
 		Board_info_files: partitionVars.BoardInfoFiles,
+		Stem:             proptools.StringPtr("android-info.txt"),
 	}
 	if len(androidInfoProps.Board_info_files) == 0 {
 		androidInfoProps.Bootloader_board_name = proptools.StringPtr(partitionVars.BootLoaderBoardName)
@@ -667,25 +847,76 @@
 		androidInfoProps,
 	)
 	androidInfoProp.HideFromMake()
-	// Create a build prop for vendor
+}
+
+func (f *filesystemCreator) createVendorBuildProp(ctx android.LoadHookContext) {
 	vendorBuildProps := &struct {
 		Name           *string
 		Vendor         *bool
 		Stem           *string
 		Product_config *string
 		Android_info   *string
+		Licenses       []string
+		Dist           android.Dist
 	}{
 		Name:           proptools.StringPtr(generatedModuleName(ctx.Config(), "vendor-build.prop")),
 		Vendor:         proptools.BoolPtr(true),
 		Stem:           proptools.StringPtr("build.prop"),
 		Product_config: proptools.StringPtr(":product_config"),
-		Android_info:   proptools.StringPtr(":" + androidInfoProp.Name()),
+		Android_info:   proptools.StringPtr(":" + generatedModuleName(ctx.Config(), "android_info.prop")),
+		Dist: android.Dist{
+			Targets: []string{"droidcore-unbundled"},
+			Dest:    proptools.StringPtr("build.prop-vendor"),
+		},
+		Licenses: []string{"Android-Apache-2.0"},
 	}
 	vendorBuildProp := ctx.CreateModule(
 		android.BuildPropFactory,
 		vendorBuildProps,
 	)
-	vendorBuildProp.HideFromMake()
+	// We don't want this to conflict with the make-built vendor build.prop, but unfortunately
+	// calling HideFromMake() prevents disting files, even in soong-only mode. So only call
+	// HideFromMake() on soong+make builds.
+	if ctx.Config().KatiEnabled() {
+		vendorBuildProp.HideFromMake()
+	}
+}
+
+func createRecoveryBuildProp(ctx android.LoadHookContext) string {
+	moduleName := generatedModuleName(ctx.Config(), "recovery-prop.default")
+
+	var vendorBuildProp *string
+	if ctx.DeviceConfig().BuildingVendorImage() && ctx.DeviceConfig().VendorPath() == "vendor" {
+		vendorBuildProp = proptools.StringPtr(":" + generatedModuleName(ctx.Config(), "vendor-build.prop"))
+	}
+
+	recoveryBuildProps := &struct {
+		Name                  *string
+		System_build_prop     *string
+		Vendor_build_prop     *string
+		Odm_build_prop        *string
+		Product_build_prop    *string
+		System_ext_build_prop *string
+
+		Recovery        *bool
+		No_full_install *bool
+		Visibility      []string
+	}{
+		Name:                  proptools.StringPtr(moduleName),
+		System_build_prop:     proptools.StringPtr(":system-build.prop"),
+		Vendor_build_prop:     vendorBuildProp,
+		Odm_build_prop:        proptools.StringPtr(":odm-build.prop"),
+		Product_build_prop:    proptools.StringPtr(":product-build.prop"),
+		System_ext_build_prop: proptools.StringPtr(":system_ext-build.prop"),
+
+		Recovery:        proptools.BoolPtr(true),
+		No_full_install: proptools.BoolPtr(true),
+		Visibility:      []string{"//visibility:public"},
+	}
+
+	ctx.CreateModule(android.RecoveryBuildPropModuleFactory, recoveryBuildProps)
+
+	return moduleName
 }
 
 // createLinkerConfigSourceFilegroups creates filegroup modules to generate linker.config.pb for the following partitions
@@ -745,7 +976,7 @@
 	}
 }
 
-func generateFsProps(ctx android.EarlyModuleContext, partitionType string) (*filesystem.FilesystemProperties, bool) {
+func generateFsProps(ctx android.EarlyModuleContext, partitions allGeneratedPartitionData, partitionType string) (*filesystem.FilesystemProperties, bool) {
 	fsProps := &filesystem.FilesystemProperties{}
 
 	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
@@ -789,6 +1020,8 @@
 	fsProps.Avb_algorithm = avbInfo.avbAlgorithm
 	// BOARD_AVB_SYSTEM_ROLLBACK_INDEX
 	fsProps.Rollback_index = avbInfo.avbRollbackIndex
+	// BOARD_AVB_SYSTEM_ROLLBACK_INDEX_LOCATION
+	fsProps.Rollback_index_location = avbInfo.avbRollbackIndexLocation
 	fsProps.Avb_hash_algorithm = avbInfo.avbHashAlgorithm
 
 	fsProps.Partition_name = proptools.StringPtr(partitionType)
@@ -801,20 +1034,30 @@
 	}
 
 	fsProps.Is_auto_generated = proptools.BoolPtr(true)
+	if partitionType != "system" {
+		mountPoint := proptools.StringPtr(partitionType)
+		// https://cs.android.com/android/platform/superproject/main/+/main:build/make/tools/releasetools/build_image.py;l=1012;drc=3f576a753594bad3fc838ccb8b1b72f7efac1d50
+		if partitionType == "userdata" {
+			mountPoint = proptools.StringPtr("data")
+		}
+		fsProps.Mount_point = mountPoint
 
-	partitionSpecificFsProps(ctx, fsProps, partitionVars, partitionType)
+	}
+
+	partitionSpecificFsProps(ctx, partitions, fsProps, partitionType)
 
 	return fsProps, true
 }
 
 type avbInfo struct {
-	avbEnable        *bool
-	avbKeyPath       *string
-	avbkeyFilegroup  *string
-	avbAlgorithm     *string
-	avbRollbackIndex *int64
-	avbMode          *string
-	avbHashAlgorithm *string
+	avbEnable                *bool
+	avbKeyPath               *string
+	avbkeyFilegroup          *string
+	avbAlgorithm             *string
+	avbRollbackIndex         *int64
+	avbRollbackIndexLocation *int64
+	avbMode                  *string
+	avbHashAlgorithm         *string
 }
 
 func getAvbInfo(config android.Config, partitionType string) avbInfo {
@@ -854,12 +1097,12 @@
 			}
 			result.avbRollbackIndex = &parsed
 		}
-		if specificPartitionVars.BoardAvbRollbackIndex != "" {
-			parsed, err := strconv.ParseInt(specificPartitionVars.BoardAvbRollbackIndex, 10, 64)
+		if specificPartitionVars.BoardAvbRollbackIndexLocation != "" {
+			parsed, err := strconv.ParseInt(specificPartitionVars.BoardAvbRollbackIndexLocation, 10, 64)
 			if err != nil {
-				panic(fmt.Sprintf("Rollback index must be an int, got %s", specificPartitionVars.BoardAvbRollbackIndex))
+				panic(fmt.Sprintf("Rollback index location must be an int, got %s", specificPartitionVars.BoardAvbRollbackIndexLocation))
 			}
-			result.avbRollbackIndex = &parsed
+			result.avbRollbackIndexLocation = &parsed
 		}
 
 		// Make allows you to pass arbitrary arguments to avbtool via this variable, but in practice
@@ -881,12 +1124,12 @@
 	return result
 }
 
-func (f *filesystemCreator) createFileListDiffTest(ctx android.ModuleContext, partitionType string) android.Path {
-	partitionModuleName := generatedModuleNameForPartition(ctx.Config(), partitionType)
-	systemImage := ctx.GetDirectDepWithTag(partitionModuleName, generatedFilesystemDepTag)
-	filesystemInfo, ok := android.OtherModuleProvider(ctx, systemImage, filesystem.FilesystemProvider)
+func (f *filesystemCreator) createFileListDiffTest(ctx android.ModuleContext, partitionType string, partitionModuleName string) android.Path {
+	partitionImage := ctx.GetDirectDepWithTag(partitionModuleName, generatedFilesystemDepTag)
+	filesystemInfo, ok := android.OtherModuleProvider(ctx, partitionImage, filesystem.FilesystemProvider)
 	if !ok {
 		ctx.ModuleErrorf("Expected module %s to provide FileysystemInfo", partitionModuleName)
+		return nil
 	}
 	makeFileList := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/file_list.txt", ctx.Config().DeviceName(), partitionType))
 	diffTestResultFile := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.txt", partitionModuleName))
@@ -939,16 +1182,16 @@
 	builder.Build("diff test "+diffTestResultFile.String(), "diff test")
 }
 
-type systemImageDepTagType struct {
+type imageDepTagType struct {
 	blueprint.BaseDependencyTag
 }
 
-var generatedFilesystemDepTag systemImageDepTagType
-var generatedVbmetaPartitionDepTag systemImageDepTagType
+var generatedFilesystemDepTag imageDepTagType
+var generatedVbmetaPartitionDepTag imageDepTagType
 
 func (f *filesystemCreator) DepsMutator(ctx android.BottomUpMutatorContext) {
-	for _, partitionType := range f.properties.Generated_partition_types {
-		ctx.AddDependency(ctx.Module(), generatedFilesystemDepTag, generatedModuleNameForPartition(ctx.Config(), partitionType))
+	for _, name := range ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions.names() {
+		ctx.AddDependency(ctx.Module(), generatedFilesystemDepTag, name)
 	}
 	for _, vbmetaModule := range f.properties.Vbmeta_module_names {
 		ctx.AddDependency(ctx.Module(), generatedVbmetaPartitionDepTag, vbmetaModule)
@@ -961,9 +1204,11 @@
 	}
 	f.HideFromMake()
 
+	partitions := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions
+
 	var content strings.Builder
 	generatedBp := android.PathForModuleOut(ctx, "soong_generated_product_config.bp")
-	for _, partition := range ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions {
+	for _, partition := range partitions.types() {
 		content.WriteString(generateBpContent(ctx, partition))
 		content.WriteString("\n")
 	}
@@ -971,13 +1216,17 @@
 
 	ctx.Phony("product_config_to_bp", generatedBp)
 
+	if !ctx.Config().KatiEnabled() {
+		// Cannot diff since the kati packaging rules will not be created.
+		return
+	}
 	var diffTestFiles []android.Path
-	for _, partitionType := range f.properties.Generated_partition_types {
-		diffTestFile := f.createFileListDiffTest(ctx, partitionType)
+	for _, partitionType := range partitions.types() {
+		diffTestFile := f.createFileListDiffTest(ctx, partitionType, partitions.nameForType(partitionType))
 		diffTestFiles = append(diffTestFiles, diffTestFile)
 		ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", partitionType), diffTestFile)
 	}
-	for _, partitionType := range f.properties.Unsupported_partition_types {
+	for _, partitionType := range slices.Concat(partitions.unsupportedTypes(), f.properties.Unsupported_partition_types) {
 		diffTestFile := createFailingCommand(ctx, fmt.Sprintf("Couldn't build %s partition", partitionType))
 		diffTestFiles = append(diffTestFiles, diffTestFile)
 		ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", partitionType), diffTestFile)
@@ -1023,13 +1272,13 @@
 }
 
 func generateBpContent(ctx android.EarlyModuleContext, partitionType string) string {
-	fsProps, fsTypeSupported := generateFsProps(ctx, partitionType)
+	fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
+	fsProps, fsTypeSupported := generateFsProps(ctx, fsGenState.soongGeneratedPartitions, partitionType)
 	if !fsTypeSupported {
 		return ""
 	}
 
 	baseProps := generateBaseProps(proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), partitionType)))
-	fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
 	deps := fsGenState.fsDeps[partitionType]
 	highPriorityDeps := fsGenState.generatedPrebuiltEtcModuleNames
 	depProps := generateDepStruct(*deps, highPriorityDeps)
diff --git a/fsgen/filesystem_creator_test.go b/fsgen/filesystem_creator_test.go
index 97c890d..2c4d3c8 100644
--- a/fsgen/filesystem_creator_test.go
+++ b/fsgen/filesystem_creator_test.go
@@ -15,17 +15,29 @@
 package fsgen
 
 import (
+	"strings"
+	"testing"
+
 	"android/soong/android"
 	"android/soong/etc"
 	"android/soong/filesystem"
 	"android/soong/java"
-	"testing"
 
 	"github.com/google/blueprint/proptools"
 )
 
 var prepareForTestWithFsgenBuildComponents = android.FixtureRegisterWithContext(registerBuildComponents)
 
+var prepareMockRamdiksNodeList = android.FixtureMergeMockFs(android.MockFS{
+	"ramdisk_node_list/ramdisk_node_list": nil,
+	"ramdisk_node_list/Android.bp": []byte(`
+		filegroup {
+			name: "ramdisk_node_list",
+			srcs: ["ramdisk_node_list"],
+		}
+	`),
+})
+
 func TestFileSystemCreatorSystemImageProps(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		android.PrepareForIntegrationTestWithAndroid,
@@ -45,6 +57,7 @@
 					},
 				}
 		}),
+		prepareMockRamdiksNodeList,
 		android.FixtureMergeMockFs(android.MockFS{
 			"external/avb/test/data/testkey_rsa4096.pem": nil,
 			"external/avb/test/Android.bp": []byte(`
@@ -61,7 +74,7 @@
 		}),
 	).RunTest(t)
 
-	fooSystem := result.ModuleForTests("test_product_generated_system_image", "android_common").Module().(interface {
+	fooSystem := result.ModuleForTests(t, "test_product_generated_system_image", "android_common").Module().(interface {
 		FsProps() filesystem.FilesystemProperties
 	})
 	android.AssertBoolEquals(
@@ -114,6 +127,7 @@
 					},
 				}
 		}),
+		prepareMockRamdiksNodeList,
 		android.FixtureMergeMockFs(android.MockFS{
 			"external/avb/test/data/testkey_rsa4096.pem": nil,
 			"build/soong/fsgen/Android.bp": []byte(`
@@ -170,6 +184,7 @@
 				}
 		}),
 		android.PrepareForNativeBridgeEnabled,
+		prepareMockRamdiksNodeList,
 		android.FixtureMergeMockFs(android.MockFS{
 			"external/avb/test/data/testkey_rsa4096.pem": nil,
 			"build/soong/fsgen/Android.bp": []byte(`
@@ -198,14 +213,14 @@
 	).RunTest(t)
 
 	var packagingProps android.PackagingProperties
-	for _, prop := range result.ModuleForTests("test_product_generated_system_image", "android_common").Module().GetProperties() {
+	for _, prop := range result.ModuleForTests(t, "test_product_generated_system_image", "android_common").Module().GetProperties() {
 		if packagingPropStruct, ok := prop.(*android.PackagingProperties); ok {
 			packagingProps = *packagingPropStruct
 		}
 	}
 	moduleDeps := packagingProps.Multilib.Lib64.Deps
 
-	eval := result.ModuleForTests("test_product_generated_system_image", "android_common").Module().ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
+	eval := result.ModuleForTests(t, "test_product_generated_system_image", "android_common").Module().ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
 	android.AssertStringListContains(
 		t,
 		"Generated system image expected to depend on \"bar\" defined in \"a/b\" namespace",
@@ -227,6 +242,7 @@
 		android.PrepareForTestWithAllowMissingDependencies,
 		prepareForTestWithFsgenBuildComponents,
 		java.PrepareForTestWithJavaBuildComponents,
+		prepareMockRamdiksNodeList,
 		android.FixtureMergeMockFs(android.MockFS{
 			"external/avb/test/data/testkey_rsa4096.pem": nil,
 			"build/soong/fsgen/Android.bp": []byte(`
@@ -273,6 +289,10 @@
 				"device/sample/etc/apns-full-conf.xml:product/etc/apns-conf-2.xml",
 				"device/sample/etc/apns-full-conf.xml:system/foo/file.txt",
 				"device/sample/etc/apns-full-conf.xml:system/foo/apns-full-conf.xml",
+				"device/sample/firmware/firmware.bin:recovery/root/firmware.bin",
+				"device/sample/firmware/firmware.bin:recovery/root/firmware-2.bin",
+				"device/sample/firmware/firmware.bin:recovery/root/lib/firmware/firmware.bin",
+				"device/sample/firmware/firmware.bin:recovery/root/lib/firmware/firmware-2.bin",
 			}
 			config.TestProductVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.PartitionQualifiedVariables =
 				map[string]android.PartitionQualifiedVariablesType{
@@ -281,6 +301,7 @@
 					},
 				}
 		}),
+		prepareMockRamdiksNodeList,
 		android.FixtureMergeMockFs(android.MockFS{
 			"external/avb/test/data/testkey_rsa4096.pem": nil,
 			"build/soong/fsgen/Android.bp": []byte(`
@@ -292,22 +313,23 @@
 			"frameworks/base/data/keyboards/Vendor_0079_Product_0011.kl": nil,
 			"frameworks/base/data/keyboards/Vendor_0079_Product_18d4.kl": nil,
 			"device/sample/etc/apns-full-conf.xml":                       nil,
+			"device/sample/firmware/firmware.bin":                        nil,
 		}),
 	).RunTest(t)
 
-	checkModuleProp := func(m android.Module, matcher func(actual interface{}) bool) bool {
+	getModuleProp := func(m android.Module, matcher func(actual interface{}) string) string {
 		for _, prop := range m.GetProperties() {
 
-			if matcher(prop) {
-				return true
+			if str := matcher(prop); str != "" {
+				return str
 			}
 		}
-		return false
+		return ""
 	}
 
 	// check generated prebuilt_* module type install path and install partition
-	generatedModule := result.ModuleForTests("system-frameworks_base_config-etc-0", "android_arm64_armv8-a").Module()
-	etcModule, _ := generatedModule.(*etc.PrebuiltEtc)
+	generatedModule := result.ModuleForTests(t, "system-frameworks_base_config-etc-0", "android_arm64_armv8-a").Module()
+	etcModule := generatedModule.(*etc.PrebuiltEtc)
 	android.AssertStringEquals(
 		t,
 		"module expected to have etc install path",
@@ -324,8 +346,8 @@
 	)
 
 	// check generated prebuilt_* module specifies correct relative_install_path property
-	generatedModule = result.ModuleForTests("system-frameworks_base_data_keyboards-usr_keylayout_subdir-0", "android_arm64_armv8-a").Module()
-	etcModule, _ = generatedModule.(*etc.PrebuiltEtc)
+	generatedModule = result.ModuleForTests(t, "system-frameworks_base_data_keyboards-usr_keylayout_subdir-0", "android_arm64_armv8-a").Module()
+	etcModule = generatedModule.(*etc.PrebuiltEtc)
 	android.AssertStringEquals(
 		t,
 		"module expected to set correct relative_install_path properties",
@@ -333,73 +355,308 @@
 		etcModule.SubDir(),
 	)
 
+	// check that generated prebuilt_* module sets correct srcs
+	eval := generatedModule.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
+	android.AssertStringEquals(
+		t,
+		"module expected to set correct srcs property",
+		"Vendor_0079_Product_0011.kl",
+		getModuleProp(generatedModule, func(actual interface{}) string {
+			if p, ok := actual.(*etc.PrebuiltEtcProperties); ok {
+				srcs := p.Srcs.GetOrDefault(eval, nil)
+				if len(srcs) == 2 {
+					return srcs[0]
+				}
+			}
+			return ""
+		}),
+	)
+	android.AssertStringEquals(
+		t,
+		"module expected to set correct srcs property",
+		"Vendor_0079_Product_18d4.kl",
+		getModuleProp(generatedModule, func(actual interface{}) string {
+			if p, ok := actual.(*etc.PrebuiltEtcProperties); ok {
+				srcs := p.Srcs.GetOrDefault(eval, nil)
+				if len(srcs) == 2 {
+					return srcs[1]
+				}
+			}
+			return ""
+		}),
+	)
+
 	// check that prebuilt_* module is not generated for non existing source file
-	android.AssertPanicMessageContains(
+	android.AssertStringEquals(
 		t,
 		"prebuilt_* module not generated for non existing source file",
-		"failed to find module \"system-some_non_existing-etc-0\"",
-		func() { result.ModuleForTests("system-some_non_existing-etc-0", "android_arm64_armv8-a") },
+		"",
+		strings.Join(result.ModuleVariantsForTests("system-some_non_existing-etc-0"), ","),
 	)
 
 	// check that duplicate src file can exist in PRODUCT_COPY_FILES and generates separate modules
-	generatedModule0 := result.ModuleForTests("product-device_sample_etc-etc-0", "android_arm64_armv8-a").Module()
-	generatedModule1 := result.ModuleForTests("product-device_sample_etc-etc-1", "android_arm64_armv8-a").Module()
+	generatedModule0 := result.ModuleForTests(t, "product-device_sample_etc-etc-0", "android_arm64_armv8-a").Module()
+	generatedModule1 := result.ModuleForTests(t, "product-device_sample_etc-etc-1", "android_arm64_armv8-a").Module()
 
 	// check that generated prebuilt_* module sets correct srcs and dsts property
-	eval := generatedModule0.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
-	android.AssertBoolEquals(
+	eval = generatedModule0.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
+	android.AssertStringEquals(
 		t,
 		"module expected to set correct srcs property",
-		true,
-		checkModuleProp(generatedModule0, func(actual interface{}) bool {
+		"apns-full-conf.xml",
+		getModuleProp(generatedModule0, func(actual interface{}) string {
 			if p, ok := actual.(*etc.PrebuiltEtcProperties); ok {
 				srcs := p.Srcs.GetOrDefault(eval, nil)
-				return len(srcs) == 1 &&
-					srcs[0] == "apns-full-conf.xml"
+				if len(srcs) == 1 {
+					return srcs[0]
+				}
 			}
-			return false
+			return ""
 		}),
 	)
-	android.AssertBoolEquals(
+	android.AssertStringEquals(
 		t,
 		"module expected to set correct dsts property",
-		true,
-		checkModuleProp(generatedModule0, func(actual interface{}) bool {
+		"apns-conf.xml",
+		getModuleProp(generatedModule0, func(actual interface{}) string {
 			if p, ok := actual.(*etc.PrebuiltDstsProperties); ok {
 				dsts := p.Dsts.GetOrDefault(eval, nil)
-				return len(dsts) == 1 &&
-					dsts[0] == "apns-conf.xml"
+				if len(dsts) == 1 {
+					return dsts[0]
+				}
 			}
-			return false
+			return ""
 		}),
 	)
 
 	// check that generated prebuilt_* module sets correct srcs and dsts property
 	eval = generatedModule1.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
-	android.AssertBoolEquals(
+	android.AssertStringEquals(
 		t,
 		"module expected to set correct srcs property",
-		true,
-		checkModuleProp(generatedModule1, func(actual interface{}) bool {
+		"apns-full-conf.xml",
+		getModuleProp(generatedModule1, func(actual interface{}) string {
 			if p, ok := actual.(*etc.PrebuiltEtcProperties); ok {
 				srcs := p.Srcs.GetOrDefault(eval, nil)
-				return len(srcs) == 1 &&
-					srcs[0] == "apns-full-conf.xml"
+				if len(srcs) == 1 {
+					return srcs[0]
+				}
 			}
-			return false
+			return ""
 		}),
 	)
-	android.AssertBoolEquals(
+	android.AssertStringEquals(
 		t,
 		"module expected to set correct dsts property",
-		true,
-		checkModuleProp(generatedModule1, func(actual interface{}) bool {
+		"apns-conf-2.xml",
+		getModuleProp(generatedModule1, func(actual interface{}) string {
 			if p, ok := actual.(*etc.PrebuiltDstsProperties); ok {
 				dsts := p.Dsts.GetOrDefault(eval, nil)
-				return len(dsts) == 1 &&
-					dsts[0] == "apns-conf-2.xml"
+				if len(dsts) == 1 {
+					return dsts[0]
+				}
 			}
-			return false
+			return ""
+		}),
+	)
+
+	generatedModule0 = result.ModuleForTests(t, "system-device_sample_etc-foo-0", "android_common").Module()
+	generatedModule1 = result.ModuleForTests(t, "system-device_sample_etc-foo-1", "android_common").Module()
+
+	// check that generated prebuilt_* module sets correct srcs and dsts property
+	eval = generatedModule0.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
+	android.AssertStringEquals(
+		t,
+		"module expected to set correct srcs property",
+		"apns-full-conf.xml",
+		getModuleProp(generatedModule0, func(actual interface{}) string {
+			if p, ok := actual.(*etc.PrebuiltEtcProperties); ok {
+				srcs := p.Srcs.GetOrDefault(eval, nil)
+				if len(srcs) == 1 {
+					return srcs[0]
+				}
+			}
+			return ""
+		}),
+	)
+	android.AssertStringEquals(
+		t,
+		"module expected to set correct dsts property",
+		"foo/file.txt",
+		getModuleProp(generatedModule0, func(actual interface{}) string {
+			if p, ok := actual.(*etc.PrebuiltDstsProperties); ok {
+				dsts := p.Dsts.GetOrDefault(eval, nil)
+				if len(dsts) == 1 {
+					return dsts[0]
+				}
+			}
+			return ""
+		}),
+	)
+
+	// check generated prebuilt_* module specifies correct install path and relative install path
+	etcModule = generatedModule1.(*etc.PrebuiltEtc)
+	android.AssertStringEquals(
+		t,
+		"module expected to have . install path",
+		".",
+		etcModule.BaseDir(),
+	)
+	android.AssertStringEquals(
+		t,
+		"module expected to set correct relative_install_path properties",
+		"foo",
+		etcModule.SubDir(),
+	)
+
+	// check that generated prebuilt_* module sets correct srcs
+	eval = generatedModule1.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
+	android.AssertStringEquals(
+		t,
+		"module expected to set correct srcs property",
+		"apns-full-conf.xml",
+		getModuleProp(generatedModule1, func(actual interface{}) string {
+			if p, ok := actual.(*etc.PrebuiltEtcProperties); ok {
+				srcs := p.Srcs.GetOrDefault(eval, nil)
+				if len(srcs) == 1 {
+					return srcs[0]
+				}
+			}
+			return ""
+		}),
+	)
+
+	generatedModule0 = result.ModuleForTests(t, "recovery-device_sample_firmware-0", "android_recovery_arm64_armv8-a").Module()
+	generatedModule1 = result.ModuleForTests(t, "recovery-device_sample_firmware-1", "android_recovery_common").Module()
+
+	// check generated prebuilt_* module specifies correct install path and relative install path
+	etcModule = generatedModule0.(*etc.PrebuiltEtc)
+	android.AssertStringEquals(
+		t,
+		"module expected to have . install path",
+		".",
+		etcModule.BaseDir(),
+	)
+	android.AssertStringEquals(
+		t,
+		"module expected to set empty relative_install_path properties",
+		"",
+		etcModule.SubDir(),
+	)
+
+	// check that generated prebuilt_* module don't set dsts
+	eval = generatedModule0.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
+	android.AssertStringEquals(
+		t,
+		"module expected to not set dsts property",
+		"",
+		getModuleProp(generatedModule0, func(actual interface{}) string {
+			if p, ok := actual.(*etc.PrebuiltDstsProperties); ok {
+				dsts := p.Dsts.GetOrDefault(eval, nil)
+				if len(dsts) != 0 {
+					return dsts[0]
+				}
+			}
+			return ""
+		}),
+	)
+
+	// check generated prebuilt_* module specifies correct install path and relative install path
+	etcModule = generatedModule1.(*etc.PrebuiltEtc)
+	android.AssertStringEquals(
+		t,
+		"module expected to have . install path",
+		".",
+		etcModule.BaseDir(),
+	)
+	android.AssertStringEquals(
+		t,
+		"module expected to set empty relative_install_path properties",
+		"",
+		etcModule.SubDir(),
+	)
+
+	// check that generated prebuilt_* module sets correct dsts
+	eval = generatedModule1.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
+	android.AssertStringEquals(
+		t,
+		"module expected to set correct dsts property",
+		"firmware-2.bin",
+		getModuleProp(generatedModule1, func(actual interface{}) string {
+			if p, ok := actual.(*etc.PrebuiltDstsProperties); ok {
+				dsts := p.Dsts.GetOrDefault(eval, nil)
+				if len(dsts) == 1 {
+					return dsts[0]
+				}
+			}
+			return ""
+		}),
+	)
+
+	generatedModule0 = result.ModuleForTests(t, "recovery-device_sample_firmware-lib_firmware-0", "android_recovery_common").Module()
+	generatedModule1 = result.ModuleForTests(t, "recovery-device_sample_firmware-lib_firmware-1", "android_recovery_common").Module()
+
+	// check generated prebuilt_* module specifies correct install path and relative install path
+	etcModule = generatedModule0.(*etc.PrebuiltEtc)
+	android.AssertStringEquals(
+		t,
+		"module expected to have . install path",
+		".",
+		etcModule.BaseDir(),
+	)
+	android.AssertStringEquals(
+		t,
+		"module expected to set correct relative_install_path properties",
+		"lib/firmware",
+		etcModule.SubDir(),
+	)
+
+	// check that generated prebuilt_* module sets correct srcs
+	eval = generatedModule0.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
+	android.AssertStringEquals(
+		t,
+		"module expected to not set dsts property",
+		"",
+		getModuleProp(generatedModule0, func(actual interface{}) string {
+			if p, ok := actual.(*etc.PrebuiltDstsProperties); ok {
+				dsts := p.Dsts.GetOrDefault(eval, nil)
+				if len(dsts) != 0 {
+					return dsts[0]
+				}
+			}
+			return ""
+		}),
+	)
+
+	// check generated prebuilt_* module specifies correct install path and relative install path
+	etcModule = generatedModule1.(*etc.PrebuiltEtc)
+	android.AssertStringEquals(
+		t,
+		"module expected to have . install path",
+		".",
+		etcModule.BaseDir(),
+	)
+	android.AssertStringEquals(
+		t,
+		"module expected to set empty relative_install_path properties",
+		"",
+		etcModule.SubDir(),
+	)
+
+	// check that generated prebuilt_* module sets correct srcs
+	eval = generatedModule1.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
+	android.AssertStringEquals(
+		t,
+		"module expected to set correct dsts property",
+		"lib/firmware/firmware-2.bin",
+		getModuleProp(generatedModule1, func(actual interface{}) string {
+			if p, ok := actual.(*etc.PrebuiltDstsProperties); ok {
+				dsts := p.Dsts.GetOrDefault(eval, nil)
+				if len(dsts) == 1 {
+					return dsts[0]
+				}
+			}
+			return ""
 		}),
 	)
 }
diff --git a/fsgen/fsgen_mutators.go b/fsgen/fsgen_mutators.go
index f0a54db..4f3d2a7 100644
--- a/fsgen/fsgen_mutators.go
+++ b/fsgen/fsgen_mutators.go
@@ -60,8 +60,8 @@
 	depCandidates []string
 	// Map of names of partition to the information of modules to be added as deps
 	fsDeps map[string]*multilibDeps
-	// List of name of partitions to be generated by the filesystem_creator module
-	soongGeneratedPartitions []string
+	// Information about the main soong-generated partitions
+	soongGeneratedPartitions allGeneratedPartitionData
 	// Mutex to protect the fsDeps
 	fsDepsMutex sync.Mutex
 	// Map of _all_ soong module names to their corresponding installation properties
@@ -105,12 +105,14 @@
 					"libgsi":                                    defaultDepCandidateProps(ctx.Config()),
 					"llndk.libraries.txt":                       defaultDepCandidateProps(ctx.Config()),
 					"logpersist.start":                          defaultDepCandidateProps(ctx.Config()),
+					"notice_xml_system":                         defaultDepCandidateProps(ctx.Config()),
 					"update_engine_sideload":                    defaultDepCandidateProps(ctx.Config()),
 					// keep-sorted end
 				},
 				"vendor": {
 					"fs_config_files_vendor":                               defaultDepCandidateProps(ctx.Config()),
 					"fs_config_dirs_vendor":                                defaultDepCandidateProps(ctx.Config()),
+					"notice_xml_vendor":                                    defaultDepCandidateProps(ctx.Config()),
 					generatedModuleName(ctx.Config(), "vendor-build.prop"): defaultDepCandidateProps(ctx.Config()),
 				},
 				"odm": {
@@ -118,39 +120,62 @@
 					// https://cs.android.com/android/_/android/platform/build/+/e4849e87ab660b59a6501b3928693db065ee873b:tools/fs_config/Android.mk;l=34;drc=8d6481b92c4b4e9b9f31a61545b6862090fcc14b;bpv=1;bpt=0
 					"fs_config_files_odm": defaultDepCandidateProps(ctx.Config()),
 					"fs_config_dirs_odm":  defaultDepCandidateProps(ctx.Config()),
+					"notice_xml_odm":      defaultDepCandidateProps(ctx.Config()),
 				},
-				"product": {},
+				"product": {
+					"notice_xml_product": defaultDepCandidateProps(ctx.Config()),
+				},
 				"system_ext": {
 					// VNDK apexes are automatically included.
 					// This hardcoded list will need to be updated if `PRODUCT_EXTRA_VNDK_VERSIONS` is updated.
 					// https://cs.android.com/android/_/android/platform/build/+/adba533072b00c53ac0f198c550a3cbd7a00e4cd:core/main.mk;l=984;bpv=1;bpt=0;drc=174db7b179592cf07cbfd2adb0119486fda911e7
-					"com.android.vndk.v30": defaultDepCandidateProps(ctx.Config()),
-					"com.android.vndk.v31": defaultDepCandidateProps(ctx.Config()),
-					"com.android.vndk.v32": defaultDepCandidateProps(ctx.Config()),
-					"com.android.vndk.v33": defaultDepCandidateProps(ctx.Config()),
-					"com.android.vndk.v34": defaultDepCandidateProps(ctx.Config()),
+					"com.android.vndk.v30":  defaultDepCandidateProps(ctx.Config()),
+					"com.android.vndk.v31":  defaultDepCandidateProps(ctx.Config()),
+					"com.android.vndk.v32":  defaultDepCandidateProps(ctx.Config()),
+					"com.android.vndk.v33":  defaultDepCandidateProps(ctx.Config()),
+					"com.android.vndk.v34":  defaultDepCandidateProps(ctx.Config()),
+					"notice_xml_system_ext": defaultDepCandidateProps(ctx.Config()),
 				},
 				"userdata": {},
 				"system_dlkm": {
 					// these are phony required deps of the phony fs_config_dirs_nonsystem
 					"fs_config_dirs_system_dlkm":  defaultDepCandidateProps(ctx.Config()),
 					"fs_config_files_system_dlkm": defaultDepCandidateProps(ctx.Config()),
+					"notice_xml_system_dlkm":      defaultDepCandidateProps(ctx.Config()),
 					// build props are automatically added to `ALL_DEFAULT_INSTALLED_MODULES`
 					"system_dlkm-build.prop": defaultDepCandidateProps(ctx.Config()),
 				},
 				"vendor_dlkm": {
 					"fs_config_dirs_vendor_dlkm":  defaultDepCandidateProps(ctx.Config()),
 					"fs_config_files_vendor_dlkm": defaultDepCandidateProps(ctx.Config()),
+					"notice_xml_vendor_dlkm":      defaultDepCandidateProps(ctx.Config()),
 					"vendor_dlkm-build.prop":      defaultDepCandidateProps(ctx.Config()),
 				},
 				"odm_dlkm": {
 					"fs_config_dirs_odm_dlkm":  defaultDepCandidateProps(ctx.Config()),
 					"fs_config_files_odm_dlkm": defaultDepCandidateProps(ctx.Config()),
+					"notice_xml_odm_dlkm":      defaultDepCandidateProps(ctx.Config()),
 					"odm_dlkm-build.prop":      defaultDepCandidateProps(ctx.Config()),
 				},
 				"ramdisk":        {},
 				"vendor_ramdisk": {},
-				"recovery":       {},
+				"recovery": {
+					"sepolicy.recovery":                     defaultDepCandidateProps(ctx.Config()),
+					"plat_file_contexts.recovery":           defaultDepCandidateProps(ctx.Config()),
+					"plat_service_contexts.recovery":        defaultDepCandidateProps(ctx.Config()),
+					"plat_property_contexts.recovery":       defaultDepCandidateProps(ctx.Config()),
+					"system_ext_file_contexts.recovery":     defaultDepCandidateProps(ctx.Config()),
+					"system_ext_service_contexts.recovery":  defaultDepCandidateProps(ctx.Config()),
+					"system_ext_property_contexts.recovery": defaultDepCandidateProps(ctx.Config()),
+					"vendor_file_contexts.recovery":         defaultDepCandidateProps(ctx.Config()),
+					"vendor_service_contexts.recovery":      defaultDepCandidateProps(ctx.Config()),
+					"vendor_property_contexts.recovery":     defaultDepCandidateProps(ctx.Config()),
+					"odm_file_contexts.recovery":            defaultDepCandidateProps(ctx.Config()),
+					"odm_property_contexts.recovery":        defaultDepCandidateProps(ctx.Config()),
+					"product_file_contexts.recovery":        defaultDepCandidateProps(ctx.Config()),
+					"product_service_contexts.recovery":     defaultDepCandidateProps(ctx.Config()),
+					"product_property_contexts.recovery":    defaultDepCandidateProps(ctx.Config()),
+				},
 			},
 			fsDepsMutex:                     sync.Mutex{},
 			moduleToInstallationProps:       map[string]installationProperties{},
@@ -162,8 +187,14 @@
 			(*fsGenState.fsDeps["product"])["system_other_avbpubkey"] = defaultDepCandidateProps(ctx.Config())
 		}
 
+		if len(ctx.Config().DeviceManifestFiles()) > 0 {
+			(*fsGenState.fsDeps["vendor"])["vendor_manifest.xml"] = defaultDepCandidateProps(ctx.Config())
+		}
+
 		// Add common resources `prebuilt_res` module as dep of recovery partition
 		(*fsGenState.fsDeps["recovery"])[fmt.Sprintf("recovery-resources-common-%s", getDpi(ctx))] = defaultDepCandidateProps(ctx.Config())
+		(*fsGenState.fsDeps["recovery"])[getRecoveryFontModuleName(ctx)] = defaultDepCandidateProps(ctx.Config())
+		(*fsGenState.fsDeps["recovery"])[createRecoveryBuildProp(ctx)] = defaultDepCandidateProps(ctx.Config())
 
 		return &fsGenState
 	}).(*FsGenState)
@@ -264,9 +295,12 @@
 	removeOverriddenDeps(mctx)
 	fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
 	fsDeps := fsGenState.fsDeps
-	soongGeneratedPartitionMap := getAllSoongGeneratedPartitionNames(mctx.Config(), fsGenState.soongGeneratedPartitions)
 	m := mctx.Module()
-	if partition, ok := soongGeneratedPartitionMap[m.Name()]; ok {
+	if partition := fsGenState.soongGeneratedPartitions.typeForName(m.Name()); partition != "" {
+		if fsGenState.soongGeneratedPartitions.isHandwritten(m.Name()) {
+			// Handwritten image, don't modify it
+			return
+		}
 		depsStruct := generateDepStruct(*fsDeps[partition], fsGenState.generatedPrebuiltEtcModuleNames)
 		if err := proptools.AppendMatchingProperties(m.GetProperties(), depsStruct, nil); err != nil {
 			mctx.ModuleErrorf(err.Error())
diff --git a/fsgen/prebuilt_etc_modules_gen.go b/fsgen/prebuilt_etc_modules_gen.go
index 014c662..b3666a1 100644
--- a/fsgen/prebuilt_etc_modules_gen.go
+++ b/fsgen/prebuilt_etc_modules_gen.go
@@ -199,6 +199,7 @@
 		"etc/firmware":        etc.PrebuiltFirmwareFactory,
 		"etc/firmware_8475":        etc.PrebuiltFirmwareFactory,
 		"firmware":            etc.PrebuiltFirmwareFactory,
+		"gpu":                 etc.PrebuiltGPUFactory,
 		"first_stage_ramdisk": etc.PrebuiltFirstStageRamdiskFactory,
 		"fonts":               etc.PrebuiltFontFactory,
 		"framework":           etc.PrebuiltFrameworkFactory,
@@ -210,10 +211,12 @@
 		"optee":               etc.PrebuiltOpteeFactory,
 		"overlay":             etc.PrebuiltOverlayFactory,
 		"priv-app":            etc.PrebuiltPrivAppFactory,
+		"radio":               etc.PrebuiltRadioFactory,
 		"sbin":                etc.PrebuiltSbinFactory,
 		"system":              etc.PrebuiltSystemFactory,
 		"res":                 etc.PrebuiltResFactory,
 		"rfs":                 etc.PrebuiltRfsFactory,
+		"tee":                 etc.PrebuiltTeeFactory,
 		"tts":                 etc.PrebuiltVoicepackFactory,
 		"tvconfig":            etc.PrebuiltTvConfigFactory,
 		"tvservice":           etc.PrebuiltTvServiceFactory,
@@ -225,6 +228,7 @@
 		"usr/idc":             etc.PrebuiltUserIdcFactory,
 		"vendor":              etc.PrebuiltVendorFactory,
 		"vendor_dlkm":         etc.PrebuiltVendorDlkmFactory,
+		"vendor_overlay":      etc.PrebuiltVendorOverlayFactory,
 		"wallpaper":           etc.PrebuiltWallpaperFactory,
 		"wlc_upt":             etc.PrebuiltWlcUptFactory,
 	}
@@ -334,6 +338,10 @@
 			propsList = append(propsList, &prebuiltInstallInRootProperties{
 				Install_in_root: proptools.BoolPtr(true),
 			})
+			// Discard any previously picked module and force it to prebuilt_{root,any} as
+			// they are the only modules allowed to specify the `install_in_root` property.
+			etcInstallPathKey = ""
+			relDestDirFromInstallDirBase = destDir
 		}
 
 		// Set appropriate srcs, dsts, and releative_install_path based on
@@ -355,16 +363,14 @@
 		}
 
 		if allCopyFileNamesUnchanged {
-			// Specify relative_install_path if it is not installed in the root directory of the
-			// partition
+			// Specify relative_install_path if it is not installed in the base directory of the module.
+			// In case of prebuilt_{root,any} this is equivalent to the root of the partition.
 			if !android.InList(relDestDirFromInstallDirBase, []string{"", "."}) {
 				propsList = append(propsList, &prebuiltSubdirProperties{
 					Relative_install_path: proptools.StringPtr(relDestDirFromInstallDirBase),
 				})
 			}
 		} else {
-			// If dsts property has to be set and the selected module type is prebuilt_root,
-			// use prebuilt_any instead.
 			dsts := proptools.NewConfigurable[[]string](nil, nil)
 			for _, installBaseFile := range installBaseFiles {
 				dsts.AppendSimpleValue([]string{filepath.Join(relDestDirFromInstallDirBase, installBaseFile)})
diff --git a/fsgen/super_img.go b/fsgen/super_img.go
index 8ee3bf2..1d610f6 100644
--- a/fsgen/super_img.go
+++ b/fsgen/super_img.go
@@ -19,6 +19,7 @@
 
 	"android/soong/android"
 	"android/soong/filesystem"
+
 	"github.com/google/blueprint/proptools"
 )
 
@@ -26,21 +27,48 @@
 	return partitionVars.ProductBuildSuperPartition
 }
 
-func createSuperImage(ctx android.LoadHookContext, partitions []string, partitionVars android.PartitionVariables) {
+func createSuperImage(
+	ctx android.LoadHookContext,
+	partitions allGeneratedPartitionData,
+	partitionVars android.PartitionVariables,
+	systemOtherImageName string,
+) []string {
 	baseProps := &struct {
 		Name *string
 	}{
-		Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "super")),
+		Name: proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "super")),
 	}
 
 	superImageProps := &filesystem.SuperImageProperties{
-		Metadata_device:        proptools.StringPtr(partitionVars.BoardSuperPartitionMetadataDevice),
-		Block_devices:          partitionVars.BoardSuperPartitionBlockDevices,
-		Ab_update:              proptools.BoolPtr(partitionVars.AbOtaUpdater),
-		Retrofit:               proptools.BoolPtr(partitionVars.ProductRetrofitDynamicPartitions),
-		Virtual_ab:             proptools.BoolPtr(partitionVars.ProductVirtualAbOta),
-		Virtual_ab_retrofit:    proptools.BoolPtr(partitionVars.ProductVirtualAbOtaRetrofit),
-		Use_dynamic_partitions: proptools.BoolPtr(partitionVars.ProductUseDynamicPartitions),
+		Metadata_device:               proptools.StringPtr(partitionVars.BoardSuperPartitionMetadataDevice),
+		Block_devices:                 partitionVars.BoardSuperPartitionBlockDevices,
+		Ab_update:                     proptools.BoolPtr(partitionVars.AbOtaUpdater),
+		Retrofit:                      proptools.BoolPtr(partitionVars.ProductRetrofitDynamicPartitions),
+		Use_dynamic_partitions:        proptools.BoolPtr(partitionVars.ProductUseDynamicPartitions),
+		Super_image_in_update_package: proptools.BoolPtr(partitionVars.BoardSuperImageInUpdatePackage),
+		Create_super_empty:            proptools.BoolPtr(partitionVars.BuildingSuperEmptyImage),
+	}
+	if partitionVars.ProductVirtualAbOta {
+		superImageProps.Virtual_ab.Enable = proptools.BoolPtr(true)
+		superImageProps.Virtual_ab.Retrofit = proptools.BoolPtr(partitionVars.ProductVirtualAbOtaRetrofit)
+		superImageProps.Virtual_ab.Compression = proptools.BoolPtr(partitionVars.ProductVirtualAbCompression)
+		if partitionVars.ProductVirtualAbCompressionMethod != "" {
+			superImageProps.Virtual_ab.Compression_method = proptools.StringPtr(partitionVars.ProductVirtualAbCompressionMethod)
+		}
+		if partitionVars.ProductVirtualAbCompressionFactor != "" {
+			factor, err := strconv.ParseInt(partitionVars.ProductVirtualAbCompressionFactor, 10, 32)
+			if err != nil {
+				ctx.ModuleErrorf("Compression factor must be an int, got %q", partitionVars.ProductVirtualAbCompressionFactor)
+			}
+			superImageProps.Virtual_ab.Compression_factor = proptools.Int64Ptr(factor)
+		}
+		if partitionVars.ProductVirtualAbCowVersion != "" {
+			version, err := strconv.ParseInt(partitionVars.ProductVirtualAbCowVersion, 10, 32)
+			if err != nil {
+				ctx.ModuleErrorf("COW version must be an int, got %q", partitionVars.ProductVirtualAbCowVersion)
+			}
+			superImageProps.Virtual_ab.Cow_version = proptools.Int64Ptr(version)
+		}
 	}
 	size, _ := strconv.ParseInt(partitionVars.BoardSuperPartitionSize, 10, 64)
 	superImageProps.Size = proptools.Int64Ptr(size)
@@ -58,34 +86,49 @@
 	}
 	superImageProps.Partition_groups = partitionGroupsInfo
 
+	if systemOtherImageName != "" {
+		superImageProps.System_other_partition = proptools.StringPtr(systemOtherImageName)
+	}
+
+	var superImageSubpartitions []string
 	partitionNameProps := &filesystem.SuperImagePartitionNameProperties{}
-	if android.InList("system", partitions) {
-		partitionNameProps.System_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
+	if modName := partitions.nameForType("system"); modName != "" {
+		partitionNameProps.System_partition = proptools.StringPtr(modName)
+		superImageSubpartitions = append(superImageSubpartitions, "system")
 	}
-	if android.InList("system_ext", partitions) {
-		partitionNameProps.System_ext_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
+	if modName := partitions.nameForType("system_ext"); modName != "" {
+		partitionNameProps.System_ext_partition = proptools.StringPtr(modName)
+		superImageSubpartitions = append(superImageSubpartitions, "system_ext")
 	}
-	if android.InList("system_dlkm", partitions) {
-		partitionNameProps.System_dlkm_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_dlkm"))
+	if modName := partitions.nameForType("system_dlkm"); modName != "" {
+		partitionNameProps.System_dlkm_partition = proptools.StringPtr(modName)
+		superImageSubpartitions = append(superImageSubpartitions, "system_dlkm")
 	}
-	if android.InList("system_other", partitions) {
-		partitionNameProps.System_other_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_other"))
+	if modName := partitions.nameForType("system_other"); modName != "" {
+		partitionNameProps.System_other_partition = proptools.StringPtr(modName)
+		superImageSubpartitions = append(superImageSubpartitions, "system_other")
 	}
-	if android.InList("product", partitions) {
-		partitionNameProps.Product_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "product"))
+	if modName := partitions.nameForType("product"); modName != "" {
+		partitionNameProps.Product_partition = proptools.StringPtr(modName)
+		superImageSubpartitions = append(superImageSubpartitions, "product")
 	}
-	if android.InList("vendor", partitions) {
-		partitionNameProps.Vendor_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor"))
+	if modName := partitions.nameForType("vendor"); modName != "" {
+		partitionNameProps.Vendor_partition = proptools.StringPtr(modName)
+		superImageSubpartitions = append(superImageSubpartitions, "vendor")
 	}
-	if android.InList("vendor_dlkm", partitions) {
-		partitionNameProps.Vendor_dlkm_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_dlkm"))
+	if modName := partitions.nameForType("vendor_dlkm"); modName != "" {
+		partitionNameProps.Vendor_dlkm_partition = proptools.StringPtr(modName)
+		superImageSubpartitions = append(superImageSubpartitions, "vendor_dlkm")
 	}
-	if android.InList("odm", partitions) {
-		partitionNameProps.Odm_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "odm"))
+	if modName := partitions.nameForType("odm"); modName != "" {
+		partitionNameProps.Odm_partition = proptools.StringPtr(modName)
+		superImageSubpartitions = append(superImageSubpartitions, "odm")
 	}
-	if android.InList("odm_dlkm", partitions) {
-		partitionNameProps.Odm_dlkm_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "odm_dlkm"))
+	if modName := partitions.nameForType("odm_dlkm"); modName != "" {
+		partitionNameProps.Odm_dlkm_partition = proptools.StringPtr(modName)
+		superImageSubpartitions = append(superImageSubpartitions, "odm_dlkm")
 	}
 
 	ctx.CreateModule(filesystem.SuperImageFactory, baseProps, superImageProps, partitionNameProps)
+	return superImageSubpartitions
 }
diff --git a/fsgen/util.go b/fsgen/util.go
index 9ab3ad8..008f9fe 100644
--- a/fsgen/util.go
+++ b/fsgen/util.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"android/soong/filesystem"
 	"fmt"
 	"strconv"
 	"strings"
@@ -58,3 +59,21 @@
 
 	return recoveryDensity
 }
+
+// Returns the name of the appropriate prebuilt module for installing font.png file.
+// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2536;drc=a6af369e71ded123734523ea640b97b70a557cb9
+func getRecoveryFontModuleName(ctx android.LoadHookContext) string {
+	if android.InList(getDpi(ctx), []string{"xxxhdpi", "xxhdpi", "xhdpi"}) {
+		return "recovery-fonts-18"
+	}
+	return "recovery-fonts-12"
+}
+
+// Returns a new list of symlinks with prefix added to the dest directory for all symlinks
+func symlinksWithNamePrefix(symlinks []filesystem.SymlinkDefinition, prefix string) []filesystem.SymlinkDefinition {
+	ret := make([]filesystem.SymlinkDefinition, len(symlinks))
+	for i, symlink := range symlinks {
+		ret[i] = symlink.CopyWithNamePrefix(prefix)
+	}
+	return ret
+}
diff --git a/fsgen/vbmeta_partitions.go b/fsgen/vbmeta_partitions.go
index 11c5759..594c404 100644
--- a/fsgen/vbmeta_partitions.go
+++ b/fsgen/vbmeta_partitions.go
@@ -17,9 +17,7 @@
 import (
 	"android/soong/android"
 	"android/soong/filesystem"
-	"slices"
 	"strconv"
-	"strings"
 
 	"github.com/google/blueprint/proptools"
 )
@@ -32,6 +30,27 @@
 	partitionName string
 }
 
+// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4849;drc=62e20f0d218f60bae563b4ee742d88cca1fc1901
+var avbPartitions = []string{
+	"boot",
+	"init_boot",
+	"vendor_boot",
+	"vendor_kernel_boot",
+	"system",
+	"vendor",
+	"product",
+	"system_ext",
+	"odm",
+	"vendor_dlkm",
+	"odm_dlkm",
+	"system_dlkm",
+	"dtbo",
+	"pvmfw",
+	"recovery",
+	"vbmeta_system",
+	"vbmeta_vendor",
+}
+
 // Creates the vbmeta partition and the chained vbmeta partitions. Returns the list of module names
 // that the function created. May return nil if the product isn't using avb.
 //
@@ -43,7 +62,7 @@
 // like vbmeta_system might contain the avb metadata for just a few products. In cuttlefish
 // vbmeta_system contains metadata about product, system, and system_ext. Using chained partitions,
 // that group of partitions can be updated independently from the other signed partitions.
-func createVbmetaPartitions(ctx android.LoadHookContext, generatedPartitionTypes []string) []vbmetaModuleInfo {
+func (f *filesystemCreator) createVbmetaPartitions(ctx android.LoadHookContext, partitions allGeneratedPartitionData) []vbmetaModuleInfo {
 	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
 	// Some products seem to have BuildingVbmetaImage as true even when BoardAvbEnable is false
 	if !partitionVars.BuildingVbmetaImage || !partitionVars.BoardAvbEnable {
@@ -52,14 +71,17 @@
 
 	var result []vbmetaModuleInfo
 
-	var chainedPartitions []string
-	var partitionTypesHandledByChainedPartitions []string
+	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4593;drc=62e20f0d218f60bae563b4ee742d88cca1fc1901
+	var internalAvbPartitionsInChainedVbmetaImages []string
+	var chainedPartitionTypes []string
 	for _, chainedName := range android.SortedKeys(partitionVars.ChainedVbmetaPartitions) {
 		props := partitionVars.ChainedVbmetaPartitions[chainedName]
+		filesystemPartitionType := chainedName
 		chainedName = "vbmeta_" + chainedName
 		if len(props.Partitions) == 0 {
 			continue
 		}
+		internalAvbPartitionsInChainedVbmetaImages = append(internalAvbPartitionsInChainedVbmetaImages, props.Partitions...)
 		if len(props.Key) == 0 {
 			ctx.ModuleErrorf("No key found for chained avb partition %q", chainedName)
 			continue
@@ -92,26 +114,24 @@
 
 		var partitionModules []string
 		for _, partition := range props.Partitions {
-			partitionTypesHandledByChainedPartitions = append(partitionTypesHandledByChainedPartitions, partition)
-			if !slices.Contains(generatedPartitionTypes, partition) {
-				// The partition is probably unsupported.
-				continue
+			if modName := partitions.nameForType(partition); modName != "" {
+				partitionModules = append(partitionModules, modName)
 			}
-			partitionModules = append(partitionModules, generatedModuleNameForPartition(ctx.Config(), partition))
 		}
 
-		name := generatedModuleName(ctx.Config(), chainedName)
+		name := generatedModuleNameForPartition(ctx.Config(), chainedName)
 		ctx.CreateModuleInDirectory(
 			filesystem.VbmetaFactory,
 			".", // Create in the root directory for now so its easy to get the key
 			&filesystem.VbmetaProperties{
-				Partition_name:          proptools.StringPtr(chainedName),
-				Stem:                    proptools.StringPtr(chainedName + ".img"),
-				Private_key:             proptools.StringPtr(props.Key),
-				Algorithm:               &props.Algorithm,
-				Rollback_index:          rollbackIndex,
-				Rollback_index_location: &ril,
-				Partitions:              proptools.NewSimpleConfigurable(partitionModules),
+				Partition_name:            proptools.StringPtr(chainedName),
+				Filesystem_partition_type: proptools.StringPtr(filesystemPartitionType),
+				Stem:                      proptools.StringPtr(chainedName + ".img"),
+				Private_key:               proptools.StringPtr(props.Key),
+				Algorithm:                 &props.Algorithm,
+				Rollback_index:            rollbackIndex,
+				Rollback_index_location:   &ril,
+				Partitions:                proptools.NewSimpleConfigurable(partitionModules),
 			}, &struct {
 				Name *string
 			}{
@@ -119,15 +139,15 @@
 			},
 		).HideFromMake()
 
-		chainedPartitions = append(chainedPartitions, name)
-
 		result = append(result, vbmetaModuleInfo{
 			moduleName:    name,
 			partitionName: chainedName,
 		})
+
+		chainedPartitionTypes = append(chainedPartitionTypes, chainedName)
 	}
 
-	vbmetaModuleName := generatedModuleName(ctx.Config(), "vbmeta")
+	vbmetaModuleName := generatedModuleNameForPartition(ctx.Config(), "vbmeta")
 
 	var algorithm *string
 	var ri *int64
@@ -148,19 +168,83 @@
 		ri = &parsedRi
 	}
 
-	var partitionModules []string
-	for _, partitionType := range generatedPartitionTypes {
-		if slices.Contains(partitionTypesHandledByChainedPartitions, partitionType) {
-			// Already handled by a chained vbmeta partition
+	// --chain_partition argument is only set for partitions that set
+	// `BOARD_AVB_<partition name>_KEY_PATH` value and is not "recovery"
+	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4823;drc=62e20f0d218f60bae563b4ee742d88cca1fc1901
+	includeAsChainedPartitionInVbmeta := func(partition string) bool {
+		val, ok := partitionVars.PartitionQualifiedVariables[partition]
+		return ok && len(val.BoardAvbKeyPath) > 0 && partition != "recovery"
+	}
+
+	// --include_descriptors_from_image is passed if both conditions are met:
+	// - `BOARD_AVB_<partition name>_KEY_PATH` value is not set
+	// - not included in INTERNAL_AVB_PARTITIONS_IN_CHAINED_VBMETA_IMAGES
+	// for partitions that set INSTALLED_<partition name>IMAGE_TARGET
+	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4827;drc=62e20f0d218f60bae563b4ee742d88cca1fc1901
+	includeAsIncludedPartitionInVbmeta := func(partition string) bool {
+		if android.InList(partition, internalAvbPartitionsInChainedVbmetaImages) {
+			// Already handled by chained vbmeta partitions
+			return false
+		}
+		partitionQualifiedVars := partitionVars.PartitionQualifiedVariables[partition]
+
+		// The return logic in the switch cases below are identical to
+		// ifdef INSTALLED_<partition name>IMAGE_TARGET
+		switch partition {
+		case "boot":
+			return partitionQualifiedVars.BuildingImage || partitionQualifiedVars.PrebuiltImage || partitionVars.BoardUsesRecoveryAsBoot
+		case "vendor_kernel_boot", "dtbo":
+			return partitionQualifiedVars.PrebuiltImage
+		case "system":
+			return partitionQualifiedVars.BuildingImage
+		case "init_boot", "vendor_boot", "vendor", "product", "system_ext", "odm", "vendor_dlkm", "odm_dlkm", "system_dlkm":
+			return partitionQualifiedVars.BuildingImage || partitionQualifiedVars.PrebuiltImage
+		// TODO: Import BOARD_USES_PVMFWIMAGE
+		// ifeq ($(BOARD_USES_PVMFWIMAGE),true)
+		// case "pvmfw":
+		case "recovery":
+			// ifdef INSTALLED_RECOVERYIMAGE_TARGET
+			return !ctx.DeviceConfig().BoardUsesRecoveryAsBoot() && !ctx.DeviceConfig().BoardMoveRecoveryResourcesToVendorBoot()
+		// Technically these partitions are determined based on len(BOARD_AVB_VBMETA_SYSTEM) and
+		// len(BOARD_AVB_VBMETA_VENDOR) but if these are non empty these partitions are
+		// already included in the chained partitions.
+		case "vbmeta_system", "vbmeta_vendor":
+			return false
+		default:
+			return false
+		}
+	}
+
+	var chainedPartitionModules []string
+	var includePartitionModules []string
+	allGeneratedPartitionTypes := append(partitions.types(),
+		chainedPartitionTypes...,
+	)
+	if len(f.properties.Boot_image) > 0 {
+		allGeneratedPartitionTypes = append(allGeneratedPartitionTypes, "boot")
+	}
+	if len(f.properties.Init_boot_image) > 0 {
+		allGeneratedPartitionTypes = append(allGeneratedPartitionTypes, "init_boot")
+	}
+	if len(f.properties.Vendor_boot_image) > 0 {
+		allGeneratedPartitionTypes = append(allGeneratedPartitionTypes, "vendor_boot")
+	}
+
+	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4919;drc=62e20f0d218f60bae563b4ee742d88cca1fc1901
+	for _, partitionType := range android.SortedUniqueStrings(append(avbPartitions, chainedPartitionTypes...)) {
+		if !android.InList(partitionType, allGeneratedPartitionTypes) {
+			// Skip if the partition is not auto generated
 			continue
 		}
-		if strings.Contains(partitionType, "ramdisk") || strings.Contains(partitionType, "boot") {
-			// ramdisk is never signed with avb information
-			// boot partitions just have the avb footer, and don't have a corresponding vbmeta
-			// partition.
-			continue
+		name := partitions.nameForType(partitionType)
+		if name == "" {
+			name = generatedModuleNameForPartition(ctx.Config(), partitionType)
 		}
-		partitionModules = append(partitionModules, generatedModuleNameForPartition(ctx.Config(), partitionType))
+		if includeAsChainedPartitionInVbmeta(partitionType) {
+			chainedPartitionModules = append(chainedPartitionModules, name)
+		} else if includeAsIncludedPartitionInVbmeta(partitionType) {
+			includePartitionModules = append(includePartitionModules, name)
+		}
 	}
 
 	ctx.CreateModuleInDirectory(
@@ -171,8 +255,9 @@
 			Algorithm:          algorithm,
 			Private_key:        key,
 			Rollback_index:     ri,
-			Chained_partitions: chainedPartitions,
-			Partitions:         proptools.NewSimpleConfigurable(partitionModules),
+			Chained_partitions: chainedPartitionModules,
+			Partitions:         proptools.NewSimpleConfigurable(includePartitionModules),
+			Partition_name:     proptools.StringPtr("vbmeta"),
 		}, &struct {
 			Name *string
 		}{
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index aa393a2..f08378d 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -22,6 +22,7 @@
 	"sort"
 	"strings"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -309,14 +310,14 @@
 	return false
 }
 
-func IsValidConfig(fuzzModule FuzzPackagedModule, moduleName string) bool {
-	var config = fuzzModule.FuzzProperties.Fuzz_config
+func IsValidConfig(fuzzModule *FuzzPackagedModuleInfo, moduleName string) bool {
+	var config = fuzzModule.FuzzConfig
 	if config != nil {
 		if !config.Vector.isValidVector() {
 			panic(fmt.Errorf("Invalid vector in fuzz config in %s", moduleName))
 		}
 
-		if !config.Service_privilege.isValidServicePrivilege() {
+		if !config.ServicePrivilege.isValidServicePrivilege() {
 			panic(fmt.Errorf("Invalid service_privilege in fuzz config in %s", moduleName))
 		}
 
@@ -324,15 +325,15 @@
 			panic(fmt.Errorf("Invalid users (user_data) in fuzz config in %s", moduleName))
 		}
 
-		if !config.Fuzzed_code_usage.isValidFuzzedCodeUsage() {
+		if !config.FuzzedCodeUsage.isValidFuzzedCodeUsage() {
 			panic(fmt.Errorf("Invalid fuzzed_code_usage in fuzz config in %s", moduleName))
 		}
 
-		if !config.Automatically_route_to.isValidAutomaticallyRouteTo() {
+		if !config.AutomaticallyRouteTo.isValidAutomaticallyRouteTo() {
 			panic(fmt.Errorf("Invalid automatically_route_to in fuzz config in %s", moduleName))
 		}
 
-		if !config.Use_platform_libs.isValidUsePlatformLibs() {
+		if !config.UsePlatformLibs.isValidUsePlatformLibs() {
 			panic(fmt.Errorf("Invalid use_platform_libs in fuzz config in %s", moduleName))
 		}
 	}
@@ -419,6 +420,20 @@
 	// Optional list of data files to be installed to the fuzz target's output
 	// directory. Directory structure relative to the module is preserved.
 	Data []string `android:"path"`
+	// Same as data, but adds dependencies on modules using the device's os variant, and common
+	// architecture's variant. Can be useful to add device-built apps to the data of a host
+	// test.
+	Device_common_data []string `android:"path_device_common"`
+	// Same as data, but adds dependencies on modules using the device's os variant, and the
+	// device's first architecture's variant. Can be useful to add device-built apps to the data
+	// of a host test.
+	Device_first_data []string `android:"path_device_first"`
+
+	// Same as data, but will add dependencies on modules using the host's os variation and
+	// the common arch variation. Useful for a device test that wants to depend on a host
+	// module, for example to include a custom Tradefed test runner.
+	Host_common_data []string `android:"path_host_common"`
+
 	// Optional dictionary to be installed to the fuzz target's output directory.
 	Dictionary *string `android:"path"`
 	// Define the fuzzing frameworks this fuzz target can be built for. If
@@ -437,6 +452,62 @@
 	Data           android.Paths
 }
 
+type FuzzConfigInfo struct {
+	Vector Vector
+	// How privileged the service being fuzzed is.
+	ServicePrivilege ServicePrivilege
+	// Whether the service being fuzzed handles data from multiple users or only
+	// a single one.
+	Users UserData
+	// Specifies the use state of the code being fuzzed. This state factors into
+	// how an issue is handled.
+	FuzzedCodeUsage FuzzedCodeUsage
+	// Which team to route this to, if it should be routed automatically.
+	AutomaticallyRouteTo AutomaticallyRouteTo
+	// Specifies libs used to initialize ART (java only, 'use_none' for no initialization)
+	UsePlatformLibs UsePlatformLibs
+	// Specify whether to enable continuous fuzzing on devices. Defaults to true.
+	FuzzOnHaikuDevice bool
+	// Specify whether to enable continuous fuzzing on host. Defaults to true.
+	FuzzOnHaikuHost bool
+	// Specifies whether fuzz target should check presubmitted code changes for crashes.
+	// Defaults to false.
+	UseForPresubmit bool
+}
+type FuzzPackagedModuleInfo struct {
+	FuzzConfig *FuzzConfigInfo
+	Dictionary android.Path
+	Corpus     android.Paths
+	Config     android.Path
+	Data       android.Paths
+}
+
+var FuzzPackagedModuleInfoProvider = blueprint.NewProvider[FuzzPackagedModuleInfo]()
+
+func SetFuzzPackagedModuleInfo(ctx android.ModuleContext, fm *FuzzPackagedModule) {
+	info := FuzzPackagedModuleInfo{
+		Dictionary: fm.Dictionary,
+		Config:     fm.Config,
+		Corpus:     fm.Corpus,
+		Data:       fm.Data,
+	}
+	if fm.FuzzProperties.Fuzz_config != nil {
+		info.FuzzConfig = &FuzzConfigInfo{
+			Vector:               fm.FuzzProperties.Fuzz_config.Vector,
+			ServicePrivilege:     fm.FuzzProperties.Fuzz_config.Service_privilege,
+			Users:                fm.FuzzProperties.Fuzz_config.Users,
+			FuzzedCodeUsage:      fm.FuzzProperties.Fuzz_config.Fuzzed_code_usage,
+			AutomaticallyRouteTo: fm.FuzzProperties.Fuzz_config.Automatically_route_to,
+			FuzzOnHaikuDevice:    BoolDefault(fm.FuzzProperties.Fuzz_config.Fuzz_on_haiku_device, true),
+			FuzzOnHaikuHost:      BoolDefault(fm.FuzzProperties.Fuzz_config.Fuzz_on_haiku_host, true),
+			UsePlatformLibs:      fm.FuzzProperties.Fuzz_config.Use_platform_libs,
+			UseForPresubmit:      BoolDefault(fm.FuzzProperties.Fuzz_config.Use_for_presubmit, false),
+		}
+	}
+
+	android.SetProvider(ctx, FuzzPackagedModuleInfoProvider, info)
+}
+
 func GetFramework(ctx android.LoadHookContext, lang Lang) Framework {
 	framework := ctx.Config().Getenv("FUZZ_FRAMEWORK")
 
@@ -495,7 +566,9 @@
 	return true
 }
 
-func (s *FuzzPackager) PackageArtifacts(ctx android.SingletonContext, module android.Module, fuzzModule FuzzPackagedModule, archDir android.OutputPath, builder *android.RuleBuilder) []FileToZip {
+// TODO(b/397766191): Change the signature to take ModuleProxy
+// Please only access the module's internal data through providers.
+func (s *FuzzPackager) PackageArtifacts(ctx android.SingletonContext, module android.Module, fuzzModule *FuzzPackagedModuleInfo, archDir android.OutputPath, builder *android.RuleBuilder) []FileToZip {
 	// Package the corpora into a zipfile.
 	var files []FileToZip
 	if fuzzModule.Corpus != nil {
@@ -534,7 +607,9 @@
 	return files
 }
 
-func (s *FuzzPackager) BuildZipFile(ctx android.SingletonContext, module android.Module, fuzzModule FuzzPackagedModule, files []FileToZip, builder *android.RuleBuilder, archDir android.OutputPath, archString string, hostOrTargetString string, archOs ArchOs, archDirs map[ArchOs][]FileToZip) ([]FileToZip, bool) {
+// TODO(b/397766191): Change the signature to take ModuleProxy
+// Please only access the module's internal data through providers.
+func (s *FuzzPackager) BuildZipFile(ctx android.SingletonContext, module android.Module, fuzzModule *FuzzPackagedModuleInfo, files []FileToZip, builder *android.RuleBuilder, archDir android.OutputPath, archString string, hostOrTargetString string, archOs ArchOs, archDirs map[ArchOs][]FileToZip) ([]FileToZip, bool) {
 	fuzzZip := archDir.Join(ctx, module.Name()+".zip")
 
 	command := builder.Command().BuiltTool("soong_zip").
@@ -556,10 +631,10 @@
 	builder.Build("create-"+fuzzZip.String(),
 		"Package "+module.Name()+" for "+archString+"-"+hostOrTargetString)
 
-	if config := fuzzModule.FuzzProperties.Fuzz_config; config != nil {
-		if strings.Contains(hostOrTargetString, "host") && !BoolDefault(config.Fuzz_on_haiku_host, true) {
+	if config := fuzzModule.FuzzConfig; config != nil {
+		if strings.Contains(hostOrTargetString, "host") && !config.FuzzOnHaikuHost {
 			return archDirs[archOs], false
-		} else if !strings.Contains(hostOrTargetString, "host") && !BoolDefault(config.Fuzz_on_haiku_device, true) {
+		} else if !strings.Contains(hostOrTargetString, "host") && !config.FuzzOnHaikuDevice {
 			return archDirs[archOs], false
 		}
 	}
diff --git a/genrule/Android.bp b/genrule/Android.bp
index 49df480..b82f2a9 100644
--- a/genrule/Android.bp
+++ b/genrule/Android.bp
@@ -14,7 +14,6 @@
         "soong-shared",
     ],
     srcs: [
-        "allowlists.go",
         "genrule.go",
         "locations.go",
     ],
diff --git a/genrule/genrule.go b/genrule/genrule.go
index ac62b8d..a7c09e7 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -88,9 +88,7 @@
 }
 
 type SourceFileGenerator interface {
-	GeneratedSourceFiles() android.Paths
-	GeneratedHeaderDirs() android.Paths
-	GeneratedDeps() android.Paths
+	android.SourceFileGenerator
 }
 
 // Alias for android.HostToolProvider
@@ -114,8 +112,8 @@
 
 func (t hostToolDependencyTag) AllowDisabledModuleDependencyProxy(
 	ctx android.OtherModuleProviderContext, target android.ModuleProxy) bool {
-	return android.OtherModuleProviderOrDefault(
-		ctx, target, android.CommonModuleInfoKey).ReplacedByPrebuilt
+	return android.OtherModulePointerProviderOrDefault(
+		ctx, target, android.CommonModuleInfoProvider).ReplacedByPrebuilt
 }
 
 var _ android.AllowDisabledModuleDependency = (*hostToolDependencyTag)(nil)
@@ -283,6 +281,7 @@
 			"hardware/google/camera/common/hal/aidl_service:aidl_camera_build_version",
 			"tools/tradefederation/core:tradefed_zip",
 			"vendor/google/services/LyricCameraHAL/src/apex:com.google.pixel.camera.hal.manifest",
+			"vendor/google_tradefederation/core:gen_google_tradefed_zip",
 			// go/keep-sorted end
 		}
 		allowlistMap := make(map[string]bool, len(allowlist))
@@ -351,10 +350,10 @@
 				// replaced the dependency.
 				module := android.PrebuiltGetPreferred(ctx, proxy)
 				tool := ctx.OtherModuleName(module)
-				if h, ok := android.OtherModuleProvider(ctx, module, android.HostToolProviderKey); ok {
+				if h, ok := android.OtherModuleProvider(ctx, module, android.HostToolProviderInfoProvider); ok {
 					// A HostToolProvider provides the path to a tool, which will be copied
 					// into the sandbox.
-					if !android.OtherModuleProviderOrDefault(ctx, module, android.CommonModuleInfoKey).Enabled {
+					if !android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider).Enabled {
 						if ctx.Config().AllowMissingDependencies() {
 							ctx.AddMissingDependencies([]string{tool})
 						} else {
@@ -455,7 +454,6 @@
 	srcFiles = append(srcFiles, addLabelsForInputs("device_first_srcs", g.properties.Device_first_srcs.GetOrDefault(ctx, nil), nil)...)
 	srcFiles = append(srcFiles, addLabelsForInputs("device_common_srcs", g.properties.Device_common_srcs.GetOrDefault(ctx, nil), nil)...)
 	srcFiles = append(srcFiles, addLabelsForInputs("common_os_srcs", g.properties.Common_os_srcs.GetOrDefault(ctx, nil), nil)...)
-	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcFiles.Strings()})
 
 	var copyFrom android.Paths
 	var outputFiles android.WritablePaths
@@ -729,11 +727,8 @@
 var _ android.ApexModule = (*Module)(nil)
 
 // Implements android.ApexModule
-func (g *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
-	sdkVersion android.ApiLevel) error {
-	// Because generated outputs are checked by client modules(e.g. cc_library, ...)
-	// we can safely ignore the check here.
-	return nil
+func (m *Module) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
+	return android.MinApiLevel
 }
 
 func generatorFactory(taskGenerator taskFunc, props ...interface{}) *Module {
@@ -974,30 +969,9 @@
 	return module
 }
 
-var sandboxingAllowlistKey = android.NewOnceKey("genruleSandboxingAllowlistKey")
-
-type sandboxingAllowlistSets struct {
-	sandboxingDenyModuleSet map[string]bool
-}
-
-func getSandboxingAllowlistSets(ctx android.PathContext) *sandboxingAllowlistSets {
-	return ctx.Config().Once(sandboxingAllowlistKey, func() interface{} {
-		sandboxingDenyModuleSet := map[string]bool{}
-
-		android.AddToStringSet(sandboxingDenyModuleSet, SandboxingDenyModuleList)
-		return &sandboxingAllowlistSets{
-			sandboxingDenyModuleSet: sandboxingDenyModuleSet,
-		}
-	}).(*sandboxingAllowlistSets)
-}
-
 func getSandboxedRuleBuilder(ctx android.ModuleContext, r *android.RuleBuilder) *android.RuleBuilder {
 	if !ctx.DeviceConfig().GenruleSandboxing() {
 		return r.SandboxTools()
 	}
-	sandboxingAllowlistSets := getSandboxingAllowlistSets(ctx)
-	if sandboxingAllowlistSets.sandboxingDenyModuleSet[ctx.ModuleName()] {
-		return r.SandboxTools()
-	}
 	return r.SandboxInputs()
 }
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index f190750..dcec297 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -24,7 +24,6 @@
 
 	"android/soong/android"
 
-	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -516,7 +515,7 @@
 
 	for _, test := range testcases {
 		t.Run(test.name, func(t *testing.T) {
-			gen := result.ModuleForTests(test.name, "")
+			gen := result.ModuleForTests(t, test.name, "")
 			manifest := android.RuleBuilderSboxProtoForTests(t, result.TestContext, gen.Output("genrule.sbox.textproto"))
 			hash := manifest.Commands[0].GetInputHash()
 
@@ -644,7 +643,7 @@
 				ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
 				RunTestWithBp(t, testGenruleBp()+bp)
 
-			mod := result.ModuleForTests("gen", "")
+			mod := result.ModuleForTests(t, "gen", "")
 			if expectedErrors != nil {
 				return
 			}
@@ -694,13 +693,6 @@
 
 	expectedCmd := "cp in1 __SBOX_SANDBOX_DIR__/out/out"
 	android.AssertStringEquals(t, "cmd", expectedCmd, gen.rawCommands[0])
-
-	srcsFileProvider, ok := android.OtherModuleProvider(result.TestContext, gen, blueprint.SrcsFileProviderKey)
-	if !ok {
-		t.Fatal("Expected genrule to have a SrcsFileProviderData, but did not")
-	}
-	expectedSrcs := []string{"in1"}
-	android.AssertDeepEquals(t, "srcs", expectedSrcs, srcsFileProvider.SrcPaths)
 }
 
 func TestGenruleAllowMissingDependencies(t *testing.T) {
@@ -727,7 +719,7 @@
 				ctx.SetAllowMissingDependencies(true)
 			})).RunTestWithBp(t, bp)
 
-	gen := result.ModuleForTests("gen", "").Output("out")
+	gen := result.ModuleForTests(t, "gen", "").Output("out")
 	if gen.Rule != android.ErrorRule {
 		t.Errorf("Expected missing dependency error rule for gen, got %q", gen.Rule.String())
 	}
@@ -758,15 +750,15 @@
 	android.AssertPathsRelativeToTopEquals(t,
 		"genrule.tag with output",
 		[]string{"out/soong/.intermediates/gen/gen/foo"},
-		result.ModuleForTests("gen_foo", "").Module().(*useSource).srcs)
+		result.ModuleForTests(t, "gen_foo", "").Module().(*useSource).srcs)
 	android.AssertPathsRelativeToTopEquals(t,
 		"genrule.tag with output in subdir",
 		[]string{"out/soong/.intermediates/gen/gen/sub/bar"},
-		result.ModuleForTests("gen_bar", "").Module().(*useSource).srcs)
+		result.ModuleForTests(t, "gen_bar", "").Module().(*useSource).srcs)
 	android.AssertPathsRelativeToTopEquals(t,
 		"genrule.tag with all",
 		[]string{"out/soong/.intermediates/gen/gen/foo", "out/soong/.intermediates/gen/gen/sub/bar"},
-		result.ModuleForTests("gen_all", "").Module().(*useSource).srcs)
+		result.ModuleForTests(t, "gen_all", "").Module().(*useSource).srcs)
 }
 
 func TestGenruleInterface(t *testing.T) {
diff --git a/golang/golang.go b/golang/golang.go
index d33f5e0..9e0744a 100644
--- a/golang/golang.go
+++ b/golang/golang.go
@@ -97,17 +97,16 @@
 	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)
+	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)
-		}
+	// Modules in an unexported namespace have no install rule, only add modules in the exported namespaces
+	// to the blueprint_tools phony rules.
+	if g.ExportedToMake() && !usedByBootstrap(ctx.ModuleName()) {
+		// Don't add the installed file of bootstrap tools to the deps of `blueprint_tools`.
+		// 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.
+		ctx.Phony("blueprint_tools", installPath)
 	}
 
 	ctx.SetOutputFiles(android.Paths{outputFile}, "")
diff --git a/golang/golang_test.go b/golang/golang_test.go
index 0a4baed..89a8761 100644
--- a/golang/golang_test.go
+++ b/golang/golang_test.go
@@ -45,9 +45,9 @@
 		}),
 	).RunTestWithBp(t, bp)
 
-	bin := result.ModuleForTests("gobin", result.Config.BuildOSTarget.String())
+	bin := result.ModuleForTests(t, "gobin", result.Config.BuildOSTarget.String())
 
-	expected := "^out/soong/host/" + result.Config.PrebuiltOS() + "/bin/go/gobin/?[^/]*/obj/gobin$"
+	expected := "^out/host/" + result.Config.PrebuiltOS() + "/bin/go/gobin/?[^/]*/obj/gobin$"
 	actual := android.PathsRelativeToTop(bin.OutputFiles(result.TestContext, t, ""))
 	if len(actual) != 1 {
 		t.Fatalf("Expected 1 output file, got %v", actual)
diff --git a/java/Android.bp b/java/Android.bp
index 885e682..99d9c38 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -51,6 +51,7 @@
         "gen.go",
         "generated_java_library.go",
         "genrule.go",
+        "genrule_combiner.go",
         "hiddenapi.go",
         "hiddenapi_modular.go",
         "hiddenapi_monolithic.go",
@@ -77,6 +78,7 @@
         "system_modules.go",
         "systemserver_classpath_fragment.go",
         "testing.go",
+        "tracereferences.go",
         "tradefed.go",
     ],
     testSrcs: [
@@ -95,6 +97,7 @@
         "droiddoc_test.go",
         "droidstubs_test.go",
         "fuzz_test.go",
+        "genrule_combiner_test.go",
         "genrule_test.go",
         "generated_java_library_test.go",
         "hiddenapi_singleton_test.go",
diff --git a/java/aar.go b/java/aar.go
index 7322b09..86c6bfa 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -217,13 +217,9 @@
 }
 
 func (a *aapt) useResourceProcessorBusyBox(ctx android.BaseModuleContext) bool {
-	return BoolDefault(a.aaptProperties.Use_resource_processor, ctx.Config().UseResourceProcessorByDefault()) &&
+	return BoolDefault(a.aaptProperties.Use_resource_processor, true) &&
 		// TODO(b/331641946): remove this when ResourceProcessorBusyBox supports generating shared libraries.
-		!slices.Contains(a.aaptProperties.Aaptflags, "--shared-lib") &&
-		// Use the legacy resource processor in kythe builds.
-		// The legacy resource processor creates an R.srcjar, which kythe can use for generating crossrefs.
-		// TODO(b/354854007): Re-enable BusyBox in kythe builds
-		!ctx.Config().EmitXrefRules()
+		!slices.Contains(a.aaptProperties.Aaptflags, "--shared-lib")
 }
 
 func (a *aapt) filterProduct() string {
@@ -391,8 +387,9 @@
 		versionName = proptools.NinjaEscape(versionName)
 		linkFlags = append(linkFlags, "--version-name ", versionName)
 	}
-
-	linkFlags, compileFlags = android.FilterList(linkFlags, []string{"--legacy"})
+	// Split the flags by prefix, as --png-compression-level has the "=value" suffix.
+	linkFlags, compileFlags = android.FilterListByPrefix(linkFlags,
+		[]string{"--legacy", "--png-compression-level"})
 
 	// Always set --pseudo-localize, it will be stripped out later for release
 	// builds that don't want it.
@@ -878,13 +875,15 @@
 	rroDirsDepSetBuilder := depset.NewBuilder[rroDir](depset.TOPOLOGICAL)
 	manifestsDepSetBuilder := depset.NewBuilder[android.Path](depset.TOPOLOGICAL)
 
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		depTag := ctx.OtherModuleDependencyTag(module)
 
 		var exportPackage android.Path
-		aarDep, _ := module.(AndroidLibraryDependency)
-		if aarDep != nil {
-			exportPackage = aarDep.ExportPackage()
+		var aarDep *AndroidLibraryDependencyInfo
+		javaInfo, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider)
+		if ok && javaInfo.AndroidLibraryDependencyInfo != nil {
+			aarDep = javaInfo.AndroidLibraryDependencyInfo
+			exportPackage = aarDep.ExportPackage
 		}
 
 		switch depTag {
@@ -892,7 +891,7 @@
 			// Nothing, instrumentationForTag is treated as libTag for javac but not for aapt2.
 		case sdkLibTag, libTag, rroDepTag:
 			if exportPackage != nil {
-				sharedResourcesNodeDepSets = append(sharedResourcesNodeDepSets, aarDep.ResourcesNodeDepSet())
+				sharedResourcesNodeDepSets = append(sharedResourcesNodeDepSets, aarDep.ResourcesNodeDepSet)
 				sharedLibs = append(sharedLibs, exportPackage)
 			}
 		case frameworkResTag, omniromResTag:
@@ -901,9 +900,9 @@
 			}
 		case staticLibTag:
 			if exportPackage != nil {
-				staticResourcesNodeDepSets = append(staticResourcesNodeDepSets, aarDep.ResourcesNodeDepSet())
-				rroDirsDepSetBuilder.Transitive(aarDep.RRODirsDepSet())
-				manifestsDepSetBuilder.Transitive(aarDep.ManifestsDepSet())
+				staticResourcesNodeDepSets = append(staticResourcesNodeDepSets, aarDep.ResourcesNodeDepSet)
+				rroDirsDepSetBuilder.Transitive(aarDep.RRODirsDepSet)
+				manifestsDepSetBuilder.Transitive(aarDep.ManifestsDepSet)
 			}
 		}
 
@@ -939,6 +938,18 @@
 	return staticResourcesNodes, sharedResourcesNodes, staticRRODirs, staticManifests, sharedLibs, flags
 }
 
+type AndroidLibraryInfo struct {
+	// Empty for now
+}
+
+var AndroidLibraryInfoProvider = blueprint.NewProvider[AndroidLibraryInfo]()
+
+type AARImportInfo struct {
+	// Empty for now
+}
+
+var AARImportInfoProvider = blueprint.NewProvider[AARImportInfo]()
+
 type AndroidLibrary struct {
 	Library
 	aapt
@@ -1025,7 +1036,7 @@
 		extraSrcJars = android.Paths{a.aapt.aaptSrcJar}
 	}
 
-	a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars, nil)
+	javaInfo := a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars, nil)
 
 	a.aarFile = android.PathForModuleOut(ctx, ctx.ModuleName()+".aar")
 	var res android.Paths
@@ -1034,7 +1045,7 @@
 	}
 
 	prebuiltJniPackages := android.Paths{}
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		if info, ok := android.OtherModuleProvider(ctx, module, JniPackageProvider); ok {
 			prebuiltJniPackages = append(prebuiltJniPackages, info.JniPackages...)
 		}
@@ -1049,7 +1060,16 @@
 		AconfigTextFiles: aconfigTextFilePaths,
 	})
 
+	android.SetProvider(ctx, AndroidLibraryInfoProvider, AndroidLibraryInfo{})
+
+	if javaInfo != nil {
+		setExtraJavaInfo(ctx, a, javaInfo)
+		android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+	}
+
 	a.setOutputFiles(ctx)
+
+	buildComplianceMetadata(ctx)
 }
 
 func (a *AndroidLibrary) setOutputFiles(ctx android.ModuleContext) {
@@ -1445,7 +1465,7 @@
 	var transitiveStaticLibsImplementationJars []depset.DepSet[android.Path]
 	var transitiveStaticLibsResourceJars []depset.DepSet[android.Path]
 
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 			tag := ctx.OtherModuleDependencyTag(module)
 			switch tag {
@@ -1467,12 +1487,7 @@
 	completeStaticLibsResourceJars := depset.New(depset.PREORDER, nil, transitiveStaticLibsResourceJars)
 
 	var implementationJarFile android.Path
-	var combineJars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		combineJars = completeStaticLibsImplementationJars.ToList()
-	} else {
-		combineJars = append(android.Paths{classpathFile}, staticJars...)
-	}
+	combineJars := completeStaticLibsImplementationJars.ToList()
 
 	if len(combineJars) > 1 {
 		implementationJarOutputPath := android.PathForModuleOut(ctx, "combined", jarName)
@@ -1483,12 +1498,8 @@
 	}
 
 	var resourceJarFile android.Path
-	var resourceJars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		resourceJars = completeStaticLibsResourceJars.ToList()
-	} else {
-		resourceJars = staticResourceJars
-	}
+	resourceJars := completeStaticLibsResourceJars.ToList()
+
 	if len(resourceJars) > 1 {
 		combinedJar := android.PathForModuleOut(ctx, "res-combined", jarName)
 		TransformJarsToJar(ctx, combinedJar, "for resources", resourceJars, android.OptionalPath{},
@@ -1499,12 +1510,8 @@
 	}
 
 	// merge implementation jar with resources if necessary
-	var implementationAndResourcesJars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		implementationAndResourcesJars = append(slices.Clone(resourceJars), combineJars...)
-	} else {
-		implementationAndResourcesJars = android.PathsIfNonNil(resourceJarFile, implementationJarFile)
-	}
+	implementationAndResourcesJars := append(slices.Clone(resourceJars), combineJars...)
+
 	var implementationAndResourcesJar android.Path
 	if len(implementationAndResourcesJars) > 1 {
 		combinedJar := android.PathForModuleOut(ctx, "withres", jarName)
@@ -1519,12 +1526,7 @@
 	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
 	a.implementationAndResourcesJarFile = implementationAndResourcesJar.WithoutRel()
 
-	var headerJars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		headerJars = completeStaticLibsHeaderJars.ToList()
-	} else {
-		headerJars = append(android.Paths{classpathFile}, staticHeaderJars...)
-	}
+	headerJars := completeStaticLibsHeaderJars.ToList()
 	if len(headerJars) > 1 {
 		headerJarFile := android.PathForModuleOut(ctx, "turbine-combined", jarName)
 		TransformJarsToJar(ctx, headerJarFile, "combine header jars", headerJars, android.OptionalPath{}, false, nil, nil)
@@ -1533,14 +1535,9 @@
 		a.headerJarFile = headerJars[0]
 	}
 
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		ctx.CheckbuildFile(classpathFile)
-	} else {
-		ctx.CheckbuildFile(a.headerJarFile)
-		ctx.CheckbuildFile(a.implementationJarFile)
-	}
+	ctx.CheckbuildFile(classpathFile)
 
-	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+	javaInfo := &JavaInfo{
 		HeaderJars:                             android.PathsIfNonNil(a.headerJarFile),
 		LocalHeaderJars:                        android.PathsIfNonNil(classpathFile),
 		TransitiveStaticLibsHeaderJars:         completeStaticLibsHeaderJars,
@@ -1553,7 +1550,9 @@
 		ImplementationJars:                     android.PathsIfNonNil(a.implementationJarFile),
 		StubsLinkType:                          Implementation,
 		// TransitiveAconfigFiles: // TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
-	})
+	}
+	setExtraJavaInfo(ctx, a, javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
 
 	if proptools.Bool(a.properties.Extract_jni) {
 		for _, t := range ctx.MultiTargets() {
@@ -1580,8 +1579,12 @@
 		JniPackages: a.jniPackages,
 	})
 
+	android.SetProvider(ctx, AARImportInfoProvider, AARImportInfo{})
+
 	ctx.SetOutputFiles([]android.Path{a.implementationAndResourcesJarFile}, "")
 	ctx.SetOutputFiles([]android.Path{a.aarPath}, ".aar")
+
+	buildComplianceMetadata(ctx)
 }
 
 func (a *AARImport) HeaderJars() android.Paths {
@@ -1609,14 +1612,21 @@
 var _ android.ApexModule = (*AARImport)(nil)
 
 // Implements android.ApexModule
-func (a *AARImport) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
-	return a.depIsInSameApex(ctx, dep)
+func (m *AARImport) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return AARImportDepInSameApexChecker{}
+}
+
+type AARImportDepInSameApexChecker struct {
+	android.BaseDepInSameApexChecker
+}
+
+func (m AARImportDepInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool {
+	return depIsInSameApex(tag)
 }
 
 // Implements android.ApexModule
-func (a *AARImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
-	sdkVersion android.ApiLevel) error {
-	return nil
+func (a *AARImport) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
+	return android.MinApiLevel
 }
 
 var _ android.PrebuiltInterface = (*AARImport)(nil)
@@ -1646,5 +1656,5 @@
 }
 
 func (a *AARImport) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
-	dpInfo.Jars = append(dpInfo.Jars, a.headerJarFile.String(), a.rJar.String())
+	dpInfo.Jars = append(dpInfo.Jars, a.implementationJarFile.String(), a.rJar.String())
 }
diff --git a/java/aar_test.go b/java/aar_test.go
index aa4f0af..088ad6c 100644
--- a/java/aar_test.go
+++ b/java/aar_test.go
@@ -21,6 +21,7 @@
 )
 
 func TestAarImportProducesJniPackages(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -50,8 +51,9 @@
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
 			appMod := ctx.Module(tc.name, "android_common")
-			appTestMod := ctx.ModuleForTests(tc.name, "android_common")
+			appTestMod := ctx.ModuleForTests(t, tc.name, "android_common")
 
 			info, ok := android.OtherModuleProvider(ctx, appMod, JniPackageProvider)
 			if !ok {
@@ -84,6 +86,7 @@
 }
 
 func TestLibraryFlagsPackages(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 	).RunTestWithBp(t, `
@@ -114,7 +117,7 @@
 		}
 	`)
 
-	foo := result.ModuleForTests("foo", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
 
 	// android_library module depends on aconfig_declarations listed in flags_packages
 	android.AssertBoolEquals(t, "foo expected to depend on bar", true,
@@ -133,6 +136,7 @@
 }
 
 func TestAndroidLibraryOutputFilesRel(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -155,9 +159,9 @@
 		}
 	`)
 
-	foo := result.ModuleForTests("foo", "android_common")
-	bar := result.ModuleForTests("bar", "android_common")
-	baz := result.ModuleForTests("baz", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
+	bar := result.ModuleForTests(t, "bar", "android_common")
+	baz := result.ModuleForTests(t, "baz", "android_common")
 
 	fooOutputPaths := foo.OutputFiles(result.TestContext, t, "")
 	barOutputPaths := bar.OutputFiles(result.TestContext, t, "")
diff --git a/java/android_manifest_test.go b/java/android_manifest_test.go
index 7c91884..ce227b9 100644
--- a/java/android_manifest_test.go
+++ b/java/android_manifest_test.go
@@ -21,6 +21,7 @@
 )
 
 func TestManifestMerger(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app {
 			name: "app",
@@ -80,7 +81,7 @@
 
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, bp)
 
-	manifestMergerRule := result.ModuleForTests("app", "android_common").Rule("manifestMerger")
+	manifestMergerRule := result.ModuleForTests(t, "app", "android_common").Rule("manifestMerger")
 	android.AssertPathRelativeToTopEquals(t, "main manifest",
 		"out/soong/.intermediates/app/android_common/manifest_fixer/AndroidManifest.xml",
 		manifestMergerRule.Input)
@@ -100,6 +101,7 @@
 }
 
 func TestManifestValuesApplicationIdSetsPackageName(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_test {
 			name: "test",
@@ -127,7 +129,7 @@
 
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, bp)
 
-	manifestMergerRule := result.ModuleForTests("test", "android_common").Rule("manifestMerger")
+	manifestMergerRule := result.ModuleForTests(t, "test", "android_common").Rule("manifestMerger")
 	android.AssertStringMatches(t,
 		"manifest merger args",
 		manifestMergerRule.Args["args"],
diff --git a/java/androidmk.go b/java/androidmk.go
index eb33589..e9b1f64 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -23,13 +23,12 @@
 	"github.com/google/blueprint/proptools"
 )
 
-func (library *Library) AndroidMkEntriesHostDex() android.AndroidMkEntries {
-	hostDexNeeded := Bool(library.deviceProperties.Hostdex) && !library.Host()
-	if library.hideApexVariantFromMake {
-		hostDexNeeded = false
-	}
+func (library *Library) hostDexNeeded() bool {
+	return Bool(library.deviceProperties.Hostdex) && !library.Host() && !library.hideApexVariantFromMake
+}
 
-	if hostDexNeeded {
+func (library *Library) AndroidMkEntriesHostDex() android.AndroidMkEntries {
+	if library.hostDexNeeded() {
 		var output android.Path
 		if library.dexJarFile.IsSet() {
 			output = library.dexJarFile.Path()
@@ -71,11 +70,7 @@
 	if library.hideApexVariantFromMake {
 		// For a java library built for an APEX, we don't need a Make module for itself. Otherwise, it
 		// will conflict with the platform variant because they have the same module name in the
-		// makefile. However, we need to add its dexpreopt outputs as sub-modules, if it is preopted.
-		dexpreoptEntries := library.dexpreopter.AndroidMkEntriesForApex()
-		if len(dexpreoptEntries) > 0 {
-			entriesList = append(entriesList, dexpreoptEntries...)
-		}
+		// makefile.
 		entriesList = append(entriesList, android.AndroidMkEntries{Disabled: true})
 	} else if !library.ApexModuleBase.AvailableFor(android.AvailableToPlatform) {
 		// Platform variant.  If not available for the platform, we don't need Make module.
@@ -125,6 +120,14 @@
 					}
 				},
 			},
+			ExtraFooters: []android.AndroidMkExtraFootersFunc{
+				func(w io.Writer, name, prefix, moduleDir string) {
+					if library.apiXmlFile != nil {
+						fmt.Fprintf(w, "$(call declare-1p-target,%s,)\n", library.apiXmlFile.String())
+						fmt.Fprintf(w, "$(eval $(call copy-one-file,%s,$(TARGET_OUT_COMMON_INTERMEDIATES)/%s))\n", library.apiXmlFile.String(), library.apiXmlFile.Base())
+					}
+				},
+			},
 		})
 	}
 
@@ -555,73 +558,11 @@
 		},
 		ExtraFooters: []android.AndroidMkExtraFootersFunc{
 			func(w io.Writer, name, prefix, moduleDir string) {
-				if dstubs.apiFile != nil {
-					fmt.Fprintf(w, ".PHONY: %s %s.txt\n", dstubs.Name(), dstubs.Name())
-					fmt.Fprintf(w, "%s %s.txt: %s\n", dstubs.Name(), dstubs.Name(), dstubs.apiFile)
-				}
-				if dstubs.removedApiFile != nil {
-					fmt.Fprintf(w, ".PHONY: %s %s.txt\n", dstubs.Name(), dstubs.Name())
-					fmt.Fprintf(w, "%s %s.txt: %s\n", dstubs.Name(), dstubs.Name(), dstubs.removedApiFile)
-				}
-				if dstubs.checkCurrentApiTimestamp != nil {
-					fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-check-current-api")
-					fmt.Fprintln(w, dstubs.Name()+"-check-current-api:",
-						dstubs.checkCurrentApiTimestamp.String())
-
-					fmt.Fprintln(w, ".PHONY: checkapi")
-					fmt.Fprintln(w, "checkapi:",
-						dstubs.checkCurrentApiTimestamp.String())
-
-					fmt.Fprintln(w, ".PHONY: droidcore")
-					fmt.Fprintln(w, "droidcore: checkapi")
-				}
-				if dstubs.updateCurrentApiTimestamp != nil {
-					fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-update-current-api")
-					fmt.Fprintln(w, dstubs.Name()+"-update-current-api:",
-						dstubs.updateCurrentApiTimestamp.String())
-
-					fmt.Fprintln(w, ".PHONY: update-api")
-					fmt.Fprintln(w, "update-api:",
-						dstubs.updateCurrentApiTimestamp.String())
-				}
-				if dstubs.checkLastReleasedApiTimestamp != nil {
-					fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-check-last-released-api")
-					fmt.Fprintln(w, dstubs.Name()+"-check-last-released-api:",
-						dstubs.checkLastReleasedApiTimestamp.String())
-
-					fmt.Fprintln(w, ".PHONY: checkapi")
-					fmt.Fprintln(w, "checkapi:",
-						dstubs.checkLastReleasedApiTimestamp.String())
-
-					fmt.Fprintln(w, ".PHONY: droidcore")
-					fmt.Fprintln(w, "droidcore: checkapi")
-				}
 				if dstubs.apiLintTimestamp != nil {
-					fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-api-lint")
-					fmt.Fprintln(w, dstubs.Name()+"-api-lint:",
-						dstubs.apiLintTimestamp.String())
-
-					fmt.Fprintln(w, ".PHONY: checkapi")
-					fmt.Fprintln(w, "checkapi:",
-						dstubs.Name()+"-api-lint")
-
-					fmt.Fprintln(w, ".PHONY: droidcore")
-					fmt.Fprintln(w, "droidcore: checkapi")
-
 					if dstubs.apiLintReport != nil {
-						fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n", dstubs.Name()+"-api-lint",
-							dstubs.apiLintReport.String(), "apilint/"+dstubs.Name()+"-lint-report.txt")
 						fmt.Fprintf(w, "$(call declare-0p-target,%s)\n", dstubs.apiLintReport.String())
 					}
 				}
-				if dstubs.checkNullabilityWarningsTimestamp != nil {
-					fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-check-nullability-warnings")
-					fmt.Fprintln(w, dstubs.Name()+"-check-nullability-warnings:",
-						dstubs.checkNullabilityWarningsTimestamp.String())
-
-					fmt.Fprintln(w, ".PHONY:", "droidcore")
-					fmt.Fprintln(w, "droidcore: ", dstubs.Name()+"-check-nullability-warnings")
-				}
 			},
 		},
 	}}
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 1d98b18..b4b13b1 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -23,6 +23,7 @@
 )
 
 func TestRequired(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -31,7 +32,7 @@
 		}
 	`)
 
-	mod := ctx.ModuleForTests("foo", "android_common").Module()
+	mod := ctx.ModuleForTests(t, "foo", "android_common").Module()
 	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
 
 	expected := []string{"libfoo"}
@@ -42,6 +43,7 @@
 }
 
 func TestHostdex(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -50,7 +52,7 @@
 		}
 	`)
 
-	mod := ctx.ModuleForTests("foo", "android_common").Module()
+	mod := ctx.ModuleForTests(t, "foo", "android_common").Module()
 	entriesList := android.AndroidMkEntriesForTest(t, ctx, mod)
 	if len(entriesList) != 2 {
 		t.Errorf("two entries are expected, but got %d", len(entriesList))
@@ -72,6 +74,7 @@
 }
 
 func TestHostdexRequired(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -81,7 +84,7 @@
 		}
 	`)
 
-	mod := ctx.ModuleForTests("foo", "android_common").Module()
+	mod := ctx.ModuleForTests(t, "foo", "android_common").Module()
 	entriesList := android.AndroidMkEntriesForTest(t, ctx, mod)
 	if len(entriesList) != 2 {
 		t.Errorf("two entries are expected, but got %d", len(entriesList))
@@ -103,6 +106,7 @@
 }
 
 func TestHostdexSpecificRequired(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -116,7 +120,7 @@
 		}
 	`)
 
-	mod := ctx.ModuleForTests("foo", "android_common").Module()
+	mod := ctx.ModuleForTests(t, "foo", "android_common").Module()
 	entriesList := android.AndroidMkEntriesForTest(t, ctx, mod)
 	if len(entriesList) != 2 {
 		t.Errorf("two entries are expected, but got %d", len(entriesList))
@@ -136,6 +140,7 @@
 }
 
 func TestJavaSdkLibrary_RequireXmlPermissionFile(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -153,7 +158,7 @@
 		`)
 
 	// Verify the existence of internal modules
-	result.ModuleForTests("foo-shared_library.xml", "android_common")
+	result.ModuleForTests(t, "foo-shared_library.xml", "android_common")
 
 	testCases := []struct {
 		moduleName string
@@ -163,7 +168,7 @@
 		{"foo-no_shared_library", []string{"foo-no_shared_library.impl"}},
 	}
 	for _, tc := range testCases {
-		mod := result.ModuleForTests(tc.moduleName, "android_common").Module()
+		mod := result.ModuleForTests(t, tc.moduleName, "android_common").Module()
 		entries := android.AndroidMkEntriesForTest(t, result.TestContext, mod)[0]
 		actual := entries.EntryMap["LOCAL_REQUIRED_MODULES"]
 		if !reflect.DeepEqual(tc.expected, actual) {
@@ -173,6 +178,7 @@
 }
 
 func TestImportSoongDexJar(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		java_import {
 			name: "my-java-import",
@@ -191,6 +197,7 @@
 }
 
 func TestAndroidTestHelperApp_LocalDisableTestConfig(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_test_helper_app {
 			name: "foo",
@@ -198,7 +205,7 @@
 		}
 	`)
 
-	mod := ctx.ModuleForTests("foo", "android_common").Module()
+	mod := ctx.ModuleForTests(t, "foo", "android_common").Module()
 	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
 
 	expected := []string{"true"}
@@ -209,6 +216,7 @@
 }
 
 func TestGetOverriddenPackages(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(
 		t, `
 		android_app {
@@ -246,7 +254,7 @@
 	}
 
 	for _, expected := range expectedVariants {
-		mod := ctx.ModuleForTests(expected.name, expected.variantName).Module()
+		mod := ctx.ModuleForTests(t, expected.name, expected.variantName).Module()
 		entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
 		actual := entries.EntryMap["LOCAL_OVERRIDES_PACKAGES"]
 
@@ -255,6 +263,7 @@
 }
 
 func TestJniAsRequiredDeps(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		cc.PrepareForTestWithCcDefaultModules,
@@ -295,7 +304,7 @@
 	}
 
 	for _, tc := range testcases {
-		mod := ctx.ModuleForTests(tc.name, "android_common").Module()
+		mod := ctx.ModuleForTests(t, tc.name, "android_common").Module()
 		entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)[0]
 		required := entries.EntryMap["LOCAL_REQUIRED_MODULES"]
 		android.AssertDeepEquals(t, "unexpected required deps", tc.expected, required)
diff --git a/java/app.go b/java/app.go
index 64ecc55..a3414f6 100644
--- a/java/app.go
+++ b/java/app.go
@@ -70,6 +70,19 @@
 
 	// EmbeddedJNILibs is the list of paths to JNI libraries that were embedded in the APK.
 	EmbeddedJNILibs android.Paths
+
+	MergedManifestFile android.Path
+
+	Prebuilt                      bool
+	AppSet                        bool
+	Privileged                    bool
+	OutputFile                    android.Path
+	InstallApkName                string
+	JacocoReportClassesFile       android.Path
+	Certificate                   Certificate
+	PrivAppAllowlist              android.OptionalPath
+	OverriddenManifestPackageName *string
+	ApkCertsFile                  android.Path
 }
 
 var AppInfoProvider = blueprint.NewProvider[*AppInfo]()
@@ -399,9 +412,23 @@
 	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
 		TestOnly: true,
 	})
-	android.SetProvider(ctx, AppInfoProvider, &AppInfo{
+	appInfo := &AppInfo{
 		Updatable:     Bool(a.appProperties.Updatable),
 		TestHelperApp: true,
+	}
+	setCommonAppInfo(appInfo, a)
+	android.SetProvider(ctx, AppInfoProvider, appInfo)
+
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Tags = append(moduleInfoJSON.Tags, "tests")
+	if len(a.appTestHelperAppProperties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, a.appTestHelperAppProperties.Test_suites...)
+	} else {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
+	}
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: a.appTestHelperAppProperties.Test_suites,
 	})
 }
 
@@ -418,13 +445,28 @@
 			embeddedJniLibs = append(embeddedJniLibs, jni.path)
 		}
 	}
-	android.SetProvider(ctx, AppInfoProvider, &AppInfo{
-		Updatable:       Bool(a.appProperties.Updatable),
-		TestHelperApp:   false,
-		EmbeddedJNILibs: embeddedJniLibs,
-	})
+	overriddenName := a.OverriddenManifestPackageName()
+	appInfo := &AppInfo{
+		Updatable:                     Bool(a.appProperties.Updatable),
+		TestHelperApp:                 false,
+		EmbeddedJNILibs:               embeddedJniLibs,
+		MergedManifestFile:            a.mergedManifest,
+		OverriddenManifestPackageName: &overriddenName,
+	}
+	setCommonAppInfo(appInfo, a)
+	android.SetProvider(ctx, AppInfoProvider, appInfo)
 
 	a.requiredModuleNames = a.getRequiredModuleNames(ctx)
+
+	if a.dexer.proguardDictionary.Valid() {
+		android.SetProvider(ctx, ProguardProvider, ProguardInfo{
+			ModuleName:         ctx.ModuleName(),
+			Class:              "APPS",
+			ProguardDictionary: a.dexer.proguardDictionary.Path(),
+			ProguardUsageZip:   a.dexer.proguardUsageZip.Path(),
+			ClassesJar:         a.implementationAndResourcesJar,
+		})
+	}
 }
 
 func (a *AndroidApp) getRequiredModuleNames(ctx android.ModuleContext) []string {
@@ -487,18 +529,28 @@
 // This check is enforced for "updatable" APKs (including APK-in-APEX).
 func (a *AndroidApp) checkJniLibsSdkVersion(ctx android.ModuleContext, minSdkVersion android.ApiLevel) {
 	// It's enough to check direct JNI deps' sdk_version because all transitive deps from JNI deps are checked in cc.checkLinkType()
-	ctx.VisitDirectDeps(func(m android.Module) {
+	ctx.VisitDirectDepsProxy(func(m android.ModuleProxy) {
 		if !IsJniDepTag(ctx.OtherModuleDependencyTag(m)) {
 			return
 		}
-		dep, _ := m.(*cc.Module)
+		if _, ok := android.OtherModuleProvider(ctx, m, cc.CcInfoProvider); !ok {
+			panic(fmt.Errorf("jni dependency is not a cc module: %v", m))
+		}
+		commonInfo, ok := android.OtherModuleProvider(ctx, m, android.CommonModuleInfoProvider)
+		if !ok {
+			panic(fmt.Errorf("jni dependency doesn't have CommonModuleInfo provider: %v", m))
+		}
 		// The domain of cc.sdk_version is "current" and <number>
 		// We can rely on android.SdkSpec to convert it to <number> so that "current" is
 		// handled properly regardless of sdk finalization.
-		jniSdkVersion, err := android.SdkSpecFrom(ctx, dep.MinSdkVersion()).EffectiveVersion(ctx)
+		ver := ""
+		if !commonInfo.MinSdkVersion.IsPlatform {
+			ver = commonInfo.MinSdkVersion.ApiLevel.String()
+		}
+		jniSdkVersion, err := android.SdkSpecFrom(ctx, ver).EffectiveVersion(ctx)
 		if err != nil || minSdkVersion.LessThan(jniSdkVersion) {
-			ctx.OtherModuleErrorf(dep, "min_sdk_version(%v) is higher than min_sdk_version(%v) of the containing android_app(%v)",
-				dep.MinSdkVersion(), minSdkVersion, ctx.ModuleName())
+			ctx.OtherModuleErrorf(m, "min_sdk_version(%v) is higher than min_sdk_version(%v) of the containing android_app(%v)",
+				ver, minSdkVersion, ctx.ModuleName())
 			return
 		}
 
@@ -561,10 +613,10 @@
 }
 
 func getAconfigFilePaths(ctx android.ModuleContext) (aconfigTextFilePaths android.Paths) {
-	ctx.VisitDirectDeps(func(dep android.Module) {
+	ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(dep)
 		switch tag {
-		case staticLibTag:
+		case staticLibTag, rroDepTag:
 			if flagPackages, ok := android.OtherModuleProvider(ctx, dep, FlagsPackagesProvider); ok {
 				aconfigTextFilePaths = append(aconfigTextFilePaths, flagPackages.AconfigTextFiles...)
 			}
@@ -640,7 +692,7 @@
 	}
 
 	// Use non final ids if we are doing optimized shrinking and are using R8.
-	nonFinalIds := a.dexProperties.optimizedResourceShrinkingEnabled(ctx) && a.dexer.effectiveOptimizeEnabled()
+	nonFinalIds := a.dexProperties.optimizedResourceShrinkingEnabled(ctx) && a.dexer.effectiveOptimizeEnabled(ctx)
 
 	aconfigTextFilePaths := getAconfigFilePaths(ctx)
 
@@ -667,7 +719,7 @@
 
 func (a *AndroidApp) proguardBuildActions(ctx android.ModuleContext) {
 	var staticLibProguardFlagFiles android.Paths
-	ctx.VisitDirectDeps(func(m android.Module) {
+	ctx.VisitDirectDepsProxy(func(m android.ModuleProxy) {
 		depProguardInfo, _ := android.OtherModuleProvider(ctx, m, ProguardSpecInfoProvider)
 		staticLibProguardFlagFiles = append(staticLibProguardFlagFiles, depProguardInfo.UnconditionallyExportedProguardFlags.ToList()...)
 		if ctx.OtherModuleDependencyTag(m) == staticLibTag {
@@ -704,7 +756,7 @@
 	return android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
 }
 
-func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) (android.Path, android.Path) {
+func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) (android.Path, android.Path, *JavaInfo) {
 	a.dexpreopter.installPath = a.installPath(ctx)
 	a.dexpreopter.isApp = true
 	if a.dexProperties.Uncompress_dex == nil {
@@ -719,6 +771,7 @@
 
 	var packageResources = a.exportPackage
 
+	javaInfo := &JavaInfo{}
 	if ctx.ModuleName() != "framework-res" && ctx.ModuleName() != "omnirom-res" {
 		if a.dexProperties.resourceShrinkingEnabled(ctx) {
 			protoFile := android.PathForModuleOut(ctx, packageResources.Base()+".proto.apk")
@@ -742,7 +795,7 @@
 			extraSrcJars = android.Paths{a.aapt.aaptSrcJar}
 		}
 
-		a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars, nil)
+		javaInfo = a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars, nil)
 		if a.dexProperties.resourceShrinkingEnabled(ctx) {
 			binaryResources := android.PathForModuleOut(ctx, packageResources.Base()+".binary.out.apk")
 			aapt2Convert(ctx, binaryResources, a.dexer.resourcesOutput.Path(), "binary")
@@ -750,7 +803,7 @@
 		}
 	}
 
-	return a.dexJarFile.PathOrNil(), packageResources
+	return a.dexJarFile.PathOrNil(), packageResources, javaInfo
 }
 
 func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, prebuiltJniPackages android.Paths, ctx android.ModuleContext) android.WritablePath {
@@ -960,7 +1013,7 @@
 	a.linter.resources = a.aapt.resourceFiles
 	a.linter.buildModuleReportZip = ctx.Config().UnbundledBuildApps()
 
-	dexJarFile, packageResources := a.dexBuildActions(ctx)
+	dexJarFile, packageResources, javaInfo := a.dexBuildActions(ctx)
 
 	// No need to check the SDK version of the JNI deps unless we embed them
 	checkNativeSdkVersion := a.shouldEmbedJnis(ctx) && !Bool(a.appProperties.Jni_uses_platform_apis)
@@ -1076,7 +1129,28 @@
 		},
 	)
 
+	if javaInfo != nil {
+		javaInfo.OutputFile = a.outputFile
+		setExtraJavaInfo(ctx, a, javaInfo)
+		android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+	}
+
+	android.SetProvider(ctx, android.ApexBundleDepsDataProvider, android.ApexBundleDepsData{
+		FlatListPath: a.FlatListPath(),
+		Updatable:    a.Updatable(),
+	})
+
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Class = []string{"APPS"}
+	if !a.embeddedJniLibs {
+		for _, jniLib := range a.jniLibs {
+			moduleInfoJSON.ExtraRequired = append(moduleInfoJSON.ExtraRequired, jniLib.name)
+		}
+	}
+
 	a.setOutputFiles(ctx)
+
+	buildComplianceMetadata(ctx)
 }
 
 func (a *AndroidApp) setOutputFiles(ctx android.ModuleContext) {
@@ -1108,29 +1182,35 @@
 			app.SdkVersion(ctx).Kind != android.SdkCorePlatform && !app.RequiresStableAPIs(ctx)
 	}
 	jniLib, prebuiltJniPackages := collectJniDeps(ctx, shouldCollectRecursiveNativeDeps,
-		checkNativeSdkVersion, func(parent, child android.Module) bool {
+		checkNativeSdkVersion, func(parent, child android.ModuleProxy) bool {
 			apkInApex := ctx.Module().(android.ApexModule).NotInPlatform()
-			childLinkable, _ := child.(cc.LinkableInterface)
-			parentLinkable, _ := parent.(cc.LinkableInterface)
-			useStubsOfDep := childLinkable.IsStubs()
-			if apkInApex && parentLinkable != nil {
+			childLinkable, _ := android.OtherModuleProvider(ctx, child, cc.LinkableInfoProvider)
+			parentIsLinkable := false
+			if android.EqualModules(ctx.Module(), parent) {
+				parentLinkable, _ := ctx.Module().(cc.LinkableInterface)
+				parentIsLinkable = parentLinkable != nil
+			} else {
+				_, parentIsLinkable = android.OtherModuleProvider(ctx, parent, cc.LinkableInfoProvider)
+			}
+			useStubsOfDep := childLinkable.IsStubs
+			if apkInApex && parentIsLinkable {
 				// APK-in-APEX
 				// If the parent is a linkable interface, use stubs if the dependency edge crosses an apex boundary.
-				useStubsOfDep = useStubsOfDep || (childLinkable.HasStubsVariants() && cc.ShouldUseStubForApex(ctx, parent, child))
+				useStubsOfDep = useStubsOfDep || (childLinkable.HasStubsVariants && cc.ShouldUseStubForApex(ctx, parent, child))
 			}
-			return !childLinkable.IsNdk(ctx.Config()) && !useStubsOfDep
+			return !childLinkable.IsNdk && !useStubsOfDep
 		})
 
 	var certificates []Certificate
 
 	var directImplementationDeps android.Paths
 	var transitiveImplementationDeps []depset.DepSet[android.Path]
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
 
 		if tag == certificateTag {
-			if dep, ok := module.(*AndroidAppCertificate); ok {
+			if dep, ok := android.OtherModuleProvider(ctx, module, AndroidAppCertificateInfoProvider); ok {
 				certificates = append(certificates, dep.Certificate)
 			} else {
 				ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", otherName)
@@ -1154,22 +1234,25 @@
 func collectJniDeps(ctx android.ModuleContext,
 	shouldCollectRecursiveNativeDeps bool,
 	checkNativeSdkVersion bool,
-	filter func(parent, child android.Module) bool) ([]jniLib, android.Paths) {
+	filter func(parent, child android.ModuleProxy) bool) ([]jniLib, android.Paths) {
 	var jniLibs []jniLib
 	var prebuiltJniPackages android.Paths
 	seenModulePaths := make(map[string]bool)
 
-	ctx.WalkDeps(func(module android.Module, parent android.Module) bool {
+	ctx.WalkDepsProxy(func(module, parent android.ModuleProxy) bool {
+		if !android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider).Enabled {
+			return false
+		}
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
 
 		if IsJniDepTag(tag) || cc.IsSharedDepTag(tag) {
-			if dep, ok := module.(cc.LinkableInterface); ok {
+			if dep, ok := android.OtherModuleProvider(ctx, module, cc.LinkableInfoProvider); ok {
 				if filter != nil && !filter(parent, module) {
 					return false
 				}
 
-				lib := dep.OutputFile()
+				lib := dep.OutputFile
 				if lib.Valid() {
 					path := lib.Path()
 					if seenModulePaths[path.String()] {
@@ -1177,7 +1260,8 @@
 					}
 					seenModulePaths[path.String()] = true
 
-					if checkNativeSdkVersion && dep.SdkVersion() == "" {
+					commonInfo := android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider)
+					if checkNativeSdkVersion && commonInfo.SdkVersion == "" {
 						ctx.PropertyErrorf("jni_libs", "JNI dependency %q uses platform APIs, but this module does not",
 							otherName)
 					}
@@ -1185,11 +1269,11 @@
 					jniLibs = append(jniLibs, jniLib{
 						name:           ctx.OtherModuleName(module),
 						path:           path,
-						target:         module.Target(),
-						coverageFile:   dep.CoverageOutputFile(),
-						unstrippedFile: dep.UnstrippedOutputFile(),
-						partition:      dep.Partition(),
-						installPaths:   android.OtherModuleProviderOrDefault(ctx, dep, android.InstallFilesProvider).InstallFiles,
+						target:         commonInfo.Target,
+						coverageFile:   dep.CoverageOutputFile,
+						unstrippedFile: dep.UnstrippedOutputFile,
+						partition:      dep.Partition,
+						installPaths:   android.OtherModuleProviderOrDefault(ctx, module, android.InstallFilesProvider).InstallFiles,
 					})
 				} else if ctx.Config().AllowMissingDependencies() {
 					ctx.AddMissingDependencies([]string{otherName})
@@ -1214,10 +1298,13 @@
 }
 
 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 {
-			if !do(ctx, parent, am, isExternal) {
+	ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
+		// TODO(ccross): Should this use android.DepIsInSameApex?  Right now it is applying the android app
+		// heuristics to every transitive dependency, when it should probably be using the heuristics of the
+		// immediate parent.
+		isExternal := !a.GetDepInSameApexChecker().OutgoingDepIsInSameApex(ctx.OtherModuleDependencyTag(child))
+		if am, ok := android.OtherModuleProvider(ctx, child, android.CommonModuleInfoProvider); ok && am.IsApexModule {
+			if !do(ctx, parent, child, isExternal) {
 				return false
 			}
 		}
@@ -1231,12 +1318,12 @@
 	}
 
 	depsInfo := android.DepNameToDepInfoMap{}
-	a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+	a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from, to android.ModuleProxy, externalDep bool) bool {
 		depName := to.Name()
 
 		// Skip dependencies that are only available to APEXes; they are developed with updatability
 		// in mind and don't need manual approval.
-		if to.(android.ApexModule).NotAvailableForPlatform() {
+		if android.OtherModulePointerProviderOrDefault(ctx, to, android.CommonModuleInfoProvider).NotAvailableForPlatform {
 			return true
 		}
 
@@ -1246,18 +1333,9 @@
 			depsInfo[depName] = info
 		} else {
 			toMinSdkVersion := "(no version)"
-			if m, ok := to.(interface {
-				MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel
-			}); ok {
-				if v := m.MinSdkVersion(ctx); !v.IsNone() {
-					toMinSdkVersion = v.String()
-				}
-			} else if m, ok := to.(interface{ MinSdkVersion() string }); ok {
-				// TODO(b/175678607) eliminate the use of MinSdkVersion returning
-				// string
-				if v := m.MinSdkVersion(); v != "" {
-					toMinSdkVersion = v
-				}
+			if info, ok := android.OtherModuleProvider(ctx, to, android.CommonModuleInfoProvider); ok &&
+				!info.MinSdkVersion.IsPlatform && info.MinSdkVersion.ApiLevel != nil {
+				toMinSdkVersion = info.MinSdkVersion.ApiLevel.String()
 			}
 			depsInfo[depName] = android.ApexModuleDepInfo{
 				To:            depName,
@@ -1292,11 +1370,19 @@
 	return a.overridableAppProperties.Certificate.GetOrDefault(ctx, "")
 }
 
-func (a *AndroidApp) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
-	if IsJniDepTag(ctx.OtherModuleDependencyTag(dep)) {
+func (m *AndroidApp) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return AppDepInSameApexChecker{}
+}
+
+type AppDepInSameApexChecker struct {
+	android.BaseDepInSameApexChecker
+}
+
+func (m AppDepInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool {
+	if IsJniDepTag(tag) {
 		return true
 	}
-	return a.Library.DepIsInSameApex(ctx, dep)
+	return depIsInSameApex(tag)
 }
 
 func (a *AndroidApp) Privileged() bool {
@@ -1560,6 +1646,9 @@
 	}
 	a.generateAndroidBuildActions(ctx)
 
+	for _, c := range a.testProperties.Test_options.Tradefed_options {
+		configs = append(configs, c)
+	}
 	for _, module := range a.testProperties.Test_mainline_modules {
 		configs = append(configs, tradefed.Option{Name: "config-descriptor:metadata", Key: "mainline-param", Value: module})
 	}
@@ -1573,6 +1662,24 @@
 	a.data = append(a.data, android.PathsForModuleSrc(ctx, a.testProperties.Device_common_data)...)
 	a.data = append(a.data, android.PathsForModuleSrc(ctx, a.testProperties.Device_first_data)...)
 	a.data = append(a.data, android.PathsForModuleSrc(ctx, a.testProperties.Device_first_prefer32_data)...)
+	a.data = append(a.data, android.PathsForModuleSrc(ctx, a.testProperties.Host_common_data)...)
+
+	// Install test deps
+	if !ctx.Config().KatiEnabled() {
+		pathInTestCases := android.PathForModuleInstall(ctx, ctx.Module().Name())
+		if a.testConfig != nil {
+			ctx.InstallFile(pathInTestCases, ctx.Module().Name()+".config", a.testConfig)
+		}
+		dynamicConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "DynamicConfig.xml")
+		if dynamicConfig.Valid() {
+			ctx.InstallFile(pathInTestCases, ctx.Module().Name()+".dynamic", dynamicConfig.Path())
+		}
+		testDeps := append(a.data, a.extraTestConfigs...)
+		for _, data := range android.SortedUniquePaths(testDeps) {
+			dataPath := android.DataPath{SrcPath: data}
+			ctx.InstallTestData(pathInTestCases, []android.DataPath{dataPath})
+		}
+	}
 
 	android.SetProvider(ctx, tradefed.BaseTestProviderKey, tradefed.BaseTestProviderData{
 		TestcaseRelDataFiles:    testcaseRel(a.data),
@@ -1591,6 +1698,26 @@
 		TopLevelTarget: true,
 	})
 
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Tags = append(moduleInfoJSON.Tags, "tests")
+	if a.testConfig != nil {
+		moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, a.testConfig.String())
+	}
+	moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, a.extraTestConfigs.Strings()...)
+	if len(a.testProperties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, a.testProperties.Test_suites...)
+	} else {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
+	}
+
+	if _, ok := testConfig.(android.WritablePath); ok {
+		moduleInfoJSON.AutoTestConfig = []string{"true"}
+	}
+	moduleInfoJSON.TestMainlineModules = append(moduleInfoJSON.TestMainlineModules, a.testProperties.Test_mainline_modules...)
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: a.testProperties.Test_suites,
+	})
 }
 
 func testcaseRel(paths android.Paths) []string {
@@ -1679,7 +1806,7 @@
 	module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
 	module.appProperties.AlwaysPackageNativeLibs = true
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 
 	module.addHostAndDeviceProperties()
 	module.AddProperties(
@@ -1730,12 +1857,14 @@
 
 	// TODO(b/192032291): Disable by default after auditing downstream usage.
 	module.Module.dexProperties.Optimize.EnabledByDefault = true
+	module.Module.dexProperties.Optimize.Ignore_library_extends_program = proptools.BoolPtr(true)
+	module.Module.dexProperties.Optimize.Proguard_compatibility = proptools.BoolPtr(false)
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
 	module.appProperties.AlwaysPackageNativeLibs = true
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 
 	module.addHostAndDeviceProperties()
 	module.AddProperties(
@@ -1762,6 +1891,12 @@
 	Certificate *string
 }
 
+type AndroidAppCertificateInfo struct {
+	Certificate Certificate
+}
+
+var AndroidAppCertificateInfoProvider = blueprint.NewProvider[AndroidAppCertificateInfo]()
+
 // android_app_certificate modules can be referenced by the certificates property of android_app modules to select
 // the signing key.
 func AndroidAppCertificateFactory() android.Module {
@@ -1777,6 +1912,10 @@
 		Pem: android.PathForModuleSrc(ctx, cert+".x509.pem"),
 		Key: android.PathForModuleSrc(ctx, cert+".pk8"),
 	}
+
+	android.SetProvider(ctx, AndroidAppCertificateInfoProvider, AndroidAppCertificateInfo{
+		Certificate: c.Certificate,
+	})
 }
 
 type OverrideAndroidApp struct {
@@ -1931,7 +2070,7 @@
 		return clcMap
 	}
 
-	ctx.VisitDirectDeps(func(m android.Module) {
+	ctx.VisitDirectDepsProxy(func(m android.ModuleProxy) {
 		tag, isUsesLibTag := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag)
 		if !isUsesLibTag {
 			return
@@ -1939,31 +2078,35 @@
 
 		dep := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(m))
 
+		javaInfo, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider)
+		if !ok {
+			return
+		}
 		// Skip stub libraries. A dependency on the implementation library has been added earlier,
 		// so it will be added to CLC, but the stub shouldn't be. Stub libraries can be distingushed
 		// from implementation libraries by their name, which is different as it has a suffix.
-		if comp, ok := m.(SdkLibraryComponentDependency); ok {
-			if impl := comp.OptionalSdkLibraryImplementation(); impl != nil && *impl != dep {
+		if comp := javaInfo.SdkLibraryComponentDependencyInfo; comp != nil {
+			if impl := comp.OptionalSdkLibraryImplementation; impl != nil && *impl != dep {
 				return
 			}
 		}
 
-		if lib, ok := m.(UsesLibraryDependency); ok {
+		if lib := javaInfo.UsesLibraryDependencyInfo; lib != nil {
 			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 {
+				if tag == usesLibOptTag && javaInfo.DexJarBuildPath.PathOrNil() == nil {
 					u.shouldDisableDexpreopt = true
 					return
 				}
 			}
 			libName := dep
-			if ulib, ok := m.(ProvidesUsesLib); ok && ulib.ProvidesUsesLib() != nil {
-				libName = *ulib.ProvidesUsesLib()
+			if ulib := javaInfo.ProvidesUsesLibInfo; ulib != nil && ulib.ProvidesUsesLib != nil {
+				libName = *ulib.ProvidesUsesLib
 			}
 			clcMap.AddContext(ctx, tag.sdkVersion, libName, tag.optional,
-				lib.DexJarBuildPath(ctx).PathOrNil(), lib.DexJarInstallPath(),
-				lib.ClassLoaderContexts())
+				javaInfo.DexJarBuildPath.PathOrNil(), lib.DexJarInstallPath,
+				lib.ClassLoaderContexts)
 		} else if ctx.Config().AllowMissingDependencies() {
 			ctx.AddMissingDependencies([]string{dep})
 		} else {
@@ -2055,3 +2198,33 @@
 	classLoaderContexts *dexpreopt.ClassLoaderContextMap) {
 	u.verifyUsesLibraries(ctx, apk, nil, classLoaderContexts) // for APKs manifest_check does not write output file
 }
+
+// androidApp is an interface to handle all app modules (android_app, android_app_import, etc.) in
+// the same way.
+type androidApp interface {
+	android.Module
+	Privileged() bool
+	InstallApkName() string
+	OutputFile() android.Path
+	JacocoReportClassesFile() android.Path
+	Certificate() Certificate
+	BaseModuleName() string
+	PrivAppAllowlist() android.OptionalPath
+}
+
+var _ androidApp = (*AndroidApp)(nil)
+var _ androidApp = (*AndroidAppImport)(nil)
+var _ androidApp = (*AndroidTestHelperApp)(nil)
+
+func setCommonAppInfo(appInfo *AppInfo, m androidApp) {
+	appInfo.Privileged = m.Privileged()
+	appInfo.OutputFile = m.OutputFile()
+	appInfo.InstallApkName = m.InstallApkName()
+	appInfo.JacocoReportClassesFile = m.JacocoReportClassesFile()
+	appInfo.Certificate = m.Certificate()
+	appInfo.PrivAppAllowlist = m.PrivAppAllowlist()
+}
+
+type AppInfos []AppInfo
+
+var AppInfosProvider = blueprint.NewProvider[AppInfos]()
diff --git a/java/app_import.go b/java/app_import.go
index 8951c7d..9fb13ba 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -43,6 +43,12 @@
 		Description: "Uncompress embedded JNI libs",
 	})
 
+	stripEmbeddedJniLibsUnusedArchRule = pctx.AndroidStaticRule("strip-embedded-jni-libs-from-unused-arch", blueprint.RuleParams{
+		Command:     `${config.Zip2ZipCmd} -i $in -o $out -x 'lib/**/*.so' $extraArgs`,
+		CommandDeps: []string{"${config.Zip2ZipCmd}"},
+		Description: "Remove all JNI libs from unused architectures",
+	}, "extraArgs")
+
 	uncompressDexRule = pctx.AndroidStaticRule("uncompress-dex", blueprint.RuleParams{
 		Command: `if (zipinfo $in '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then ` +
 			`${config.Zip2ZipCmd} -i $in -o $out -0 'classes*.dex'` +
@@ -56,6 +62,18 @@
 		CommandDeps: []string{"build/soong/scripts/check_prebuilt_presigned_apk.py", "${config.Aapt2Cmd}", "${config.ZipAlign}"},
 		Description: "Check presigned apk",
 	}, "extraArgs")
+
+	extractApkRule = pctx.AndroidStaticRule("extract-apk", blueprint.RuleParams{
+		Command:     "unzip -p $in $extract_apk > $out",
+		Description: "Extract specific sub apk",
+	}, "extract_apk")
+
+	gzipRule = pctx.AndroidStaticRule("gzip",
+		blueprint.RuleParams{
+			Command:     "prebuilts/build-tools/path/linux-x86/gzip -9 -c $in > $out",
+			CommandDeps: []string{"prebuilts/build-tools/path/linux-x86/gzip"},
+			Description: "gzip $out",
+		})
 )
 
 func RegisterAppImportBuildComponents(ctx android.RegistrationContext) {
@@ -150,10 +168,19 @@
 	// the prebuilt is Name() without "prebuilt_" prefix
 	Source_module_name *string
 
+	// Whether stripping all libraries from unused architectures.
+	Strip_unused_jni_arch *bool
+
 	// Path to the .prebuilt_info file of the prebuilt app.
 	// In case of mainline modules, the .prebuilt_info file contains the build_id that was used
 	// to generate the prebuilt.
 	Prebuilt_info *string `android:"path"`
+
+	// Path of extracted apk which is extracted from prebuilt apk. Use this extracted to import.
+	Extract_apk proptools.Configurable[string]
+
+	// Compress the output APK using gzip. Defaults to false.
+	Compress_apk proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"`
 }
 
 func (a *AndroidAppImport) IsInstallable() bool {
@@ -278,6 +305,19 @@
 	})
 }
 
+func (a *AndroidAppImport) extractSubApk(
+	ctx android.ModuleContext, inputPath android.Path, outputPath android.WritablePath) {
+	extractApkPath := a.properties.Extract_apk.GetOrDefault(ctx, "")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   extractApkRule,
+		Input:  inputPath,
+		Output: outputPath,
+		Args: map[string]string{
+			"extract_apk": extractApkPath,
+		},
+	})
+}
+
 // Returns whether this module should have the dex file stored uncompressed in the APK.
 func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool {
 	if ctx.Config().UnbundledBuild() || proptools.Bool(a.properties.Preprocessed) {
@@ -292,8 +332,34 @@
 	return shouldUncompressDex(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), &a.dexpreopter)
 }
 
+func (a *AndroidAppImport) stripEmbeddedJniLibsUnusedArch(
+	ctx android.ModuleContext, inputPath android.Path, outputPath android.WritablePath) {
+	var wantedJniLibSlice []string
+	for _, target := range ctx.MultiTargets() {
+		supported_abis := target.Arch.Abi
+		for _, arch := range supported_abis {
+			wantedJniLibSlice = append(wantedJniLibSlice, " -X 'lib/"+arch+"/*.so'")
+		}
+	}
+	wantedJniLibString := strings.Join(wantedJniLibSlice, " ")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   stripEmbeddedJniLibsUnusedArchRule,
+		Input:  inputPath,
+		Output: outputPath,
+		Args: map[string]string{
+			"extraArgs": wantedJniLibString,
+		},
+	})
+}
+
 func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.generateAndroidBuildActions(ctx)
+
+	appInfo := &AppInfo{
+		Prebuilt: true,
+	}
+	setCommonAppInfo(appInfo, a)
+	android.SetProvider(ctx, AppInfoProvider, appInfo)
 }
 
 func (a *AndroidAppImport) InstallApkName() string {
@@ -336,10 +402,14 @@
 		ctx.ModuleErrorf("One and only one of certficate, presigned (implied by preprocessed), and default_dev_cert properties must be set")
 	}
 
-	// TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK
 	// TODO: LOCAL_PACKAGE_SPLITS
 
 	srcApk := a.prebuilt.SingleSourcePath(ctx)
+	if a.properties.Extract_apk.GetOrDefault(ctx, "") != "" {
+		extract_apk := android.PathForModuleOut(ctx, "extract-apk", ctx.ModuleName()+".apk")
+		a.extractSubApk(ctx, srcApk, extract_apk)
+		srcApk = extract_apk
+	}
 
 	// TODO: Install or embed JNI libraries
 
@@ -347,6 +417,13 @@
 	jnisUncompressed := android.PathForModuleOut(ctx, "jnis-uncompressed", ctx.ModuleName()+".apk")
 	a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed)
 
+	// Strip all embedded JNI libs and include only required ones accordingly to the module's compile_multilib
+	if Bool(a.properties.Strip_unused_jni_arch) {
+		jnisStripped := android.PathForModuleOut(ctx, "jnis-stripped", ctx.ModuleName()+".apk")
+		a.stripEmbeddedJniLibsUnusedArch(ctx, jnisUncompressed, jnisStripped)
+		jnisUncompressed = jnisStripped
+	}
+
 	var pathFragments []string
 	relInstallPath := String(a.properties.Relative_install_path)
 
@@ -366,7 +443,9 @@
 
 	a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries(ctx)
 	a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
-	if a.usesLibrary.shouldDisableDexpreopt {
+
+	// Disable Dexpreopt if Compress_apk is true. It follows the build/make/core/app_prebuilt_internal.mk
+	if a.usesLibrary.shouldDisableDexpreopt || a.properties.Compress_apk.GetOrDefault(ctx, false) {
 		a.dexpreopter.disableDexpreopt()
 	}
 
@@ -385,7 +464,13 @@
 		jnisUncompressed = dexUncompressed
 	}
 
-	apkFilename := proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+".apk")
+	defaultApkFilename := a.BaseModuleName()
+	if a.properties.Compress_apk.GetOrDefault(ctx, false) {
+		defaultApkFilename += ".apk.gz"
+	} else {
+		defaultApkFilename += ".apk"
+	}
+	apkFilename := proptools.StringDefault(a.properties.Filename, defaultApkFilename)
 
 	// TODO: Handle EXTERNAL
 
@@ -425,7 +510,16 @@
 		a.certificate = PresignedCertificate
 	}
 
-	// TODO: Optionally compress the output apk.
+	if a.properties.Compress_apk.GetOrDefault(ctx, false) {
+		outputFile := android.PathForModuleOut(ctx, "compressed_apk", apkFilename)
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        gzipRule,
+			Input:       a.outputFile,
+			Output:      outputFile,
+			Description: "Compressing " + a.outputFile.Base(),
+		})
+		a.outputFile = outputFile
+	}
 
 	if apexInfo.IsForPlatform() {
 		a.installPath = ctx.InstallFile(installDir, apkFilename, a.outputFile)
@@ -443,6 +537,8 @@
 
 	ctx.SetOutputFiles([]android.Path{a.outputFile}, "")
 
+	buildComplianceMetadata(ctx)
+
 	// TODO: androidmk converter jni libs
 }
 
@@ -538,7 +634,15 @@
 	return Bool(a.properties.Privileged)
 }
 
-func (a *AndroidAppImport) DepIsInSameApex(_ android.BaseModuleContext, _ android.Module) bool {
+func (m *AndroidAppImport) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return AppImportDepInSameApexChecker{}
+}
+
+type AppImportDepInSameApexChecker struct {
+	android.BaseDepInSameApexChecker
+}
+
+func (m AppImportDepInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool {
 	// android_app_import might have extra dependencies via uses_libs property.
 	// Don't track the dependency as we don't automatically add those libraries
 	// to the classpath. It should be explicitly added to java_libs property of APEX
@@ -556,10 +660,8 @@
 var _ android.ApexModule = (*AndroidAppImport)(nil)
 
 // Implements android.ApexModule
-func (j *AndroidAppImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
-	sdkVersion android.ApiLevel) error {
-	// Do not check for prebuilts against the min_sdk_version of enclosing APEX
-	return nil
+func (m *AndroidAppImport) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
+	return android.MinApiLevel
 }
 
 func createVariantGroupType(variants []string, variantGroupName string) reflect.Type {
@@ -682,7 +784,29 @@
 func (a *AndroidTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.generateAndroidBuildActions(ctx)
 
+	a.updateModuleInfoJSON(ctx)
+
 	a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: a.testProperties.Test_suites,
+	})
+}
+
+func (a *AndroidTestImport) updateModuleInfoJSON(ctx android.ModuleContext) {
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Class = []string{"APPS"}
+	moduleInfoJSON.CompatibilitySuites = []string{"null-suite"}
+	if len(a.testProperties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = a.testProperties.Test_suites
+	}
+	moduleInfoJSON.SystemSharedLibs = []string{"none"}
+	moduleInfoJSON.Tags = []string{"tests"}
+	moduleInfoJSON.RegisterNameOverride = a.BaseModuleName()
+	testConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "AndroidTest.xml")
+	if testConfig.Valid() {
+		moduleInfoJSON.TestConfig = []string{testConfig.String()}
+	}
 }
 
 func (a *AndroidTestImport) InstallInTestcases() bool {
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 54a5e75..2600767 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -26,6 +26,7 @@
 )
 
 func TestAndroidAppImport(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "foo",
@@ -37,7 +38,47 @@
 		}
 		`)
 
-	variant := ctx.ModuleForTests("foo", "android_common")
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
+
+	// Check dexpreopt outputs.
+	if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule == nil ||
+		variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.odex").Rule == nil {
+		t.Errorf("can't find dexpreopt outputs")
+	}
+
+	// Check cert signing flag.
+	signedApk := variant.Output("signed/foo.apk")
+	signingFlag := signedApk.Args["certificates"]
+	expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8"
+	if expected != signingFlag {
+		t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
+	}
+	rule := variant.Rule("genProvenanceMetaData")
+	android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String())
+	android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
+	android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
+	android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"])
+}
+
+func TestAndroidAppImportWithDefaults(t *testing.T) {
+	t.Parallel()
+	ctx, _ := testJava(t, `
+		android_app_import {
+			name: "foo",
+			defaults: ["foo_defaults"],
+		}
+
+		java_defaults {
+			name: "foo_defaults",
+			apk: "prebuilts/apk/app.apk",
+			certificate: "platform",
+			dex_preopt: {
+				enabled: true,
+			},
+		}
+		`)
+
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
 
 	// Check dexpreopt outputs.
 	if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule == nil ||
@@ -60,6 +101,7 @@
 }
 
 func TestAndroidAppImport_NoDexPreopt(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "foo",
@@ -71,7 +113,7 @@
 		}
 		`)
 
-	variant := ctx.ModuleForTests("foo", "android_common")
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
 
 	// Check dexpreopt outputs. They shouldn't exist.
 	if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule != nil ||
@@ -87,6 +129,7 @@
 }
 
 func TestAndroidAppImport_Presigned(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "foo",
@@ -98,7 +141,7 @@
 		}
 		`)
 
-	variant := ctx.ModuleForTests("foo", "android_common")
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
 
 	// Check dexpreopt outputs.
 	if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule == nil ||
@@ -121,6 +164,7 @@
 }
 
 func TestAndroidAppImport_SigningLineage(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 	  android_app_import {
 			name: "foo",
@@ -137,7 +181,7 @@
 		}
 	`)
 
-	variant := ctx.ModuleForTests("foo", "android_common")
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
 
 	signedApk := variant.Output("signed/foo.apk")
 	// Check certificates
@@ -164,6 +208,7 @@
 }
 
 func TestAndroidAppImport_SigningLineageFilegroup(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 	  android_app_import {
 			name: "foo",
@@ -178,7 +223,7 @@
 		}
 	`)
 
-	variant := ctx.ModuleForTests("foo", "android_common")
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
 
 	signedApk := variant.Output("signed/foo.apk")
 	// Check cert signing lineage flag.
@@ -196,6 +241,7 @@
 }
 
 func TestAndroidAppImport_DefaultDevCert(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "foo",
@@ -207,7 +253,7 @@
 		}
 		`)
 
-	variant := ctx.ModuleForTests("foo", "android_common")
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
 
 	// Check dexpreopt outputs.
 	if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule == nil ||
@@ -231,6 +277,7 @@
 }
 
 func TestAndroidAppImport_DpiVariants(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app_import {
 			name: "foo",
@@ -302,7 +349,7 @@
 			}),
 		).RunTestWithBp(t, bp)
 
-		variant := result.ModuleForTests("foo", "android_common")
+		variant := result.ModuleForTests(t, "foo", "android_common")
 		input := variant.Output("jnis-uncompressed/foo.apk").Input.String()
 		if strings.HasSuffix(input, test.expected) {
 			t.Errorf("wrong src apk, expected: %q got: %q", test.expected, input)
@@ -317,6 +364,7 @@
 }
 
 func TestAndroidAppImport_Filename(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "foo",
@@ -325,10 +373,25 @@
 		}
 
 		android_app_import {
+			name: "foo_compressed",
+			apk: "prebuilts/apk/app.apk",
+			presigned: true,
+			compress_apk: true,
+		}
+
+		android_app_import {
 			name: "bar",
 			apk: "prebuilts/apk/app.apk",
 			presigned: true,
-			filename: "bar_sample.apk"
+			filename: "bar_sample.apk",
+		}
+
+		android_app_import {
+			name: "compressed_bar",
+			apk: "prebuilts/apk/app.apk",
+			presigned: true,
+			filename: "bar_sample.apk",
+			compress_apk: true,
 		}
 		`)
 
@@ -347,16 +410,30 @@
 			expectedMetaDataPath: "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto",
 		},
 		{
+			name:                 "foo_compressed",
+			expected:             "foo_compressed.apk.gz",
+			onDevice:             "/system/app/foo_compressed/foo_compressed.apk.gz",
+			expectedArtifactPath: "prebuilts/apk/app.apk",
+			expectedMetaDataPath: "out/soong/.intermediates/provenance_metadata/foo_compressed/provenance_metadata.textproto",
+		},
+		{
 			name:                 "bar",
 			expected:             "bar_sample.apk",
 			onDevice:             "/system/app/bar/bar_sample.apk",
 			expectedArtifactPath: "prebuilts/apk/app.apk",
 			expectedMetaDataPath: "out/soong/.intermediates/provenance_metadata/bar/provenance_metadata.textproto",
 		},
+		{
+			name:                 "compressed_bar",
+			expected:             "bar_sample.apk",
+			onDevice:             "/system/app/compressed_bar/bar_sample.apk",
+			expectedArtifactPath: "prebuilts/apk/app.apk",
+			expectedMetaDataPath: "out/soong/.intermediates/provenance_metadata/compressed_bar/provenance_metadata.textproto",
+		},
 	}
 
 	for _, test := range testCases {
-		variant := ctx.ModuleForTests(test.name, "android_common")
+		variant := ctx.ModuleForTests(t, test.name, "android_common")
 		if variant.MaybeOutput(test.expected).Rule == nil {
 			t.Errorf("can't find output named %q - all outputs: %v", test.expected, variant.AllOutputs())
 		}
@@ -380,6 +457,7 @@
 }
 
 func TestAndroidAppImport_ArchVariants(t *testing.T) {
+	t.Parallel()
 	// The test config's target arch is ARM64.
 	testCases := []struct {
 		name         string
@@ -505,9 +583,10 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			ctx, _ := testJava(t, test.bp)
 
-			variant := ctx.ModuleForTests("foo", "android_common")
+			variant := ctx.ModuleForTests(t, "foo", "android_common")
 			if test.expected == "" {
 				if variant.Module().Enabled(android.PanickingConfigAndErrorContext(ctx)) {
 					t.Error("module should have been disabled, but wasn't")
@@ -530,6 +609,7 @@
 }
 
 func TestAndroidAppImport_SoongConfigVariables(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name         string
 		bp           string
@@ -572,6 +652,7 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			ctx := android.GroupFixturePreparers(
 				prepareForJavaTest,
 				android.PrepareForTestWithSoongConfigModuleBuildComponents,
@@ -584,7 +665,7 @@
 				}),
 			).RunTestWithBp(t, test.bp).TestContext
 
-			variant := ctx.ModuleForTests("foo", "android_common")
+			variant := ctx.ModuleForTests(t, "foo", "android_common")
 			if test.expected == "" {
 				if variant.Module().Enabled(android.PanickingConfigAndErrorContext(ctx)) {
 					t.Error("module should have been disabled, but wasn't")
@@ -607,6 +688,7 @@
 }
 
 func TestAndroidAppImport_overridesDisabledAndroidApp(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app {
 			name: "foo",
@@ -622,7 +704,7 @@
 		}
 		`)
 
-	variant := ctx.ModuleForTests("prebuilt_foo", "android_common")
+	variant := ctx.ModuleForTests(t, "prebuilt_foo", "android_common")
 	a := variant.Module().(*AndroidAppImport)
 	// The prebuilt module should still be enabled and active even if the source-based counterpart
 	// is disabled.
@@ -635,6 +717,7 @@
 }
 
 func TestAndroidAppImport_relativeInstallPath(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app_import {
 			name: "no_relative_install_path",
@@ -664,28 +747,49 @@
 	}{
 		{
 			name:                "no_relative_install_path",
-			expectedInstallPath: "out/soong/target/product/test_device/system/app/no_relative_install_path/no_relative_install_path.apk",
+			expectedInstallPath: "out/target/product/test_device/system/app/no_relative_install_path/no_relative_install_path.apk",
 			errorMessage:        "Install path is not correct when relative_install_path is missing",
 		},
 		{
 			name:                "relative_install_path",
-			expectedInstallPath: "out/soong/target/product/test_device/system/app/my/path/relative_install_path/relative_install_path.apk",
+			expectedInstallPath: "out/target/product/test_device/system/app/my/path/relative_install_path/relative_install_path.apk",
 			errorMessage:        "Install path is not correct for app when relative_install_path is present",
 		},
 		{
 			name:                "privileged_relative_install_path",
-			expectedInstallPath: "out/soong/target/product/test_device/system/priv-app/my/path/privileged_relative_install_path/privileged_relative_install_path.apk",
+			expectedInstallPath: "out/target/product/test_device/system/priv-app/my/path/privileged_relative_install_path/privileged_relative_install_path.apk",
 			errorMessage:        "Install path is not correct for privileged app when relative_install_path is present",
 		},
 	}
 	for _, testCase := range testCases {
-		ctx, _ := testJava(t, bp)
-		mod := ctx.ModuleForTests(testCase.name, "android_common").Module().(*AndroidAppImport)
-		android.AssertPathRelativeToTopEquals(t, testCase.errorMessage, testCase.expectedInstallPath, mod.installPath)
+		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
+			ctx, _ := testJava(t, bp)
+			mod := ctx.ModuleForTests(t, testCase.name, "android_common").Module().(*AndroidAppImport)
+			android.AssertPathRelativeToTopEquals(t, testCase.errorMessage, testCase.expectedInstallPath, mod.installPath)
+		})
 	}
 }
 
+func TestAndroidAppImport_ExtractApk(t *testing.T) {
+	t.Parallel()
+	ctx, _ := testJava(t, `
+		android_app_import {
+			name: "foo",
+			apk: "prebuilts/apk/app.apk",
+			certificate: "platform",
+			extract_apk: "extract_path/sub_app.apk"
+		}
+		`)
+
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
+	extractRuleArgs := variant.Output("extract-apk/foo.apk").BuildParams.Args
+	if extractRuleArgs["extract_apk"] != "extract_path/sub_app.apk" {
+		t.Errorf("Unexpected extract apk args: %s", extractRuleArgs["extract_apk"])
+	}
+}
 func TestAndroidTestImport(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_test_import {
 			name: "foo",
@@ -697,7 +801,7 @@
 		}
 		`)
 
-	test := ctx.ModuleForTests("foo", "android_common").Module().(*AndroidTestImport)
+	test := ctx.ModuleForTests(t, "foo", "android_common").Module().(*AndroidTestImport)
 
 	// Check android mks.
 	entries := android.AndroidMkEntriesForTest(t, ctx, test)[0]
@@ -714,6 +818,7 @@
 }
 
 func TestAndroidTestImport_NoJinUncompressForPresigned(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_test_import {
 			name: "foo",
@@ -734,16 +839,16 @@
 		}
 		`)
 
-	variant := ctx.ModuleForTests("foo", "android_common")
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
 	jniRule := variant.Output("jnis-uncompressed/foo.apk").BuildParams.Rule.String()
 	if jniRule == android.Cp.String() {
-		t.Errorf("Unexpected JNI uncompress rule command: " + jniRule)
+		t.Errorf("Unexpected JNI uncompress rule command: %s", jniRule)
 	}
 
-	variant = ctx.ModuleForTests("foo_presigned", "android_common")
+	variant = ctx.ModuleForTests(t, "foo_presigned", "android_common")
 	jniRule = variant.Output("jnis-uncompressed/foo_presigned.apk").BuildParams.Rule.String()
 	if jniRule != android.Cp.String() {
-		t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
+		t.Errorf("Unexpected JNI uncompress rule: %s", jniRule)
 	}
 	if variant.MaybeOutput("zip-aligned/foo_presigned.apk").Rule == nil {
 		t.Errorf("Presigned test apk should be aligned")
@@ -751,6 +856,7 @@
 }
 
 func TestAndroidTestImport_Preprocessed(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_test_import {
 			name: "foo",
@@ -761,10 +867,10 @@
 		`)
 
 	apkName := "foo.apk"
-	variant := ctx.ModuleForTests("foo", "android_common")
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
 	jniRule := variant.Output("jnis-uncompressed/" + apkName).BuildParams.Rule.String()
 	if jniRule != android.Cp.String() {
-		t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
+		t.Errorf("Unexpected JNI uncompress rule: %s", jniRule)
 	}
 
 	// Make sure signing and aligning were skipped.
@@ -777,9 +883,11 @@
 }
 
 func TestAndroidAppImport_Preprocessed(t *testing.T) {
+	t.Parallel()
 	for _, dontUncompressPrivAppDexs := range []bool{false, true} {
 		name := fmt.Sprintf("dontUncompressPrivAppDexs:%t", dontUncompressPrivAppDexs)
 		t.Run(name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -804,10 +912,10 @@
 
 			// non-privileged app
 			apkName := "foo.apk"
-			variant := result.ModuleForTests("foo", "android_common")
+			variant := result.ModuleForTests(t, "foo", "android_common")
 			outputBuildParams := variant.Output(apkName).BuildParams
 			if outputBuildParams.Rule.String() != android.Cp.String() {
-				t.Errorf("Unexpected prebuilt android_app_import rule: " + outputBuildParams.Rule.String())
+				t.Errorf("Unexpected prebuilt android_app_import rule: %s", outputBuildParams.Rule.String())
 			}
 
 			// Make sure compression and aligning were validated.
@@ -817,7 +925,7 @@
 
 			validationBuildParams := variant.Output("validated-prebuilt/check.stamp").BuildParams
 			if validationBuildParams.Rule.String() != checkPresignedApkRule.String() {
-				t.Errorf("Unexpected validation rule: " + validationBuildParams.Rule.String())
+				t.Errorf("Unexpected validation rule: %s", validationBuildParams.Rule.String())
 			}
 
 			expectedScriptArgs := "--preprocessed"
@@ -826,10 +934,10 @@
 
 			// privileged app
 			apkName = "bar.apk"
-			variant = result.ModuleForTests("bar", "android_common")
+			variant = result.ModuleForTests(t, "bar", "android_common")
 			outputBuildParams = variant.Output(apkName).BuildParams
 			if outputBuildParams.Rule.String() != android.Cp.String() {
-				t.Errorf("Unexpected prebuilt android_app_import rule: " + outputBuildParams.Rule.String())
+				t.Errorf("Unexpected prebuilt android_app_import rule: %s", outputBuildParams.Rule.String())
 			}
 
 			// Make sure compression and aligning were validated.
@@ -839,7 +947,7 @@
 
 			validationBuildParams = variant.Output("validated-prebuilt/check.stamp").BuildParams
 			if validationBuildParams.Rule.String() != checkPresignedApkRule.String() {
-				t.Errorf("Unexpected validation rule: " + validationBuildParams.Rule.String())
+				t.Errorf("Unexpected validation rule: %s", validationBuildParams.Rule.String())
 			}
 
 			expectedScriptArgs = "--privileged"
@@ -854,6 +962,7 @@
 }
 
 func TestAndroidTestImport_UncompressDex(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name string
 		bp   string
@@ -894,7 +1003,7 @@
 			}),
 		).RunTestWithBp(t, bp)
 
-		foo := result.ModuleForTests("foo", "android_common")
+		foo := result.ModuleForTests(t, "foo", "android_common")
 		actual := foo.MaybeRule("uncompress-dex").Rule != nil
 
 		expect := !unbundled
@@ -917,6 +1026,7 @@
 				name := fmt.Sprintf("%s,unbundled:%t,dontUncompressPrivAppDexs:%t",
 					tt.name, unbundled, dontUncompressPrivAppDexs)
 				t.Run(name, func(t *testing.T) {
+					t.Parallel()
 					test(t, tt.bp, unbundled, dontUncompressPrivAppDexs)
 				})
 			}
@@ -925,6 +1035,7 @@
 }
 
 func TestAppImportMissingCertificateAllowMissingDependencies(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.PrepareForTestWithAllowMissingDependencies,
@@ -936,7 +1047,7 @@
 			certificate: ":missing_certificate",
 		}`)
 
-	foo := result.ModuleForTests("foo", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
 	fooApk := foo.Output("signed/foo.apk")
 	if fooApk.Rule != android.ErrorRule {
 		t.Fatalf("expected ErrorRule for foo.apk, got %s", fooApk.Rule.String())
diff --git a/java/app_set.go b/java/app_set.go
index 7997570..6a2c678 100644
--- a/java/app_set.go
+++ b/java/app_set.go
@@ -192,6 +192,12 @@
 		},
 	)
 
+	android.SetProvider(ctx, AppInfoProvider, &AppInfo{
+		AppSet:       true,
+		Privileged:   as.Privileged(),
+		OutputFile:   as.OutputFile(),
+		ApkCertsFile: as.apkcertsFile,
+	})
 }
 
 func (as *AndroidAppSet) InstallBypassMake() bool { return true }
diff --git a/java/app_set_test.go b/java/app_set_test.go
index c02b359..9b4c44b 100644
--- a/java/app_set_test.go
+++ b/java/app_set_test.go
@@ -24,13 +24,14 @@
 )
 
 func TestAndroidAppSet(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app_set {
 			name: "foo",
 			set: "prebuilts/apks/app.apks",
 			prerelease: true,
 		}`)
-	module := result.ModuleForTests("foo", "android_common")
+	module := result.ModuleForTests(t, "foo", "android_common")
 	const packedSplitApks = "foo.zip"
 	params := module.Output(packedSplitApks)
 	if params.Rule == nil {
@@ -65,6 +66,7 @@
 }
 
 func TestAndroidAppSet_Variants(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app_set {
 			name: "foo",
@@ -113,24 +115,24 @@
 	}
 
 	for _, test := range testCases {
-		ctx := android.GroupFixturePreparers(
-			PrepareForTestWithJavaDefaultModules,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
-				variables.Platform_sdk_version = &test.sdkVersion
-			}),
-			android.FixtureModifyConfig(func(config android.Config) {
-				config.Targets[android.Android] = test.targets
-			}),
-		).RunTestWithBp(t, bp)
+		t.Run(test.name, func(t *testing.T) {
+			ctx := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
+					variables.Platform_sdk_version = &test.sdkVersion
+				}),
+				android.FixtureModifyConfig(func(config android.Config) {
+					config.Targets[android.Android] = test.targets
+				}),
+			).RunTestWithBp(t, bp)
 
-		module := ctx.ModuleForTests("foo", "android_common")
-		const packedSplitApks = "foo.zip"
-		params := module.Output(packedSplitApks)
-		for k, v := range test.expected {
-			t.Run(test.name, func(t *testing.T) {
+			module := ctx.ModuleForTests(t, "foo", "android_common")
+			const packedSplitApks = "foo.zip"
+			params := module.Output(packedSplitApks)
+			for k, v := range test.expected {
 				android.AssertStringEquals(t, fmt.Sprintf("arg value for `%s`", k), v, params.Args[k])
-			})
-		}
+			}
+		})
 	}
 }
diff --git a/java/app_test.go b/java/app_test.go
index e9422a6..c6b22c7 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"path/filepath"
 	"reflect"
+	"regexp"
 	"sort"
 	"strings"
 	"testing"
@@ -41,6 +42,7 @@
 }
 
 func TestApp(t *testing.T) {
+	t.Parallel()
 	resourceFiles := []string{
 		"res/layout/layout.xml",
 		"res/values/strings.xml",
@@ -55,6 +57,7 @@
 
 	for _, moduleType := range []string{"android_app", "android_library"} {
 		t.Run(moduleType, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				prepareForJavaTest,
 				android.FixtureModifyMockFS(func(fs android.MockFS) {
@@ -69,15 +72,15 @@
 				}
 			`)
 
-			foo := result.ModuleForTests("foo", "android_common")
+			foo := result.ModuleForTests(t, "foo", "android_common")
 
 			var expectedLinkImplicits []string
 
 			manifestFixer := foo.Output("manifest_fixer/AndroidManifest.xml")
 			expectedLinkImplicits = append(expectedLinkImplicits, manifestFixer.Output.String())
 
-			frameworkRes := result.ModuleForTests("framework-res", "android_common")
-			omniromRes := result.ModuleForTests("omnirom-res", "android_common")
+			frameworkRes := result.ModuleForTests(t, "framework-res", "android_common")
+			omniromRes := result.ModuleForTests(t,"omnirom-res", "android_common")
 			expectedLinkImplicits = append(expectedLinkImplicits,
 				frameworkRes.Output("package-res.apk").Output.String())
 			expectedLinkImplicits = append(expectedLinkImplicits,
@@ -96,13 +99,14 @@
 			expectedLinkImplicits = append(expectedLinkImplicits, list.Output.String())
 
 			// Check that the link rule uses
-			res := result.ModuleForTests("foo", "android_common").Output("package-res.apk")
+			res := result.ModuleForTests(t, "foo", "android_common").Output("package-res.apk")
 			android.AssertDeepEquals(t, "aapt2 link implicits", expectedLinkImplicits, res.Implicits.Strings())
 		})
 	}
 }
 
 func TestAppSplits(t *testing.T) {
+	t.Parallel()
 	ctx := testApp(t, `
 				android_app {
 					name: "foo",
@@ -111,7 +115,7 @@
 					sdk_version: "current"
 				}`)
 
-	foo := ctx.ModuleForTests("foo", "android_common")
+	foo := ctx.ModuleForTests(t, "foo", "android_common")
 
 	expectedOutputs := []string{
 		"out/soong/.intermediates/foo/android_common/foo.apk",
@@ -127,6 +131,7 @@
 }
 
 func TestPlatformAPIs(t *testing.T) {
+	t.Parallel()
 	testJava(t, `
 		android_app {
 			name: "foo",
@@ -161,6 +166,7 @@
 }
 
 func TestAndroidAppLinkType(t *testing.T) {
+	t.Parallel()
 	testJava(t, `
 		android_app {
 			name: "foo",
@@ -250,6 +256,7 @@
 }
 
 func TestUpdatableApps(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name          string
 		bp            string
@@ -361,6 +368,7 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			errorHandler := android.FixtureExpectsNoErrors
 			if test.expectedError != "" {
 				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(test.expectedError)
@@ -375,6 +383,7 @@
 }
 
 func TestUpdatableApps_TransitiveDepsShouldSetMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	testJavaError(t, `module "bar".*: should support min_sdk_version\(29\)`, cc.GatherRequiredDepsForTest(android.Android)+`
 		android_app {
 			name: "foo",
@@ -393,6 +402,7 @@
 }
 
 func TestUpdatableApps_JniLibsShouldShouldSupportMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		android_app {
 			name: "foo",
@@ -413,6 +423,7 @@
 }
 
 func TestUpdatableApps_JniLibShouldBeBuiltAgainstMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	bp := cc.GatherRequiredDepsForTest(android.Android) + `
 		android_app {
 			name: "foo",
@@ -440,11 +451,11 @@
 
 	ctx, _ := testJavaWithFS(t, bp, fs)
 
-	inputs := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared").Description("link").Implicits
+	inputs := ctx.ModuleForTests(t, "libjni", "android_arm64_armv8-a_sdk_shared").Description("link").Implicits
 	var crtbeginFound, crtendFound bool
-	expectedCrtBegin := ctx.ModuleForTests("crtbegin_so",
+	expectedCrtBegin := ctx.ModuleForTests(t, "crtbegin_so",
 		"android_arm64_armv8-a_sdk_29").Rule("noAddrSig").Output
-	expectedCrtEnd := ctx.ModuleForTests("crtend_so",
+	expectedCrtEnd := ctx.ModuleForTests(t, "crtend_so",
 		"android_arm64_armv8-a_sdk_29").Rule("noAddrSig").Output
 	implicits := []string{}
 	for _, input := range inputs {
@@ -468,6 +479,7 @@
 }
 
 func TestUpdatableApps_ErrorIfJniLibDoesntSupportMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	bp := cc.GatherRequiredDepsForTest(android.Android) + `
 		android_app {
 			name: "foo",
@@ -489,6 +501,7 @@
 }
 
 func TestUpdatableApps_ErrorIfDepMinSdkVersionIsHigher(t *testing.T) {
+	t.Parallel()
 	bp := cc.GatherRequiredDepsForTest(android.Android) + `
 		android_app {
 			name: "foo",
@@ -520,6 +533,7 @@
 }
 
 func TestUpdatableApps_ApplyDefaultUpdatableModuleVersion(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -531,7 +545,7 @@
 			updatable: true,
 		}
 	`)
-	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
+	foo := result.ModuleForTests(t, "com.android.foo", "android_common").Rule("manifestFixer")
 	android.AssertStringDoesContain(t,
 		"com.android.foo: expected manifest fixer to set override-placeholder-version to RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION",
 		foo.BuildParams.Args["args"],
@@ -540,6 +554,7 @@
 }
 
 func TestUpdatableApps_ApplyOverrideApexManifestDefaultVersion(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.FixtureMergeEnv(map[string]string{
@@ -554,7 +569,7 @@
 			updatable: true,
 		}
 	`)
-	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
+	foo := result.ModuleForTests(t, "com.android.foo", "android_common").Rule("manifestFixer")
 	android.AssertStringDoesContain(t,
 		"com.android.foo: expected manifest fixer to set override-placeholder-version to 1234",
 		foo.BuildParams.Args["args"],
@@ -563,6 +578,7 @@
 }
 
 func TestResourceDirs(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name      string
 		prop      string
@@ -599,12 +615,13 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 				fs.AddToFixture(),
 			).RunTestWithBp(t, fmt.Sprintf(bp, testCase.prop))
 
-			module := result.ModuleForTests("foo", "android_common")
+			module := result.ModuleForTests(t, "foo", "android_common")
 			resourceList := module.MaybeOutput("aapt2/res.list")
 
 			var resources []string
@@ -620,6 +637,7 @@
 }
 
 func TestLibraryAssets(t *testing.T) {
+	t.Parallel()
 	bp := `
 			android_app {
 				name: "foo",
@@ -714,7 +732,8 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			m := ctx.ModuleForTests(test.name, "android_common")
+			t.Parallel()
+			m := ctx.ModuleForTests(t, test.name, "android_common")
 
 			// Check asset flag in aapt2 link flags
 			var aapt2link android.TestingBuildParams
@@ -749,6 +768,7 @@
 }
 
 func TestAppJavaResources(t *testing.T) {
+	t.Parallel()
 	bp := `
 			android_app {
 				name: "foo",
@@ -766,7 +786,7 @@
 
 	ctx := testApp(t, bp)
 
-	foo := ctx.ModuleForTests("foo", "android_common")
+	foo := ctx.ModuleForTests(t, "foo", "android_common")
 	fooResources := foo.Output("res/foo.jar")
 	fooDexJar := foo.Output("dex-withres/foo.jar")
 	fooDexJarAligned := foo.Output("dex-withres-aligned/foo.jar")
@@ -784,7 +804,7 @@
 		t.Errorf("expected aligned dex jar %q in foo apk inputs %q", w, g)
 	}
 
-	bar := ctx.ModuleForTests("bar", "android_common")
+	bar := ctx.ModuleForTests(t, "bar", "android_common")
 	barResources := bar.Output("res/bar.jar")
 	barApk := bar.Rule("combineApk")
 
@@ -794,6 +814,7 @@
 }
 
 func TestAndroidResourceProcessor(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name                            string
 		appUsesRP                       bool
@@ -869,15 +890,24 @@
 			},
 			appSrcJars: []string{"out/soong/.intermediates/app/android_common/gen/android/R.srcjar"},
 			appClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
-				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
-				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
+				"out/soong/.intermediates/shared/android_common/turbine/shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
+				"out/soong/.intermediates/direct/android_common/turbine/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
-				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
+				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 
 			directResources: nil,
@@ -890,21 +920,23 @@
 			directImports: []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
 			directSrcJars: []string{"out/soong/.intermediates/direct/android_common/gen/android/R.srcjar"},
 			directClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
-				"out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 			directCombined: []string{
 				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
 				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 
 			transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"},
 			transitiveOverlays:  nil,
 			transitiveImports:   []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
 			transitiveSrcJars:   []string{"out/soong/.intermediates/transitive/android_common/gen/android/R.srcjar"},
-			transitiveClasspath: []string{"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar"},
+			transitiveClasspath: []string{"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar"},
 			transitiveCombined:  nil,
 
 			sharedResources: nil,
@@ -918,9 +950,9 @@
 			},
 			sharedSrcJars: []string{"out/soong/.intermediates/shared/android_common/gen/android/R.srcjar"},
 			sharedClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
-				"out/soong/.intermediates/shared_transitive_shared/android_common/turbine-combined/shared_transitive_shared.jar",
-				"out/soong/.intermediates/shared_transitive_static/android_common/turbine-combined/shared_transitive_static.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
+				"out/soong/.intermediates/shared_transitive_shared/android_common/turbine/shared_transitive_shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
 			},
 			sharedCombined: []string{
 				"out/soong/.intermediates/shared/android_common/javac/shared.jar",
@@ -967,17 +999,26 @@
 			},
 			appSrcJars: nil,
 			appClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				"out/soong/.intermediates/app/android_common/busybox/R.jar",
-				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
-				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
+				"out/soong/.intermediates/shared/android_common/turbine/shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
+				"out/soong/.intermediates/direct/android_common/turbine/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
 				"out/soong/.intermediates/app/android_common/busybox/R.jar",
-				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
+				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 
 			directResources: nil,
@@ -990,18 +1031,20 @@
 			},
 			directSrcJars: nil,
 			directClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				"out/soong/.intermediates/direct/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive_import_dep/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive_import/android_common/busybox/R.jar",
-				"out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 			directCombined: []string{
 				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
 				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 
 			transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"},
@@ -1009,7 +1052,7 @@
 			transitiveImports:   []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
 			transitiveSrcJars:   nil,
 			transitiveClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				"out/soong/.intermediates/transitive/android_common/busybox/R.jar",
 			},
 			transitiveCombined: nil,
@@ -1023,12 +1066,12 @@
 			},
 			sharedSrcJars: nil,
 			sharedClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				"out/soong/.intermediates/shared/android_common/busybox/R.jar",
 				"out/soong/.intermediates/shared_transitive_static/android_common/busybox/R.jar",
 				"out/soong/.intermediates/shared_transitive_shared/android_common/busybox/R.jar",
-				"out/soong/.intermediates/shared_transitive_shared/android_common/turbine-combined/shared_transitive_shared.jar",
-				"out/soong/.intermediates/shared_transitive_static/android_common/turbine-combined/shared_transitive_static.jar",
+				"out/soong/.intermediates/shared_transitive_shared/android_common/turbine/shared_transitive_shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
 			},
 			sharedCombined: []string{
 				"out/soong/.intermediates/shared/android_common/javac/shared.jar",
@@ -1072,18 +1115,27 @@
 			},
 			appSrcJars: nil,
 			appClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				// R.jar has to come before direct.jar
 				"out/soong/.intermediates/app/android_common/busybox/R.jar",
-				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
-				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
+				"out/soong/.intermediates/shared/android_common/turbine/shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
+				"out/soong/.intermediates/direct/android_common/turbine/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
 				"out/soong/.intermediates/app/android_common/busybox/R.jar",
-				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
+				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 
 			dontVerifyDirect:           true,
@@ -1115,15 +1167,24 @@
 			},
 			appSrcJars: []string{"out/soong/.intermediates/app/android_common/gen/android/R.srcjar"},
 			appClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
-				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
-				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
+				"out/soong/.intermediates/shared/android_common/turbine/shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
+				"out/soong/.intermediates/direct/android_common/turbine/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
-				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
+				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 
 			directResources: nil,
@@ -1136,17 +1197,19 @@
 			},
 			directSrcJars: nil,
 			directClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				"out/soong/.intermediates/direct/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive_import_dep/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive_import/android_common/busybox/R.jar",
-				"out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 			directCombined: []string{
 				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
 				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 
 			dontVerifyTransitive:       true,
@@ -1177,15 +1240,24 @@
 			},
 			appSrcJars: []string{"out/soong/.intermediates/app/android_common/gen/android/R.srcjar"},
 			appClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
-				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
-				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
+				"out/soong/.intermediates/shared/android_common/turbine/shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
+				"out/soong/.intermediates/direct/android_common/turbine/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
-				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
+				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 
 			directResources: nil,
@@ -1198,14 +1270,16 @@
 			directImports: []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
 			directSrcJars: []string{"out/soong/.intermediates/direct/android_common/gen/android/R.srcjar"},
 			directClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
-				"out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 			directCombined: []string{
 				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
 				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 
 			transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"},
@@ -1213,7 +1287,7 @@
 			transitiveImports:   []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
 			transitiveSrcJars:   nil,
 			transitiveClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				"out/soong/.intermediates/transitive/android_common/busybox/R.jar",
 			},
 			transitiveCombined: nil,
@@ -1226,6 +1300,7 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
 			bp := fmt.Sprintf(`
 				android_app {
 					name: "app",
@@ -1333,7 +1408,7 @@
 			}
 
 			getAaptInfo := func(moduleName string) (aaptInfo aaptInfo) {
-				mod := result.ModuleForTests(moduleName, "android_common")
+				mod := result.ModuleForTests(t, moduleName, "android_common")
 				resourceListRule := mod.MaybeOutput("aapt2/res.list")
 				overlayListRule := mod.MaybeOutput("aapt2/overlay.list")
 				aaptRule := mod.Rule("aapt2Link")
@@ -1422,6 +1497,7 @@
 }
 
 func TestAndroidResourceOverlays(t *testing.T) {
+	t.Parallel()
 	type moduleAndVariant struct {
 		module  string
 		variant string
@@ -1460,7 +1536,6 @@
 					"device/vendor/blah/overlay/bar/res/values/strings.xml",
 				},
 				{"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",
 				},
@@ -1495,12 +1570,10 @@
 					"device/vendor/blah/overlay/bar/res/values/strings.xml",
 				},
 				{"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",
 				},
 			},
@@ -1540,7 +1613,6 @@
 				},
 				{"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",
 				},
 			},
@@ -1618,6 +1690,7 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 				fs.AddToFixture(),
@@ -1649,7 +1722,7 @@
 			}
 
 			getResources := func(moduleName, variantName string) (resourceFiles, overlayFiles, rroDirs []string) {
-				module := result.ModuleForTests(moduleName, variantName)
+				module := result.ModuleForTests(t, moduleName, variantName)
 				resourceList := module.MaybeOutput("aapt2/res.list")
 				if resourceList.Rule != nil {
 					resourceFiles = resourceListToFiles(module, android.PathsRelativeToTop(resourceList.Inputs))
@@ -1708,7 +1781,7 @@
 }
 
 func checkSdkVersion(t *testing.T, result *android.TestResult, expectedSdkVersion string) {
-	foo := result.ModuleForTests("foo", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
 	link := foo.Output("package-res.apk")
 	linkFlags := strings.Split(link.Args["flags"], " ")
 	min := android.IndexList("--min-sdk-version", linkFlags)
@@ -1727,6 +1800,7 @@
 }
 
 func TestAppSdkVersion(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name                  string
 		sdkVersion            string
@@ -1794,6 +1868,7 @@
 	for _, moduleType := range []string{"android_app", "android_library"} {
 		for _, test := range testCases {
 			t.Run(moduleType+" "+test.name, func(t *testing.T) {
+				t.Parallel()
 				platformApiProp := ""
 				if test.platformApis {
 					platformApiProp = "platform_apis: true,"
@@ -1830,6 +1905,7 @@
 }
 
 func TestVendorAppSdkVersion(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name                                  string
 		sdkVersion                            string
@@ -1872,6 +1948,7 @@
 		for _, sdkKind := range []string{"", "system_"} {
 			for _, test := range testCases {
 				t.Run(moduleType+" "+test.name, func(t *testing.T) {
+					t.Parallel()
 					bp := fmt.Sprintf(`%s {
 						name: "foo",
 						srcs: ["a.java"],
@@ -1903,6 +1980,7 @@
 }
 
 func TestJNIABI(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
 			name: "libjni",
@@ -1959,7 +2037,8 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			app := ctx.ModuleForTests(test.name, "android_common")
+			t.Parallel()
+			app := ctx.ModuleForTests(t, test.name, "android_common")
 			jniLibZip := app.Output("jnilibs.zip")
 			var abis []string
 			args := strings.Fields(jniLibZip.Args["jarArgs"])
@@ -1977,6 +2056,7 @@
 }
 
 func TestAppSdkVersionByPartition(t *testing.T) {
+	t.Parallel()
 	testJavaError(t, "sdk_version must have a value when the module is located at vendor or product", `
 		android_app {
 			name: "foo",
@@ -2021,6 +2101,7 @@
 }
 
 func TestJNIPackaging(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
 			name: "libjni",
@@ -2092,7 +2173,8 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			app := ctx.ModuleForTests(test.name, "android_common")
+			t.Parallel()
+			app := ctx.ModuleForTests(t, test.name, "android_common")
 			jniLibZip := app.MaybeOutput("jnilibs.zip")
 			if g, w := (jniLibZip.Rule != nil), test.packaged; g != w {
 				t.Errorf("expected jni packaged %v, got %v", w, g)
@@ -2112,6 +2194,7 @@
 }
 
 func TestJNISDK(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
 			name: "libjni",
@@ -2173,16 +2256,17 @@
 		{name: "app_vendor", vendorJNI: true},
 	}
 
-	platformJNI := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_shared").
+	platformJNI := ctx.ModuleForTests(t, "libjni", "android_arm64_armv8-a_shared").
 		Output("libjni.so").Output.String()
-	sdkJNI := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared").
+	sdkJNI := ctx.ModuleForTests(t, "libjni", "android_arm64_armv8-a_sdk_shared").
 		Output("libjni.so").Output.String()
-	vendorJNI := ctx.ModuleForTests("libvendorjni", "android_vendor_arm64_armv8-a_shared").
+	vendorJNI := ctx.ModuleForTests(t, "libvendorjni", "android_vendor_arm64_armv8-a_shared").
 		Output("libvendorjni.so").Output.String()
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			app := ctx.ModuleForTests(test.name, "android_common")
+			t.Parallel()
+			app := ctx.ModuleForTests(t, test.name, "android_common")
 
 			jniLibZip := app.MaybeOutput("jnilibs.zip")
 			if len(jniLibZip.Implicits) != 1 {
@@ -2207,6 +2291,7 @@
 	}
 
 	t.Run("jni_uses_platform_apis_error", func(t *testing.T) {
+		t.Parallel()
 		testJavaError(t, `jni_uses_platform_apis: can only be set for modules that set sdk_version`, `
 			android_test {
 				name: "app_platform",
@@ -2217,6 +2302,7 @@
 	})
 
 	t.Run("jni_uses_sdk_apis_error", func(t *testing.T) {
+		t.Parallel()
 		testJavaError(t, `jni_uses_sdk_apis: can only be set for modules that do not set sdk_version`, `
 			android_test {
 				name: "app_sdk",
@@ -2229,6 +2315,7 @@
 }
 
 func TestCertificates(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name                     string
 		bp                       string
@@ -2366,6 +2453,7 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -2381,7 +2469,7 @@
 				}),
 			).RunTestWithBp(t, test.bp)
 
-			foo := result.ModuleForTests("foo", "android_common")
+			foo := result.ModuleForTests(t, "foo", "android_common")
 
 			certificate := foo.Module().(*AndroidApp).certificate
 			android.AssertPathRelativeToTopEquals(t, "certificates key", test.expectedCertificate+".pk8", certificate.Key)
@@ -2403,6 +2491,7 @@
 }
 
 func TestRequestV4SigningFlag(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name     string
 		bp       string
@@ -2447,11 +2536,12 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 			).RunTestWithBp(t, test.bp)
 
-			foo := result.ModuleForTests("foo", "android_common")
+			foo := result.ModuleForTests(t, "foo", "android_common")
 
 			signapk := foo.Output("foo.apk")
 			signFlags := signapk.Args["flags"]
@@ -2461,6 +2551,7 @@
 }
 
 func TestPackageNameOverride(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name                string
 		bp                  string
@@ -2479,7 +2570,7 @@
 			packageNameOverride: "",
 			expected: []string{
 				"out/soong/.intermediates/foo/android_common/foo.apk",
-				"out/soong/target/product/test_device/system/app/foo/foo.apk",
+				"out/target/product/test_device/system/app/foo/foo.apk",
 			},
 		},
 		{
@@ -2495,7 +2586,7 @@
 			expected: []string{
 				// The package apk should be still be the original name for test dependencies.
 				"out/soong/.intermediates/foo/android_common/bar.apk",
-				"out/soong/target/product/test_device/system/app/bar/bar.apk",
+				"out/target/product/test_device/system/app/bar/bar.apk",
 			},
 		},
 		{
@@ -2511,13 +2602,14 @@
 			packageNameOverride: "",
 			expected: []string{
 				"out/soong/.intermediates/foo/android_common/bar.apk",
-				"out/soong/target/product/test_device/system/app/bar/bar.apk",
+				"out/target/product/test_device/system/app/bar/bar.apk",
 			},
 		},
 	}
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -2527,7 +2619,7 @@
 				}),
 			).RunTestWithBp(t, test.bp)
 
-			foo := result.ModuleForTests("foo", "android_common")
+			foo := result.ModuleForTests(t, "foo", "android_common")
 
 			outSoongDir := result.Config.SoongOutDir()
 
@@ -2546,6 +2638,7 @@
 }
 
 func TestInstrumentationTargetOverridden(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app {
 			name: "foo",
@@ -2567,7 +2660,7 @@
 		}),
 	).RunTestWithBp(t, bp)
 
-	bar := result.ModuleForTests("bar", "android_common")
+	bar := result.ModuleForTests(t, "bar", "android_common")
 	res := bar.Output("package-res.apk")
 	aapt2Flags := res.Args["flags"]
 	e := "--rename-instrumentation-target-package org.dandroid.bp"
@@ -2577,6 +2670,7 @@
 }
 
 func TestOverrideAndroidApp(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(
 		t, `
 		android_app {
@@ -2654,7 +2748,7 @@
 			name:             "foo",
 			moduleName:       "foo",
 			variantName:      "android_common",
-			apkPath:          "out/soong/target/product/test_device/system/app/foo/foo.apk",
+			apkPath:          "out/target/product/test_device/system/app/foo/foo.apk",
 			certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 			certSigningFlags: "",
 			overrides:        []string{"qux"},
@@ -2666,7 +2760,7 @@
 			name:             "foo",
 			moduleName:       "bar",
 			variantName:      "android_common_bar",
-			apkPath:          "out/soong/target/product/test_device/system/app/bar/bar.apk",
+			apkPath:          "out/target/product/test_device/system/app/bar/bar.apk",
 			certFlag:         "cert/new_cert.x509.pem cert/new_cert.pk8",
 			certSigningFlags: "--lineage lineage.bin --rotation-min-sdk-version 32",
 			overrides:        []string{"qux", "foo"},
@@ -2678,7 +2772,7 @@
 			name:             "foo",
 			moduleName:       "baz",
 			variantName:      "android_common_baz",
-			apkPath:          "out/soong/target/product/test_device/system/app/baz/baz.apk",
+			apkPath:          "out/target/product/test_device/system/app/baz/baz.apk",
 			certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 			certSigningFlags: "",
 			overrides:        []string{"qux", "foo"},
@@ -2690,7 +2784,7 @@
 			name:             "foo",
 			moduleName:       "baz_no_rename_resources",
 			variantName:      "android_common_baz_no_rename_resources",
-			apkPath:          "out/soong/target/product/test_device/system/app/baz_no_rename_resources/baz_no_rename_resources.apk",
+			apkPath:          "out/target/product/test_device/system/app/baz_no_rename_resources/baz_no_rename_resources.apk",
 			certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 			certSigningFlags: "",
 			overrides:        []string{"qux", "foo"},
@@ -2702,7 +2796,7 @@
 			name:             "foo_no_rename_resources",
 			moduleName:       "baz_base_no_rename_resources",
 			variantName:      "android_common_baz_base_no_rename_resources",
-			apkPath:          "out/soong/target/product/test_device/system/app/baz_base_no_rename_resources/baz_base_no_rename_resources.apk",
+			apkPath:          "out/target/product/test_device/system/app/baz_base_no_rename_resources/baz_base_no_rename_resources.apk",
 			certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 			certSigningFlags: "",
 			overrides:        []string{"qux", "foo_no_rename_resources"},
@@ -2714,7 +2808,7 @@
 			name:             "foo_no_rename_resources",
 			moduleName:       "baz_override_base_rename_resources",
 			variantName:      "android_common_baz_override_base_rename_resources",
-			apkPath:          "out/soong/target/product/test_device/system/app/baz_override_base_rename_resources/baz_override_base_rename_resources.apk",
+			apkPath:          "out/target/product/test_device/system/app/baz_override_base_rename_resources/baz_override_base_rename_resources.apk",
 			certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 			certSigningFlags: "",
 			overrides:        []string{"qux", "foo_no_rename_resources"},
@@ -2724,7 +2818,7 @@
 		},
 	}
 	for _, expected := range expectedVariants {
-		variant := result.ModuleForTests(expected.name, expected.variantName)
+		variant := result.ModuleForTests(t, expected.name, expected.variantName)
 
 		// Check the final apk name
 		variant.Output(expected.apkPath)
@@ -2759,6 +2853,7 @@
 }
 
 func TestOverrideAndroidAppOverrides(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(
 		t, `
 		android_app {
@@ -2808,7 +2903,7 @@
 		},
 	}
 	for _, expected := range expectedVariants {
-		variant := ctx.ModuleForTests(expected.name, expected.variantName)
+		variant := ctx.ModuleForTests(t, expected.name, expected.variantName)
 
 		// Check if the overrides field values are correctly aggregated.
 		mod := variant.Module().(*AndroidApp)
@@ -2817,6 +2912,7 @@
 }
 
 func TestOverrideAndroidAppWithPrebuilt(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(
 		t, `
 		android_app {
@@ -2839,19 +2935,20 @@
 		`)
 
 	// An app that has an override that also has a prebuilt should not be hidden.
-	foo := result.ModuleForTests("foo", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
 	if foo.Module().IsHideFromMake() {
 		t.Errorf("expected foo to have HideFromMake false")
 	}
 
 	// An override that also has a prebuilt should be hidden.
-	barOverride := result.ModuleForTests("foo", "android_common_bar")
+	barOverride := result.ModuleForTests(t, "foo", "android_common_bar")
 	if !barOverride.Module().IsHideFromMake() {
 		t.Errorf("expected bar override variant of foo to have HideFromMake true")
 	}
 }
 
 func TestOverrideAndroidAppStem(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app {
 			name: "foo",
@@ -2891,41 +2988,42 @@
 		{
 			moduleName:  "foo",
 			variantName: "android_common",
-			apkPath:     "out/soong/target/product/test_device/system/app/foo/foo.apk",
+			apkPath:     "out/target/product/test_device/system/app/foo/foo.apk",
 		},
 		{
 			moduleName:  "foo",
 			variantName: "android_common_bar",
-			apkPath:     "out/soong/target/product/test_device/system/app/bar/bar.apk",
+			apkPath:     "out/target/product/test_device/system/app/bar/bar.apk",
 		},
 		{
 			moduleName:  "foo",
 			variantName: "android_common_baz",
-			apkPath:     "out/soong/target/product/test_device/system/app/baz_stem/baz_stem.apk",
+			apkPath:     "out/target/product/test_device/system/app/baz_stem/baz_stem.apk",
 		},
 		{
 			moduleName:  "foo2",
 			variantName: "android_common",
-			apkPath:     "out/soong/target/product/test_device/system/app/foo2_stem/foo2_stem.apk",
+			apkPath:     "out/target/product/test_device/system/app/foo2_stem/foo2_stem.apk",
 		},
 		{
 			moduleName:  "foo2",
 			variantName: "android_common_bar2",
 			// Note that this may cause the duplicate output error.
-			apkPath: "out/soong/target/product/test_device/system/app/foo2_stem/foo2_stem.apk",
+			apkPath: "out/target/product/test_device/system/app/foo2_stem/foo2_stem.apk",
 		},
 		{
 			moduleName:  "foo2",
 			variantName: "android_common_baz2",
-			apkPath:     "out/soong/target/product/test_device/system/app/baz2_stem/baz2_stem.apk",
+			apkPath:     "out/target/product/test_device/system/app/baz2_stem/baz2_stem.apk",
 		},
 	} {
-		variant := ctx.ModuleForTests(expected.moduleName, expected.variantName)
+		variant := ctx.ModuleForTests(t, expected.moduleName, expected.variantName)
 		variant.Output(expected.apkPath)
 	}
 }
 
 func TestOverrideAndroidAppDependency(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app {
 			name: "foo",
@@ -2953,15 +3051,15 @@
 		`)
 
 	// Verify baz, which depends on the overridden module foo, has the correct classpath javac arg.
-	javac := ctx.ModuleForTests("baz", "android_common").Rule("javac")
-	fooTurbine := "out/soong/.intermediates/foo/android_common/turbine-combined/foo.jar"
+	javac := ctx.ModuleForTests(t, "baz", "android_common").Rule("javac")
+	fooTurbine := "out/soong/.intermediates/foo/android_common/turbine/foo.jar"
 	if !strings.Contains(javac.Args["classpath"], fooTurbine) {
 		t.Errorf("baz classpath %v does not contain %q", javac.Args["classpath"], fooTurbine)
 	}
 
 	// Verify qux, which depends on the overriding module bar, has the correct classpath javac arg.
-	javac = ctx.ModuleForTests("qux", "android_common").Rule("javac")
-	barTurbine := "out/soong/.intermediates/foo/android_common_bar/turbine-combined/foo.jar"
+	javac = ctx.ModuleForTests(t, "qux", "android_common").Rule("javac")
+	barTurbine := "out/soong/.intermediates/foo/android_common_bar/turbine/foo.jar"
 	if !strings.Contains(javac.Args["classpath"], barTurbine) {
 		t.Errorf("qux classpath %v does not contain %q", javac.Args["classpath"], barTurbine)
 	}
@@ -3024,10 +3122,10 @@
 		},
 	}
 	for _, expected := range expectedVariants {
-		variant := ctx.ModuleForTests("foo_test", expected.variantName)
+		variant := ctx.ModuleForTests(t, "foo_test", expected.variantName)
 
 		// Check the final apk name
-		variant.Output("out/soong" + expected.apkPath)
+		variant.Output("out" + expected.apkPath)
 
 		// Check if the overrides field values are correctly aggregated.
 		mod := variant.Module().(*AndroidTest)
@@ -3038,7 +3136,7 @@
 
 		// Check if javac classpath has the correct jar file path. This checks instrumentation_for overrides.
 		javac := variant.Rule("javac")
-		turbine := filepath.Join("out", "soong", ".intermediates", "foo", expected.targetVariant, "turbine-combined", "foo.jar")
+		turbine := filepath.Join("out", "soong", ".intermediates", "foo", expected.targetVariant, "turbine", "foo.jar")
 		if !strings.Contains(javac.Args["classpath"], turbine) {
 			t.Errorf("classpath %q does not contain %q", javac.Args["classpath"], turbine)
 		}
@@ -3115,7 +3213,7 @@
 	}
 
 	for _, test := range testCases {
-		variant := ctx.ModuleForTests(test.moduleName, test.variantName)
+		variant := ctx.ModuleForTests(t, test.moduleName, test.variantName)
 		params := variant.MaybeOutput("test_config_fixer/AndroidTest.xml")
 
 		if len(test.expectedFlags) > 0 {
@@ -3161,6 +3259,7 @@
 }
 
 func TestStl(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
 			name: "libjni",
@@ -3203,7 +3302,8 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			app := ctx.ModuleForTests(test.name, "android_common")
+			t.Parallel()
+			app := ctx.ModuleForTests(t, test.name, "android_common")
 			jniLibZip := app.Output("jnilibs.zip")
 			var jnis []string
 			args := strings.Fields(jniLibZip.Args["jarArgs"])
@@ -3380,8 +3480,8 @@
 		}),
 	).RunTestWithBp(t, bp)
 
-	app := result.ModuleForTests("app", "android_common")
-	prebuilt := result.ModuleForTests("prebuilt", "android_common")
+	app := result.ModuleForTests(t, "app", "android_common")
+	prebuilt := result.ModuleForTests(t, "prebuilt", "android_common")
 
 	// Test that implicit dependencies on java_sdk_library instances are passed to the manifest.
 	// These also include explicit `uses_libs`/`optional_uses_libs` entries, as they may be
@@ -3433,6 +3533,7 @@
 }
 
 func TestDexpreoptBcp(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_sdk_library {
 			name: "foo",
@@ -3475,6 +3576,7 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				prepareForJavaTest,
 				PrepareForTestWithJavaSdkLibraryFiles,
@@ -3484,7 +3586,7 @@
 				dexpreopt.FixtureSetPreoptWithUpdatableBcp(test.with),
 			).RunTestWithBp(t, bp)
 
-			app := result.ModuleForTests("app", "android_common")
+			app := result.ModuleForTests(t, "app", "android_common")
 			cmd := app.Rule("dexpreopt").RuleParams.Command
 			bcp := " -Xbootclasspath-locations:" + test.expect + " " // space at the end matters
 			android.AssertStringDoesContain(t, "dexpreopt app bcp", cmd, bcp)
@@ -3493,6 +3595,7 @@
 }
 
 func TestCodelessApp(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name   string
 		bp     string
@@ -3557,9 +3660,10 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			ctx := testApp(t, test.bp)
 
-			foo := ctx.ModuleForTests("foo", "android_common")
+			foo := ctx.ModuleForTests(t, "foo", "android_common")
 			manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
 			if strings.Contains(manifestFixerArgs, "--has-no-code") != test.noCode {
 				t.Errorf("unexpected manifest_fixer args: %q", manifestFixerArgs)
@@ -3569,6 +3673,7 @@
 }
 
 func TestUncompressDex(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name string
 		bp   string
@@ -3656,7 +3761,7 @@
 			}),
 		).RunTestWithBp(t, bp)
 
-		foo := result.ModuleForTests("foo", "android_common")
+		foo := result.ModuleForTests(t, "foo", "android_common")
 		dex := foo.Rule("r8")
 		uncompressedInDexJar := strings.Contains(dex.Args["zipFlags"], "-L 0")
 		aligned := foo.MaybeRule("zipalign").Rule != nil
@@ -3668,10 +3773,13 @@
 
 	for _, tt := range testCases {
 		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
 			t.Run("platform", func(t *testing.T) {
+				t.Parallel()
 				test(t, tt.bp, tt.uncompressedPlatform, false)
 			})
 			t.Run("unbundled", func(t *testing.T) {
+				t.Parallel()
 				test(t, tt.bp, tt.uncompressedUnbundled, true)
 			})
 		})
@@ -3693,6 +3801,7 @@
 }
 
 func TestExportedProguardFlagFiles(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app {
 			name: "foo",
@@ -3738,7 +3847,7 @@
 
 	`)
 
-	m := ctx.ModuleForTests("foo", "android_common")
+	m := ctx.ModuleForTests(t, "foo", "android_common")
 	r8 := m.Rule("java.r8")
 	implicits := r8.Implicits.RelativeToTop().Strings()
 	android.AssertStringListContains(t, "r8 implicits", implicits, "lib1proguard.cfg")
@@ -3754,6 +3863,7 @@
 }
 
 func TestTargetSdkVersionManifestFixer(t *testing.T) {
+	t.Parallel()
 	platform_sdk_codename := "Tiramisu"
 	platform_sdk_version := 33
 	testCases := []struct {
@@ -3806,43 +3916,47 @@
 		},
 	}
 	for _, testCase := range testCases {
-		targetSdkVersionTemplate := ""
-		if testCase.targetSdkVersionInBp != "" {
-			targetSdkVersionTemplate = fmt.Sprintf(`target_sdk_version: "%s",`, testCase.targetSdkVersionInBp)
-		}
-		bp := fmt.Sprintf(`
+		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
+			targetSdkVersionTemplate := ""
+			if testCase.targetSdkVersionInBp != "" {
+				targetSdkVersionTemplate = fmt.Sprintf(`target_sdk_version: "%s",`, testCase.targetSdkVersionInBp)
+			}
+			bp := fmt.Sprintf(`
 			android_app {
 				name: "foo",
 				sdk_version: "current",
 				%s
 			}
 			`, targetSdkVersionTemplate)
-		fixture := android.GroupFixturePreparers(
-			prepareForJavaTest,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				if testCase.platformSdkFinal {
-					variables.Platform_sdk_final = proptools.BoolPtr(true)
-				}
-				// explicitly set platform_sdk_codename to make the test deterministic
-				variables.Platform_sdk_codename = &platform_sdk_codename
-				variables.Platform_sdk_version = &platform_sdk_version
-				variables.Platform_version_active_codenames = []string{platform_sdk_codename}
-				// create a non-empty list if unbundledBuild==true
-				if testCase.unbundledBuild {
-					variables.Unbundled_build_apps = []string{"apex_a", "apex_b"}
-				}
-			}),
-		)
+			fixture := android.GroupFixturePreparers(
+				prepareForJavaTest,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					if testCase.platformSdkFinal {
+						variables.Platform_sdk_final = proptools.BoolPtr(true)
+					}
+					// explicitly set platform_sdk_codename to make the test deterministic
+					variables.Platform_sdk_codename = &platform_sdk_codename
+					variables.Platform_sdk_version = &platform_sdk_version
+					variables.Platform_version_active_codenames = []string{platform_sdk_codename}
+					// create a non-empty list if unbundledBuild==true
+					if testCase.unbundledBuild {
+						variables.Unbundled_build_apps = []string{"apex_a", "apex_b"}
+					}
+				}),
+			)
 
-		result := fixture.RunTestWithBp(t, bp)
-		foo := result.ModuleForTests("foo", "android_common")
+			result := fixture.RunTestWithBp(t, bp)
+			foo := result.ModuleForTests(t, "foo", "android_common")
 
-		manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
-		android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
+			manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
+			android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
+		})
 	}
 }
 
 func TestDefaultAppTargetSdkVersionForUpdatableModules(t *testing.T) {
+	t.Parallel()
 	platform_sdk_codename := "Tiramisu"
 	platform_sdk_version := 33
 	testCases := []struct {
@@ -3898,11 +4012,13 @@
 		},
 	}
 	for _, testCase := range testCases {
-		targetSdkVersionTemplate := ""
-		if testCase.targetSdkVersionInBp != nil {
-			targetSdkVersionTemplate = fmt.Sprintf(`target_sdk_version: "%s",`, *testCase.targetSdkVersionInBp)
-		}
-		bp := fmt.Sprintf(`
+		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
+			targetSdkVersionTemplate := ""
+			if testCase.targetSdkVersionInBp != nil {
+				targetSdkVersionTemplate = fmt.Sprintf(`target_sdk_version: "%s",`, *testCase.targetSdkVersionInBp)
+			}
+			bp := fmt.Sprintf(`
 			android_app {
 				name: "foo",
 				sdk_version: "current",
@@ -3913,30 +4029,32 @@
 			}
 			`, targetSdkVersionTemplate, testCase.updatable, testCase.updatable) // enforce default target sdk version if app is updatable
 
-		fixture := android.GroupFixturePreparers(
-			PrepareForTestWithJavaDefaultModules,
-			android.PrepareForTestWithAllowMissingDependencies,
-			android.PrepareForTestWithAndroidMk,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				// explicitly set following platform variables to make the test deterministic
-				variables.Platform_sdk_final = &testCase.platform_sdk_final
-				variables.Platform_sdk_version = &platform_sdk_version
-				variables.Platform_sdk_codename = &platform_sdk_codename
-				variables.Platform_version_active_codenames = []string{platform_sdk_codename}
-				variables.Unbundled_build = proptools.BoolPtr(true)
-				variables.Unbundled_build_apps = []string{"sampleModule"}
-			}),
-		)
+			fixture := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				android.PrepareForTestWithAllowMissingDependencies,
+				android.PrepareForTestWithAndroidMk,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					// explicitly set following platform variables to make the test deterministic
+					variables.Platform_sdk_final = &testCase.platform_sdk_final
+					variables.Platform_sdk_version = &platform_sdk_version
+					variables.Platform_sdk_codename = &platform_sdk_codename
+					variables.Platform_version_active_codenames = []string{platform_sdk_codename}
+					variables.Unbundled_build = proptools.BoolPtr(true)
+					variables.Unbundled_build_apps = []string{"sampleModule"}
+				}),
+			)
 
-		result := fixture.RunTestWithBp(t, bp)
-		foo := result.ModuleForTests("foo", "android_common")
+			result := fixture.RunTestWithBp(t, bp)
+			foo := result.ModuleForTests(t, "foo", "android_common")
 
-		manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
-		android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+*testCase.targetSdkVersionExpected)
+			manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
+			android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+*testCase.targetSdkVersionExpected)
+		})
 	}
 }
 
 func TestEnforceDefaultAppTargetSdkVersionFlag(t *testing.T) {
+	t.Parallel()
 	platform_sdk_codename := "Tiramisu"
 	platform_sdk_version := 33
 	testCases := []struct {
@@ -3981,8 +4099,10 @@
 		},
 	}
 	for _, testCase := range testCases {
-		errExpected := testCase.expectedError != ""
-		bp := fmt.Sprintf(`
+		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
+			errExpected := testCase.expectedError != ""
+			bp := fmt.Sprintf(`
 			android_app {
 				name: "foo",
 				enforce_default_target_sdk_version: %t,
@@ -3993,35 +4113,37 @@
 			}
 			`, testCase.enforceDefaultTargetSdkVersion, testCase.targetSdkVersionInBp, testCase.updatable)
 
-		fixture := android.GroupFixturePreparers(
-			PrepareForTestWithJavaDefaultModules,
-			android.PrepareForTestWithAllowMissingDependencies,
-			android.PrepareForTestWithAndroidMk,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				// explicitly set following platform variables to make the test deterministic
-				variables.Platform_sdk_final = &testCase.platform_sdk_final
-				variables.Platform_sdk_version = &platform_sdk_version
-				variables.Platform_sdk_codename = &platform_sdk_codename
-				variables.Unbundled_build = proptools.BoolPtr(true)
-				variables.Unbundled_build_apps = []string{"sampleModule"}
-			}),
-		)
+			fixture := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				android.PrepareForTestWithAllowMissingDependencies,
+				android.PrepareForTestWithAndroidMk,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					// explicitly set following platform variables to make the test deterministic
+					variables.Platform_sdk_final = &testCase.platform_sdk_final
+					variables.Platform_sdk_version = &platform_sdk_version
+					variables.Platform_sdk_codename = &platform_sdk_codename
+					variables.Unbundled_build = proptools.BoolPtr(true)
+					variables.Unbundled_build_apps = []string{"sampleModule"}
+				}),
+			)
 
-		errorHandler := android.FixtureExpectsNoErrors
-		if errExpected {
-			errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(testCase.expectedError)
-		}
-		result := fixture.ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, bp)
+			errorHandler := android.FixtureExpectsNoErrors
+			if errExpected {
+				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(testCase.expectedError)
+			}
+			result := fixture.ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, bp)
 
-		if !errExpected {
-			foo := result.ModuleForTests("foo", "android_common")
-			manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
-			android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
-		}
+			if !errExpected {
+				foo := result.ModuleForTests(t, "foo", "android_common")
+				manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
+				android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
+			}
+		})
 	}
 }
 
 func TestEnforceDefaultAppTargetSdkVersionFlagForTests(t *testing.T) {
+	t.Parallel()
 	platform_sdk_codename := "Tiramisu"
 	platform_sdk_version := 33
 	testCases := []struct {
@@ -4054,8 +4176,10 @@
 		},
 	}
 	for _, testCase := range testCases {
-		errExpected := testCase.expectedError != ""
-		bp := fmt.Sprintf(`
+		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
+			errExpected := testCase.expectedError != ""
+			bp := fmt.Sprintf(`
 			android_test {
 				name: "foo",
 				enforce_default_target_sdk_version: %t,
@@ -4064,35 +4188,37 @@
 			}
 		`, testCase.enforceDefaultTargetSdkVersion, testCase.targetSdkVersionInBp)
 
-		fixture := android.GroupFixturePreparers(
-			PrepareForTestWithJavaDefaultModules,
-			android.PrepareForTestWithAllowMissingDependencies,
-			android.PrepareForTestWithAndroidMk,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				// explicitly set following platform variables to make the test deterministic
-				variables.Platform_sdk_final = &testCase.platform_sdk_final
-				variables.Platform_sdk_version = &platform_sdk_version
-				variables.Platform_sdk_codename = &platform_sdk_codename
-				variables.Unbundled_build = proptools.BoolPtr(true)
-				variables.Unbundled_build_apps = []string{"sampleModule"}
-			}),
-		)
+			fixture := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				android.PrepareForTestWithAllowMissingDependencies,
+				android.PrepareForTestWithAndroidMk,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					// explicitly set following platform variables to make the test deterministic
+					variables.Platform_sdk_final = &testCase.platform_sdk_final
+					variables.Platform_sdk_version = &platform_sdk_version
+					variables.Platform_sdk_codename = &platform_sdk_codename
+					variables.Unbundled_build = proptools.BoolPtr(true)
+					variables.Unbundled_build_apps = []string{"sampleModule"}
+				}),
+			)
 
-		errorHandler := android.FixtureExpectsNoErrors
-		if errExpected {
-			errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(testCase.expectedError)
-		}
-		result := fixture.ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, bp)
+			errorHandler := android.FixtureExpectsNoErrors
+			if errExpected {
+				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(testCase.expectedError)
+			}
+			result := fixture.ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, bp)
 
-		if !errExpected {
-			foo := result.ModuleForTests("foo", "android_common")
-			manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
-			android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
-		}
+			if !errExpected {
+				foo := result.ModuleForTests(t, "foo", "android_common")
+				manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
+				android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
+			}
+		})
 	}
 }
 
 func TestAppMissingCertificateAllowMissingDependencies(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.PrepareForTestWithAllowMissingDependencies,
@@ -4113,7 +4239,7 @@
 			sdk_version: "current",
 		}`)
 
-	foo := result.ModuleForTests("foo", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
 	fooApk := foo.Output("foo.apk")
 	if fooApk.Rule != android.ErrorRule {
 		t.Fatalf("expected ErrorRule for foo.apk, got %s", fooApk.Rule.String())
@@ -4122,6 +4248,7 @@
 }
 
 func TestAppIncludesJniPackages(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -4184,7 +4311,8 @@
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			app := ctx.ModuleForTests(tc.name, "android_common")
+			t.Parallel()
+			app := ctx.ModuleForTests(t, tc.name, "android_common")
 
 			outputFile := "jnilibs.zip"
 			jniOutputLibZip := app.MaybeOutput(outputFile)
@@ -4208,6 +4336,7 @@
 }
 
 func TestTargetSdkVersionMtsTests(t *testing.T) {
+	t.Parallel()
 	platformSdkCodename := "Tiramisu"
 	android_test := "android_test"
 	android_test_helper_app := "android_test_helper_app"
@@ -4263,14 +4392,18 @@
 		}),
 	)
 	for _, testCase := range testCases {
-		result := fixture.RunTestWithBp(t, fmt.Sprintf(bpTemplate, testCase.moduleType, testCase.targetSdkVersionInBp, testCase.testSuites))
-		mytest := result.ModuleForTests("mytest", "android_common")
-		manifestFixerArgs := mytest.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
-		android.AssertStringDoesContain(t, testCase.desc, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
+		t.Run(testCase.desc, func(t *testing.T) {
+			t.Parallel()
+			result := fixture.RunTestWithBp(t, fmt.Sprintf(bpTemplate, testCase.moduleType, testCase.targetSdkVersionInBp, testCase.testSuites))
+			mytest := result.ModuleForTests(t, "mytest", "android_common")
+			manifestFixerArgs := mytest.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
+			android.AssertStringDoesContain(t, testCase.desc, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
+		})
 	}
 }
 
 func TestPrivappAllowlist(t *testing.T) {
+	t.Parallel()
 	testJavaError(t, "privileged must be set in order to use privapp_allowlist", `
 		android_app {
 			name: "foo",
@@ -4296,8 +4429,8 @@
 		}
 		`,
 	)
-	app := result.ModuleForTests("foo", "android_common")
-	overrideApp := result.ModuleForTests("foo", "android_common_bar")
+	app := result.ModuleForTests(t, "foo", "android_common")
+	overrideApp := result.ModuleForTests(t, "foo", "android_common_bar")
 
 	// verify that privapp allowlist is created for override apps
 	overrideApp.Output("out/soong/.intermediates/foo/android_common_bar/privapp_allowlist_com.google.android.foo.xml")
@@ -4308,11 +4441,12 @@
 	}
 
 	// verify that permissions are copied to device
-	app.Output("out/soong/target/product/test_device/system/etc/permissions/foo.xml")
-	overrideApp.Output("out/soong/target/product/test_device/system/etc/permissions/bar.xml")
+	app.Output("out/target/product/test_device/system/etc/permissions/foo.xml")
+	overrideApp.Output("out/target/product/test_device/system/etc/permissions/bar.xml")
 }
 
 func TestPrivappAllowlistAndroidMk(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.PrepareForTestWithAndroidMk,
@@ -4333,8 +4467,8 @@
 		}
 		`,
 	)
-	baseApp := result.ModuleForTests("foo", "android_common")
-	overrideApp := result.ModuleForTests("foo", "android_common_bar")
+	baseApp := result.ModuleForTests(t, "foo", "android_common")
+	overrideApp := result.ModuleForTests(t, "foo", "android_common_bar")
 
 	baseAndroidApp := baseApp.Module().(*AndroidApp)
 	baseEntries := android.AndroidMkEntriesForTest(t, result.TestContext, baseAndroidApp)[0]
@@ -4392,6 +4526,7 @@
 }
 
 func TestAppFlagsPackages(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		android.FixtureMergeMockFs(
@@ -4429,7 +4564,7 @@
 		}
 	`)
 
-	foo := ctx.ModuleForTests("foo", "android_common")
+	foo := ctx.ModuleForTests(t, "foo", "android_common")
 
 	// android_app module depends on aconfig_declarations listed in flags_packages
 	android.AssertBoolEquals(t, "foo expected to depend on bar", true,
@@ -4456,6 +4591,7 @@
 }
 
 func TestAppFlagsPackagesPropagation(t *testing.T) {
+	t.Parallel()
 	ctx := testApp(t, `
 		aconfig_declarations {
 			name: "foo",
@@ -4513,7 +4649,7 @@
 		}
 	`)
 
-	bazApp := ctx.ModuleForTests("baz_app", "android_common")
+	bazApp := ctx.ModuleForTests(t, "baz_app", "android_common")
 
 	// android_app module depends on aconfig_declarations listed in flags_packages
 	// and that of static libs, but not libs
@@ -4533,6 +4669,7 @@
 
 // Test that dexpreopt is disabled if an optional_uses_libs exists, but does not provide an implementation.
 func TestNoDexpreoptOptionalUsesLibDoesNotHaveImpl(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_sdk_library_import {
 			name: "sdklib_noimpl",
@@ -4550,7 +4687,7 @@
 		}
 	`
 	result := prepareForJavaTest.RunTestWithBp(t, bp)
-	dexpreopt := result.ModuleForTests("app", "android_common").MaybeRule("dexpreopt").Rule
+	dexpreopt := result.ModuleForTests(t, "app", "android_common").MaybeRule("dexpreopt").Rule
 	android.AssertBoolEquals(t, "dexpreopt should be disabled if optional_uses_libs does not have an implementation", true, dexpreopt == nil)
 }
 
@@ -4592,7 +4729,77 @@
 	assertTestOnlyAndTopLevel(t, ctx, expectedTestOnly, expectedTopLevel)
 }
 
+func TestTestConfigTemplate(t *testing.T) {
+	t.Parallel()
+	ctx := android.GroupFixturePreparers(
+		prepareForJavaTest,
+	).RunTestWithBp(t, `
+		android_test {
+			name: "android-test",
+			test_config_template: "AndroidTestTemplate.xml",
+			test_options: {
+				tradefed_options: [
+					{
+						name: "name1",
+						key: "key1",
+						value: "value1",
+					},
+					{
+						name: "name2",
+						key: "key2",
+						value: "value2",
+					},
+				],
+				test_runner_options: [
+					{
+						name: "name3",
+						key: "key3",
+						value: "value3",
+					},
+					{
+						name: "name4",
+						key: "key4",
+						value: "value4",
+					},
+				],
+			},
+		}
+	`)
+	type option struct {
+		name  string
+		key   string
+		value string
+	}
+	re := regexp.MustCompile(`<option name="(.*)" key="(.*)" value="(.*)" />`)
+	parse_options := func(optionsString string) []option {
+		lines := strings.Split(optionsString, `\n`)
+		var ret []option
+		for _, l := range lines {
+			sm := re.FindStringSubmatch(l)
+			if sm == nil {
+				continue
+			}
+			ret = append(ret, option{sm[1], sm[2], sm[3]})
+		}
+		return ret
+	}
+	rule := ctx.ModuleForTests(t, "android-test", "android_common").Rule("autogenInstrumentationTest")
+	android.AssertSameArray(t, "extraConfigs mismatch",
+		[]option{
+			{"name1", "key1", "value1"},
+			{"name2", "key2", "value2"},
+		},
+		parse_options(rule.Args["extraConfigs"]))
+	android.AssertSameArray(t, "extraTestRunnerConfigs mismatch",
+		[]option{
+			{"name3", "key3", "value3"},
+			{"name4", "key4", "value4"},
+		},
+		parse_options(rule.Args["extraTestRunnerConfigs"]))
+}
+
 func TestAppStem(t *testing.T) {
+	t.Parallel()
 	ctx := testApp(t, `
 				android_app {
 					name: "foo",
@@ -4601,7 +4808,7 @@
 					sdk_version: "current",
 				}`)
 
-	foo := ctx.ModuleForTests("foo", "android_common")
+	foo := ctx.ModuleForTests(t, "foo", "android_common")
 
 	outputs := fmt.Sprint(foo.AllOutputs())
 	if !strings.Contains(outputs, "foo-new.apk") {
@@ -4610,6 +4817,7 @@
 }
 
 func TestAppMinSdkVersionOverride(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -4626,8 +4834,8 @@
 			min_sdk_version: "33",
 		}
 	`)
-	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
-	fooOverride := result.ModuleForTests("com.android.foo", "android_common_com.android.go.foo").Rule("manifestFixer")
+	foo := result.ModuleForTests(t, "com.android.foo", "android_common").Rule("manifestFixer")
+	fooOverride := result.ModuleForTests(t, "com.android.foo", "android_common_com.android.go.foo").Rule("manifestFixer")
 
 	android.AssertStringDoesContain(t,
 		"com.android.foo: expected manifest fixer to set minSdkVersion to T",
@@ -4643,6 +4851,7 @@
 }
 
 func TestNotApplyDefaultUpdatableModuleVersion(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -4653,7 +4862,7 @@
 			min_sdk_version: "31",
 		}
 	`)
-	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
+	foo := result.ModuleForTests(t, "com.android.foo", "android_common").Rule("manifestFixer")
 	android.AssertStringDoesNotContain(t,
 		"com.android.foo: expected manifest fixer to not set override-placeholder-version",
 		foo.BuildParams.Args["args"],
@@ -4662,6 +4871,7 @@
 }
 
 func TestNotApplyOverrideApexManifestDefaultVersion(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.FixtureMergeEnv(map[string]string{
@@ -4675,7 +4885,7 @@
 			min_sdk_version: "31",
 		}
 	`)
-	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
+	foo := result.ModuleForTests(t, "com.android.foo", "android_common").Rule("manifestFixer")
 	android.AssertStringDoesNotContain(t,
 		"com.android.foo: expected manifest fixer to not set override-placeholder-version",
 		foo.BuildParams.Args["args"],
@@ -4684,6 +4894,7 @@
 }
 
 func TestResourcesWithFlagDirectories(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.FixtureMergeMockFs(android.MockFS{
@@ -4702,7 +4913,7 @@
 			],
 		}
 	`)
-	fooModule := result.ModuleForTests("foo", "android_common")
+	fooModule := result.ModuleForTests(t, "foo", "android_common")
 	compileOutputPaths := fooModule.Rule("aapt2Compile").Outputs.Strings()
 
 	android.AssertStringListContains(
@@ -4788,17 +4999,20 @@
 		},
 	}
 	for _, tc := range testCases {
-		result := android.GroupFixturePreparers(
-			PrepareForTestWithJavaDefaultModules,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.EnforceRROTargets = []string{"*"}
-			}),
-			android.OptionalFixturePreparer(tc.preparer),
-		).RunTestWithBp(t, bp)
-		vendorOverlayApk := result.ModuleForTests("foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").MaybeOutput("foo__test_product__auto_generated_rro_vendor.apk")
-		android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, vendorOverlayApk.Rule != nil)
-		overrideVendorOverlayApk := result.ModuleForTests("override_foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").MaybeOutput("override_foo__test_product__auto_generated_rro_vendor.apk")
-		android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, overrideVendorOverlayApk.Rule != nil)
+		t.Run(tc.desc, func(t *testing.T) {
+			t.Parallel()
+			result := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.EnforceRROTargets = []string{"*"}
+				}),
+				android.OptionalFixturePreparer(tc.preparer),
+			).RunTestWithBp(t, bp)
+			vendorOverlayApk := result.ModuleForTests(t, "foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").MaybeOutput("foo__test_product__auto_generated_rro_vendor.apk")
+			android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, vendorOverlayApk.Rule != nil)
+			overrideVendorOverlayApk := result.ModuleForTests(t, "override_foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").MaybeOutput("override_foo__test_product__auto_generated_rro_vendor.apk")
+			android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, overrideVendorOverlayApk.Rule != nil)
+		})
 	}
 }
 
@@ -4855,22 +5069,25 @@
 		},
 	}
 	for _, tc := range testCases {
-		result := android.GroupFixturePreparers(
-			PrepareForTestWithJavaDefaultModules,
-			android.PrepareForTestWithSoongConfigModuleBuildComponents,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.EnforceRROTargets = []string{"*"}
-			}),
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.DeviceResourceOverlays = []string{"device/company/test_product"}
-			}),
-			android.MockFS{
-				"res/foo.xml": nil,
-				"device/company/test_product/res/foo.xml": nil,
-			}.AddToFixture(),
-			android.OptionalFixturePreparer(tc.preparer),
-		).RunTestWithBp(t, bp)
-		overrideVendorOverlayApk := result.ModuleForTests("override_foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").Module().(*AutogenRuntimeResourceOverlay)
-		android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, overrideVendorOverlayApk.exportPackage != nil)
+		t.Run(tc.desc, func(t *testing.T) {
+			t.Parallel()
+			result := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				android.PrepareForTestWithSoongConfigModuleBuildComponents,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.EnforceRROTargets = []string{"*"}
+				}),
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.DeviceResourceOverlays = []string{"device/company/test_product"}
+				}),
+				android.MockFS{
+					"res/foo.xml": nil,
+					"device/company/test_product/res/foo.xml": nil,
+				}.AddToFixture(),
+				android.OptionalFixturePreparer(tc.preparer),
+			).RunTestWithBp(t, bp)
+			overrideVendorOverlayApk := result.ModuleForTests(t, "override_foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").Module().(*AutogenRuntimeResourceOverlay)
+			android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, overrideVendorOverlayApk.exportPackage != nil)
+		})
 	}
 }
diff --git a/java/base.go b/java/base.go
index c0ac4ab..8aa0109 100644
--- a/java/base.go
+++ b/java/base.go
@@ -60,6 +60,9 @@
 	// This is most useful in the arch/multilib variants to remove non-common files
 	Exclude_srcs []string `android:"path,arch_variant"`
 
+	// list of Kotlin source files that should excluded from the list of common_srcs.
+	Exclude_common_srcs []string `android:"path,arch_variant"`
+
 	// list of directories containing Java resources
 	Java_resource_dirs []string `android:"arch_variant"`
 
@@ -87,6 +90,10 @@
 	// list of module-specific flags that will be used for kotlinc compiles
 	Kotlincflags []string `android:"arch_variant"`
 
+	// Kotlin language version to target. Currently only 1.9 and 2 are supported.
+	// See kotlinc's `-language-version` flag.
+	Kotlin_lang_version *string
+
 	// list of java libraries that will be in the classpath
 	Libs []string `android:"arch_variant"`
 
@@ -106,6 +113,10 @@
 	// if not blank, used as prefix to generate repackage rule
 	Jarjar_prefix *string
 
+	// Number of shards for jarjar. It needs to be an integer represented as a string.
+	// TODO(b/383559945) change it to int, once Configurable supports the type.
+	Jarjar_shards proptools.Configurable[string]
+
 	// If not blank, set the java version passed to javac as -source and -target
 	Java_version *string
 
@@ -362,13 +373,13 @@
 	e.initSdkLibraryComponent(module)
 }
 
-// Module/Import's DepIsInSameApex(...) delegates to this method.
+// Module/Import's OutgoingDepIsInSameApex(...) delegates to this method.
 //
-// This cannot implement DepIsInSameApex(...) directly as that leads to ambiguity with
+// This cannot implement OutgoingDepIsInSameApex(...) directly as that leads to ambiguity with
 // the one provided by ApexModuleBase.
-func (e *embeddableInModuleAndImport) depIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+func depIsInSameApex(tag blueprint.DependencyTag) bool {
 	// dependencies other than the static linkage are all considered crossing APEX boundary
-	if staticLibTag == ctx.OtherModuleDependencyTag(dep) {
+	if tag == staticLibTag {
 		return true
 	}
 	return false
@@ -618,7 +629,7 @@
 			return nil
 		}
 		if info.SdkVersion.Kind == android.SdkCorePlatform {
-			if useLegacyCorePlatformApi(ctx, android.OtherModuleProviderOrDefault(ctx, module, android.CommonModuleInfoKey).BaseModuleName) {
+			if useLegacyCorePlatformApi(ctx, android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider).BaseModuleName) {
 				return fmt.Errorf("non stable SDK %v - uses legacy core platform", info.SdkVersion)
 			} else {
 				// Treat stable core platform as stable.
@@ -645,14 +656,17 @@
 
 	// Make sure this module doesn't statically link to modules with lower-ranked SDK link type.
 	// See rank() for details.
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(module)
-		switch module.(type) {
-		// TODO(satayev): cover other types as well, e.g. imports
-		case *Library, *AndroidLibrary:
+		libInfo, isJavaLibrary := android.OtherModuleProvider(ctx, module, JavaLibraryInfoProvider)
+		_, isAndroidLibrary := android.OtherModuleProvider(ctx, module, AndroidLibraryInfoProvider)
+		_, isJavaAconfigLibrary := android.OtherModuleProvider(ctx, module, android.CodegenInfoProvider)
+		// Exclude java_aconfig_library modules to maintain consistency with existing behavior.
+		if (isJavaLibrary && !libInfo.Prebuilt && !isJavaAconfigLibrary) || isAndroidLibrary {
+			// TODO(satayev): cover other types as well, e.g. imports
 			switch tag {
 			case bootClasspathTag, sdkLibTag, libTag, staticLibTag, java9LibTag:
-				j.checkSdkLinkType(ctx, module.(moduleWithSdkDep), tag.(dependencyTag))
+				j.checkSdkLinkType(ctx, module)
 			}
 		}
 	})
@@ -835,13 +849,18 @@
 }
 
 func (j *Module) AvailableFor(what string) bool {
-	if what == android.AvailableToPlatform && Bool(j.deviceProperties.Hostdex) {
+	return android.CheckAvailableForApex(what, j.ApexAvailableFor())
+}
+
+func (j *Module) ApexAvailableFor() []string {
+	list := j.ApexModuleBase.ApexAvailable()
+	if Bool(j.deviceProperties.Hostdex) {
 		// Exception: for hostdex: true libraries, the platform variant is created
 		// even if it's not marked as available to platform. In that case, the platform
 		// variant is used only for the hostdex and not installed to the device.
-		return true
+		list = append(list, android.AvailableToPlatform)
 	}
-	return j.ApexModuleBase.AvailableFor(what)
+	return android.FirstUniqueStrings(list)
 }
 
 func (j *Module) staticLibs(ctx android.BaseModuleContext) []string {
@@ -869,6 +888,10 @@
 	// Add dependency on libraries that provide additional hidden api annotations.
 	ctx.AddVariationDependencies(nil, hiddenApiAnnotationsTag, j.properties.Hiddenapi_additional_annotations...)
 
+	// Add dependency on (soft) downstream libs from which to trace references during optimization.
+	traceRefs := j.dexProperties.Optimize.Trace_references_from.GetOrDefault(ctx, []string{})
+	ctx.AddVariationDependencies(nil, traceReferencesTag, traceRefs...)
+
 	// 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 {
@@ -922,7 +945,7 @@
 
 	if j.useCompose(ctx) {
 		ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), kotlinPluginTag,
-			"androidx.compose.compiler_compiler-hosted-plugin")
+			"kotlin-compose-compiler-plugin")
 	}
 }
 
@@ -1141,7 +1164,7 @@
 	j.properties.Generated_srcjars = append(j.properties.Generated_srcjars, path)
 }
 
-func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspathJars, extraCombinedJars, extraDepCombinedJars android.Paths) {
+func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspathJars, extraCombinedJars, extraDepCombinedJars android.Paths) *JavaInfo {
 	// Auto-propagating jarjar rules
 	jarjarProviderData := j.collectJarJarRules(ctx)
 	if jarjarProviderData != nil {
@@ -1182,7 +1205,7 @@
 		flags = protoFlags(ctx, &j.properties, &j.protoProperties, flags)
 	}
 
-	kotlinCommonSrcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Common_srcs, nil)
+	kotlinCommonSrcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Common_srcs, j.properties.Exclude_common_srcs)
 	if len(kotlinCommonSrcFiles.FilterOutByExt(".kt")) > 0 {
 		ctx.PropertyErrorf("common_srcs", "common_srcs must be .kt files")
 	}
@@ -1229,7 +1252,6 @@
 	uniqueSrcFiles = append(uniqueSrcFiles, uniqueJavaFiles...)
 	uniqueSrcFiles = append(uniqueSrcFiles, uniqueKtFiles...)
 	j.uniqueSrcFiles = uniqueSrcFiles
-	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: uniqueSrcFiles.Strings()})
 
 	// We don't currently run annotation processors in turbine, which means we can't use turbine
 	// generated header jars when an annotation processor that generates API is enabled.  One
@@ -1265,7 +1287,7 @@
 		localHeaderJars, combinedHeaderJarFile := j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName,
 			extraCombinedJars)
 
-		combinedHeaderJarFile, jarjared := j.jarjarIfNecessary(ctx, combinedHeaderJarFile, jarName, "turbine")
+		combinedHeaderJarFile, jarjared := j.jarjarIfNecessary(ctx, combinedHeaderJarFile, jarName, "turbine", false)
 		if jarjared {
 			localHeaderJars = android.Paths{combinedHeaderJarFile}
 			transitiveStaticLibsHeaderJars = nil
@@ -1276,22 +1298,19 @@
 			transitiveStaticLibsHeaderJars = nil
 		}
 		if ctx.Failed() {
-			return
+			return nil
 		}
 		j.headerJarFile = combinedHeaderJarFile
 
-		if ctx.Config().UseTransitiveJarsInClasspath() {
-			if len(localHeaderJars) > 0 {
-				ctx.CheckbuildFile(localHeaderJars...)
-			} else {
-				// There are no local sources or resources in this module, so there is nothing to checkbuild.
-				ctx.UncheckedModule()
-			}
+		if len(localHeaderJars) > 0 {
+			ctx.CheckbuildFile(localHeaderJars...)
 		} else {
-			ctx.CheckbuildFile(j.headerJarFile)
+			// There are no local sources or resources in this module, so there is nothing to checkbuild.
+			ctx.UncheckedModule()
 		}
 
-		android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+		j.outputFile = j.headerJarFile
+		return &JavaInfo{
 			HeaderJars:                          android.PathsIfNonNil(j.headerJarFile),
 			LocalHeaderJars:                     localHeaderJars,
 			TransitiveStaticLibsHeaderJars:      depset.New(depset.PREORDER, localHeaderJars, transitiveStaticLibsHeaderJars),
@@ -1304,10 +1323,7 @@
 			StubsLinkType:                       j.stubsLinkType,
 			AconfigIntermediateCacheOutputPaths: deps.aconfigProtoFiles,
 			SdkVersion:                          j.SdkVersion(ctx),
-		})
-
-		j.outputFile = j.headerJarFile
-		return
+		}
 	}
 
 	if srcFiles.HasExt(".kt") {
@@ -1320,6 +1336,26 @@
 		kotlincFlags := j.properties.Kotlincflags
 		CheckKotlincFlags(ctx, kotlincFlags)
 
+		// Available kotlin versions can be found at
+		// https://github.com/JetBrains/kotlin/blob/master/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt#L560
+		// in the `LanguageVersion` class.
+		// For now, avoid targeting language versions directly, as we'd like to kee our source
+		// code version aligned as much as possible. Ideally, after defaulting to "2", we
+		// can remove the "1.9" option entirely, or at least make it emit a warning.
+		kotlin_default_lang_version := "1.9"
+		if build_flag_lang_version, ok := ctx.Config().GetBuildFlag("RELEASE_KOTLIN_LANG_VERSION"); ok {
+			kotlin_default_lang_version = build_flag_lang_version
+		}
+		kotlin_lang_version := proptools.StringDefault(j.properties.Kotlin_lang_version, kotlin_default_lang_version)
+		switch kotlin_lang_version {
+		case "1.9":
+			kotlincFlags = append(kotlincFlags, "-language-version 1.9")
+		case "2":
+			kotlincFlags = append(kotlincFlags, "-Xsuppress-version-warnings", "-Xconsistent-data-class-copy-visibility")
+		default:
+			ctx.PropertyErrorf("kotlin_lang_version", "Must be one of `1.9` or `2`")
+		}
+
 		// Workaround for KT-46512
 		kotlincFlags = append(kotlincFlags, "-Xsam-conversions=class")
 
@@ -1364,7 +1400,7 @@
 		kotlinHeaderJar := android.PathForModuleOut(ctx, "kotlin_headers", jarName)
 		j.kotlinCompile(ctx, kotlinJar, kotlinHeaderJar, uniqueSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
 		if ctx.Failed() {
-			return
+			return nil
 		}
 
 		kotlinJarPath, _ := j.repackageFlagsIfNecessary(ctx, kotlinJar, jarName, "kotlinc")
@@ -1401,7 +1437,7 @@
 		shardingHeaderJars = localHeaderJars
 
 		var jarjared bool
-		j.headerJarFile, jarjared = j.jarjarIfNecessary(ctx, combinedHeaderJarFile, jarName, "turbine")
+		j.headerJarFile, jarjared = j.jarjarIfNecessary(ctx, combinedHeaderJarFile, jarName, "turbine", false)
 		if jarjared {
 			// jarjar modifies transitive static dependencies, use the combined header jar and drop the transitive
 			// static libs header jars.
@@ -1434,20 +1470,27 @@
 			// build.
 			flags = enableErrorproneFlags(flags)
 		} else if hasErrorproneableFiles && ctx.Config().RunErrorProne() && j.properties.Errorprone.Enabled == nil {
-			// Otherwise, if the RUN_ERROR_PRONE environment variable is set, create
-			// a new jar file just for compiling with the errorprone compiler to.
-			// This is because we don't want to cause the java files to get completely
-			// rebuilt every time the state of the RUN_ERROR_PRONE variable changes.
-			// We also don't want to run this if errorprone is enabled by default for
-			// this module, or else we could have duplicated errorprone messages.
-			errorproneFlags := enableErrorproneFlags(flags)
-			errorprone := android.PathForModuleOut(ctx, "errorprone", jarName)
-			errorproneAnnoSrcJar := android.PathForModuleOut(ctx, "errorprone", "anno.srcjar")
+			if ctx.Config().RunErrorProneInline() {
+				// On CI, we're not going to toggle back/forth between errorprone and non-errorprone
+				// builds, so it's faster if we don't compile the module twice and instead always
+				// compile the module with errorprone.
+				flags = enableErrorproneFlags(flags)
+			} else {
+				// Otherwise, if the RUN_ERROR_PRONE environment variable is set, create
+				// a new jar file just for compiling with the errorprone compiler to.
+				// This is because we don't want to cause the java files to get completely
+				// rebuilt every time the state of the RUN_ERROR_PRONE variable changes.
+				// We also don't want to run this if errorprone is enabled by default for
+				// this module, or else we could have duplicated errorprone messages.
+				errorproneFlags := enableErrorproneFlags(flags)
+				errorprone := android.PathForModuleOut(ctx, "errorprone", jarName)
+				errorproneAnnoSrcJar := android.PathForModuleOut(ctx, "errorprone", "anno.srcjar")
 
-			transformJavaToClasses(ctx, errorprone, -1, uniqueJavaFiles, srcJars, errorproneAnnoSrcJar, errorproneFlags, nil,
-				"errorprone", "errorprone")
+				transformJavaToClasses(ctx, errorprone, -1, uniqueJavaFiles, srcJars, errorproneAnnoSrcJar, errorproneFlags, nil,
+					"errorprone", "errorprone")
 
-			extraJarDeps = append(extraJarDeps, errorprone)
+				extraJarDeps = append(extraJarDeps, errorprone)
+			}
 		}
 
 		if enableSharding {
@@ -1484,7 +1527,7 @@
 			localImplementationJars = append(localImplementationJars, classes)
 		}
 		if ctx.Failed() {
-			return
+			return nil
 		}
 	}
 
@@ -1524,7 +1567,7 @@
 		resourceJar := android.PathForModuleOut(ctx, "res", jarName)
 		TransformResourcesToJar(ctx, resourceJar, resArgs, resDeps)
 		if ctx.Failed() {
-			return
+			return nil
 		}
 		localResourceJars = append(localResourceJars, resourceJar)
 	}
@@ -1561,12 +1604,7 @@
 	completeStaticLibsResourceJars := depset.New(depset.PREORDER, localResourceJars, deps.transitiveStaticLibsResourceJars)
 
 	var combinedResourceJar android.Path
-	var resourceJars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		resourceJars = completeStaticLibsResourceJars.ToList()
-	} else {
-		resourceJars = append(slices.Clone(localResourceJars), deps.staticResourceJars...)
-	}
+	resourceJars := completeStaticLibsResourceJars.ToList()
 	if len(resourceJars) == 1 {
 		combinedResourceJar = resourceJars[0]
 	} else if len(resourceJars) > 0 {
@@ -1587,12 +1625,7 @@
 
 	completeStaticLibsImplementationJars := depset.New(depset.PREORDER, localImplementationJars, deps.transitiveStaticLibsImplementationJars)
 
-	var jars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		jars = completeStaticLibsImplementationJars.ToList()
-	} else {
-		jars = append(slices.Clone(localImplementationJars), deps.staticJars...)
-	}
+	jars := completeStaticLibsImplementationJars.ToList()
 
 	jars = append(jars, extraDepCombinedJars...)
 
@@ -1628,7 +1661,7 @@
 	}
 
 	// jarjar implementation jar if necessary
-	jarjarFile, jarjarred := j.jarjarIfNecessary(ctx, outputFile, jarName, "")
+	jarjarFile, jarjarred := j.jarjarIfNecessary(ctx, outputFile, jarName, "", true)
 	if jarjarred {
 		localImplementationJars = android.Paths{jarjarFile}
 		completeStaticLibsImplementationJars = depset.New(depset.PREORDER, localImplementationJars, nil)
@@ -1637,7 +1670,7 @@
 
 	// jarjar resource jar if necessary
 	if combinedResourceJar != nil {
-		resourceJarJarFile, jarjarred := j.jarjarIfNecessary(ctx, combinedResourceJar, jarName, "resource")
+		resourceJarJarFile, jarjarred := j.jarjarIfNecessary(ctx, combinedResourceJar, jarName, "resource", false)
 		combinedResourceJar = resourceJarJarFile
 		if jarjarred {
 			localResourceJars = android.Paths{resourceJarJarFile}
@@ -1646,7 +1679,7 @@
 	}
 
 	if ctx.Failed() {
-		return
+		return nil
 	}
 
 	if j.ravenizer.enabled {
@@ -1710,7 +1743,7 @@
 		CheckJarPackages(ctx, pkgckFile, outputFile, j.properties.Permitted_packages)
 
 		if ctx.Failed() {
-			return
+			return nil
 		}
 	}
 
@@ -1723,7 +1756,7 @@
 		headerJarFile := android.PathForModuleOut(ctx, "javac-header", jarName)
 		convertImplementationJarToHeaderJar(ctx, j.implementationJarFile, headerJarFile)
 		j.headerJarFile = headerJarFile
-		if len(localImplementationJars) == 1 && ctx.Config().UseTransitiveJarsInClasspath() {
+		if len(localImplementationJars) == 1 {
 			localHeaderJarFile := android.PathForModuleOut(ctx, "local-javac-header", jarName)
 			convertImplementationJarToHeaderJar(ctx, localImplementationJars[0], localHeaderJarFile)
 			localHeaderJars = append(localHeaderJars, localHeaderJarFile)
@@ -1735,7 +1768,7 @@
 	// enforce syntax check to jacoco filters for any build (http://b/183622051)
 	specs := j.jacocoModuleToZipCommand(ctx)
 	if ctx.Failed() {
-		return
+		return nil
 	}
 
 	completeStaticLibsImplementationJarsToCombine := completeStaticLibsImplementationJars
@@ -1753,16 +1786,10 @@
 
 	// merge implementation jar with resources if necessary
 	var implementationAndResourcesJarsToCombine android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		resourceJars := completeStaticLibsResourceJars.ToList()
-		if len(resourceJars) > 0 {
-			implementationAndResourcesJarsToCombine = append(resourceJars, completeStaticLibsImplementationJarsToCombine.ToList()...)
-			implementationAndResourcesJarsToCombine = append(implementationAndResourcesJarsToCombine, extraDepCombinedJars...)
-		}
-	} else {
-		if combinedResourceJar != nil {
-			implementationAndResourcesJarsToCombine = android.Paths{combinedResourceJar, outputFile}
-		}
+	combinedResourceJars := completeStaticLibsResourceJars.ToList()
+	if len(combinedResourceJars) > 0 {
+		implementationAndResourcesJarsToCombine = slices.Concat(combinedResourceJars,
+			completeStaticLibsImplementationJarsToCombine.ToList(), extraDepCombinedJars)
 	}
 
 	if len(implementationAndResourcesJarsToCombine) > 0 {
@@ -1789,7 +1816,7 @@
 				classesJar:    outputFile,
 				jarName:       jarName,
 			}
-			if j.GetProfileGuided(ctx) && j.optimizeOrObfuscateEnabled() && !j.EnableProfileRewriting(ctx) {
+			if j.GetProfileGuided(ctx) && j.optimizeOrObfuscateEnabled(ctx) && !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.",
 				)
@@ -1803,7 +1830,7 @@
 			}
 			dexOutputFile, dexArtProfileOutput := j.dexer.compileDex(ctx, params)
 			if ctx.Failed() {
-				return
+				return nil
 			}
 
 			// If r8/d8 provides a profile that matches the optimized dex, use that for dexpreopt.
@@ -1812,18 +1839,9 @@
 			}
 
 			// merge dex jar with resources if necessary
-			var dexAndResourceJarsToCombine android.Paths
-			if ctx.Config().UseTransitiveJarsInClasspath() {
-				resourceJars := completeStaticLibsResourceJars.ToList()
-				if len(resourceJars) > 0 {
-					dexAndResourceJarsToCombine = append(android.Paths{dexOutputFile}, resourceJars...)
-				}
-			} else {
-				if combinedResourceJar != nil {
-					dexAndResourceJarsToCombine = android.Paths{dexOutputFile, combinedResourceJar}
-				}
-			}
-			if len(dexAndResourceJarsToCombine) > 0 {
+			if len(combinedResourceJars) > 0 {
+				dexAndResourceJarsToCombine := append(android.Paths{dexOutputFile}, combinedResourceJars...)
+
 				combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName)
 				TransformJarsToJar(ctx, combinedJar, "for dex resources", dexAndResourceJarsToCombine, android.OptionalPath{},
 					false, nil, nil)
@@ -1862,7 +1880,7 @@
 		}
 
 		if ctx.Failed() {
-			return
+			return nil
 		}
 	}
 
@@ -1895,21 +1913,19 @@
 
 	j.collectTransitiveSrcFiles(ctx, srcFiles)
 
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		if len(localImplementationJars) > 0 || len(localResourceJars) > 0 || len(localHeaderJars) > 0 {
-			ctx.CheckbuildFile(localImplementationJars...)
-			ctx.CheckbuildFile(localResourceJars...)
-			ctx.CheckbuildFile(localHeaderJars...)
-		} else {
-			// There are no local sources or resources in this module, so there is nothing to checkbuild.
-			ctx.UncheckedModule()
-		}
+	if len(localImplementationJars) > 0 || len(localResourceJars) > 0 || len(localHeaderJars) > 0 {
+		ctx.CheckbuildFile(localImplementationJars...)
+		ctx.CheckbuildFile(localResourceJars...)
+		ctx.CheckbuildFile(localHeaderJars...)
 	} else {
-		ctx.CheckbuildFile(j.implementationJarFile)
-		ctx.CheckbuildFile(j.headerJarFile)
+		// There are no local sources or resources in this module, so there is nothing to checkbuild.
+		ctx.UncheckedModule()
 	}
 
-	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
+	j.outputFile = outputFile.WithoutRel()
+
+	return &JavaInfo{
 		HeaderJars:           android.PathsIfNonNil(j.headerJarFile),
 		RepackagedHeaderJars: android.PathsIfNonNil(repackagedHeaderJarFile),
 
@@ -1934,10 +1950,8 @@
 		StubsLinkType:                       j.stubsLinkType,
 		AconfigIntermediateCacheOutputPaths: j.aconfigCacheFiles,
 		SdkVersion:                          j.SdkVersion(ctx),
-	})
-
-	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
-	j.outputFile = outputFile.WithoutRel()
+		OutputFile:                          j.outputFile,
+	}
 }
 
 func (j *Module) useCompose(ctx android.BaseModuleContext) bool {
@@ -1945,7 +1959,7 @@
 }
 
 func collectDepProguardSpecInfo(ctx android.ModuleContext) (transitiveProguardFlags, transitiveUnconditionalExportedFlags []depset.DepSet[android.Path]) {
-	ctx.VisitDirectDeps(func(m android.Module) {
+	ctx.VisitDirectDepsProxy(func(m android.ModuleProxy) {
 		depProguardInfo, _ := android.OtherModuleProvider(ctx, m, ProguardSpecInfoProvider)
 		depTag := ctx.OtherModuleDependencyTag(m)
 
@@ -2041,7 +2055,9 @@
 		} else if strings.HasPrefix(flag, "-Xintellij-plugin-root") {
 			ctx.PropertyErrorf("kotlincflags",
 				"Bad flag: `%s`, only use internal compiler for consistency.", flag)
-		} else if inList(flag, config.KotlincIllegalFlags) {
+		} else if slices.ContainsFunc(config.KotlincIllegalFlags, func(f string) bool {
+			return strings.HasPrefix(flag, f)
+		}) {
 			ctx.PropertyErrorf("kotlincflags", "Flag `%s` already used by build system", flag)
 		} else if flag == "-include-runtime" {
 			ctx.PropertyErrorf("kotlincflags", "Bad flag: `%s`, do not include runtime.", flag)
@@ -2070,13 +2086,8 @@
 
 	// Combine any static header libraries into classes-header.jar. If there is only
 	// one input jar this step will be skipped.
-	var jars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		depSet := depset.New(depset.PREORDER, localHeaderJars, deps.transitiveStaticLibsHeaderJars)
-		jars = depSet.ToList()
-	} else {
-		jars = append(slices.Clone(localHeaderJars), deps.staticHeaderJars...)
-	}
+	depSet := depset.New(depset.PREORDER, localHeaderJars, deps.transitiveStaticLibsHeaderJars)
+	jars := depSet.ToList()
 
 	// we cannot skip the combine step for now if there is only one jar
 	// since we have to strip META-INF/TRANSITIVE dir from turbine.jar
@@ -2116,7 +2127,7 @@
 	directStaticLibs := android.Paths{}
 	transitiveLibs := []depset.DepSet[android.Path]{}
 	transitiveStaticLibs := []depset.DepSet[android.Path]{}
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		// don't add deps of the prebuilt version of the same library
 		if ctx.ModuleName() == android.RemoveOptionalPrebuiltPrefix(module.Name()) {
 			return
@@ -2184,6 +2195,8 @@
 func (j *Module) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
 	if j.expandJarjarRules != nil {
 		dpInfo.Jarjar_rules = append(dpInfo.Jarjar_rules, j.expandJarjarRules.String())
+	}
+	if j.headerJarFile != nil {
 		// Add the header jar so that the rdeps can be resolved to the repackaged classes.
 		dpInfo.Jars = append(dpInfo.Jars, j.headerJarFile.String())
 	}
@@ -2202,29 +2215,33 @@
 
 func (j *Module) hasCode(ctx android.ModuleContext) bool {
 	srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
-	return len(srcFiles) > 0 || len(ctx.GetDirectDepsWithTag(staticLibTag)) > 0
+	return len(srcFiles) > 0 || len(ctx.GetDirectDepsProxyWithTag(staticLibTag)) > 0
 }
 
 // Implements android.ApexModule
-func (j *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
-	return j.depIsInSameApex(ctx, dep)
+func (m *Module) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return JavaDepInSameApexChecker{}
+}
+
+type JavaDepInSameApexChecker struct {
+	android.BaseDepInSameApexChecker
+}
+
+func (m JavaDepInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool {
+	return depIsInSameApex(tag)
 }
 
 // Implements android.ApexModule
-func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
+func (j *Module) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
 	sdkVersionSpec := j.SdkVersion(ctx)
 	minSdkVersion := j.MinSdkVersion(ctx)
-	if !minSdkVersion.Specified() {
-		return fmt.Errorf("min_sdk_version is not specified")
-	}
+
 	// If the module is compiling against core (via sdk_version), skip comparison check.
 	if sdkVersionSpec.Kind == android.SdkCore {
-		return nil
+		return android.MinApiLevel
 	}
-	if minSdkVersion.GreaterThan(sdkVersion) {
-		return fmt.Errorf("newer SDK(%v)", minSdkVersion)
-	}
-	return nil
+
+	return minSdkVersion
 }
 
 func (j *Module) Stem() string {
@@ -2240,7 +2257,7 @@
 
 func (j *Module) collectTransitiveSrcFiles(ctx android.ModuleContext, mine android.Paths) {
 	var fromDeps []depset.DepSet[android.Path]
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(module)
 		if tag == staticLibTag {
 			if depInfo, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
@@ -2359,7 +2376,7 @@
 // checkSdkLinkType make sures the given dependency doesn't have a lower SDK link type rank than
 // this module's. See the comment on rank() for details and an example.
 func (j *Module) checkSdkLinkType(
-	ctx android.ModuleContext, dep moduleWithSdkDep, tag dependencyTag) {
+	ctx android.ModuleContext, dep android.ModuleProxy) {
 	if ctx.Host() {
 		return
 	}
@@ -2368,7 +2385,12 @@
 	if stubs {
 		return
 	}
-	depLinkType, _ := dep.getSdkLinkType(ctx, ctx.OtherModuleName(dep))
+	info, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider)
+	if !ok || info.ModuleWithSdkDepInfo == nil {
+		panic(fmt.Errorf("dependency doesn't have ModuleWithSdkDepInfo: %v", dep))
+	}
+
+	depLinkType := info.ModuleWithSdkDepInfo.SdkLinkType
 
 	if myLinkType.rank() < depLinkType.rank() {
 		ctx.ModuleErrorf("compiles against %v, but dependency %q is compiling against %v. "+
@@ -2393,7 +2415,7 @@
 	var transitiveStaticJarsImplementationLibs []depset.DepSet[android.Path]
 	var transitiveStaticJarsResourceLibs []depset.DepSet[android.Path]
 
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
 
@@ -2427,7 +2449,7 @@
 				deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars...)
 				transitiveBootClasspathHeaderJars = append(transitiveBootClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
 			case sdkLibTag, libTag, instrumentationForTag:
-				if _, ok := module.(*Plugin); ok {
+				if _, ok := android.OtherModuleProvider(ctx, module, JavaPluginInfoProvider); ok {
 					ctx.ModuleErrorf("a java_plugin (%s) cannot be used as a libs dependency", otherName)
 				}
 				deps.classpath = append(deps.classpath, dep.HeaderJars...)
@@ -2445,7 +2467,7 @@
 				deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars...)
 				transitiveJava9ClasspathHeaderJars = append(transitiveJava9ClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
 			case staticLibTag:
-				if _, ok := module.(*Plugin); ok {
+				if _, ok := android.OtherModuleProvider(ctx, module, JavaPluginInfoProvider); ok {
 					ctx.ModuleErrorf("a java_plugin (%s) cannot be used as a static_libs dependency", otherName)
 				}
 				deps.classpath = append(deps.classpath, dep.HeaderJars...)
@@ -2465,40 +2487,40 @@
 				transitiveStaticJarsImplementationLibs = append(transitiveStaticJarsImplementationLibs, dep.TransitiveStaticLibsImplementationJars)
 				transitiveStaticJarsResourceLibs = append(transitiveStaticJarsResourceLibs, dep.TransitiveStaticLibsResourceJars)
 			case pluginTag:
-				if plugin, ok := module.(*Plugin); ok {
-					if plugin.pluginProperties.Processor_class != nil {
-						addPlugins(&deps, dep.ImplementationAndResourcesJars, *plugin.pluginProperties.Processor_class)
+				if plugin, ok := android.OtherModuleProvider(ctx, module, JavaPluginInfoProvider); ok {
+					if plugin.ProcessorClass != nil {
+						addPlugins(&deps, dep.ImplementationAndResourcesJars, *plugin.ProcessorClass)
 					} else {
 						addPlugins(&deps, dep.ImplementationAndResourcesJars)
 					}
 					// Turbine doesn't run annotation processors, so any module that uses an
 					// annotation processor that generates API is incompatible with the turbine
 					// optimization.
-					deps.disableTurbine = deps.disableTurbine || Bool(plugin.pluginProperties.Generates_api)
+					deps.disableTurbine = deps.disableTurbine || plugin.GeneratesApi
 				} else {
 					ctx.PropertyErrorf("plugins", "%q is not a java_plugin module", otherName)
 				}
 			case errorpronePluginTag:
-				if _, ok := module.(*Plugin); ok {
+				if _, ok := android.OtherModuleProvider(ctx, module, JavaPluginInfoProvider); ok {
 					deps.errorProneProcessorPath = append(deps.errorProneProcessorPath, dep.ImplementationAndResourcesJars...)
 				} else {
 					ctx.PropertyErrorf("plugins", "%q is not a java_plugin module", otherName)
 				}
 			case exportedPluginTag:
-				if plugin, ok := module.(*Plugin); ok {
+				if plugin, ok := android.OtherModuleProvider(ctx, module, JavaPluginInfoProvider); ok {
 					j.exportedPluginJars = append(j.exportedPluginJars, dep.ImplementationAndResourcesJars...)
-					if plugin.pluginProperties.Processor_class != nil {
-						j.exportedPluginClasses = append(j.exportedPluginClasses, *plugin.pluginProperties.Processor_class)
+					if plugin.ProcessorClass != nil {
+						j.exportedPluginClasses = append(j.exportedPluginClasses, *plugin.ProcessorClass)
 					}
 					// Turbine doesn't run annotation processors, so any module that uses an
 					// annotation processor that generates API is incompatible with the turbine
 					// optimization.
-					j.exportedDisableTurbine = Bool(plugin.pluginProperties.Generates_api)
+					j.exportedDisableTurbine = plugin.GeneratesApi
 				} else {
 					ctx.PropertyErrorf("exported_plugins", "%q is not a java_plugin module", otherName)
 				}
 			case kotlinPluginTag:
-				if _, ok := module.(*KotlinPlugin); ok {
+				if _, ok := android.OtherModuleProvider(ctx, module, KotlinPluginInfoProvider); ok {
 					deps.kotlinPlugins = append(deps.kotlinPlugins, dep.ImplementationAndResourcesJars...)
 				} else {
 					ctx.PropertyErrorf("kotlin_plugins", "%q is not a kotlin_plugin module", otherName)
@@ -2510,21 +2532,21 @@
 					JavaInfo: dep,
 				})
 			}
-		} else if dep, ok := module.(android.SourceFileProducer); ok {
+		} else if dep, ok := android.OtherModuleProvider(ctx, module, android.SourceFilesInfoProvider); ok {
 			switch tag {
 			case sdkLibTag, libTag:
-				checkProducesJars(ctx, dep)
-				deps.classpath = append(deps.classpath, dep.Srcs()...)
-				deps.dexClasspath = append(deps.classpath, dep.Srcs()...)
+				checkProducesJars(ctx, dep, module)
+				deps.classpath = append(deps.classpath, dep.Srcs...)
+				deps.dexClasspath = append(deps.classpath, dep.Srcs...)
 				transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars,
-					depset.New(depset.PREORDER, dep.Srcs(), nil))
+					depset.New(depset.PREORDER, dep.Srcs, nil))
 			case staticLibTag:
-				checkProducesJars(ctx, dep)
-				deps.classpath = append(deps.classpath, dep.Srcs()...)
-				deps.staticJars = append(deps.staticJars, dep.Srcs()...)
-				deps.staticHeaderJars = append(deps.staticHeaderJars, dep.Srcs()...)
+				checkProducesJars(ctx, dep, module)
+				deps.classpath = append(deps.classpath, dep.Srcs...)
+				deps.staticJars = append(deps.staticJars, dep.Srcs...)
+				deps.staticHeaderJars = append(deps.staticHeaderJars, dep.Srcs...)
 
-				depHeaderJars := depset.New(depset.PREORDER, dep.Srcs(), nil)
+				depHeaderJars := depset.New(depset.PREORDER, dep.Srcs, nil)
 				transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars, depHeaderJars)
 				transitiveStaticJarsHeaderLibs = append(transitiveStaticJarsHeaderLibs, depHeaderJars)
 				transitiveStaticJarsImplementationLibs = append(transitiveStaticJarsImplementationLibs, depHeaderJars)
@@ -2577,14 +2599,12 @@
 	deps.transitiveStaticLibsImplementationJars = transitiveStaticJarsImplementationLibs
 	deps.transitiveStaticLibsResourceJars = transitiveStaticJarsResourceLibs
 
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		depSet := depset.New(depset.PREORDER, nil, transitiveClasspathHeaderJars)
-		deps.classpath = depSet.ToList()
-		depSet = depset.New(depset.PREORDER, nil, transitiveBootClasspathHeaderJars)
-		deps.bootClasspath = depSet.ToList()
-		depSet = depset.New(depset.PREORDER, nil, transitiveJava9ClasspathHeaderJars)
-		deps.java9Classpath = depSet.ToList()
-	}
+	depSet := depset.New(depset.PREORDER, nil, transitiveClasspathHeaderJars)
+	deps.classpath = depSet.ToList()
+	depSet = depset.New(depset.PREORDER, nil, transitiveBootClasspathHeaderJars)
+	deps.bootClasspath = depSet.ToList()
+	depSet = depset.New(depset.PREORDER, nil, transitiveJava9ClasspathHeaderJars)
+	deps.java9Classpath = depSet.ToList()
 
 	if ctx.Device() {
 		sdkDep := decodeSdkDep(ctx, android.SdkContext(j))
@@ -2625,18 +2645,11 @@
 	RenameUseExclude
 )
 
-type RenameUseElement struct {
-	DepName   string
-	RenameUse DependencyUse
-	Why       string // token for determining where in the logic the decision was made.
-}
-
 type JarJarProviderData struct {
 	// Mapping of class names: original --> renamed.  If the value is "", the class will be
 	// renamed by the next rdep that has the jarjar_prefix attribute (or this module if it has
 	// attribute). Rdeps of that module will inherit the renaming.
-	Rename    map[string]string
-	RenameUse []RenameUseElement
+	Rename map[string]string
 }
 
 func (this JarJarProviderData) GetDebugString() string {
@@ -2707,7 +2720,7 @@
 	module := ctx.Module()
 	moduleName := module.Name()
 
-	ctx.VisitDirectDeps(func(m android.Module) {
+	ctx.VisitDirectDepsProxy(func(m android.ModuleProxy) {
 		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
@@ -2715,93 +2728,89 @@
 		//
 		// Note well: there are probably cases that are getting to the unconditional return
 		// and are therefore wrong.
-		shouldIncludeRenames := func() (DependencyUse, string) {
+		shouldIncludeRenames := func() DependencyUse {
 			if moduleName == m.Name() {
-				return RenameUseInclude, "name" // If we have the same module name, include the renames.
+				return RenameUseInclude // If we have the same module name, include the renames.
 			}
 			if sc, ok := module.(android.SdkContext); ok {
 				if ctx.Device() {
 					sdkDep := decodeSdkDep(ctx, sc)
 					if !sdkDep.invalidVersion && sdkDep.useFiles {
-						return RenameUseExclude, "useFiles"
+						return RenameUseExclude
 					}
 				}
 			}
 			if IsJniDepTag(tag) || tag == certificateTag || tag == proguardRaiseTag {
-				return RenameUseExclude, "tags"
+				return RenameUseExclude
 			}
 			if _, ok := android.OtherModuleProvider(ctx, m, SdkLibraryInfoProvider); ok {
 				switch tag {
 				case sdkLibTag, libTag:
-					return RenameUseExclude, "sdklibdep" // matches collectDeps()
+					return RenameUseExclude // matches collectDeps()
 				}
-				return RenameUseInvalid, "sdklibdep" // dep is not used in collectDeps()
+				return RenameUseInvalid // dep is not used in collectDeps()
 			} else if ji, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
 				switch ji.StubsLinkType {
 				case Stubs:
-					return RenameUseExclude, "info"
+					return RenameUseExclude
 				case Implementation:
-					return RenameUseInclude, "info"
+					return RenameUseInclude
 				default:
 					//fmt.Printf("collectDirectDepsProviders: %v -> %v StubsLinkType unknown\n", module, m)
 					// Fall through to the heuristic logic.
 				}
-				switch reflect.TypeOf(m).String() {
-				case "*java.GeneratedJavaLibraryModule":
+				if _, ok := android.OtherModuleProvider(ctx, m, android.CodegenInfoProvider); ok {
 					// Probably a java_aconfig_library module.
-					// TODO: make this check better.
-					return RenameUseInclude, "reflect"
+					return RenameUseInclude
 				}
 				switch tag {
 				case bootClasspathTag:
-					return RenameUseExclude, "tagswitch"
+					return RenameUseExclude
 				case sdkLibTag, libTag, instrumentationForTag:
-					return RenameUseInclude, "tagswitch"
+					return RenameUseInclude
 				case java9LibTag:
-					return RenameUseExclude, "tagswitch"
+					return RenameUseExclude
 				case staticLibTag:
-					return RenameUseInclude, "tagswitch"
+					return RenameUseInclude
 				case pluginTag:
-					return RenameUseInclude, "tagswitch"
+					return RenameUseInclude
 				case errorpronePluginTag:
-					return RenameUseInclude, "tagswitch"
+					return RenameUseInclude
 				case exportedPluginTag:
-					return RenameUseInclude, "tagswitch"
+					return RenameUseInclude
 				case kotlinPluginTag:
-					return RenameUseInclude, "tagswitch"
+					return RenameUseInclude
 				default:
-					return RenameUseExclude, "tagswitch"
+					return RenameUseExclude
 				}
-			} else if _, ok := m.(android.SourceFileProducer); ok {
+			} else if _, ok := android.OtherModuleProvider(ctx, m, android.SourceFilesInfoProvider); ok {
 				switch tag {
 				case sdkLibTag, libTag, staticLibTag:
-					return RenameUseInclude, "srcfile"
+					return RenameUseInclude
 				default:
-					return RenameUseExclude, "srcfile"
+					return RenameUseExclude
 				}
 			} else if _, ok := android.OtherModuleProvider(ctx, m, android.CodegenInfoProvider); ok {
-				return RenameUseInclude, "aconfig_declarations_group"
+				return RenameUseInclude
 			} else {
 				switch tag {
 				case bootClasspathTag:
-					return RenameUseExclude, "else"
+					return RenameUseExclude
 				case systemModulesTag:
-					return RenameUseInclude, "else"
+					return RenameUseInclude
 				}
 			}
 			// If we got here, choose the safer option, which may lead to a build failure, rather
 			// than runtime failures on the device.
-			return RenameUseExclude, "end"
+			return RenameUseExclude
 		}
 
 		if result == nil {
 			result = &JarJarProviderData{
-				Rename:    make(map[string]string),
-				RenameUse: make([]RenameUseElement, 0),
+				Rename: make(map[string]string),
 			}
 		}
-		how, why := shouldIncludeRenames()
-		result.RenameUse = append(result.RenameUse, RenameUseElement{DepName: m.Name(), RenameUse: how, Why: why})
+		how := shouldIncludeRenames()
 		if how != RenameUseInclude {
 			// Nothing to merge.
 			return
@@ -2906,14 +2915,18 @@
 // Get the jarjar rule text for a given provider for the fully resolved rules. Classes that map
 // to "" won't be in this list because they shouldn't be renamed yet.
 func getJarJarRuleText(provider *JarJarProviderData) string {
-	result := ""
+	result := strings.Builder{}
 	for _, orig := range android.SortedKeys(provider.Rename) {
 		renamed := provider.Rename[orig]
 		if renamed != "" {
-			result += "rule " + orig + " " + renamed + "\n"
+			result.WriteString("rule ")
+			result.WriteString(orig)
+			result.WriteString(" ")
+			result.WriteString(renamed)
+			result.WriteString("\n")
 		}
 	}
-	return result
+	return result.String()
 }
 
 // Repackage the flags if the jarjar rule txt for the flags is generated
@@ -2926,12 +2939,23 @@
 	return repackagedJarjarFile, true
 }
 
-func (j *Module) jarjarIfNecessary(ctx android.ModuleContext, infile android.Path, jarName, info string) (android.Path, bool) {
+func (j *Module) jarjarIfNecessary(ctx android.ModuleContext, infile android.Path, jarName, info string, useShards bool) (android.Path, bool) {
 	if j.expandJarjarRules == nil {
 		return infile, false
 	}
 	jarjarFile := android.PathForModuleOut(ctx, "jarjar", info, jarName)
-	TransformJarJar(ctx, jarjarFile, infile, j.expandJarjarRules)
+
+	totalShards := 1
+	if useShards {
+		totalShardsStr := j.properties.Jarjar_shards.GetOrDefault(ctx, "1")
+		ts, err := strconv.Atoi(totalShardsStr)
+		if err != nil {
+			ctx.PropertyErrorf("jarjar_shards", "jarjar_shards must be an integer represented as a string")
+			return infile, false
+		}
+		totalShards = ts
+	}
+	TransformJarJarWithShards(ctx, jarjarFile, infile, j.expandJarjarRules, totalShards)
 	return jarjarFile, true
 
 }
diff --git a/java/bootclasspath.go b/java/bootclasspath.go
index 3413cf3..98fb417 100644
--- a/java/bootclasspath.go
+++ b/java/bootclasspath.go
@@ -15,6 +15,8 @@
 package java
 
 import (
+	"fmt"
+
 	"android/soong/android"
 
 	"github.com/google/blueprint"
@@ -23,36 +25,9 @@
 
 // Contains code that is common to both platform_bootclasspath and bootclasspath_fragment.
 
-func init() {
-	registerBootclasspathBuildComponents(android.InitRegistrationContext)
-}
-
-func registerBootclasspathBuildComponents(ctx android.RegistrationContext) {
-	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("bootclasspath_deps", bootclasspathDepsMutator)
-	})
-}
-
-// BootclasspathDepsMutator is the interface that a module must implement if it wants to add
-// dependencies onto APEX specific variants of bootclasspath fragments or bootclasspath contents.
-type BootclasspathDepsMutator interface {
-	// BootclasspathDepsMutator implementations should add dependencies using
-	// addDependencyOntoApexModulePair and addDependencyOntoApexVariants.
-	BootclasspathDepsMutator(ctx android.BottomUpMutatorContext)
-}
-
-// bootclasspathDepsMutator is called during the final deps phase after all APEX variants have
-// been created so can add dependencies onto specific APEX variants of modules.
-func bootclasspathDepsMutator(ctx android.BottomUpMutatorContext) {
-	m := ctx.Module()
-	if p, ok := m.(BootclasspathDepsMutator); ok {
-		p.BootclasspathDepsMutator(ctx)
-	}
-}
-
 // addDependencyOntoApexVariants adds dependencies onto the appropriate apex specific variants of
 // the module as specified in the ApexVariantReference list.
-func addDependencyOntoApexVariants(ctx android.BottomUpMutatorContext, propertyName string, refs []ApexVariantReference, tag blueprint.DependencyTag) {
+func addDependencyOntoApexVariants(ctx android.BottomUpMutatorContext, propertyName string, refs []ApexVariantReference, tagType bootclasspathDependencyTagType) {
 	for i, ref := range refs {
 		apex := proptools.StringDefault(ref.Apex, "platform")
 
@@ -62,7 +37,7 @@
 		}
 		name := proptools.String(ref.Module)
 
-		addDependencyOntoApexModulePair(ctx, apex, name, tag)
+		addDependencyOntoApexModulePair(ctx, apex, name, tagType)
 	}
 }
 
@@ -75,68 +50,154 @@
 // module when both source and prebuilt modules are available.
 //
 // Use gatherApexModulePairDepsWithTag to retrieve the dependencies.
-func addDependencyOntoApexModulePair(ctx android.BottomUpMutatorContext, apex string, name string, tag blueprint.DependencyTag) {
-	var variations []blueprint.Variation
-	if !android.IsConfiguredJarForPlatform(apex) {
-		// Pick the correct apex variant.
-		variations = []blueprint.Variation{
-			{Mutator: "apex", Variation: apex},
-		}
+func addDependencyOntoApexModulePair(ctx android.BottomUpMutatorContext, apex string, name string, tagType bootclasspathDependencyTagType) {
+	tag := bootclasspathDependencyTag{
+		typ: tagType,
 	}
-
 	target := ctx.Module().Target()
-	variations = append(variations, target.Variations()...)
-
-	addedDep := false
-	if ctx.OtherModuleDependencyVariantExists(variations, name) {
-		ctx.AddFarVariationDependencies(variations, tag, name)
-		addedDep = true
+	if android.IsConfiguredJarForPlatform(apex) {
+		// Platform variant, add a direct dependency.
+		ctx.AddFarVariationDependencies(target.Variations(), tag, name)
+	} else {
+		// A module in an apex.  Dependencies can't be added directly onto an apex variation, as that would
+		// require constructing a full ApexInfo configuration, which can't be predicted here.  Add a dependency
+		// on the apex instead, and annotate the dependency tag with the desired module in the apex.
+		tag.moduleInApex = name
+		ctx.AddFarVariationDependencies(target.Variations(), tag, apex)
 	}
 
-	// Add a dependency on the prebuilt module if it exists.
-	prebuiltName := android.PrebuiltNameFromSource(name)
-	if ctx.OtherModuleDependencyVariantExists(variations, prebuiltName) {
-		ctx.AddVariationDependencies(variations, tag, prebuiltName)
-		addedDep = true
-	}
-
-	// If no appropriate variant existing for this, so no dependency could be added, then it is an
-	// error, unless missing dependencies are allowed. The simplest way to handle that is to add a
-	// dependency that will not be satisfied and the default behavior will handle it.
-	if !addedDep {
-		// Add dependency on the unprefixed (i.e. source or renamed prebuilt) module which we know does
-		// not exist. The resulting error message will contain useful information about the available
-		// variants.
-		reportMissingVariationDependency(ctx, variations, name)
-
-		// Add dependency on the missing prefixed prebuilt variant too if a module with that name exists
-		// so that information about its available variants will be reported too.
-		if ctx.OtherModuleExists(prebuiltName) {
-			reportMissingVariationDependency(ctx, variations, prebuiltName)
-		}
-	}
 }
 
-// reportMissingVariationDependency intentionally adds a dependency on a missing variation in order
-// to generate an appropriate error message with information about the available variations.
-func reportMissingVariationDependency(ctx android.BottomUpMutatorContext, variations []blueprint.Variation, name string) {
-	ctx.AddFarVariationDependencies(variations, nil, name)
+// gatherFragments collects fragments that are direct dependencies of this module, as well as
+// any fragments in apexes via the dependency on the apex.  It returns a list of the fragment
+// modules and map from apex name to the fragment in that apex.
+func gatherFragments(ctx android.BaseModuleContext) ([]android.Module, map[string]android.Module) {
+	var fragments []android.Module
+
+	type fragmentInApex struct {
+		module string
+		apex   string
+	}
+
+	var fragmentsInApexes []fragmentInApex
+
+	// Find any direct dependencies, as well as a list of the modules in apexes.
+	ctx.VisitDirectDeps(func(module android.Module) {
+		t := ctx.OtherModuleDependencyTag(module)
+		if bcpTag, ok := t.(bootclasspathDependencyTag); ok && bcpTag.typ == fragment {
+			if bcpTag.moduleInApex != "" {
+				fragmentsInApexes = append(fragmentsInApexes, fragmentInApex{bcpTag.moduleInApex, ctx.OtherModuleName(module)})
+			} else {
+				fragments = append(fragments, module)
+			}
+		}
+	})
+
+	fragmentsMap := make(map[string]android.Module)
+	for _, fragmentInApex := range fragmentsInApexes {
+		var found android.Module
+		// Find a desired module in an apex.
+		ctx.WalkDeps(func(child, parent android.Module) bool {
+			t := ctx.OtherModuleDependencyTag(child)
+			if parent == ctx.Module() {
+				if bcpTag, ok := t.(bootclasspathDependencyTag); ok && bcpTag.typ == fragment && ctx.OtherModuleName(child) == fragmentInApex.apex {
+					// This is the dependency from this module to the apex, recurse into it.
+					return true
+				}
+			} else if android.IsDontReplaceSourceWithPrebuiltTag(t) {
+				return false
+			} else if t == android.PrebuiltDepTag {
+				return false
+			} else if IsBootclasspathFragmentContentDepTag(t) {
+				return false
+			} else if android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(child)) == fragmentInApex.module {
+				// This is the desired module inside the apex.
+				if found != nil && child != found {
+					panic(fmt.Errorf("found two conflicting modules %q in apex %q: %s and %s",
+						fragmentInApex.module, fragmentInApex.apex, found, child))
+				}
+				found = child
+			}
+			return false
+		})
+		if found != nil {
+			if existing, exists := fragmentsMap[fragmentInApex.apex]; exists {
+				ctx.ModuleErrorf("apex %s has multiple fragments, %s and %s", fragmentInApex.apex, fragmentInApex.module, existing)
+			} else {
+				fragmentsMap[fragmentInApex.apex] = found
+				fragments = append(fragments, found)
+			}
+		} else if !ctx.Config().AllowMissingDependencies() {
+			ctx.ModuleErrorf("failed to find fragment %q in apex %q\n",
+				fragmentInApex.module, fragmentInApex.apex)
+		}
+	}
+	return fragments, fragmentsMap
 }
 
 // gatherApexModulePairDepsWithTag returns the list of dependencies with the supplied tag that was
 // added by addDependencyOntoApexModulePair.
-func gatherApexModulePairDepsWithTag(ctx android.BaseModuleContext, tag blueprint.DependencyTag) []android.Module {
+func gatherApexModulePairDepsWithTag(ctx android.BaseModuleContext, tagType bootclasspathDependencyTagType) ([]android.Module, map[android.Module]string) {
 	var modules []android.Module
-	isActiveModulePred := func(module android.Module) bool {
-		return isActiveModule(ctx, module)
+	modulesToApex := make(map[android.Module]string)
+
+	type moduleInApex struct {
+		module string
+		apex   string
 	}
-	ctx.VisitDirectDepsIf(isActiveModulePred, func(module android.Module) {
+
+	var modulesInApexes []moduleInApex
+
+	ctx.VisitDirectDeps(func(module android.Module) {
 		t := ctx.OtherModuleDependencyTag(module)
-		if t == tag {
-			modules = append(modules, module)
+		if bcpTag, ok := t.(bootclasspathDependencyTag); ok && bcpTag.typ == tagType {
+			if bcpTag.moduleInApex != "" {
+				modulesInApexes = append(modulesInApexes, moduleInApex{bcpTag.moduleInApex, ctx.OtherModuleName(module)})
+			} else {
+				modules = append(modules, module)
+			}
 		}
 	})
-	return modules
+
+	for _, moduleInApex := range modulesInApexes {
+		var found android.Module
+		ctx.WalkDeps(func(child, parent android.Module) bool {
+			t := ctx.OtherModuleDependencyTag(child)
+			if parent == ctx.Module() {
+				if bcpTag, ok := t.(bootclasspathDependencyTag); ok && bcpTag.typ == tagType && ctx.OtherModuleName(child) == moduleInApex.apex {
+					// recurse into the apex
+					return true
+				}
+			} else if tagType != fragment && android.IsFragmentInApexTag(t) {
+				return true
+			} else if android.IsDontReplaceSourceWithPrebuiltTag(t) {
+				return false
+			} else if t == android.PrebuiltDepTag {
+				return false
+			} else if IsBootclasspathFragmentContentDepTag(t) {
+				return false
+			} else if android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(child)) == moduleInApex.module {
+				if found != nil && child != found {
+					panic(fmt.Errorf("found two conflicting modules %q in apex %q: %s and %s",
+						moduleInApex.module, moduleInApex.apex, found, child))
+				}
+				found = child
+			}
+			return false
+		})
+		if found != nil {
+			modules = append(modules, found)
+			if existing, exists := modulesToApex[found]; exists && existing != moduleInApex.apex {
+				ctx.ModuleErrorf("module %s is in two apexes, %s and %s", moduleInApex.module, existing, moduleInApex.apex)
+			} else {
+				modulesToApex[found] = moduleInApex.apex
+			}
+		} else if !ctx.Config().AllowMissingDependencies() {
+			ctx.ModuleErrorf("failed to find module %q in apex %q\n",
+				moduleInApex.module, moduleInApex.apex)
+		}
+	}
+	return modules, modulesToApex
 }
 
 // ApexVariantReference specifies a particular apex variant of a module.
@@ -165,7 +226,7 @@
 // addDependenciesOntoFragments adds dependencies to the fragments specified in this properties
 // structure.
 func (p *BootclasspathFragmentsDepsProperties) addDependenciesOntoFragments(ctx android.BottomUpMutatorContext) {
-	addDependencyOntoApexVariants(ctx, "fragments", p.Fragments, bootclasspathFragmentDepTag)
+	addDependencyOntoApexVariants(ctx, "fragments", p.Fragments, fragment)
 }
 
 // bootclasspathDependencyTag defines dependencies from/to bootclasspath_fragment,
@@ -174,23 +235,38 @@
 type bootclasspathDependencyTag struct {
 	blueprint.BaseDependencyTag
 
-	name string
+	typ bootclasspathDependencyTagType
+
+	// moduleInApex is set to the name of the desired module when this dependency points
+	// to the apex that the modules is contained in.
+	moduleInApex string
 }
 
+type bootclasspathDependencyTagType int
+
+const (
+	// The tag used for dependencies onto bootclasspath_fragments.
+	fragment bootclasspathDependencyTagType = iota
+	// The tag used for dependencies onto platform_bootclasspath.
+	platform
+	dexpreoptBootJar
+	artBootJar
+	platformBootJar
+	apexBootJar
+)
+
 func (t bootclasspathDependencyTag) ExcludeFromVisibilityEnforcement() {
 }
 
+func (t bootclasspathDependencyTag) LicenseAnnotations() []android.LicenseAnnotation {
+	return []android.LicenseAnnotation{android.LicenseAnnotationSharedDependency}
+}
+
 // Dependencies that use the bootclasspathDependencyTag instances are only added after all the
 // visibility checking has been done so this has no functional effect. However, it does make it
 // clear that visibility is not being enforced on these tags.
 var _ android.ExcludeFromVisibilityEnforcementTag = bootclasspathDependencyTag{}
 
-// The tag used for dependencies onto bootclasspath_fragments.
-var bootclasspathFragmentDepTag = bootclasspathDependencyTag{name: "fragment"}
-
-// The tag used for dependencies onto platform_bootclasspath.
-var platformBootclasspathDepTag = bootclasspathDependencyTag{name: "platform"}
-
 // BootclasspathNestedAPIProperties defines properties related to the API provided by parts of the
 // bootclasspath that are nested within the main BootclasspathAPIProperties.
 type BootclasspathNestedAPIProperties struct {
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 375a1aa..a09416d 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -41,6 +41,10 @@
 	ctx.RegisterModuleType("prebuilt_bootclasspath_fragment", prebuiltBootclasspathFragmentFactory)
 }
 
+type BootclasspathFragmentInfo struct{}
+
+var BootclasspathFragmentInfoProvider = blueprint.NewProvider[BootclasspathFragmentInfo]()
+
 // BootclasspathFragmentSdkMemberType is the member type used to add bootclasspath_fragments to
 // the SDK snapshot. It is exported for use by apex.
 var BootclasspathFragmentSdkMemberType = &bootclasspathFragmentMemberType{
@@ -89,6 +93,19 @@
 // The tag used for the dependency between the bootclasspath_fragment module and its contents.
 var bootclasspathFragmentContentDepTag = bootclasspathFragmentContentDependencyTag{}
 
+type moduleInFragmentDependencyTag struct {
+	blueprint.DependencyTag
+}
+
+func (m moduleInFragmentDependencyTag) ExcludeFromVisibilityEnforcement() {
+}
+
+// moduleInFragmentDepTag is added alongside bootclasspathFragmentContentDependencyTag,
+// but doesn't set ReplaceSourceWithPrebuilt.  It is used to find modules in the fragment
+// by traversing from the apex to the fragment to the module, which prevents having to
+// construct a dependency on the apex variant of the fragment directly.
+var moduleInFragmentDepTag = moduleInFragmentDependencyTag{}
+
 var _ android.ExcludeFromVisibilityEnforcementTag = bootclasspathFragmentContentDepTag
 var _ android.ReplaceSourceWithPrebuilt = bootclasspathFragmentContentDepTag
 var _ android.SdkMemberDependencyTag = bootclasspathFragmentContentDepTag
@@ -239,6 +256,8 @@
 	profilePathErr error
 }
 
+var _ android.ApexModule = (*BootclasspathFragmentModule)(nil)
+
 // commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt
 // bootclasspath fragment modules.
 type commonBootclasspathFragment interface {
@@ -290,6 +309,10 @@
 	return m
 }
 
+func (m *BootclasspathFragmentModule) UniqueApexVariations() bool {
+	return true
+}
+
 func (m *BootclasspathFragmentModule) bootclasspathFragmentPropertyCheck(ctx android.ModuleContext) {
 	contents := m.properties.Contents.GetOrDefault(ctx, nil)
 	if len(contents) == 0 {
@@ -393,11 +416,17 @@
 	return i.profileInstallPathInApex
 }
 
-func (b *BootclasspathFragmentModule) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
-	tag := ctx.OtherModuleDependencyTag(dep)
+func (m *BootclasspathFragmentModule) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return BootclasspathFragmentDepInSameApexChecker{}
+}
 
+type BootclasspathFragmentDepInSameApexChecker struct {
+	android.BaseDepInSameApexChecker
+}
+
+func (b BootclasspathFragmentDepInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool {
 	// If the module is a default module, do not check the tag
-	if _, ok := dep.(*Defaults); ok {
+	if tag == android.DefaultsDepTag {
 		return true
 	}
 	if IsBootclasspathFragmentContentDepTag(tag) {
@@ -408,17 +437,31 @@
 		// Cross-cutting metadata dependencies are metadata.
 		return false
 	}
+	if tag == moduleInFragmentDepTag {
+		return true
+	}
 	// Dependency to the bootclasspath fragment of another apex
 	// e.g. concsrypt-bootclasspath-fragment --> art-bootclasspath-fragment
-	if tag == bootclasspathFragmentDepTag {
+	if bcpTag, ok := tag.(bootclasspathDependencyTag); ok && bcpTag.typ == fragment {
 		return false
-
 	}
-	panic(fmt.Errorf("boot_image module %q should not have a dependency on %q via tag %s", b, dep, android.PrettyPrintTag(tag)))
+	if tag == moduleInFragmentDepTag {
+		return false
+	}
+	if tag == dexpreopt.Dex2oatDepTag {
+		return false
+	}
+	if tag == android.PrebuiltDepTag {
+		return false
+	}
+	if _, ok := tag.(hiddenAPIStubsDependencyTag); ok {
+		return false
+	}
+	panic(fmt.Errorf("boot_image module should not have a dependency tag %s", android.PrettyPrintTag(tag)))
 }
 
-func (b *BootclasspathFragmentModule) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
-	return nil
+func (m *BootclasspathFragmentModule) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
+	return android.MinApiLevel
 }
 
 // ComponentDepsMutator adds dependencies onto modules before any prebuilt modules without a
@@ -456,24 +499,24 @@
 		}
 	}
 
-	if !dexpreopt.IsDex2oatNeeded(ctx) {
-		return
+	if dexpreopt.IsDex2oatNeeded(ctx) {
+		// Add a dependency onto the dex2oat tool which is needed for creating the boot image. The
+		// path is retrieved from the dependency by GetGlobalSoongConfig(ctx).
+		dexpreopt.RegisterToolDeps(ctx)
 	}
 
-	// Add a dependency onto the dex2oat tool which is needed for creating the boot image. The
-	// path is retrieved from the dependency by GetGlobalSoongConfig(ctx).
-	dexpreopt.RegisterToolDeps(ctx)
-
 	// Add a dependency to `all_apex_contributions` to determine if prebuilts are active.
 	// If prebuilts are active, `contents` validation on the source bootclasspath fragment should be disabled.
 	if _, isPrebuiltModule := ctx.Module().(*PrebuiltBootclasspathFragmentModule); !isPrebuiltModule {
 		ctx.AddDependency(b, android.AcDepTag, "all_apex_contributions")
 	}
-}
 
-func (b *BootclasspathFragmentModule) BootclasspathDepsMutator(ctx android.BottomUpMutatorContext) {
 	// Add dependencies on all the fragments.
 	b.properties.BootclasspathFragmentsDepsProperties.addDependenciesOntoFragments(ctx)
+
+	for _, name := range b.properties.Contents.GetOrDefault(ctx, nil) {
+		ctx.AddDependency(ctx.Module(), moduleInFragmentDepTag, name)
+	}
 }
 
 func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -496,7 +539,7 @@
 		}
 	})
 
-	fragments := gatherApexModulePairDepsWithTag(ctx, bootclasspathFragmentDepTag)
+	fragments, _ := gatherFragments(ctx)
 
 	// Perform hidden API processing.
 	hiddenAPIOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments)
@@ -518,6 +561,8 @@
 	if !ctx.IsFinalModule(ctx.Module()) {
 		b.HideFromMake()
 	}
+
+	android.SetProvider(ctx, BootclasspathFragmentInfoProvider, BootclasspathFragmentInfo{})
 }
 
 // getProfileProviderApex returns the name of the apex that provides a boot image profile, or an
@@ -529,19 +574,18 @@
 	}
 
 	// Bootclasspath fragment modules that are for the platform do not produce boot related files.
-	apexInfos, _ := android.ModuleProvider(ctx, android.AllApexInfoProvider)
-	if apexInfos == nil {
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	if apexInfo.IsForPlatform() {
 		return ""
 	}
 
-	for _, apexInfo := range apexInfos.ApexInfos {
-		for _, apex := range apexInfo.InApexVariants {
-			if isProfileProviderApex(ctx, apex) {
-				return apex
+	for _, config := range genBootImageConfigs(ctx) {
+		if config.profileProviderModule == b.BaseModuleName() {
+			if len(config.profileImports) > 0 {
+				return config.profileImports[0]
 			}
 		}
 	}
-
 	return ""
 }
 
@@ -605,7 +649,7 @@
 			if android.IsModulePrebuilt(ctx.Module()) {
 				// prebuilt bcpf. the validation of this will be done at the top-level apex
 				providerClasspathFragmentValidationInfoProvider(ctx, unknown)
-			} else if !disableSourceApexVariant(ctx) {
+			} else if !disableSourceApexVariant(ctx) && android.IsModulePreferred(ctx.Module()) {
 				// source bcpf, and prebuilt apex are not selected.
 				ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown)
 			}
@@ -1141,6 +1185,13 @@
 	android.InitPrebuiltModule(m, &[]string{"placeholder"})
 	android.InitApexModule(m)
 	android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(m)
+
+	m.SetDefaultableHook(func(mctx android.DefaultableHookContext) {
+		if mctx.Config().AlwaysUsePrebuiltSdks() {
+			m.prebuilt.ForcePrefer()
+		}
+	})
 
 	return m
 }
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 3aa1258..87b853c 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -31,6 +31,7 @@
 )
 
 func TestBootclasspathFragment_UnknownImageName(t *testing.T) {
+	t.Parallel()
 	prepareForTestWithBootclasspathFragment.
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
 			`\Qimage_name: unknown image name "unknown", expected "art"\E`)).
@@ -50,6 +51,7 @@
 }
 
 func TestPrebuiltBootclasspathFragment_UnknownImageName(t *testing.T) {
+	t.Parallel()
 	prepareForTestWithBootclasspathFragment.
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
 			`\Qimage_name: unknown image name "unknown", expected "art"\E`)).
@@ -68,6 +70,7 @@
 }
 
 func TestBootclasspathFragmentInconsistentArtConfiguration_Platform(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
 		dexpreopt.FixtureSetArtBootJars("platform:foo", "apex:bar"),
@@ -99,6 +102,7 @@
 }
 
 func TestBootclasspathFragmentInconsistentArtConfiguration_ApexMixture(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
 		dexpreopt.FixtureSetArtBootJars("apex1:foo", "apex2:bar"),
@@ -131,6 +135,7 @@
 }
 
 func TestBootclasspathFragment_Coverage(t *testing.T) {
+	t.Parallel()
 	prepareWithBp := android.FixtureWithRootAndroidBp(`
 		bootclasspath_fragment {
 			name: "myfragment",
@@ -204,11 +209,13 @@
 	)
 
 	t.Run("without coverage", func(t *testing.T) {
+		t.Parallel()
 		result := preparer.RunTest(t)
 		checkContents(t, result, "mybootlib")
 	})
 
 	t.Run("with coverage", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			prepareForTestWithFrameworkJacocoInstrumentation,
 			preparer,
@@ -364,7 +371,7 @@
 		}
 	`)
 
-	fragment := result.ModuleForTests("myfragment", "android_common")
+	fragment := result.ModuleForTests(t, "myfragment", "android_common")
 	dependencyStubDexFlag := "--dependency-stub-dex=out/soong/.intermediates/default/java/android-non-updatable.stubs.test_module_lib/android_common/dex/android-non-updatable.stubs.test_module_lib.jar"
 	stubFlagsCommand := fragment.Output("modular-hiddenapi/stub-flags.csv").RuleParams.Command
 	android.AssertStringDoesContain(t,
@@ -472,7 +479,7 @@
 
 	// Make sure that the signature-patterns.csv is passed all the appropriate package properties
 	// from the bootclasspath_fragment and its contents.
-	fragment := result.ModuleForTests("mybootclasspathfragment", "android_common")
+	fragment := result.ModuleForTests(t, "mybootclasspathfragment", "android_common")
 	rule := fragment.Output("modular-hiddenapi/signature-patterns.csv")
 	expectedCommand := strings.Join([]string{
 		"--split-package newlibrary",
diff --git a/java/builder.go b/java/builder.go
index 895ddb6..dff0032 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -46,6 +46,7 @@
 				`mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
 				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
 				`(if [ -s $srcJarDir/list ] || [ -s $out.rsp ] ; then ` +
+				`${config.FindInputDeltaCmd} --template '' --target "$out" --inputs_file "$out.rsp" && ` +
 				`${config.SoongJavacWrapper} $javaTemplate${config.JavacCmd} ` +
 				`${config.JavacHeapFlags} ${config.JavacVmFlags} ${config.CommonJdkFlags} ` +
 				`$processorpath $processor $javacFlags $bootClasspath $classpath ` +
@@ -55,8 +56,10 @@
 				`$zipTemplate${config.SoongZipCmd} -jar -o $out.tmp -C $outDir -D $outDir && ` +
 				`if ! cmp -s "$out.tmp" "$out"; then mv "$out.tmp" "$out"; fi && ` +
 				`if ! cmp -s "$annoSrcJar.tmp" "$annoSrcJar"; then mv "$annoSrcJar.tmp" "$annoSrcJar"; fi && ` +
+				`if [ -f "$out.pc_state.new" ]; then mv "$out.pc_state.new" "$out.pc_state"; fi && ` +
 				`rm -rf "$srcJarDir" "$outDir"`,
 			CommandDeps: []string{
+				"${config.FindInputDeltaCmd}",
 				"${config.JavacCmd}",
 				"${config.SoongZipCmd}",
 				"${config.ZipSyncCmd}",
@@ -165,7 +168,7 @@
 				"${config.JavaCmd}",
 			},
 			Rspfile:        "$out.rsp",
-			RspfileContent: "$in",
+			RspfileContent: "$in_newline",
 			Restat:         true,
 		},
 		&remoteexec.REParams{Labels: map[string]string{"type": "tool", "name": "turbine"},
@@ -223,6 +226,12 @@
 		},
 		"jarArgs")
 
+	extractR8Rules = pctx.AndroidStaticRule("extractR8Rules",
+		blueprint.RuleParams{
+			Command:     `${config.ExtractR8RulesCmd} --rules-output $out --include-origin-comments $in`,
+			CommandDeps: []string{"${config.ExtractR8RulesCmd}"},
+		})
+
 	jarjar = pctx.AndroidStaticRule("jarjar",
 		blueprint.RuleParams{
 			Command: "" +
@@ -235,12 +244,12 @@
 				// for newly repackaged classes. Dropping @UnsupportedAppUsage on repackaged classes
 				// avoids adding new hiddenapis after jarjar'ing.
 				" -DremoveAndroidCompatAnnotations=true" +
-				" -jar ${config.JarjarCmd} process $rulesFile $in $out && " +
+				" -jar ${config.JarjarCmd} process $rulesFile $in $out $total_shards $shard_index && " +
 				// Turn a missing output file into a ninja error
 				`[ -e ${out} ] || (echo "Missing output file"; exit 1)`,
 			CommandDeps: []string{"${config.JavaCmd}", "${config.JarjarCmd}", "$rulesFile"},
 		},
-		"rulesFile")
+		"rulesFile", "total_shards", "shard_index")
 
 	packageCheck = pctx.AndroidStaticRule("packageCheck",
 		blueprint.RuleParams{
@@ -301,7 +310,7 @@
 
 	gatherReleasedFlaggedApisRule = pctx.AndroidStaticRule("gatherReleasedFlaggedApisRule",
 		blueprint.RuleParams{
-			Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}' ` +
+			Command: `${aconfig} dump-cache --dedup --format=protobuf ` +
 				`--out ${out} ` +
 				`${flags_path} ` +
 				`${filter_args} `,
@@ -311,8 +320,15 @@
 
 	generateMetalavaRevertAnnotationsRule = pctx.AndroidStaticRule("generateMetalavaRevertAnnotationsRule",
 		blueprint.RuleParams{
-			Command:     `${keep-flagged-apis} ${in} > ${out}`,
-			CommandDeps: []string{"${keep-flagged-apis}"},
+			Command:     `${aconfig-to-metalava-flags} ${in} > ${out}`,
+			CommandDeps: []string{"${aconfig-to-metalava-flags}"},
+		})
+
+	generateApiXMLRule = pctx.AndroidStaticRule("generateApiXMLRule",
+		blueprint.RuleParams{
+			Command:     `${config.JavaCmd} ${config.JavaVmFlags} -Xmx4g -jar ${config.MetalavaJar} jar-to-jdiff ${in} ${out}`,
+			CommandDeps: []string{"${config.JavaCmd}", "${config.MetalavaJar}"},
+			Description: "Converting API file to XML",
 		})
 )
 
@@ -323,7 +339,7 @@
 	pctx.HostBinToolVariable("aconfig", "aconfig")
 	pctx.HostBinToolVariable("ravenizer", "ravenizer")
 	pctx.HostBinToolVariable("apimapper", "apimapper")
-	pctx.HostBinToolVariable("keep-flagged-apis", "keep-flagged-apis")
+	pctx.HostBinToolVariable("aconfig-to-metalava-flags", "aconfig-to-metalava-flags")
 }
 
 type javaBuilderFlags struct {
@@ -456,9 +472,10 @@
 	const srcJarArgsLimit = 32 * 1024
 	if len(srcJarArgs) > srcJarArgsLimit {
 		srcJarRspFile := android.PathForModuleOut(ctx, "turbine", "srcjars.rsp")
-		android.WriteFileRule(ctx, srcJarRspFile, srcJarArgs)
+		android.WriteFileRule(ctx, srcJarRspFile, strings.Join(srcJars.Strings(), "\n"))
 		srcJarArgs = "@" + srcJarRspFile.String()
 		implicits = append(implicits, srcJarRspFile)
+		rspFiles = append(rspFiles, srcJarRspFile)
 		rbeInputs = append(rbeInputs, srcJarRspFile)
 	} else {
 		rbeInputs = append(rbeInputs, srcJars...)
@@ -488,7 +505,7 @@
 	const classpathLimit = 32 * 1024
 	if len(classpathFlags) > classpathLimit {
 		classpathRspFile := android.PathForModuleOut(ctx, dir, "classpath.rsp")
-		android.WriteFileRule(ctx, classpathRspFile, classpathFlags)
+		android.WriteFileRule(ctx, classpathRspFile, strings.Join(classpath.Strings(), "\n"))
 		classpathFlags = "@" + classpathRspFile.String()
 		implicits = append(implicits, classpathRspFile)
 		rspFiles = append(rspFiles, classpathRspFile)
@@ -736,6 +753,16 @@
 	})
 }
 
+func TransformJarToR8Rules(ctx android.ModuleContext, outputFile android.WritablePath,
+	jar android.Path) {
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   extractR8Rules,
+		Output: outputFile,
+		Input:  jar,
+	})
+}
+
 func convertImplementationJarToHeaderJar(ctx android.ModuleContext, implementationJarFile android.Path,
 	headerJarFile android.WritablePath) {
 	ctx.Build(pctx, android.BuildParams{
@@ -747,16 +774,58 @@
 
 func TransformJarJar(ctx android.ModuleContext, outputFile android.WritablePath,
 	classesJar android.Path, rulesFile android.Path) {
+	TransformJarJarWithShards(ctx, outputFile, classesJar, rulesFile, 1)
+}
+
+func TransformJarJarWithShards(ctx android.ModuleContext, outputFile android.WritablePath,
+	classesJar android.Path, rulesFile android.Path, totalShards int) {
+
+	// If the total number of shards is 1, just run jarjar as-is, with `total_shards` = 1
+	// and `shard_index` == 0, which effectively disables sharding
+	if totalShards == 1 {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        jarjar,
+			Description: "jarjar",
+			Output:      outputFile,
+			Input:       classesJar,
+			Implicit:    rulesFile,
+			Args: map[string]string{
+				"rulesFile":    rulesFile.String(),
+				"total_shards": "1",
+				"shard_index":  "0",
+			},
+		})
+		return
+	}
+
+	// Otherwise, run multiple jarjar instances and use merge_zips to combine the output.
+	tempJars := make([]android.Path, 0)
+	totalStr := strconv.Itoa(totalShards)
+	for i := 0; i < totalShards; i++ {
+		iStr := strconv.Itoa(i)
+		tempOut := outputFile.ReplaceExtension(ctx, "-"+iStr+".jar")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        jarjar,
+			Description: "jarjar (" + iStr + "/" + totalStr + ")",
+			Output:      tempOut,
+			Input:       classesJar,
+			Implicit:    rulesFile,
+			Args: map[string]string{
+				"rulesFile":    rulesFile.String(),
+				"total_shards": totalStr,
+				"shard_index":  iStr,
+			},
+		})
+		tempJars = append(tempJars, tempOut)
+	}
+
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        jarjar,
-		Description: "jarjar",
+		Rule:        combineJar,
+		Description: "merge jarjar shards",
 		Output:      outputFile,
-		Input:       classesJar,
-		Implicit:    rulesFile,
-		Args: map[string]string{
-			"rulesFile": rulesFile.String(),
-		},
+		Inputs:      tempJars,
 	})
+
 }
 
 func CheckJarPackages(ctx android.ModuleContext, outputFile android.WritablePath,
diff --git a/java/classpath_element.go b/java/classpath_element.go
index abbcae7..4af2770 100644
--- a/java/classpath_element.go
+++ b/java/classpath_element.go
@@ -108,33 +108,18 @@
 //
 // e.g. Given the following input:
 //
-//	libraries: com.android.art:core-oj, com.android.art:core-libart, framework, ext
-//	fragments: com.android.art:art-bootclasspath-fragment
+//		libraries: core-oj, core-libart, framework, ext
+//		fragments: art-bootclasspath-fragment
+//	    libraryToApex: core-oj: com.android.art, core-libart: com.android.art
+//	    apexNameToFragment: com.android.art: art-bootclasspath-fragment
 //
 // Then this will return:
 //
 //	ClasspathFragmentElement(art-bootclasspath-fragment, [core-oj, core-libart]),
 //	ClasspathLibraryElement(framework),
 //	ClasspathLibraryElement(ext),
-func CreateClasspathElements(ctx ClasspathElementContext, libraries []android.Module, fragments []android.Module) ClasspathElements {
-	// Create a map from apex name to the fragment module. This makes it easy to find the fragment
-	// associated with a particular apex.
-	apexToFragment := map[string]android.Module{}
-	for _, fragment := range fragments {
-		apexInfo, ok := android.OtherModuleProvider(ctx, fragment, android.ApexInfoProvider)
-		if !ok {
-			ctx.ModuleErrorf("fragment %s is not part of an apex", fragment)
-			continue
-		}
-
-		for _, apex := range apexInfo.InApexVariants {
-			if existing, ok := apexToFragment[apex]; ok {
-				ctx.ModuleErrorf("apex %s has multiple fragments, %s and %s", apex, fragment, existing)
-				continue
-			}
-			apexToFragment[apex] = fragment
-		}
-	}
+func CreateClasspathElements(ctx ClasspathElementContext, libraries []android.Module, fragments []android.Module,
+	libraryToApex map[android.Module]string, apexNameToFragment map[string]android.Module) ClasspathElements {
 
 	fragmentToElement := map[android.Module]*ClasspathFragmentElement{}
 	elements := []ClasspathElement{}
@@ -144,31 +129,28 @@
 	// Iterate over the libraries to construct the ClasspathElements list.
 	for _, library := range libraries {
 		var element ClasspathElement
-		if apexInfo, ok := android.OtherModuleProvider(ctx, library, android.ApexInfoProvider); ok {
-
+		if libraryApex, ok := libraryToApex[library]; ok {
 			var fragment android.Module
 
 			// Make sure that the library is in only one fragment of the classpath.
-			for _, apex := range apexInfo.InApexVariants {
-				if f, ok := apexToFragment[apex]; ok {
-					if fragment == nil {
-						// This is the first fragment so just save it away.
-						fragment = f
-					} else if f != fragment {
-						// This apex variant of the library is in a different fragment.
-						ctx.ModuleErrorf("library %s is in two separate fragments, %s and %s", library, fragment, f)
-						// Skip over this library entirely as otherwise the resulting classpath elements would
-						// be invalid.
-						continue skipLibrary
-					}
-				} else {
-					// There is no fragment associated with the library's apex.
+			if f, ok := apexNameToFragment[libraryApex]; ok {
+				if fragment == nil {
+					// This is the first fragment so just save it away.
+					fragment = f
+				} else if f != fragment {
+					// This apex variant of the library is in a different fragment.
+					ctx.ModuleErrorf("library %s is in two separate fragments, %s and %s", library, fragment, f)
+					// Skip over this library entirely as otherwise the resulting classpath elements would
+					// be invalid.
+					continue skipLibrary
 				}
+			} else {
+				// There is no fragment associated with the library's apex.
 			}
 
 			if fragment == nil {
 				ctx.ModuleErrorf("library %s is from apexes %s which have no corresponding fragment in %s",
-					library, apexInfo.InApexVariants, fragments)
+					library, []string{libraryApex}, fragments)
 				// Skip over this library entirely as otherwise the resulting classpath elements would
 				// be invalid.
 				continue skipLibrary
diff --git a/java/config/config.go b/java/config/config.go
index 87703d8..fdb8d78 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -159,6 +159,7 @@
 	pctx.SourcePathVariable("ResourceProcessorBusyBox", "prebuilts/bazel/common/android_tools/android_tools/all_android_tools_deploy.jar")
 
 	pctx.HostBinToolVariable("GenKotlinBuildFileCmd", "gen-kotlin-build-file")
+	pctx.HostBinToolVariable("FindInputDeltaCmd", "find_input_delta")
 
 	pctx.SourcePathVariable("JarArgsCmd", "build/soong/scripts/jar-args.sh")
 	pctx.SourcePathVariable("PackageCheckCmd", "build/soong/scripts/package-check.sh")
@@ -170,7 +171,9 @@
 	pctx.HostBinToolVariable("ApiCheckCmd", "apicheck")
 	pctx.HostBinToolVariable("D8Cmd", "d8")
 	pctx.HostBinToolVariable("R8Cmd", "r8")
+	pctx.HostBinToolVariable("ExtractR8RulesCmd", "extract-r8-rules")
 	pctx.HostBinToolVariable("ResourceShrinkerCmd", "resourceshrinker")
+	pctx.HostBinToolVariable("TraceReferencesCmd", "tracereferences")
 	pctx.HostBinToolVariable("HiddenAPICmd", "hiddenapi")
 	pctx.HostBinToolVariable("ExtractApksCmd", "extract_apks")
 	pctx.VariableFunc("TurbineJar", func(ctx android.PackageVarContext) string {
diff --git a/java/config/kotlin.go b/java/config/kotlin.go
index 302d021..ffb025d 100644
--- a/java/config/kotlin.go
+++ b/java/config/kotlin.go
@@ -21,6 +21,7 @@
 	KotlincIllegalFlags = []string{
 		"-no-jdk",
 		"-no-stdlib",
+		"-language-version",
 	}
 )
 
@@ -56,5 +57,5 @@
 	// 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"}, " "))
+	pctx.StaticVariable("KotlincKytheGlobalFlags", strings.Join([]string{}, " "))
 }
diff --git a/java/container_test.go b/java/container_test.go
index 25cfa4c..35a3020 100644
--- a/java/container_test.go
+++ b/java/container_test.go
@@ -26,6 +26,7 @@
 }
 
 func TestJavaContainersModuleProperties(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 	).RunTestWithBp(t, `
@@ -154,7 +155,7 @@
 	}
 
 	for _, c := range testcases {
-		m := result.ModuleForTests(c.moduleName, "android_common")
+		m := result.ModuleForTests(t, c.moduleName, "android_common")
 		containers, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), m.Module(), android.ContainersInfoProvider)
 		belongingContainers := containers.BelongingContainers()
 		checkContainerMatch(t, c.moduleName, "system", c.isSystemContainer, android.InList(android.SystemContainer, belongingContainers))
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index bfacea6..04def3e 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -140,7 +140,7 @@
 		d.combinedHeaderJar = d.headerJars[0]
 	}
 
-	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+	javaInfo := &JavaInfo{
 		HeaderJars:                             d.headerJars,
 		LocalHeaderJars:                        d.headerJars,
 		TransitiveStaticLibsHeaderJars:         depset.New(depset.PREORDER, nil, transitiveHeaderJars),
@@ -154,7 +154,9 @@
 		StubsLinkType:                          Implementation,
 		// TODO: Not sure if aconfig flags that have been moved between device and host variants
 		// make sense.
-	})
+	}
+	setExtraJavaInfo(ctx, d, javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
 
 }
 
diff --git a/java/device_host_converter_test.go b/java/device_host_converter_test.go
index 6ccc5c1..42e3b46 100644
--- a/java/device_host_converter_test.go
+++ b/java/device_host_converter_test.go
@@ -15,13 +15,15 @@
 package java
 
 import (
-	"android/soong/android"
 	"slices"
 	"strings"
 	"testing"
+
+	"android/soong/android"
 )
 
 func TestDeviceForHost(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "device_module",
@@ -52,15 +54,15 @@
 
 	ctx, config := testJava(t, bp)
 
-	deviceModule := ctx.ModuleForTests("device_module", "android_common")
-	deviceTurbineCombined := deviceModule.Output("turbine-combined/device_module.jar")
+	deviceModule := ctx.ModuleForTests(t, "device_module", "android_common")
+	deviceTurbine := deviceModule.Output("turbine/device_module.jar")
 	deviceJavac := deviceModule.Output("javac/device_module.jar")
 	deviceRes := deviceModule.Output("res/device_module.jar")
 
-	deviceImportModule := ctx.ModuleForTests("device_import_module", "android_common")
-	deviceImportCombined := deviceImportModule.Output("combined/device_import_module.jar")
+	deviceImportModule := ctx.ModuleForTests(t, "device_import_module", "android_common")
+	deviceImportCombined := deviceImportModule.Output("local-combined/device_import_module.jar")
 
-	hostModule := ctx.ModuleForTests("host_module", config.BuildOSCommonTarget.String())
+	hostModule := ctx.ModuleForTests(t, "host_module", config.BuildOSCommonTarget.String())
 	hostJavac := hostModule.Output("javac/host_module.jar")
 	hostRes := hostModule.Output("res/host_module.jar")
 	combined := hostModule.Output("combined/host_module.jar")
@@ -68,7 +70,7 @@
 
 	// check classpath of host module with dependency on device_for_host_module
 	expectedClasspath := "-classpath " + strings.Join(android.Paths{
-		deviceTurbineCombined.Output,
+		deviceTurbine.Output,
 		deviceImportCombined.Output,
 	}.Strings(), ":")
 
@@ -102,6 +104,7 @@
 }
 
 func TestHostForDevice(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library_host {
 			name: "host_module",
@@ -133,15 +136,15 @@
 
 	ctx, config := testJava(t, bp)
 
-	hostModule := ctx.ModuleForTests("host_module", config.BuildOSCommonTarget.String())
+	hostModule := ctx.ModuleForTests(t, "host_module", config.BuildOSCommonTarget.String())
 	hostJavac := hostModule.Output("javac/host_module.jar")
-	hostJavacHeader := hostModule.Output("javac-header/host_module.jar")
+	hostJavacHeader := hostModule.Output("local-javac-header/host_module.jar")
 	hostRes := hostModule.Output("res/host_module.jar")
 
-	hostImportModule := ctx.ModuleForTests("host_import_module", config.BuildOSCommonTarget.String())
-	hostImportCombined := hostImportModule.Output("combined/host_import_module.jar")
+	hostImportModule := ctx.ModuleForTests(t, "host_import_module", config.BuildOSCommonTarget.String())
+	hostImportCombined := hostImportModule.Output("local-combined/host_import_module.jar")
 
-	deviceModule := ctx.ModuleForTests("device_module", "android_common")
+	deviceModule := ctx.ModuleForTests(t, "device_module", "android_common")
 	deviceJavac := deviceModule.Output("javac/device_module.jar")
 	deviceRes := deviceModule.Output("res/device_module.jar")
 	combined := deviceModule.Output("combined/device_module.jar")
diff --git a/java/dex.go b/java/dex.go
index 2b3c931..e3058e9 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -42,13 +42,39 @@
 		// True if the module containing this has it set by default.
 		EnabledByDefault bool `blueprint:"mutated"`
 
+		// If true, then `d8` will be used on eng builds instead of `r8`, even though
+		// optimize.enabled is true.
+		D8_on_eng *bool
+
+		// Whether to allow that library classes inherit from program classes.
+		// Defaults to false.
+		Ignore_library_extends_program *bool
+
 		// Whether to continue building even if warnings are emitted.  Defaults to true.
 		Ignore_warnings *bool
 
+		// Whether runtime invisible annotations should be kept by R8. Defaults to false.
+		// This is equivalent to:
+		//   -keepattributes RuntimeInvisibleAnnotations,
+		//                   RuntimeInvisibleParameterAnnotations,
+		//                   RuntimeInvisibleTypeAnnotations
+		// This is only applicable when RELEASE_R8_ONLY_RUNTIME_VISIBLE_ANNOTATIONS is
+		// enabled and will be used to migrate away from keeping runtime invisible
+		// annotations (b/387958004).
+		Keep_runtime_invisible_annotations *bool
+
 		// If true, runs R8 in Proguard compatibility mode, otherwise runs R8 in full mode.
-		// Defaults to false for apps, true for libraries and tests.
+		// Defaults to false for apps and tests, true for libraries.
 		Proguard_compatibility *bool
 
+		// If true, R8 will not add public or protected members (fields or methods) to
+		// the API surface of the compilation unit, i.e., classes that are kept or
+		// have kept subclasses will not expose any members added by R8 for internal
+		// use. That includes renamed members if obfuscation is enabled.
+		// This should only be used for building targets that go on the bootclasspath.
+		// Defaults to false.
+		Protect_api_surface *bool
+
 		// If true, optimize for size by removing unused code.  Defaults to true for apps,
 		// false for libraries and tests.
 		Shrink *bool
@@ -81,6 +107,34 @@
 		// If true, transitive reverse dependencies of this module will have this
 		// module's proguard spec appended to their optimization action
 		Export_proguard_flags_files *bool
+
+		// Path to a file containing a list of class names that should not be compiled using R8.
+		// These classes will be compiled by D8 similar to when Optimize.Enabled is false.
+		//
+		// Example:
+		//
+		//   r8.exclude:
+		//   com.example.Foo
+		//   com.example.Bar
+		//   com.example.Bar$Baz
+		//
+		// By default all classes are compiled using R8 when Optimize.Enabled is set.
+		Exclude *string `android:"path"`
+
+		// Optional list of downstream (Java) libraries from which to trace and preserve references
+		// when optimizing. Note that this requires that the source reference does *not* have
+		// a strict lib dependency on this target; dependencies should be on intermediate targets
+		// statically linked into this target, e.g., if A references B, and we want to trace and
+		// keep references from A when optimizing B, you would create an intermediate B.impl (
+		// containing all static code), have A depend on `B.impl` via libs, and set
+		// `trace_references_from: ["A"]` on B.
+		//
+		// Also note that these are *not* inherited across targets, they must be specified at the
+		// top-level target that is optimized.
+		//
+		// TODO(b/212737576): Handle this implicitly using bottom-up deps mutation and implicit
+		// creation of a proxy `.impl` library.
+		Trace_references_from proptools.Configurable[[]string] `android:"arch_variant"`
 	}
 
 	// Keep the data uncompressed. We always need uncompressed dex for execution,
@@ -111,7 +165,12 @@
 	providesTransitiveHeaderJarsForR8
 }
 
-func (d *dexer) effectiveOptimizeEnabled() bool {
+func (d *dexer) effectiveOptimizeEnabled(ctx android.EarlyModuleContext) bool {
+	// For eng builds, if Optimize.D8_on_eng is true, then disable optimization.
+	if ctx.Config().Eng() && proptools.Bool(d.dexProperties.Optimize.D8_on_eng) {
+		return false
+	}
+	// Otherwise, use the legacy logic of a default value which can be explicitly overridden by the module.
 	return BoolDefault(d.dexProperties.Optimize.Enabled, d.dexProperties.Optimize.EnabledByDefault)
 }
 
@@ -123,8 +182,8 @@
 	return d.resourceShrinkingEnabled(ctx) && BoolDefault(d.Optimize.Optimized_shrink_resources, ctx.Config().UseOptimizedResourceShrinkingByDefault())
 }
 
-func (d *dexer) optimizeOrObfuscateEnabled() bool {
-	return d.effectiveOptimizeEnabled() && (proptools.Bool(d.dexProperties.Optimize.Optimize) || proptools.Bool(d.dexProperties.Optimize.Obfuscate))
+func (d *dexer) optimizeOrObfuscateEnabled(ctx android.EarlyModuleContext) bool {
+	return d.effectiveOptimizeEnabled(ctx) && (proptools.Bool(d.dexProperties.Optimize.Optimize) || proptools.Bool(d.dexProperties.Optimize.Obfuscate))
 }
 
 var d8, d8RE = pctx.MultiCommandRemoteStaticRules("d8",
@@ -156,6 +215,71 @@
 		},
 	}, []string{"outDir", "d8Flags", "zipFlags", "mergeZipsFlags"}, nil)
 
+// Include all of the args for d8r8, so that we can generate the partialcompileclean target's build using the same list.
+var d8r8Clean = pctx.AndroidStaticRule("d8r8-partialcompileclean",
+	blueprint.RuleParams{
+		Command: `rm -rf "${outDir}" "${outDict}" "${outConfig}" "${outUsage}" "${outUsageZip}" "${outUsageDir}" ` +
+			`"${resourcesOutput}" "${outR8ArtProfile}" ${builtOut}`,
+	}, "outDir", "outDict", "outConfig", "outUsage", "outUsageZip", "outUsageDir", "builtOut",
+	"d8Flags", "r8Flags", "zipFlags", "mergeZipsFlags", "resourcesOutput", "outR8ArtProfile", "implicits",
+)
+
+var d8r8, d8r8RE = pctx.MultiCommandRemoteStaticRules("d8r8",
+	blueprint.RuleParams{
+		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
+			`rm -f "$outDict" && rm -f "$outConfig" && rm -rf "${outUsageDir}" && ` +
+			`mkdir -p $$(dirname ${outUsage}) && ` +
+			`if [ -n "$${SOONG_USE_PARTIAL_COMPILE}" ]; then ` +
+			` for f in "${outConfig}" "${outDict}" "${outUsage}" "${resourcesOutput}"; do ` +
+			`   test -n "$${f}" && test ! -f "$${f}" && mkdir -p "$$(dirname "$${f}")" && touch "$${f}" || true; ` +
+			` done && ` +
+			` $d8Template${config.D8Cmd} ${config.D8Flags} $d8Flags --output $outDir --no-dex-input-jar $in; ` +
+			`else ` +
+			` $r8Template${config.R8Cmd} ${config.R8Flags} $r8Flags -injars $in --output $outDir ` +
+			` --no-data-resources ` +
+			` -printmapping ${outDict} ` +
+			` -printconfiguration ${outConfig} ` +
+			` -printusage ${outUsage} ` +
+			` --deps-file ${out}.d && ` +
+			` touch "${outDict}" "${outConfig}" "${outUsage}"; ` +
+			`fi && ` +
+			`${config.SoongZipCmd} -o ${outUsageZip} -C ${outUsageDir} -f ${outUsage} && ` +
+			`rm -rf ${outUsageDir} && ` +
+			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
+			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in && ` +
+			`rm -f "$outDir"/classes*.dex "$outDir/classes.dex.jar" `,
+		CommandDeps: []string{
+			"${config.D8Cmd}",
+			"${config.R8Cmd}",
+			"${config.SoongZipCmd}",
+			"${config.MergeZipsCmd}",
+		},
+	}, map[string]*remoteexec.REParams{
+		"$d8Template": &remoteexec.REParams{
+			Labels:          map[string]string{"type": "compile", "compiler": "d8"},
+			Inputs:          []string{"${config.D8Jar}"},
+			ExecStrategy:    "${config.RED8ExecStrategy}",
+			ToolchainInputs: []string{"${config.JavaCmd}"},
+			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		},
+		"$r8Template": &remoteexec.REParams{
+			Labels:          map[string]string{"type": "compile", "compiler": "r8"},
+			Inputs:          []string{"$implicits", "${config.R8Jar}"},
+			OutputFiles:     []string{"${outUsage}", "${outConfig}", "${outDict}", "${resourcesOutput}", "${outR8ArtProfile}"},
+			ExecStrategy:    "${config.RER8ExecStrategy}",
+			ToolchainInputs: []string{"${config.JavaCmd}"},
+			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		},
+		"$zipTemplate": &remoteexec.REParams{
+			Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
+			Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
+			OutputFiles:  []string{"$outDir/classes.dex.jar"},
+			ExecStrategy: "${config.RED8ExecStrategy}",
+			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		},
+	}, []string{"outDir", "outDict", "outConfig", "outUsage", "outUsageZip", "outUsageDir",
+		"d8Flags", "r8Flags", "zipFlags", "mergeZipsFlags", "resourcesOutput", "outR8ArtProfile"}, []string{"implicits"})
+
 var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
@@ -220,21 +344,29 @@
 		deps = append(deps, f)
 	}
 
-	var requestReleaseMode bool
+	var requestReleaseMode, requestDebugMode bool
 	requestReleaseMode, flags = android.RemoveFromList("--release", flags)
+	requestDebugMode, flags = android.RemoveFromList("--debug", flags)
 
 	if ctx.Config().Getenv("NO_OPTIMIZE_DX") != "" || ctx.Config().Getenv("GENERATE_DEX_DEBUG") != "" {
-		flags = append(flags, "--debug")
+		requestDebugMode = true
 		requestReleaseMode = false
 	}
 
 	// 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 {
+	if requestDebugMode {
+		flags = append(flags, "--debug")
+	} else if requestReleaseMode {
 		flags = append(flags, "--release")
 	} else if ctx.Config().Eng() {
 		flags = append(flags, "--debug")
+	} else if !d.effectiveOptimizeEnabled(ctx) && d.dexProperties.Optimize.EnabledByDefault {
+		// D8 uses --debug by default, whereas R8 uses --release by default.
+		// For targets that default to R8 usage (e.g., apps), but override this default, we still
+		// want D8 to run in release mode, preserving semantics as much as possible between the two.
+		flags = append(flags, "--release")
 	}
 
 	// Supplying the platform build flag disables various features like API modeling and desugaring.
@@ -306,7 +438,7 @@
 	// TODO(b/360905238): Remove SdkSystemServer exception after resolving missing class references.
 	if !dexParams.sdkVersion.Stable() || dexParams.sdkVersion.Kind == android.SdkSystemServer {
 		var proguardRaiseDeps classpath
-		ctx.VisitDirectDepsWithTag(proguardRaiseTag, func(m android.Module) {
+		ctx.VisitDirectDepsProxyWithTag(proguardRaiseTag, func(m android.ModuleProxy) {
 			if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
 				proguardRaiseDeps = append(proguardRaiseDeps, dep.RepackagedHeaderJars...)
 			}
@@ -340,11 +472,30 @@
 		android.PathForSource(ctx, "build/make/core/proguard.flags"),
 	}
 
+	if ctx.Config().UseR8GlobalCheckNotNullFlags() {
+		flagFiles = append(flagFiles, android.PathForSource(ctx,
+			"build/make/core/proguard/checknotnull.flags"))
+	}
+
 	flagFiles = append(flagFiles, d.extraProguardFlagsFiles...)
 	// TODO(ccross): static android library proguard files
 
 	flagFiles = append(flagFiles, android.PathsForModuleSrc(ctx, opt.Proguard_flags_files)...)
 
+	traceReferencesSources := android.Paths{}
+	ctx.VisitDirectDepsProxyWithTag(traceReferencesTag, func(m android.ModuleProxy) {
+		if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
+			traceReferencesSources = append(traceReferencesSources, dep.ImplementationJars...)
+		}
+	})
+	if len(traceReferencesSources) > 0 {
+		traceTarget := dexParams.classesJar
+		traceLibs := android.FirstUniquePaths(append(flags.bootClasspath.Paths(), flags.dexClasspath.Paths()...))
+		traceReferencesFlags := android.PathForModuleOut(ctx, "proguard", "trace_references.flags")
+		TraceReferences(ctx, traceReferencesSources, traceTarget, traceLibs, traceReferencesFlags)
+		flagFiles = append(flagFiles, traceReferencesFlags)
+	}
+
 	flagFiles = android.FirstUniquePaths(flagFiles)
 
 	r8Flags = append(r8Flags, android.JoinWithPrefix(flagFiles.Strings(), "-include "))
@@ -356,10 +507,22 @@
 
 	r8Flags = append(r8Flags, opt.Proguard_flags...)
 
-	if BoolDefault(opt.Proguard_compatibility, true) {
+	if BoolDefault(opt.Ignore_library_extends_program, false) {
+		r8Flags = append(r8Flags, "--ignore-library-extends-program")
+	}
+
+	if BoolDefault(opt.Keep_runtime_invisible_annotations, false) {
+		r8Flags = append(r8Flags, "--keep-runtime-invisible-annotations")
+	}
+
+	if BoolDefault(opt.Proguard_compatibility, !ctx.Config().UseR8FullModeByDefault()) {
 		r8Flags = append(r8Flags, "--force-proguard-compatibility")
 	}
 
+	if BoolDefault(opt.Protect_api_surface, false) {
+		r8Flags = append(r8Flags, "--protect-api-surface")
+	}
+
 	// Avoid unnecessary stack frame noise by only injecting source map ids for non-debug
 	// optimized or obfuscated targets.
 	if (Bool(opt.Optimize) || Bool(opt.Obfuscate)) && !debugMode {
@@ -412,6 +575,15 @@
 		artProfileOutput = profileOutput
 	}
 
+	if ctx.Config().UseR8StoreStoreFenceConstructorInlining() {
+		r8Flags = append(r8Flags, "--store-store-fence-constructor-inlining")
+	}
+
+	if opt.Exclude != nil {
+		r8Flags = append(r8Flags, "--exclude", *opt.Exclude)
+		r8Deps = append(r8Deps, android.PathForModuleSrc(ctx, *opt.Exclude))
+	}
+
 	return r8Flags, r8Deps, artProfileOutput
 }
 
@@ -448,6 +620,7 @@
 
 	// Compile classes.jar into classes.dex and then javalib.jar
 	javalibJar := android.PathForModuleOut(ctx, "dex", dexParams.jarName).OutputPath
+	cleanPhonyPath := android.PathForModuleOut(ctx, "dex", dexParams.jarName+"-partialcompileclean").OutputPath
 	outDir := android.PathForModuleOut(ctx, "dex")
 
 	zipFlags := "--ignore_missing_files"
@@ -463,8 +636,20 @@
 		mergeZipsFlags = "-stripFile META-INF/*.kotlin_module -stripFile **/*.kotlin_builtins"
 	}
 
-	useR8 := d.effectiveOptimizeEnabled()
+	useR8 := d.effectiveOptimizeEnabled(ctx)
+	useD8 := !useR8 || ctx.Config().PartialCompileFlags().Use_d8
+	rbeR8 := ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_R8")
+	rbeD8 := ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8")
+	var rule blueprint.Rule
+	var description string
 	var artProfileOutputPath *android.OutputPath
+	var implicitOutputs android.WritablePaths
+	var deps android.Paths
+	args := map[string]string{
+		"zipFlags":       zipFlags,
+		"outDir":         outDir.String(),
+		"mergeZipsFlags": mergeZipsFlags,
+	}
 	if useR8 {
 		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
 		d.proguardDictionary = android.OptionalPathForPath(proguardDictionary)
@@ -477,83 +662,89 @@
 		d.proguardUsageZip = android.OptionalPathForPath(proguardUsageZip)
 		resourcesOutput := android.PathForModuleOut(ctx, "package-res-shrunken.apk")
 		d.resourcesOutput = android.OptionalPathForPath(resourcesOutput)
-		implicitOutputs := android.WritablePaths{
+		implicitOutputs = append(implicitOutputs, android.WritablePaths{
 			proguardDictionary,
 			proguardUsageZip,
 			proguardConfiguration,
-		}
+		}...)
+		description = "r8"
 		debugMode := android.InList("--debug", commonFlags)
 		r8Flags, r8Deps, r8ArtProfileOutputPath := d.r8Flags(ctx, dexParams, debugMode)
-		rule := r8
-		args := map[string]string{
-			"r8Flags":        strings.Join(append(commonFlags, r8Flags...), " "),
-			"zipFlags":       zipFlags,
-			"outDict":        proguardDictionary.String(),
-			"outConfig":      proguardConfiguration.String(),
-			"outUsageDir":    proguardUsageDir.String(),
-			"outUsage":       proguardUsage.String(),
-			"outUsageZip":    proguardUsageZip.String(),
-			"outDir":         outDir.String(),
-			"mergeZipsFlags": mergeZipsFlags,
-		}
+		deps = append(deps, r8Deps...)
+		args["r8Flags"] = strings.Join(append(commonFlags, r8Flags...), " ")
 		if r8ArtProfileOutputPath != nil {
 			artProfileOutputPath = r8ArtProfileOutputPath
-			implicitOutputs = append(
-				implicitOutputs,
-				artProfileOutputPath,
-			)
 			// Add the implicit r8 Art profile output to args so that r8RE knows
 			// about this implicit output
-			args["outR8ArtProfile"] = artProfileOutputPath.String()
+			args["outR8ArtProfile"] = r8ArtProfileOutputPath.String()
 		}
-
-		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_R8") {
-			rule = r8RE
-			args["implicits"] = strings.Join(r8Deps.Strings(), ",")
-		}
+		args["outDict"] = proguardDictionary.String()
+		args["outConfig"] = proguardConfiguration.String()
+		args["outUsageDir"] = proguardUsageDir.String()
+		args["outUsage"] = proguardUsage.String()
+		args["outUsageZip"] = proguardUsageZip.String()
 		if d.resourcesInput.Valid() {
 			implicitOutputs = append(implicitOutputs, resourcesOutput)
 			args["resourcesOutput"] = resourcesOutput.String()
 		}
-		ctx.Build(pctx, android.BuildParams{
-			Rule:            rule,
-			Description:     "r8",
-			Output:          javalibJar,
-			ImplicitOutputs: implicitOutputs,
-			Input:           dexParams.classesJar,
-			Implicits:       r8Deps,
-			Args:            args,
-		})
-	} else {
-		implicitOutputs := android.WritablePaths{}
+
+		rule = r8
+		if rbeR8 {
+			rule = r8RE
+			args["implicits"] = strings.Join(deps.Strings(), ",")
+		}
+	}
+	if useD8 {
+		description = "d8"
 		d8Flags, d8Deps, d8ArtProfileOutputPath := d.d8Flags(ctx, dexParams)
+		deps = append(deps, d8Deps...)
+		deps = append(deps, commonDeps...)
+		args["d8Flags"] = strings.Join(append(commonFlags, d8Flags...), " ")
 		if d8ArtProfileOutputPath != nil {
 			artProfileOutputPath = d8ArtProfileOutputPath
-			implicitOutputs = append(
-				implicitOutputs,
-				artProfileOutputPath,
-			)
 		}
-		d8Deps = append(d8Deps, commonDeps...)
-		rule := d8
-		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8") {
+		// If we are generating both d8 and r8, only use RBE when both are enabled.
+		switch {
+		case useR8 && rule == r8:
+			rule = d8r8
+			description = "d8r8"
+		case useR8 && rule == r8RE && rbeD8:
+			rule = d8r8RE
+			description = "d8r8"
+		case rbeD8:
 			rule = d8RE
+		default:
+			rule = d8
 		}
-		ctx.Build(pctx, android.BuildParams{
-			Rule:            rule,
-			Description:     "d8",
-			Output:          javalibJar,
-			Input:           dexParams.classesJar,
-			ImplicitOutputs: implicitOutputs,
-			Implicits:       d8Deps,
-			Args: map[string]string{
-				"d8Flags":        strings.Join(append(commonFlags, d8Flags...), " "),
-				"zipFlags":       zipFlags,
-				"outDir":         outDir.String(),
-				"mergeZipsFlags": mergeZipsFlags,
-			},
-		})
 	}
+	if artProfileOutputPath != nil {
+		implicitOutputs = append(
+			implicitOutputs,
+			artProfileOutputPath,
+		)
+	}
+	ctx.Build(pctx, android.BuildParams{
+		Rule:            rule,
+		Description:     description,
+		Output:          javalibJar,
+		ImplicitOutputs: implicitOutputs,
+		Input:           dexParams.classesJar,
+		Implicits:       deps,
+		Args:            args,
+	})
+	if useR8 && useD8 {
+		// Generate the rule for partial compile clean.
+		args["builtOut"] = javalibJar.String()
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        d8r8Clean,
+			Description: "d8r8Clean",
+			Output:      cleanPhonyPath,
+			Args:        args,
+			PhonyOutput: true,
+		})
+		ctx.Phony("partialcompileclean", cleanPhonyPath)
+	}
+
 	if proptools.Bool(d.dexProperties.Uncompress_dex) {
 		alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", dexParams.jarName).OutputPath
 		TransformZipAlign(ctx, alignedJavalibJar, javalibJar, nil)
@@ -562,3 +753,13 @@
 
 	return javalibJar, artProfileOutputPath
 }
+
+type ProguardInfo struct {
+	ModuleName         string
+	Class              string
+	ProguardDictionary android.Path
+	ProguardUsageZip   android.Path
+	ClassesJar         android.Path
+}
+
+var ProguardProvider = blueprint.NewProvider[ProguardInfo]()
diff --git a/java/dex_test.go b/java/dex_test.go
index 8bc28e6..8c1e5f7 100644
--- a/java/dex_test.go
+++ b/java/dex_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"strconv"
 	"testing"
 
 	"android/soong/android"
@@ -24,6 +25,7 @@
 )
 
 func TestR8(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app {
 			name: "app",
@@ -58,26 +60,27 @@
 		}
 	`)
 
-	app := result.ModuleForTests("app", "android_common")
-	stableApp := result.ModuleForTests("stable_app", "android_common")
-	corePlatformApp := result.ModuleForTests("core_platform_app", "android_common")
-	lib := result.ModuleForTests("lib", "android_common")
-	staticLib := result.ModuleForTests("static_lib", "android_common")
+	app := result.ModuleForTests(t, "app", "android_common")
+	stableApp := result.ModuleForTests(t, "stable_app", "android_common")
+	corePlatformApp := result.ModuleForTests(t, "core_platform_app", "android_common")
+	lib := result.ModuleForTests(t, "lib", "android_common")
+	staticLib := result.ModuleForTests(t, "static_lib", "android_common")
 
 	appJavac := app.Rule("javac")
 	appR8 := app.Rule("r8")
 	stableAppR8 := stableApp.Rule("r8")
 	corePlatformAppR8 := corePlatformApp.Rule("r8")
-	libHeader := lib.Output("turbine-combined/lib.jar").Output
-	staticLibHeader := staticLib.Output("turbine-combined/static_lib.jar").Output
+	libHeader := lib.Output("turbine/lib.jar").Output
+	libCombinedHeader := lib.Output("turbine-combined/lib.jar").Output
+	staticLibHeader := staticLib.Output("turbine/static_lib.jar").Output
 
 	android.AssertStringDoesContain(t, "expected lib header jar in app javac classpath",
 		appJavac.Args["classpath"], libHeader.String())
 	android.AssertStringDoesContain(t, "expected static_lib header jar in app javac classpath",
 		appJavac.Args["classpath"], staticLibHeader.String())
 
-	android.AssertStringDoesContain(t, "expected lib header jar in app r8 classpath",
-		appR8.Args["r8Flags"], libHeader.String())
+	android.AssertStringDoesContain(t, "expected lib combined header jar in app r8 classpath",
+		appR8.Args["r8Flags"], libCombinedHeader.String())
 	android.AssertStringDoesNotContain(t, "expected no static_lib header jar in app r8 classpath",
 		appR8.Args["r8Flags"], staticLibHeader.String())
 	android.AssertStringDoesContain(t, "expected -ignorewarnings in app r8 flags",
@@ -91,6 +94,7 @@
 }
 
 func TestR8TransitiveDeps(t *testing.T) {
+	t.Parallel()
 	bp := `
 		override_android_app {
 			name: "override_app",
@@ -192,6 +196,7 @@
 
 	for _, tc := range testcases {
 		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
 			fixturePreparer := PrepareForTestWithJavaDefaultModules
 			if tc.unbundled {
 				fixturePreparer = android.GroupFixturePreparers(
@@ -206,14 +211,14 @@
 			result := fixturePreparer.RunTestWithBp(t, bp)
 
 			getHeaderJar := func(name string) android.Path {
-				mod := result.ModuleForTests(name, "android_common")
+				mod := result.ModuleForTests(t, name, "android_common")
 				return mod.Output("turbine-combined/" + name + ".jar").Output
 			}
 
-			appR8 := result.ModuleForTests("app", "android_common").Rule("r8")
-			overrideAppR8 := result.ModuleForTests("app", "android_common_override_app").Rule("r8")
+			appR8 := result.ModuleForTests(t, "app", "android_common").Rule("r8")
+			overrideAppR8 := result.ModuleForTests(t, "app", "android_common_override_app").Rule("r8")
 			appHeader := getHeaderJar("app")
-			overrideAppHeader := result.ModuleForTests("app", "android_common_override_app").Output("turbine-combined/app.jar").Output
+			overrideAppHeader := result.ModuleForTests(t, "app", "android_common_override_app").Output("turbine-combined/app.jar").Output
 			libHeader := getHeaderJar("lib")
 			transitiveLibHeader := getHeaderJar("transitive_lib")
 			transitiveLib2Header := getHeaderJar("transitive_lib_2")
@@ -222,7 +227,7 @@
 			repeatedDepHeader := getHeaderJar("repeated_dep")
 			usesLibHeader := getHeaderJar("uses_lib")
 			optionalUsesLibHeader := getHeaderJar("optional_uses_lib")
-			prebuiltLibHeader := result.ModuleForTests("prebuilt_lib", "android_common").Output("combined/lib.jar").Output
+			prebuiltLibHeader := result.ModuleForTests(t, "prebuilt_lib", "android_common").Output("combined/lib.jar").Output
 
 			for _, rule := range []android.TestingBuildParams{appR8, overrideAppR8} {
 				android.AssertStringDoesNotContain(t, "expected no app header jar in app r8 classpath",
@@ -259,6 +264,7 @@
 }
 
 func TestR8Flags(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app {
 			name: "app",
@@ -273,7 +279,7 @@
 		}
 	`)
 
-	app := result.ModuleForTests("app", "android_common")
+	app := result.ModuleForTests(t, "app", "android_common")
 	appR8 := app.Rule("r8")
 	android.AssertStringDoesContain(t, "expected -dontshrink in app r8 flags",
 		appR8.Args["r8Flags"], "-dontshrink")
@@ -288,6 +294,7 @@
 }
 
 func TestD8(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -306,29 +313,52 @@
 			name: "static_lib",
 			srcs: ["foo.java"],
 		}
+
+		android_app {
+			name: "app",
+			srcs: ["foo.java"],
+			platform_apis: true,
+			optimize: {
+				enabled: false,
+			},
+		}
 	`)
 
-	foo := result.ModuleForTests("foo", "android_common")
-	lib := result.ModuleForTests("lib", "android_common")
-	staticLib := result.ModuleForTests("static_lib", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
+	lib := result.ModuleForTests(t, "lib", "android_common")
+	app := result.ModuleForTests(t, "app", "android_common")
+	staticLib := result.ModuleForTests(t, "static_lib", "android_common")
 
 	fooJavac := foo.Rule("javac")
 	fooD8 := foo.Rule("d8")
-	libHeader := lib.Output("turbine-combined/lib.jar").Output
-	staticLibHeader := staticLib.Output("turbine-combined/static_lib.jar").Output
+	appD8 := app.Rule("d8")
+	libHeader := lib.Output("turbine/lib.jar").Output
+	libCombinedHeader := lib.Output("turbine-combined/lib.jar").Output
+	staticLibHeader := staticLib.Output("turbine/static_lib.jar").Output
 
 	android.AssertStringDoesContain(t, "expected lib header jar in foo javac classpath",
 		fooJavac.Args["classpath"], libHeader.String())
 	android.AssertStringDoesContain(t, "expected static_lib header jar in foo javac classpath",
 		fooJavac.Args["classpath"], staticLibHeader.String())
 
-	android.AssertStringDoesContain(t, "expected lib header jar in foo d8 classpath",
-		fooD8.Args["d8Flags"], libHeader.String())
+	android.AssertStringDoesContain(t, "expected lib combined header jar in foo d8 classpath",
+		fooD8.Args["d8Flags"], libCombinedHeader.String())
 	android.AssertStringDoesNotContain(t, "expected no  static_lib header jar in foo javac classpath",
 		fooD8.Args["d8Flags"], staticLibHeader.String())
+
+	// A --release flag is added only for targets that opt out of default R8 behavior (e.g., apps).
+	// For library targets that don't use R8 by default, no --debug or --release flag should be
+	// added, instead relying on default D8 behavior (--debug).
+	android.AssertStringDoesContain(t, "expected --release in app d8 flags",
+		appD8.Args["d8Flags"], "--release")
+	android.AssertStringDoesNotContain(t, "expected no --release flag in lib d8 flags",
+		fooD8.Args["d8Flags"], "--release")
+	android.AssertStringDoesNotContain(t, "expected no --debug flag in lib d8 flags",
+		fooD8.Args["d8Flags"], "--debug")
 }
 
 func TestProguardFlagsInheritanceStatic(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app {
 			name: "app",
@@ -370,7 +400,7 @@
 		}
 	`)
 
-	app := result.ModuleForTests("app", "android_common")
+	app := result.ModuleForTests(t, "app", "android_common")
 	appR8 := app.Rule("r8")
 	android.AssertStringDoesContain(t, "expected primary_lib's proguard flags from direct dep",
 		appR8.Args["r8Flags"], "primary.flags")
@@ -383,6 +413,7 @@
 }
 
 func TestProguardFlagsInheritance(t *testing.T) {
+	t.Parallel()
 	directDepFlagsFileName := "direct_dep.flags"
 	transitiveDepFlagsFileName := "transitive_dep.flags"
 
@@ -601,6 +632,7 @@
 	for _, topLevelModuleDef := range topLevelModules {
 		for _, tc := range testcases {
 			t.Run(topLevelModuleDef.name+"-"+tc.name, func(t *testing.T) {
+				t.Parallel()
 				result := android.GroupFixturePreparers(
 					PrepareForTestWithJavaDefaultModules,
 					android.FixtureMergeMockFs(android.MockFS{
@@ -617,7 +649,7 @@
 							tc.transitiveDepExportsFlagsFiles,
 						),
 				)
-				appR8 := result.ModuleForTests("app", "android_common").Rule("r8")
+				appR8 := result.ModuleForTests(t, "app", "android_common").Rule("r8")
 
 				shouldHaveDepFlags := android.InList(directDepFlagsFileName, tc.expectedFlagsFiles)
 				if shouldHaveDepFlags {
@@ -642,6 +674,7 @@
 }
 
 func TestProguardFlagsInheritanceAppImport(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app {
 			name: "app",
@@ -658,12 +691,13 @@
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, bp)
 
-	appR8 := result.ModuleForTests("app", "android_common").Rule("r8")
+	appR8 := result.ModuleForTests(t, "app", "android_common").Rule("r8")
 	android.AssertStringDoesContain(t, "expected aarimports's proguard flags",
 		appR8.Args["r8Flags"], "proguard.txt")
 }
 
 func TestR8FlagsArtProfile(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app {
 			name: "app",
@@ -677,7 +711,7 @@
 		}
 	`)
 
-	app := result.ModuleForTests("app", "android_common")
+	app := result.ModuleForTests(t, "app", "android_common")
 	appR8 := app.Rule("r8")
 	android.AssertStringDoesContain(t, "expected --art-profile in app r8 flags",
 		appR8.Args["r8Flags"], "--art-profile")
@@ -696,6 +730,7 @@
 //
 // The rewritten profile should be used since the dex signatures in the checked-in profile will not match the optimized binary.
 func TestEnableProfileRewritingIsRequiredForOptimizedApps(t *testing.T) {
+	t.Parallel()
 	testJavaError(t,
 		"Enable_profile_rewriting must be true when profile_guided dexpreopt and R8 optimization/obfuscation is turned on",
 		`
@@ -715,11 +750,15 @@
 }
 
 func TestDebugReleaseFlags(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app {
 			name: "app",
 			srcs: ["foo.java"],
 			platform_apis: true,
+			optimize: {
+				enabled: %s,
+			},
 			dxflags: ["%s"]
 		}
 	`
@@ -728,6 +767,7 @@
 		name          string
 		envVar        string
 		isEng         bool
+		useD8         bool
 		dxFlags       string
 		expectedFlags string
 	}{
@@ -757,20 +797,36 @@
 		},
 		{
 			name:          "app_eng",
+			useD8:         true,
 			isEng:         true,
 			expectedFlags: "--debug",
 		},
 		{
 			name:    "app_release_eng",
 			isEng:   true,
+			useD8:   true,
 			dxFlags: "--release",
 			// Eng mode does *not* override explicit dxflags.
 			expectedFlags: "--release",
 		},
+		{
+			name:  "app_d8",
+			useD8: true,
+			// D8 usage w/ apps should explicitly enable --release mode.
+			expectedFlags: "--release",
+		},
+		{
+			name:    "app_d8_debug",
+			useD8:   true,
+			dxFlags: "--debug",
+			// D8 usage w/ apps respects overriding dxFlags.
+			expectedFlags: "--debug",
+		},
 	}
 
 	for _, tc := range testcases {
 		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
 			fixturePreparer := PrepareForTestWithJavaDefaultModules
 			fixturePreparer = android.GroupFixturePreparers(
 				fixturePreparer,
@@ -788,11 +844,16 @@
 					}),
 				)
 			}
-			result := fixturePreparer.RunTestWithBp(t, fmt.Sprintf(bp, tc.dxFlags))
+			result := fixturePreparer.RunTestWithBp(t, fmt.Sprintf(bp, strconv.FormatBool(!tc.useD8), tc.dxFlags))
 
-			appR8 := result.ModuleForTests("app", "android_common").Rule("r8")
-			android.AssertStringDoesContain(t, "expected flag in R8 flags",
-				appR8.Args["r8Flags"], tc.expectedFlags)
+			dexRuleKey := "r8"
+			if tc.useD8 {
+				dexRuleKey = "d8"
+			}
+			dexFlagsKey := dexRuleKey + "Flags"
+			appDex := result.ModuleForTests(t, "app", "android_common").Rule(dexRuleKey)
+			android.AssertStringDoesContain(t, "expected flag in dex flags",
+				appDex.Args[dexFlagsKey], tc.expectedFlags)
 
 			var unexpectedFlags string
 			if tc.expectedFlags == "--debug" {
@@ -801,9 +862,52 @@
 				unexpectedFlags = "--debug"
 			}
 			if unexpectedFlags != "" {
-				android.AssertStringDoesNotContain(t, "unexpected flag in R8 flags",
-					appR8.Args["r8Flags"], unexpectedFlags)
+				android.AssertStringDoesNotContain(t, "unexpected flag in dex flags",
+					appDex.Args[dexFlagsKey], unexpectedFlags)
 			}
 		})
 	}
 }
+
+func TestTraceReferences(t *testing.T) {
+	t.Parallel()
+	bp := `
+		android_app {
+			name: "app",
+			libs: ["lib.impl"],
+			srcs: ["foo.java"],
+			platform_apis: true,
+		}
+
+		java_library {
+			name: "lib",
+			optimize: {
+				enabled: true,
+				trace_references_from: ["app"],
+			},
+			srcs: ["bar.java"],
+			static_libs: ["lib.impl"],
+			installable: true,
+		}
+
+		java_library {
+			name: "lib.impl",
+			srcs: ["baz.java"],
+		}
+	`
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+	).RunTestWithBp(t, bp)
+
+	appJar := result.ModuleForTests(t, "app", "android_common").Output("combined/app.jar").Output
+	libJar := result.ModuleForTests(t, "lib", "android_common").Output("combined/lib.jar").Output
+	libTraceRefs := result.ModuleForTests(t, "lib", "android_common").Rule("traceReferences")
+	libR8 := result.ModuleForTests(t, "lib", "android_common").Rule("r8")
+
+	android.AssertStringDoesContain(t, "expected trace reference source from app jar",
+		libTraceRefs.Args["sources"], "--source "+appJar.String())
+	android.AssertStringEquals(t, "expected trace reference target into lib jar",
+		libJar.String(), libTraceRefs.Input.String())
+	android.AssertStringDoesContain(t, "expected trace reference proguard flags in lib r8 flags",
+		libR8.Args["r8Flags"], "trace_references.flags")
+}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 5928446..e8e1cd4 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -36,61 +36,24 @@
 	// If the java module is to be installed into an APEX, this list contains information about the
 	// dexpreopt outputs to be installed on devices. Note that these dexpreopt outputs are installed
 	// outside of the APEX.
-	DexpreoptBuiltInstalledForApex() []dexpreopterInstall
+	ApexSystemServerDexpreoptInstalls() []DexpreopterInstall
 
-	// The Make entries to install the dexpreopt outputs. Derived from
-	// `DexpreoptBuiltInstalledForApex`.
-	AndroidMkEntriesForApex() []android.AndroidMkEntries
+	// ApexSystemServerDexJars returns the list of dex jars if this is an apex system server jar.
+	ApexSystemServerDexJars() android.Paths
 
 	// See `dexpreopter.outputProfilePathOnHost`.
 	OutputProfilePathOnHost() android.Path
 }
 
-type dexpreopterInstall struct {
-	// A unique name to distinguish an output from others for the same java library module. Usually in
-	// the form of `<arch>-<encoded-path>.odex/vdex/art`.
-	name string
-
-	// The name of the input java module.
-	moduleName string
-
+type DexpreopterInstall struct {
 	// The path to the dexpreopt output on host.
-	outputPathOnHost android.Path
+	OutputPathOnHost android.Path
 
 	// The directory on the device for the output to install to.
-	installDirOnDevice android.InstallPath
+	InstallDirOnDevice android.InstallPath
 
 	// The basename (the last segment of the path) for the output to install as.
-	installFileOnDevice string
-}
-
-// The full module name of the output in the makefile.
-func (install *dexpreopterInstall) FullModuleName() string {
-	return install.moduleName + install.SubModuleName()
-}
-
-// The sub-module name of the output in the makefile (the name excluding the java module name).
-func (install *dexpreopterInstall) SubModuleName() string {
-	return "-dexpreopt-" + install.name
-}
-
-// Returns Make entries for installing the file.
-//
-// This function uses a value receiver rather than a pointer receiver to ensure that the object is
-// safe to use in `android.AndroidMkExtraEntriesFunc`.
-func (install dexpreopterInstall) ToMakeEntries() android.AndroidMkEntries {
-	return android.AndroidMkEntries{
-		Class:      "ETC",
-		OutputFile: android.OptionalPathForPath(install.outputPathOnHost),
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE", install.FullModuleName())
-				entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String())
-				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice)
-				entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false")
-			},
-		},
-	}
+	InstallFileOnDevice string
 }
 
 type Dexpreopter struct {
@@ -120,8 +83,9 @@
 	classLoaderContexts dexpreopt.ClassLoaderContextMap
 
 	// See the `dexpreopt` function for details.
-	builtInstalled        string
-	builtInstalledForApex []dexpreopterInstall
+	builtInstalled                    string
+	apexSystemServerDexpreoptInstalls []DexpreopterInstall
+	apexSystemServerDexJars           android.Paths
 
 	// The config is used for two purposes:
 	// - Passing dexpreopt information about libraries from Soong to Make. This is needed when
@@ -204,27 +168,23 @@
 	}
 	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	psi := android.PrebuiltSelectionInfoMap{}
-	ctx.VisitDirectDeps(func(am android.Module) {
+	ctx.VisitDirectDepsProxy(func(am android.ModuleProxy) {
 		if prebuiltSelectionInfo, ok := android.OtherModuleProvider(ctx, am, android.PrebuiltSelectionInfoProvider); ok {
 			psi = prebuiltSelectionInfo
 		}
 	})
 
 	// Find the apex variant for this module
-	apexVariantsWithoutTestApexes := []string{}
+	apexVariants := []string{}
 	if apexInfo.BaseApexName != "" {
-		// This is a transitive dependency of an override_apex
-		apexVariantsWithoutTestApexes = append(apexVariantsWithoutTestApexes, apexInfo.BaseApexName)
-	} else {
-		_, variants, _ := android.ListSetDifference(apexInfo.InApexVariants, apexInfo.TestApexes)
-		apexVariantsWithoutTestApexes = append(apexVariantsWithoutTestApexes, variants...)
+		apexVariants = append(apexVariants, apexInfo.BaseApexName)
 	}
 	if apexInfo.ApexAvailableName != "" {
-		apexVariantsWithoutTestApexes = append(apexVariantsWithoutTestApexes, apexInfo.ApexAvailableName)
+		apexVariants = append(apexVariants, apexInfo.ApexAvailableName)
 	}
 	disableSource := false
 	// find the selected apexes
-	for _, apexVariant := range apexVariantsWithoutTestApexes {
+	for _, apexVariant := range apexVariants {
 		if len(psi.GetSelectedModulesForApiDomain(apexVariant)) > 0 {
 			// If the apex_contribution for this api domain is non-empty, disable the source variant
 			disableSource = true
@@ -279,20 +239,6 @@
 		if !isApexSystemServerJar {
 			return true
 		}
-		ai, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
-		allApexInfos := []android.ApexInfo{}
-		if allApexInfosProvider, ok := android.ModuleProvider(ctx, android.AllApexInfoProvider); ok {
-			allApexInfos = allApexInfosProvider.ApexInfos
-		}
-		if len(allApexInfos) > 0 && !ai.MinSdkVersion.EqualTo(allApexInfos[0].MinSdkVersion) {
-			// Apex system server jars are dexpreopted and installed on to the system image.
-			// Since we can have BigAndroid and Go variants of system server jar providing apexes,
-			// and these two variants can have different min_sdk_versions, hide one of the apex variants
-			// from make to prevent collisions.
-			//
-			// Unlike cc, min_sdk_version does not have an effect on the build actions of java libraries.
-			ctx.Module().MakeUninstallable()
-		}
 	} else {
 		// Don't preopt the platform variant of an APEX system server jar to avoid conflicts.
 		if isApexSystemServerJar {
@@ -347,7 +293,11 @@
 	d.installPath = android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexpreopt.GetSystemServerDexLocation(ctx, dc, libraryName), "/"))
 	// generate the rules for creating the .odex and .vdex files for this system server jar
 	dexJarFile := di.PrebuiltExportPath(ApexRootRelativePathToJavaLib(libraryName))
-
+	if dexJarFile == nil {
+		ctx.ModuleErrorf(
+			`Could not find library %s in prebuilt apex %s.
+Please make sure that the value of PRODUCT_APEX_(SYSTEM_SERVER|STANDALONE_SYSTEM_SERVER)_JARS is correct`, libraryName, ctx.ModuleName())
+	}
 	d.inputProfilePathOnHost = nil // reset: TODO(spandandas): Make dexpreopter stateless
 	if android.InList(libraryName, di.GetDexpreoptProfileGuidedExportedModuleNames()) {
 		// Set the profile path to guide optimization
@@ -539,12 +489,8 @@
 		Output(appProductPackages)
 	productPackagesRule.Restat().Build("product_packages."+dexJarStem, "dexpreopt product_packages")
 
-	// Prebuilts are active, do not copy the dexpreopt'd source javalib to out/soong/system_server_dexjars
-	// The javalib from the deapexed prebuilt will be copied to this location.
-	// TODO (b/331665856): Implement a principled solution for this.
-	copyApexSystemServerJarDex := !disableSourceApexVariant(ctx) && !ctx.Module().IsHideFromMake()
 	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(
-		ctx, globalSoong, global, dexpreoptConfig, appProductPackages, copyApexSystemServerJarDex)
+		ctx, globalSoong, global, dexpreoptConfig, appProductPackages)
 	if err != nil {
 		ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
 		return
@@ -577,7 +523,6 @@
 			partition = ""
 		}
 		installBase := filepath.Base(install.To)
-		arch := filepath.Base(installDir)
 		installPath := android.PathForModuleInPartitionInstall(ctx, partition, installDir)
 		isProfile := strings.HasSuffix(installBase, ".prof")
 
@@ -593,16 +538,13 @@
 				// libraries, only those in the system server classpath are handled here.
 				// Preopting of boot classpath jars in the ART APEX are handled in
 				// java/dexpreopt_bootjars.go, and other APEX jars are not preopted.
-				// The installs will be handled by Make as sub-modules of the java library.
-				di := dexpreopterInstall{
-					name:                arch + "-" + installBase,
-					moduleName:          libName,
-					outputPathOnHost:    install.From,
-					installDirOnDevice:  installPath,
-					installFileOnDevice: installBase,
+				// The installs will be handled the apex module that includes this library.
+				di := DexpreopterInstall{
+					OutputPathOnHost:    install.From,
+					InstallDirOnDevice:  installPath,
+					InstallFileOnDevice: installBase,
 				}
-				ctx.InstallFile(di.installDirOnDevice, di.installFileOnDevice, di.outputPathOnHost)
-				d.builtInstalledForApex = append(d.builtInstalledForApex, di)
+				d.apexSystemServerDexpreoptInstalls = append(d.apexSystemServerDexpreoptInstalls, di)
 
 			}
 		} else if !d.preventInstall {
@@ -611,9 +553,22 @@
 		}
 	}
 
+	if isApexSystemServerJar {
+		// Store the dex jar location for system server jars in apexes, the apex will copy the file into
+		// a known location for dex2oat.
+		d.apexSystemServerDexJars = append(d.apexSystemServerDexJars, dexJarFile)
+	} else if isSystemServerJar && !d.preventInstall {
+		// Copy the dex jar into a known location for dex2oat for non-apex system server jars.
+		android.CopyFileRule(ctx, dexJarFile, android.PathForOutput(ctx, dexpreopt.SystemServerDexjarsDir, dexJarFile.Base()))
+	}
+
 	if !isApexSystemServerJar {
 		d.builtInstalled = dexpreoptRule.Installs().String()
 	}
+
+	if isSystemServerJar {
+		checkSystemServerOrder(ctx, libName)
+	}
 }
 
 func getModuleInstallPathInfo(ctx android.ModuleContext, fullInstallPath string) (android.InstallPath, string, string) {
@@ -645,16 +600,12 @@
 	}
 }
 
-func (d *dexpreopter) DexpreoptBuiltInstalledForApex() []dexpreopterInstall {
-	return d.builtInstalledForApex
+func (d *dexpreopter) ApexSystemServerDexpreoptInstalls() []DexpreopterInstall {
+	return d.apexSystemServerDexpreoptInstalls
 }
 
-func (d *dexpreopter) AndroidMkEntriesForApex() []android.AndroidMkEntries {
-	var entries []android.AndroidMkEntries
-	for _, install := range d.builtInstalledForApex {
-		entries = append(entries, install.ToMakeEntries())
-	}
-	return entries
+func (d *dexpreopter) ApexSystemServerDexJars() android.Paths {
+	return d.apexSystemServerDexJars
 }
 
 func (d *dexpreopter) OutputProfilePathOnHost() android.Path {
@@ -684,3 +635,33 @@
 func (d *dexpreopter) SetRewrittenProfile(p android.Path) {
 	d.rewrittenProfile = p
 }
+
+// Check the order of jars on the system server classpath and give a warning/error if a jar precedes
+// one of its dependencies. This is not an error, but a missed optimization, as dexpreopt won't
+// have the dependency jar in the class loader context, and it won't be able to resolve any
+// references to its classes and methods.
+func checkSystemServerOrder(ctx android.ModuleContext, libName string) {
+	config := dexpreopt.GetGlobalConfig(ctx)
+	jars := config.AllSystemServerClasspathJars(ctx)
+	jarIndex := config.AllSystemServerJars(ctx).IndexOfJar(libName)
+	ctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
+		tag := ctx.OtherModuleDependencyTag(dep)
+		// Ideally this should only be walking relevant dependencies, but to maintain existing behavior
+		// for now just exclude any known irrelevant dependencies that would lead to incorrect errors.
+		if _, ok := tag.(bootclasspathDependencyTag); ok {
+			return false
+		} else if tag == traceReferencesTag {
+			// Allow ordering inversion if the dependency is purely for tracing references.
+			return false
+		}
+		depIndex := jars.IndexOfJar(dep.Name())
+		if jarIndex < depIndex && !config.BrokenSuboptimalOrderOfSystemServerJars {
+			jar := jars.Jar(jarIndex)
+			dep := jars.Jar(depIndex)
+			ctx.ModuleErrorf("non-optimal order of jars on the system server classpath:"+
+				" '%s' precedes its dependency '%s', so dexpreopt is unable to resolve any"+
+				" references from '%s' to '%s'.\n", jar, dep, jar, dep)
+		}
+		return true
+	})
+}
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 8c60d23..2287043 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -15,6 +15,7 @@
 package java
 
 import (
+	"fmt"
 	"path/filepath"
 	"strings"
 
@@ -225,7 +226,6 @@
 }
 
 var (
-	dexpreoptBootJarDepTag          = bootclasspathDependencyTag{name: "dexpreopt-boot-jar"}
 	dexBootJarsFragmentsKey         = android.NewOnceKey("dexBootJarsFragments")
 	apexContributionsMetadataDepTag = dependencyTag{name: "all_apex_contributions"}
 )
@@ -293,6 +293,9 @@
 	// Profiles imported from APEXes, in addition to the profile at the default path. Each entry must
 	// be the name of an APEX module.
 	profileImports []string
+
+	// The name of the module that provides boot image profiles, if any.
+	profileProviderModule string
 }
 
 // Target-dependent description of a boot image.
@@ -464,9 +467,6 @@
 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)
-	})
 }
 
 func SkipDexpreoptBootJars(ctx android.PathContext) bool {
@@ -502,12 +502,6 @@
 func (dbj *dexpreoptBootJars) DepsMutator(ctx android.BottomUpMutatorContext) {
 	// Create a dependency on all_apex_contributions to determine the selected mainline module
 	ctx.AddDependency(ctx.Module(), apexContributionsMetadataDepTag, "all_apex_contributions")
-}
-
-func DexpreoptBootJarsMutator(ctx android.BottomUpMutatorContext) {
-	if _, ok := ctx.Module().(*dexpreoptBootJars); !ok {
-		return
-	}
 
 	if dexpreopt.IsDex2oatNeeded(ctx) {
 		// Add a dependency onto the dex2oat tool which is needed for creating the boot image. The
@@ -521,7 +515,7 @@
 			continue
 		}
 		// For accessing the boot jars.
-		addDependenciesOntoBootImageModules(ctx, config.modules, dexpreoptBootJarDepTag)
+		addDependenciesOntoBootImageModules(ctx, config.modules, dexpreoptBootJar)
 		// Create a dependency on the apex selected using RELEASE_APEX_CONTRIBUTIONS_*
 		// TODO: b/308174306 - Remove the direct depedendency edge to the java_library (source/prebuilt) once all mainline modules
 		// have been flagged using RELEASE_APEX_CONTRIBUTIONS_*
@@ -534,11 +528,11 @@
 
 	if ctx.OtherModuleExists("platform-bootclasspath") {
 		// For accessing all bootclasspath fragments.
-		addDependencyOntoApexModulePair(ctx, "platform", "platform-bootclasspath", platformBootclasspathDepTag)
+		addDependencyOntoApexModulePair(ctx, "platform", "platform-bootclasspath", platform)
 	} else if ctx.OtherModuleExists("art-bootclasspath-fragment") {
 		// For accessing the ART bootclasspath fragment on a thin manifest (e.g., master-art) where
 		// platform-bootclasspath doesn't exist.
-		addDependencyOntoApexModulePair(ctx, "com.android.art", "art-bootclasspath-fragment", bootclasspathFragmentDepTag)
+		addDependencyOntoApexModulePair(ctx, "com.android.art", "art-bootclasspath-fragment", fragment)
 	}
 }
 
@@ -556,13 +550,13 @@
 			// We need to add a dep on only the apex listed in `contents` of the selected apex_contributions module
 			// This is not available in a structured format in `apex_contributions`, so this hack adds a dep on all `contents`
 			// (some modules like art.module.public.api do not have an apex variation since it is a pure stub module that does not get installed)
-			apexVariationOfSelected := append(ctx.Target().Variations(), blueprint.Variation{Mutator: "apex", Variation: apex})
-			if ctx.OtherModuleDependencyVariantExists(apexVariationOfSelected, selected) {
-				ctx.AddFarVariationDependencies(apexVariationOfSelected, dexpreoptBootJarDepTag, selected)
-			} else if ctx.OtherModuleDependencyVariantExists(apexVariationOfSelected, android.RemoveOptionalPrebuiltPrefix(selected)) {
-				// The prebuilt might have been renamed by prebuilt_rename mutator if the source module does not exist.
-				// Remove the prebuilt_ prefix.
-				ctx.AddFarVariationDependencies(apexVariationOfSelected, dexpreoptBootJarDepTag, android.RemoveOptionalPrebuiltPrefix(selected))
+			tag := bootclasspathDependencyTag{
+				typ: dexpreoptBootJar,
+			}
+
+			dep := android.RemoveOptionalPrebuiltPrefix(selected)
+			if ctx.OtherModuleDependencyVariantExists(ctx.Target().Variations(), dep) {
+				ctx.AddFarVariationDependencies(ctx.Target().Variations(), tag, dep)
 			}
 		}
 	}
@@ -571,23 +565,55 @@
 func gatherBootclasspathFragments(ctx android.ModuleContext) map[string]android.Module {
 	return ctx.Config().Once(dexBootJarsFragmentsKey, func() interface{} {
 		fragments := make(map[string]android.Module)
+
+		type moduleInApexPair struct {
+			module string
+			apex   string
+		}
+
+		var modulesInApexes []moduleInApexPair
+
+		// Find the list of modules in apexes.
 		ctx.WalkDeps(func(child, parent android.Module) bool {
 			if !isActiveModule(ctx, child) {
 				return false
 			}
 			tag := ctx.OtherModuleDependencyTag(child)
-			if tag == platformBootclasspathDepTag {
-				return true
-			}
-			if tag == bootclasspathFragmentDepTag {
-				apexInfo, _ := android.OtherModuleProvider(ctx, child, android.ApexInfoProvider)
-				for _, apex := range apexInfo.InApexVariants {
-					fragments[apex] = child
+			if bcpTag, ok := tag.(bootclasspathDependencyTag); ok {
+				if bcpTag.typ == platform {
+					return true
 				}
-				return false
+				if bcpTag.typ == fragment {
+					if bcpTag.moduleInApex == "" {
+						panic(fmt.Errorf("expected fragment to be in apex"))
+					}
+					modulesInApexes = append(modulesInApexes, moduleInApexPair{bcpTag.moduleInApex, ctx.OtherModuleName(child)})
+					return true
+				}
 			}
 			return false
 		})
+
+		for _, moduleInApex := range modulesInApexes {
+			// Find a desired module in an apex.
+			ctx.WalkDeps(func(child, parent android.Module) bool {
+				t := ctx.OtherModuleDependencyTag(child)
+				if bcpTag, ok := t.(bootclasspathDependencyTag); ok {
+					if bcpTag.typ == platform {
+						return true
+					}
+					if bcpTag.typ == fragment && ctx.OtherModuleName(child) == moduleInApex.apex {
+						// This is the dependency from this module to the apex, recurse into it.
+						return true
+					}
+				} else if android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(child)) == moduleInApex.module {
+					// This is the desired module inside the apex.
+					fragments[android.RemoveOptionalPrebuiltPrefix(moduleInApex.apex)] = child
+				}
+				return false
+			})
+		}
+
 		return fragments
 	}).(map[string]android.Module)
 }
@@ -649,6 +675,139 @@
 			installs: artBootImageHostInstalls,
 		},
 	)
+
+	d.buildBootZip(ctx)
+}
+
+// Build the boot.zip which contains the boot jars and their compilation output
+// We can do this only if preopt is enabled and if the product uses libart config (which sets the
+// default properties for preopting).
+// Origionally, this was only for ART Cloud.
+func (d *dexpreoptBootJars) buildBootZip(ctx android.ModuleContext) {
+	image := d.defaultBootImage
+	if image == nil || SkipDexpreoptBootJars(ctx) {
+		return
+	}
+	global := dexpreopt.GetGlobalConfig(ctx)
+	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
+	if global.DisablePreopt || global.OnlyPreoptArtBootImage {
+		return
+	}
+
+	bootclasspathDexFiles, bootclassPathLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
+	if len(bootclasspathDexFiles) == 0 {
+		return
+	}
+
+	systemServerDexjarsDir := android.PathForOutput(ctx, dexpreopt.SystemServerDexjarsDir)
+
+	bootZipMetadataTmp := android.PathForModuleOut(ctx, "boot_zip", "METADATA.txt.tmp")
+	bootZipMetadata := android.PathForModuleOut(ctx, "boot_zip", "METADATA.txt")
+	newlineFile := android.PathForModuleOut(ctx, "boot_zip", "newline.txt")
+	android.WriteFileRule(ctx, newlineFile, "")
+
+	dexPreoptRootDir := filepath.Dir(filepath.Dir(bootclasspathDexFiles[0].String()))
+
+	var sb strings.Builder
+	sb.WriteString("bootclasspath = ")
+	for i, bootclasspathJar := range bootclasspathDexFiles {
+		if i > 0 {
+			sb.WriteString(":")
+		}
+		rel, err := filepath.Rel(dexPreoptRootDir, bootclasspathJar.String())
+		if err != nil {
+			ctx.ModuleErrorf("All dexpreopt jars should be under the same rootdir %q, but %q wasn't.", dexPreoptRootDir, bootclasspathJar)
+		} else {
+			sb.WriteString(rel)
+		}
+	}
+	sb.WriteString("\nbootclasspath-locations = ")
+	for i, bootclasspathLocation := range bootclassPathLocations {
+		if i > 0 {
+			sb.WriteString(":")
+		}
+		sb.WriteString(bootclasspathLocation)
+	}
+	sb.WriteString("\nboot-image = ")
+
+	// Infix can be 'art' (ART image for testing), 'boot' (primary), or 'mainline' (mainline
+	// extension). Soong creates a set of variables for Make, one or each boot image. The only
+	// reason why the ART image is exposed to Make is testing (art gtests) and benchmarking (art
+	// golem benchmarks). Install rules that use those variables are in dex_preopt_libart.mk. Here
+	// for dexpreopt purposes the infix is always 'boot' or 'mainline'.
+	dexpreoptInfix := "boot"
+	if global.PreoptWithUpdatableBcp {
+		dexpreoptInfix = "mainline"
+	}
+
+	var dexPreoptImageZipBoot android.Path
+	var dexPreoptImageZipArt android.Path
+	var dexPreoptImageZipMainline android.Path
+	for _, current := range append(d.otherImages, image) {
+		if current.name == dexpreoptInfix {
+			_, imageLocationsOnDevice := current.getAnyAndroidVariant().imageLocations()
+			for i, location := range imageLocationsOnDevice {
+				imageLocationsOnDevice[i] = strings.TrimPrefix(location, "/")
+			}
+			sb.WriteString(strings.Join(imageLocationsOnDevice, ":"))
+		}
+		switch current.name {
+		case "boot":
+			dexPreoptImageZipBoot = current.zip
+		case "art":
+			dexPreoptImageZipArt = current.zip
+		case "mainline":
+			dexPreoptImageZipMainline = current.zip
+		}
+	}
+	sb.WriteString("\nextra-args = ")
+	android.WriteFileRuleVerbatim(ctx, bootZipMetadataTmp, sb.String())
+	ctx.Build(pctx, android.BuildParams{
+		Rule: android.Cat,
+		Inputs: []android.Path{
+			bootZipMetadataTmp,
+			globalSoong.UffdGcFlag,
+			newlineFile,
+		},
+		Output: bootZipMetadata,
+	})
+
+	bootZipFirstPart := android.PathForModuleOut(ctx, "boot_zip", "boot_first_part.zip")
+	bootZip := android.PathForModuleOut(ctx, "boot_zip", "boot.zip")
+	builder := android.NewRuleBuilder(pctx, ctx)
+	cmd := builder.Command().BuiltTool("soong_zip").
+		FlagWithOutput("-o ", bootZipFirstPart).
+		FlagWithArg("-C ", filepath.Dir(filepath.Dir(bootclasspathDexFiles[0].String())))
+	for _, bootclasspathJar := range bootclasspathDexFiles {
+		cmd.FlagWithInput("-f ", bootclasspathJar)
+	}
+	for i := range global.SystemServerJars.Len() {
+		// Use "/system" path for JARs with "platform:" prefix. These JARs counterintuitively use
+		// "platform" prefix but they will be actually installed to /system partition.
+		// For the remaining system server JARs use the partition signified by the prefix.
+		// For example, prefix "system_ext:" will use "/system_ext" path.
+		dir := global.SystemServerJars.Apex(i)
+		if dir == "platform" {
+			dir = "system"
+		}
+		jar := global.SystemServerJars.Jar(i) + ".jar"
+		cmd.FlagWithArg("-e ", dir+"/framework/"+jar)
+		cmd.FlagWithInput("-f ", systemServerDexjarsDir.Join(ctx, jar))
+	}
+	cmd.Flag("-j")
+	cmd.FlagWithInput("-f ", bootZipMetadata)
+
+	builder.Command().BuiltTool("merge_zips").
+		Output(bootZip).
+		Input(bootZipFirstPart).
+		Input(dexPreoptImageZipBoot).
+		Input(dexPreoptImageZipArt).
+		Input(dexPreoptImageZipMainline)
+
+	builder.Build("boot_zip", "build boot.zip")
+
+	ctx.DistForGoal("droidcore", bootZipMetadata)
+	ctx.DistForGoal("droidcore", bootZip)
 }
 
 // GenerateSingletonBuildActions generates build rules for the dexpreopt config for Make.
@@ -711,7 +870,8 @@
 	modules := make([]apexJarModulePair, 0, imageConfig.modules.Len())
 	for i := 0; i < imageConfig.modules.Len(); i++ {
 		found := false
-		for _, module := range gatherApexModulePairDepsWithTag(ctx, dexpreoptBootJarDepTag) {
+		dexpreoptBootJarModules, _ := gatherApexModulePairDepsWithTag(ctx, dexpreoptBootJar)
+		for _, module := range dexpreoptBootJarModules {
 			name := android.RemoveOptionalPrebuiltPrefix(module.Name())
 			if name == imageConfig.modules.Jar(i) {
 				modules = append(modules, apexJarModulePair{
@@ -794,11 +954,16 @@
 				"APEX '%[2]s' doesn't exist or is not added as a dependency of dex_bootjars",
 				pair.jarModule.Name(),
 				pair.apex)
+			return nil
 		}
 		bootclasspathFragmentInfo, _ := android.OtherModuleProvider(ctx, fragment, BootclasspathFragmentApexContentInfoProvider)
 		jar, err := bootclasspathFragmentInfo.DexBootJarPathForContentModule(pair.jarModule)
 		if err != nil {
-			ctx.ModuleErrorf("%s", err)
+			if ctx.Config().AllowMissingDependencies() {
+				ctx.AddMissingDependencies([]string{pair.jarModule.String()})
+			} else {
+				ctx.ModuleErrorf("%s", err)
+			}
 		}
 		return jar
 	}
@@ -957,9 +1122,16 @@
 
 func getApexNameToApexExportsInfoMap(ctx android.ModuleContext) apexNameToApexExportsInfoMap {
 	apexNameToApexExportsInfoMap := apexNameToApexExportsInfoMap{}
-	ctx.VisitDirectDepsWithTag(dexpreoptBootJarDepTag, func(am android.Module) {
-		if info, exists := android.OtherModuleProvider(ctx, am, android.ApexExportsInfoProvider); exists {
-			apexNameToApexExportsInfoMap[info.ApexName] = info
+
+	ctx.VisitDirectDeps(func(am android.Module) {
+		tag := ctx.OtherModuleDependencyTag(am)
+		if bcpTag, ok := tag.(bootclasspathDependencyTag); ok && bcpTag.typ == dexpreoptBootJar {
+			if bcpTag.moduleInApex == "" {
+				info, exists := android.OtherModuleProvider(ctx, am, android.ApexExportsInfoProvider)
+				if exists {
+					apexNameToApexExportsInfoMap[info.ApexName] = info
+				}
+			}
 		}
 	})
 	return apexNameToApexExportsInfoMap
@@ -1442,24 +1614,33 @@
 
 func (dbj *artBootImages) DepsMutator(ctx android.BottomUpMutatorContext) {
 	// Create a dependency on `dex_bootjars` to access the intermediate locations of host art boot image.
-	ctx.AddVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), dexpreoptBootJarDepTag, "dex_bootjars")
+	tag := bootclasspathDependencyTag{
+		typ: dexpreoptBootJar,
+	}
+	ctx.AddVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), tag, "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"))
+	ctx.VisitDirectDeps(func(m android.Module) {
+		tag := ctx.OtherModuleDependencyTag(m)
+		if bcpTag, ok := tag.(bootclasspathDependencyTag); ok && bcpTag.typ == dexpreoptBootJar {
+			if bcpTag.moduleInApex != "" {
+				panic("unhandled moduleInApex")
+			}
+			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"))
+			}
 		}
 	})
 }
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index dc0973c..fb5c325 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -67,15 +67,16 @@
 
 		// ART boot image for testing only. Do not rely on it to make any build-time decision.
 		artCfg := bootImageConfig{
-			name:                 artBootImageName,
-			enabledIfExists:      "art-bootclasspath-fragment",
-			stem:                 bootImageStem,
-			installDir:           "apex/art_boot_images/javalib",
-			modules:              global.TestOnlyArtBootImageJars,
-			preloadedClassesFile: "art/build/boot/preloaded-classes",
-			compilerFilter:       "speed-profile",
-			singleImage:          false,
-			profileImports:       profileImports,
+			name:                  artBootImageName,
+			enabledIfExists:       "art-bootclasspath-fragment",
+			stem:                  bootImageStem,
+			installDir:            "apex/art_boot_images/javalib",
+			modules:               global.TestOnlyArtBootImageJars,
+			preloadedClassesFile:  "art/build/boot/preloaded-classes",
+			compilerFilter:        "speed-profile",
+			singleImage:           false,
+			profileImports:        profileImports,
+			profileProviderModule: "art-bootclasspath-fragment",
 		}
 
 		// Framework config for the boot image extension.
diff --git a/java/dexpreopt_config_test.go b/java/dexpreopt_config_test.go
index 44d2127..99b1f23 100644
--- a/java/dexpreopt_config_test.go
+++ b/java/dexpreopt_config_test.go
@@ -23,6 +23,7 @@
 )
 
 func TestBootImageConfig(t *testing.T) {
+	t.Parallel()
 	if runtime.GOOS != "linux" {
 		t.Skipf("Skipping as boot image config test is only supported on linux not %s", runtime.GOOS)
 	}
diff --git a/java/dexpreopt_config_testing.go b/java/dexpreopt_config_testing.go
index 33c682b..241941e 100644
--- a/java/dexpreopt_config_testing.go
+++ b/java/dexpreopt_config_testing.go
@@ -1217,6 +1217,7 @@
 	}
 
 	t.Run(imageConfig.name, func(t *testing.T) {
+		t.Parallel()
 		nestedCheckBootImageConfig(t, result, imageConfig, mutated, expected)
 	})
 }
@@ -1236,7 +1237,7 @@
 	android.AssertPathRelativeToTopEquals(t, "zip", expected.zip, imageConfig.zip)
 
 	if !mutated {
-		dexBootJarModule := result.ModuleForTests("dex_bootjars", "android_common")
+		dexBootJarModule := result.ModuleForTests(t, "dex_bootjars", "android_common")
 		profileInstallInfo, _ := android.OtherModuleProvider(result, dexBootJarModule.Module(), profileInstallInfoProvider)
 		assertInstallsEqual(t, "profileInstalls", expected.profileInstalls, profileInstallInfo.profileInstalls)
 		android.AssertStringEquals(t, "profileLicenseMetadataFile", expected.profileLicenseMetadataFile, profileInstallInfo.profileLicenseMetadataFile.RelativeToTop().String())
@@ -1246,6 +1247,7 @@
 	for i, variant := range imageConfig.variants {
 		expectedVariant := expected.variants[i]
 		t.Run(variant.target.Arch.ArchType.String(), func(t *testing.T) {
+			t.Parallel()
 			android.AssertDeepEquals(t, "archType", expectedVariant.archType, variant.target.Arch.ArchType)
 			android.AssertDeepEquals(t, "dexLocations", expectedVariant.dexLocations, variant.dexLocations)
 			android.AssertDeepEquals(t, "dexLocationsDeps", expectedVariant.dexLocationsDeps, variant.dexLocationsDeps)
@@ -1279,7 +1281,7 @@
 // checkDexpreoptMakeVars checks the DEXPREOPT_ prefixed make vars produced by dexpreoptBootJars
 // singleton.
 func checkDexpreoptMakeVars(t *testing.T, result *android.TestResult, expectedLicenseMetadataFile string) {
-	vars := result.MakeVarsForTesting(func(variable android.MakeVarVariable) bool {
+	vars := result.MakeVarsForTesting(t, func(variable android.MakeVarVariable) bool {
 		return strings.HasPrefix(variable.Name(), "DEXPREOPT_")
 	})
 
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index 07d0595..f437da0 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"runtime"
-	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -30,6 +29,7 @@
 }
 
 func TestDexpreoptEnabled(t *testing.T) {
+	t.Parallel()
 	tests := []struct {
 		name        string
 		bp          string
@@ -219,6 +219,7 @@
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			preparers := android.GroupFixturePreparers(
 				PrepareForTestWithDexpreopt,
 				PrepareForTestWithFakeApexMutator,
@@ -238,7 +239,7 @@
 				variant += "_apex1000"
 			}
 
-			dexpreopt := ctx.ModuleForTests(moduleName, variant).MaybeRule("dexpreopt")
+			dexpreopt := ctx.ModuleForTests(t, moduleName, variant).MaybeRule("dexpreopt")
 			enabled := dexpreopt.Rule != nil
 
 			if enabled != test.enabled {
@@ -258,6 +259,7 @@
 }
 
 func TestDex2oatToolDeps(t *testing.T) {
+	t.Parallel()
 	if runtime.GOOS != "linux" {
 		// The host binary paths checked below are build OS dependent.
 		t.Skipf("Unsupported build OS %s", runtime.GOOS)
@@ -273,6 +275,7 @@
 		name := fmt.Sprintf("sourceEnabled:%t,prebuiltEnabled:%t,prebuiltPreferred:%t",
 			sourceEnabled, prebuiltEnabled, prebuiltPreferred)
 		t.Run(name, func(t *testing.T) {
+			t.Parallel()
 			result := preparers.RunTestWithBp(t, fmt.Sprintf(`
 					cc_binary {
 						name: "dex2oatd",
@@ -296,7 +299,7 @@
 		})
 	}
 
-	sourceDex2oatPath := "host/linux-x86/bin/dex2oatd"
+	sourceDex2oatPath := "../host/linux-x86/bin/dex2oatd"
 	prebuiltDex2oatPath := ".intermediates/prebuilt_dex2oatd/linux_glibc_x86_64/dex2oatd"
 
 	testDex2oatToolDep(true, false, false, sourceDex2oatPath)
@@ -305,7 +308,7 @@
 	testDex2oatToolDep(false, true, false, prebuiltDex2oatPath)
 }
 
-func TestDexpreoptBuiltInstalledForApex(t *testing.T) {
+func TestApexSystemServerDexpreoptInstalls(t *testing.T) {
 	preparers := android.GroupFixturePreparers(
 		PrepareForTestWithDexpreopt,
 		PrepareForTestWithFakeApexMutator,
@@ -322,28 +325,38 @@
 			sdk_version: "current",
 		}`)
 	ctx := result.TestContext
-	module := ctx.ModuleForTests("service-foo", "android_common_apex1000")
+	module := ctx.ModuleForTests(t, "service-foo", "android_common_apex1000")
 	library := module.Module().(*Library)
 
-	installs := library.dexpreopter.DexpreoptBuiltInstalledForApex()
+	installs := library.dexpreopter.ApexSystemServerDexpreoptInstalls()
+	dexJars := library.dexpreopter.ApexSystemServerDexJars()
 
 	android.AssertIntEquals(t, "install count", 2, len(installs))
+	android.AssertIntEquals(t, "dexjar count", 1, len(dexJars))
 
-	android.AssertStringEquals(t, "installs[0] FullModuleName",
-		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
-		installs[0].FullModuleName())
+	android.AssertPathRelativeToTopEquals(t, "installs[0] OutputPathOnHost",
+		"out/soong/.intermediates/service-foo/android_common_apex1000/dexpreopt/service-foo/oat/arm64/javalib.odex",
+		installs[0].OutputPathOnHost)
 
-	android.AssertStringEquals(t, "installs[0] SubModuleName",
-		"-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
-		installs[0].SubModuleName())
+	android.AssertPathRelativeToTopEquals(t, "installs[0] InstallDirOnDevice",
+		"out/target/product/test_device/system/framework/oat/arm64",
+		installs[0].InstallDirOnDevice)
 
-	android.AssertStringEquals(t, "installs[1] FullModuleName",
-		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
-		installs[1].FullModuleName())
+	android.AssertStringEquals(t, "installs[0] InstallFileOnDevice",
+		"apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
+		installs[0].InstallFileOnDevice)
 
-	android.AssertStringEquals(t, "installs[1] SubModuleName",
-		"-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
-		installs[1].SubModuleName())
+	android.AssertPathRelativeToTopEquals(t, "installs[1] OutputPathOnHost",
+		"out/soong/.intermediates/service-foo/android_common_apex1000/dexpreopt/service-foo/oat/arm64/javalib.vdex",
+		installs[1].OutputPathOnHost)
+
+	android.AssertPathRelativeToTopEquals(t, "installs[1] InstallDirOnDevice",
+		"out/target/product/test_device/system/framework/oat/arm64",
+		installs[1].InstallDirOnDevice)
+
+	android.AssertStringEquals(t, "installs[1] InstallFileOnDevice",
+		"apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
+		installs[1].InstallFileOnDevice)
 
 	// Not an APEX system server jar.
 	result = preparers.RunTestWithBp(t, `
@@ -354,101 +367,14 @@
 			sdk_version: "current",
 		}`)
 	ctx = result.TestContext
-	module = ctx.ModuleForTests("foo", "android_common")
+	module = ctx.ModuleForTests(t, "foo", "android_common")
 	library = module.Module().(*Library)
 
-	installs = library.dexpreopter.DexpreoptBuiltInstalledForApex()
+	installs = library.dexpreopter.ApexSystemServerDexpreoptInstalls()
+	dexJars = library.dexpreopter.ApexSystemServerDexJars()
 
 	android.AssertIntEquals(t, "install count", 0, len(installs))
-}
-
-func filterDexpreoptEntriesList(entriesList []android.AndroidMkEntries) []android.AndroidMkEntries {
-	var results []android.AndroidMkEntries
-	for _, entries := range entriesList {
-		if strings.Contains(entries.EntryMap["LOCAL_MODULE"][0], "-dexpreopt-") {
-			results = append(results, entries)
-		}
-	}
-	return results
-}
-
-func verifyEntries(t *testing.T, message string, expectedModule string,
-	expectedPrebuiltModuleFile string, expectedModulePath string, expectedInstalledModuleStem string,
-	entries android.AndroidMkEntries) {
-	android.AssertStringEquals(t, message+" LOCAL_MODULE", expectedModule,
-		entries.EntryMap["LOCAL_MODULE"][0])
-
-	android.AssertStringEquals(t, message+" LOCAL_MODULE_CLASS", "ETC",
-		entries.EntryMap["LOCAL_MODULE_CLASS"][0])
-
-	android.AssertStringDoesContain(t, message+" LOCAL_PREBUILT_MODULE_FILE",
-		entries.EntryMap["LOCAL_PREBUILT_MODULE_FILE"][0], expectedPrebuiltModuleFile)
-
-	android.AssertStringDoesContain(t, message+" LOCAL_MODULE_PATH",
-		entries.EntryMap["LOCAL_MODULE_PATH"][0], expectedModulePath)
-
-	android.AssertStringEquals(t, message+" LOCAL_INSTALLED_MODULE_STEM",
-		expectedInstalledModuleStem, entries.EntryMap["LOCAL_INSTALLED_MODULE_STEM"][0])
-
-	android.AssertStringEquals(t, message+" LOCAL_NOT_AVAILABLE_FOR_PLATFORM",
-		"false", entries.EntryMap["LOCAL_NOT_AVAILABLE_FOR_PLATFORM"][0])
-}
-
-func TestAndroidMkEntriesForApex(t *testing.T) {
-	preparers := android.GroupFixturePreparers(
-		PrepareForTestWithDexpreopt,
-		PrepareForTestWithFakeApexMutator,
-		dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"),
-	)
-
-	// An APEX system server jar.
-	result := preparers.RunTestWithBp(t, `
-		java_library {
-			name: "service-foo",
-			installable: true,
-			srcs: ["a.java"],
-			apex_available: ["com.android.apex1"],
-			sdk_version: "current",
-		}`)
-	ctx := result.TestContext
-	module := ctx.ModuleForTests("service-foo", "android_common_apex1000")
-
-	entriesList := android.AndroidMkEntriesForTest(t, ctx, module.Module())
-	entriesList = filterDexpreoptEntriesList(entriesList)
-
-	android.AssertIntEquals(t, "entries count", 2, len(entriesList))
-
-	verifyEntries(t,
-		"entriesList[0]",
-		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
-		"/dexpreopt/service-foo/oat/arm64/javalib.odex",
-		"/system/framework/oat/arm64",
-		"apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
-		entriesList[0])
-
-	verifyEntries(t,
-		"entriesList[1]",
-		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
-		"/dexpreopt/service-foo/oat/arm64/javalib.vdex",
-		"/system/framework/oat/arm64",
-		"apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
-		entriesList[1])
-
-	// Not an APEX system server jar.
-	result = preparers.RunTestWithBp(t, `
-		java_library {
-			name: "foo",
-			installable: true,
-			srcs: ["a.java"],
-			sdk_version: "current",
-		}`)
-	ctx = result.TestContext
-	module = ctx.ModuleForTests("foo", "android_common")
-
-	entriesList = android.AndroidMkEntriesForTest(t, ctx, module.Module())
-	entriesList = filterDexpreoptEntriesList(entriesList)
-
-	android.AssertIntEquals(t, "entries count", 0, len(entriesList))
+	android.AssertIntEquals(t, "dexjar count", 0, len(dexJars))
 }
 
 func TestGenerateProfileEvenIfDexpreoptIsDisabled(t *testing.T) {
@@ -470,7 +396,7 @@
 		}`)
 
 	ctx := result.TestContext
-	dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeRule("dexpreopt")
+	dexpreopt := ctx.ModuleForTests(t, "foo", "android_common").MaybeRule("dexpreopt")
 
 	expected := []string{"out/soong/.intermediates/foo/android_common/dexpreopt/foo/profile.prof"}
 
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 2dda72b..3faf294 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -19,6 +19,7 @@
 	"path/filepath"
 	"strings"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -194,6 +195,9 @@
 				"them instead.")
 		}
 		return false
+	} else if ctx.Config().PartialCompileFlags().Disable_stub_validation &&
+		!ctx.Config().BuildFromTextStub() {
+		return false
 	} else if String(apiToCheck.Api_file) != "" && String(apiToCheck.Removed_api_file) != "" {
 		return true
 	} else if String(apiToCheck.Api_file) != "" {
@@ -357,7 +361,7 @@
 		deps.aidlPreprocess = sdkDep.aidl
 	}
 
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
 
@@ -381,9 +385,9 @@
 				deps.classpath = append(deps.classpath, dep.HeaderJars...)
 				deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...)
 				deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.AconfigIntermediateCacheOutputPaths...)
-			} else if dep, ok := module.(android.SourceFileProducer); ok {
-				checkProducesJars(ctx, dep)
-				deps.classpath = append(deps.classpath, dep.Srcs()...)
+			} else if dep, ok := android.OtherModuleProvider(ctx, module, android.SourceFilesInfoProvider); ok {
+				checkProducesJars(ctx, dep, module)
+				deps.classpath = append(deps.classpath, dep.Srcs...)
 			} else {
 				ctx.ModuleErrorf("depends on non-java module %q", otherName)
 			}
@@ -874,6 +878,13 @@
 	Path *string
 }
 
+type ExportedDroiddocDirInfo struct {
+	Deps android.Paths
+	Dir  android.Path
+}
+
+var ExportedDroiddocDirInfoProvider = blueprint.NewProvider[ExportedDroiddocDirInfo]()
+
 type ExportedDroiddocDir struct {
 	android.ModuleBase
 
@@ -897,6 +908,11 @@
 	path := String(d.properties.Path)
 	d.dir = android.PathForModuleSrc(ctx, path)
 	d.deps = android.PathsForModuleSrc(ctx, []string{filepath.Join(path, "**/*")})
+
+	android.SetProvider(ctx, ExportedDroiddocDirInfoProvider, ExportedDroiddocDirInfo{
+		Dir:  d.dir,
+		Deps: d.deps,
+	})
 }
 
 // Defaults
diff --git a/java/droiddoc_test.go b/java/droiddoc_test.go
index 9e1ebbe..0c977bc 100644
--- a/java/droiddoc_test.go
+++ b/java/droiddoc_test.go
@@ -23,6 +23,7 @@
 )
 
 func TestDroiddoc(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJavaWithFS(t, `
 		droiddoc_exported_dir {
 		    name: "droiddoc-templates-sdk",
@@ -69,13 +70,13 @@
 			"bar-doc/a.java": nil,
 			"bar-doc/b.java": nil,
 		})
-	barStubsOutputs := ctx.ModuleForTests("bar-stubs", "android_common").OutputFiles(ctx, t, "")
+	barStubsOutputs := ctx.ModuleForTests(t, "bar-stubs", "android_common").OutputFiles(ctx, t, "")
 	if len(barStubsOutputs) != 1 {
 		t.Errorf("Expected one output from \"bar-stubs\" got %s", barStubsOutputs)
 	}
 
 	barStubsOutput := barStubsOutputs[0]
-	barDoc := ctx.ModuleForTests("bar-doc", "android_common")
+	barDoc := ctx.ModuleForTests(t, "bar-doc", "android_common")
 	javaDoc := barDoc.Rule("javadoc")
 	if g, w := android.PathsRelativeToTop(javaDoc.Implicits), android.PathRelativeToTop(barStubsOutput); !inList(w, g) {
 		t.Errorf("implicits of bar-doc must contain %q, but was %q.", w, g)
@@ -97,6 +98,7 @@
 }
 
 func TestDroiddocArgsAndFlagsCausesError(t *testing.T) {
+	t.Parallel()
 	testJavaError(t, "flags is set. Cannot set args", `
 		droiddoc_exported_dir {
 		    name: "droiddoc-templates-sdk",
diff --git a/java/droidstubs.go b/java/droidstubs.go
index e955949..b30c844 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -20,6 +20,7 @@
 	"regexp"
 	"strings"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -27,6 +28,28 @@
 	"android/soong/remoteexec"
 )
 
+type StubsInfo struct {
+	ApiVersionsXml android.Path
+	AnnotationsZip android.Path
+	ApiFile        android.Path
+	RemovedApiFile android.Path
+}
+
+type DroidStubsInfo struct {
+	CurrentApiTimestamp android.Path
+	EverythingStubsInfo StubsInfo
+	ExportableStubsInfo StubsInfo
+}
+
+var DroidStubsInfoProvider = blueprint.NewProvider[DroidStubsInfo]()
+
+type StubsSrcInfo struct {
+	EverythingStubsSrcJar android.Path
+	ExportableStubsSrcJar android.Path
+}
+
+var StubsSrcInfoProvider = blueprint.NewProvider[StubsSrcInfo]()
+
 // The values allowed for Droidstubs' Api_levels_sdk_type
 var allowedApiLevelSdkTypes = []string{"public", "system", "module-lib", "system-server"}
 
@@ -498,9 +521,9 @@
 }
 
 func (d *Droidstubs) mergeAnnoDirFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
-	ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) {
-		if t, ok := m.(*ExportedDroiddocDir); ok {
-			cmd.FlagWithArg("--merge-qualifier-annotations ", t.dir.String()).Implicits(t.deps)
+	ctx.VisitDirectDepsProxyWithTag(metalavaMergeAnnotationsDirTag, func(m android.ModuleProxy) {
+		if t, ok := android.OtherModuleProvider(ctx, m, ExportedDroiddocDirInfoProvider); ok {
+			cmd.FlagWithArg("--merge-qualifier-annotations ", t.Dir.String()).Implicits(t.Deps)
 		} else {
 			ctx.PropertyErrorf("merge_annotations_dirs",
 				"module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
@@ -509,9 +532,9 @@
 }
 
 func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
-	ctx.VisitDirectDepsWithTag(metalavaMergeInclusionAnnotationsDirTag, func(m android.Module) {
-		if t, ok := m.(*ExportedDroiddocDir); ok {
-			cmd.FlagWithArg("--merge-inclusion-annotations ", t.dir.String()).Implicits(t.deps)
+	ctx.VisitDirectDepsProxyWithTag(metalavaMergeInclusionAnnotationsDirTag, func(m android.ModuleProxy) {
+		if t, ok := android.OtherModuleProvider(ctx, m, ExportedDroiddocDirInfoProvider); ok {
+			cmd.FlagWithArg("--merge-inclusion-annotations ", t.Dir.String()).Implicits(t.Deps)
 		} else {
 			ctx.PropertyErrorf("merge_inclusion_annotations_dirs",
 				"module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
@@ -525,12 +548,12 @@
 		d.apiLevelsGenerationFlags(ctx, cmd, stubsType, apiVersionsXml)
 		apiVersions = apiVersionsXml
 	} else {
-		ctx.VisitDirectDepsWithTag(metalavaAPILevelsModuleTag, func(m android.Module) {
-			if s, ok := m.(*Droidstubs); ok {
+		ctx.VisitDirectDepsProxyWithTag(metalavaAPILevelsModuleTag, func(m android.ModuleProxy) {
+			if s, ok := android.OtherModuleProvider(ctx, m, DroidStubsInfoProvider); ok {
 				if stubsType == Everything {
-					apiVersions = s.everythingArtifacts.apiVersionsXml
+					apiVersions = s.EverythingStubsInfo.ApiVersionsXml
 				} else if stubsType == Exportable {
-					apiVersions = s.exportableArtifacts.apiVersionsXml
+					apiVersions = s.ExportableStubsInfo.ApiVersionsXml
 				} else {
 					ctx.ModuleErrorf("%s stubs type does not generate api-versions.xml file", stubsType.String())
 				}
@@ -541,7 +564,15 @@
 		})
 	}
 	if apiVersions != nil {
-		cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion().String())
+		// We are migrating from a single API level to major.minor
+		// versions and PlatformSdkVersionFull is not yet set in all
+		// release configs. If it is not set, fall back on the single
+		// API level.
+		if fullSdkVersion := ctx.Config().PlatformSdkVersionFull(); len(fullSdkVersion) > 0 {
+			cmd.FlagWithArg("--current-version ", fullSdkVersion)
+		} else {
+			cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion().String())
+		}
 		cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
 		cmd.FlagWithInput("--apply-api-levels ", apiVersions)
 	}
@@ -603,18 +634,18 @@
 
 	var dirs []string
 	var extensions_dir string
-	ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
-		if t, ok := m.(*ExportedDroiddocDir); ok {
-			extRegex := regexp.MustCompile(t.dir.String() + extensionsPattern)
+	ctx.VisitDirectDepsProxyWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.ModuleProxy) {
+		if t, ok := android.OtherModuleProvider(ctx, m, ExportedDroiddocDirInfoProvider); ok {
+			extRegex := regexp.MustCompile(t.Dir.String() + extensionsPattern)
 
 			// Grab the first extensions_dir and we find while scanning ExportedDroiddocDir.deps;
 			// ideally this should be read from prebuiltApis.properties.Extensions_*
-			for _, dep := range t.deps {
+			for _, dep := range t.Deps {
 				// Check to see if it matches an extension first.
 				depBase := dep.Base()
 				if extRegex.MatchString(dep.String()) && d.properties.Extensions_info_file != nil {
 					if extensions_dir == "" {
-						extensions_dir = t.dir.String() + "/extensions"
+						extensions_dir = t.Dir.String() + "/extensions"
 					}
 					cmd.Implicit(dep)
 				} else if depBase == filename {
@@ -622,23 +653,17 @@
 					cmd.Implicit(dep)
 				} else if depBase == AndroidPlusUpdatableJar && d.properties.Extensions_info_file != nil {
 					// The output api-versions.xml has been requested to include information on SDK
-					// extensions. That means it also needs to include
-					// so
-					// The module-lib and system-server directories should use `android-plus-updatable.jar`
-					// instead of `android.jar`. See AndroidPlusUpdatableJar for more information.
-					cmd.Implicit(dep)
-				} else if filename != "android.jar" && depBase == "android.jar" {
-					// Metalava implicitly searches these patterns:
-					//  prebuilts/tools/common/api-versions/android-%/android.jar
-					//  prebuilts/sdk/%/public/android.jar
-					// Add android.jar files from the api_levels_annotations_dirs directories to try
-					// to satisfy these patterns.  If Metalava can't find a match for an API level
-					// between 1 and 28 in at least one pattern it will fail.
+					// extensions, i.e. updatable Apis. That means it also needs to include the history of
+					// those updatable APIs. Usually, they would be included in the `android.jar` file but
+					// unfortunately, the `module-lib` and `system-server` cannot as it would lead to build
+					// cycles. So, the module-lib and system-server directories contain an
+					// `android-plus-updatable.jar` that should be used instead of `android.jar`. See
+					// AndroidPlusUpdatableJar for more information.
 					cmd.Implicit(dep)
 				}
 			}
 
-			dirs = append(dirs, t.dir.String())
+			dirs = append(dirs, t.Dir.String())
 		} else {
 			ctx.PropertyErrorf("api_levels_annotations_dirs",
 				"module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
@@ -646,11 +671,11 @@
 	})
 
 	// Generate the list of --android-jar-pattern options. The order matters so the first one which
-	// matches will be the one that is used for a specific api level..
+	// matches will be the one that is used for a specific api level.
 	for _, sdkDir := range sdkDirs {
 		for _, dir := range dirs {
 			addPattern := func(jarFilename string) {
-				cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, jarFilename))
+				cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/{version:major.minor?}/%s/%s", dir, sdkDir, jarFilename))
 			}
 
 			if sdkDir == "module-lib" || sdkDir == "system-server" {
@@ -665,6 +690,10 @@
 
 			addPattern(filename)
 		}
+
+		if extensions_dir != "" {
+			cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/{version:extension}/%s/{module}.jar", extensions_dir, sdkDir))
+		}
 	}
 
 	if d.properties.Extensions_info_file != nil {
@@ -673,7 +702,6 @@
 		}
 		info_file := android.PathForModuleSrc(ctx, *d.properties.Extensions_info_file)
 		cmd.Implicit(info_file)
-		cmd.FlagWithArg("--sdk-extensions-root ", extensions_dir)
 		cmd.FlagWithArg("--sdk-extensions-info ", info_file.String())
 	}
 }
@@ -700,7 +728,8 @@
 }
 
 func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths,
-	srcJarList android.Path, homeDir android.WritablePath, params stubsCommandConfigParams, configFiles android.Paths) *android.RuleBuilderCommand {
+	srcJarList android.Path, homeDir android.WritablePath, params stubsCommandConfigParams,
+	configFiles android.Paths, apiSurface *string) *android.RuleBuilderCommand {
 	rule.Command().Text("rm -rf").Flag(homeDir.String())
 	rule.Command().Text("mkdir -p").Flag(homeDir.String())
 
@@ -746,6 +775,8 @@
 
 	addMetalavaConfigFilesToCmd(cmd, configFiles)
 
+	addOptionalApiSurfaceToCmd(cmd, apiSurface)
+
 	return cmd
 }
 
@@ -764,6 +795,14 @@
 	cmd.FlagForEachInput("--config-file ", configFiles)
 }
 
+// addOptionalApiSurfaceToCmd adds --api-surface option is apiSurface is not `nil`.
+func addOptionalApiSurfaceToCmd(cmd *android.RuleBuilderCommand, apiSurface *string) {
+	if apiSurface != nil {
+		cmd.Flag("--api-surface")
+		cmd.Flag(*apiSurface)
+	}
+}
+
 // Pass flagged apis related flags to metalava. When aconfig_declarations property is not
 // defined for a module, simply revert all flagged apis annotations. If aconfig_declarations
 // property is defined, apply transformations and only revert the flagged apis that are not
@@ -790,19 +829,17 @@
 		}
 	}
 
-	if len(aconfigFlagsPaths) == 0 {
-		// This argument should not be added for "everything" stubs
-		cmd.Flag("--revert-annotation android.annotation.FlaggedApi")
-		return
-	}
+	// If aconfigFlagsPaths is empty then it is still important to generate the
+	// Metalava flags config file, albeit with an empty set of flags, so that all
+	// flagged APIs will be reverted.
 
-	releasedFlaggedApisFile := android.PathForModuleOut(ctx, fmt.Sprintf("released-flagged-apis-%s.txt", stubsType.String()))
-	revertAnnotationsFile := android.PathForModuleOut(ctx, fmt.Sprintf("revert-annotations-%s.txt", stubsType.String()))
+	releasedFlagsFile := android.PathForModuleOut(ctx, fmt.Sprintf("released-flags-%s.pb", stubsType.String()))
+	metalavaFlagsConfigFile := android.PathForModuleOut(ctx, fmt.Sprintf("flags-config-%s.xml", stubsType.String()))
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        gatherReleasedFlaggedApisRule,
 		Inputs:      aconfigFlagsPaths,
-		Output:      releasedFlaggedApisFile,
+		Output:      releasedFlagsFile,
 		Description: fmt.Sprintf("%s gather aconfig flags", stubsType),
 		Args: map[string]string{
 			"flags_path":  android.JoinPathsWithPrefix(aconfigFlagsPaths, "--cache "),
@@ -812,12 +849,12 @@
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        generateMetalavaRevertAnnotationsRule,
-		Input:       releasedFlaggedApisFile,
-		Output:      revertAnnotationsFile,
-		Description: fmt.Sprintf("%s revert annotations", stubsType),
+		Input:       releasedFlagsFile,
+		Output:      metalavaFlagsConfigFile,
+		Description: fmt.Sprintf("%s metalava flags config", stubsType),
 	})
 
-	cmd.FlagWithInput("@", revertAnnotationsFile)
+	cmd.FlagWithInput("--config-file ", metalavaFlagsConfigFile)
 }
 
 func (d *Droidstubs) commonMetalavaStubCmd(ctx android.ModuleContext, rule *android.RuleBuilder,
@@ -838,7 +875,8 @@
 
 	configFiles := android.PathsForModuleSrc(ctx, d.properties.ConfigFiles)
 
-	cmd := metalavaCmd(ctx, rule, d.Javadoc.srcFiles, srcJarList, homeDir, params.stubConfig, configFiles)
+	cmd := metalavaCmd(ctx, rule, d.Javadoc.srcFiles, srcJarList, homeDir, params.stubConfig,
+		configFiles, d.properties.Api_surface)
 	cmd.Implicits(d.Javadoc.implicits)
 
 	d.stubsFlags(ctx, cmd, params.stubsDir, params.stubConfig.stubsType, params.stubConfig.checkApi)
@@ -1177,6 +1215,34 @@
 	rule.Build(fmt.Sprintf("metalava_%s", params.stubConfig.stubsType.String()), "metalava merged")
 }
 
+func (d *Droidstubs) setPhonyRules(ctx android.ModuleContext) {
+	if d.apiFile != nil {
+		ctx.Phony(d.Name(), d.apiFile)
+		ctx.Phony(fmt.Sprintf("%s.txt", d.Name()), d.apiFile)
+	}
+	if d.removedApiFile != nil {
+		ctx.Phony(d.Name(), d.removedApiFile)
+		ctx.Phony(fmt.Sprintf("%s.txt", d.Name()), d.removedApiFile)
+	}
+	if d.checkCurrentApiTimestamp != nil {
+		ctx.Phony(fmt.Sprintf("%s-check-current-api", d.Name()), d.checkCurrentApiTimestamp)
+		ctx.Phony("checkapi", d.checkCurrentApiTimestamp)
+	}
+	if d.updateCurrentApiTimestamp != nil {
+		ctx.Phony(fmt.Sprintf("%s-update-current-api", d.Name()), d.updateCurrentApiTimestamp)
+		ctx.Phony("update-api", d.updateCurrentApiTimestamp)
+	}
+	if d.checkLastReleasedApiTimestamp != nil {
+		ctx.Phony(fmt.Sprintf("%s-check-last-released-api", d.Name()), d.checkLastReleasedApiTimestamp)
+	}
+	if d.apiLintTimestamp != nil {
+		ctx.Phony(fmt.Sprintf("%s-api-lint", d.Name()), d.apiLintTimestamp)
+	}
+	if d.checkNullabilityWarningsTimestamp != nil {
+		ctx.Phony(fmt.Sprintf("%s-check-nullability-warnings", d.Name()), d.checkNullabilityWarningsTimestamp)
+	}
+}
+
 func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	deps := d.Javadoc.collectDeps(ctx)
 
@@ -1185,7 +1251,7 @@
 
 	// Add options for the other optional tasks: API-lint and check-released.
 	// We generate separate timestamp files for them.
-	doApiLint := BoolDefault(d.properties.Check_api.Api_lint.Enabled, false)
+	doApiLint := BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) && !ctx.Config().PartialCompileFlags().Disable_api_lint
 	doCheckReleased := apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released")
 
 	writeSdkValues := Bool(d.properties.Write_sdk_values)
@@ -1220,6 +1286,41 @@
 	stubCmdParams.stubsType = Exportable
 	d.exportableStubCmd(ctx, stubCmdParams)
 
+	if String(d.properties.Check_nullability_warnings) != "" {
+		if d.everythingArtifacts.nullabilityWarningsFile == nil {
+			ctx.PropertyErrorf("check_nullability_warnings",
+				"Cannot specify check_nullability_warnings unless validating nullability")
+		}
+
+		checkNullabilityWarningsPath := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings))
+
+		d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_nullability_warnings.timestamp")
+
+		msg := fmt.Sprintf(`\n******************************\n`+
+			`The warnings encountered during nullability annotation validation did\n`+
+			`not match the checked in file of expected warnings. The diffs are shown\n`+
+			`above. You have two options:\n`+
+			`   1. Resolve the differences by editing the nullability annotations.\n`+
+			`   2. Update the file of expected warnings by running:\n`+
+			`         cp %s %s\n`+
+			`       and submitting the updated file as part of your change.`,
+			d.everythingArtifacts.nullabilityWarningsFile, checkNullabilityWarningsPath)
+
+		rule := android.NewRuleBuilder(pctx, ctx)
+
+		rule.Command().
+			Text("(").
+			Text("diff").Input(checkNullabilityWarningsPath).Input(d.everythingArtifacts.nullabilityWarningsFile).
+			Text("&&").
+			Text("touch").Output(d.checkNullabilityWarningsTimestamp).
+			Text(") || (").
+			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+			Text("; exit 38").
+			Text(")")
+
+		rule.Build("nullabilityWarningsCheck", "nullability warnings check")
+	}
+
 	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
 
 		if len(d.Javadoc.properties.Out) > 0 {
@@ -1269,13 +1370,25 @@
 			`Note that DISABLE_STUB_VALIDATION=true does not bypass checkapi.\n`+
 			`******************************\n`, ctx.ModuleName())
 
-		rule.Command().
+		cmd := rule.Command().
 			Text("touch").Output(d.checkCurrentApiTimestamp).
 			Text(") || (").
 			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
 			Text("; exit 38").
 			Text(")")
 
+		if d.apiLintTimestamp != nil {
+			cmd.Validation(d.apiLintTimestamp)
+		}
+
+		if d.checkLastReleasedApiTimestamp != nil {
+			cmd.Validation(d.checkLastReleasedApiTimestamp)
+		}
+
+		if d.checkNullabilityWarningsTimestamp != nil {
+			cmd.Validation(d.checkNullabilityWarningsTimestamp)
+		}
+
 		rule.Build("metalavaCurrentApiCheck", "check current API")
 
 		d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, Everything.String(), "update_current_api.timestamp")
@@ -1305,42 +1418,49 @@
 		rule.Build("metalavaCurrentApiUpdate", "update current API")
 	}
 
-	if String(d.properties.Check_nullability_warnings) != "" {
-		if d.everythingArtifacts.nullabilityWarningsFile == nil {
-			ctx.PropertyErrorf("check_nullability_warnings",
-				"Cannot specify check_nullability_warnings unless validating nullability")
-		}
-
-		checkNullabilityWarnings := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings))
-
-		d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_nullability_warnings.timestamp")
-
-		msg := fmt.Sprintf(`\n******************************\n`+
-			`The warnings encountered during nullability annotation validation did\n`+
-			`not match the checked in file of expected warnings. The diffs are shown\n`+
-			`above. You have two options:\n`+
-			`   1. Resolve the differences by editing the nullability annotations.\n`+
-			`   2. Update the file of expected warnings by running:\n`+
-			`         cp %s %s\n`+
-			`       and submitting the updated file as part of your change.`,
-			d.everythingArtifacts.nullabilityWarningsFile, checkNullabilityWarnings)
-
-		rule := android.NewRuleBuilder(pctx, ctx)
-
-		rule.Command().
-			Text("(").
-			Text("diff").Input(checkNullabilityWarnings).Input(d.everythingArtifacts.nullabilityWarningsFile).
-			Text("&&").
-			Text("touch").Output(d.checkNullabilityWarningsTimestamp).
-			Text(") || (").
-			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
-			Text("; exit 38").
-			Text(")")
-
-		rule.Build("nullabilityWarningsCheck", "nullability warnings check")
+	droidInfo := DroidStubsInfo{
+		CurrentApiTimestamp: d.CurrentApiTimestamp(),
+		EverythingStubsInfo: StubsInfo{},
+		ExportableStubsInfo: StubsInfo{},
 	}
+	setDroidInfo(ctx, d, &droidInfo.EverythingStubsInfo, Everything)
+	setDroidInfo(ctx, d, &droidInfo.ExportableStubsInfo, Exportable)
+	android.SetProvider(ctx, DroidStubsInfoProvider, droidInfo)
+
+	android.SetProvider(ctx, StubsSrcInfoProvider, StubsSrcInfo{
+		EverythingStubsSrcJar: d.stubsSrcJar,
+		ExportableStubsSrcJar: d.exportableStubsSrcJar,
+	})
 
 	d.setOutputFiles(ctx)
+
+	d.setPhonyRules(ctx)
+
+	if d.apiLintTimestamp != nil {
+		if d.apiLintReport != nil {
+			ctx.DistForGoalsWithFilename(
+				[]string{fmt.Sprintf("%s-api-lint", d.Name()), "droidcore"},
+				d.apiLintReport,
+				fmt.Sprintf("apilint/%s-lint-report.txt", d.Name()),
+			)
+		}
+	}
+}
+
+func setDroidInfo(ctx android.ModuleContext, d *Droidstubs, info *StubsInfo, typ StubsType) {
+	if typ == Everything {
+		info.ApiFile = d.apiFile
+		info.RemovedApiFile = d.removedApiFile
+		info.AnnotationsZip = d.everythingArtifacts.annotationsZip
+		info.ApiVersionsXml = d.everythingArtifacts.apiVersionsXml
+	} else if typ == Exportable {
+		info.ApiFile = d.exportableApiFile
+		info.RemovedApiFile = d.exportableRemovedApiFile
+		info.AnnotationsZip = d.exportableArtifacts.annotationsZip
+		info.ApiVersionsXml = d.exportableArtifacts.apiVersionsXml
+	} else {
+		ctx.ModuleErrorf("failed to set ApiVersionsXml, stubs type not supported: %d", typ)
+	}
 }
 
 // This method sets the outputFiles property, which is used to set the
@@ -1508,6 +1628,11 @@
 		p.stubsSrcJar = outPath
 	}
 
+	android.SetProvider(ctx, StubsSrcInfoProvider, StubsSrcInfo{
+		EverythingStubsSrcJar: p.stubsSrcJar,
+		ExportableStubsSrcJar: p.stubsSrcJar,
+	})
+
 	ctx.SetOutputFiles(android.Paths{p.stubsSrcJar}, "")
 	// prebuilt droidstubs does not output "exportable" stubs.
 	// Output the "everything" stubs srcjar file if the tag is ".exportable".
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index 1e8362c..1f9d223 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -27,6 +27,7 @@
 )
 
 func TestDroidstubs(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJavaWithFS(t, `
 		droiddoc_exported_dir {
 			name: "droiddoc-templates-sdk",
@@ -82,13 +83,13 @@
 		},
 	}
 	for _, c := range testcases {
-		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		m := ctx.ModuleForTests(t, c.moduleName, "android_common")
 		manifest := m.Output("metalava.sbox.textproto")
 		sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, manifest)
 		cmdline := String(sboxProto.Commands[0].Command)
 		android.AssertStringContainsEquals(t, "api-versions generation flag", cmdline, "--generate-api-levels", c.generate_xml)
 		if c.expectedJarFilename != "" {
-			expected := "--android-jar-pattern ./%/public/" + c.expectedJarFilename
+			expected := "--android-jar-pattern ./{version:major.minor?}/public/" + c.expectedJarFilename
 			if !strings.Contains(cmdline, expected) {
 				t.Errorf("For %q, expected metalava argument %q, but was not found %q", c.moduleName, expected, cmdline)
 			}
@@ -131,7 +132,7 @@
 			"foo-doc/a.java": nil,
 		})
 
-	m := ctx.ModuleForTests("foo-stubs", "android_common")
+	m := ctx.ModuleForTests(t, "foo-stubs", "android_common")
 	manifest := m.Output("metalava.sbox.textproto")
 	cmd := String(android.RuleBuilderSboxProtoForTests(t, ctx, manifest).Commands[0].Command)
 	r := regexp.MustCompile(`--android-jar-pattern [^ ]+/android.jar`)
@@ -139,35 +140,38 @@
 }
 
 func TestPublicDroidstubs(t *testing.T) {
+	t.Parallel()
 	patterns := getAndroidJarPatternsForDroidstubs(t, "public")
 
 	android.AssertArrayString(t, "order of patterns", []string{
-		"--android-jar-pattern somedir/%/public/android.jar",
-		"--android-jar-pattern someotherdir/%/public/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/public/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/public/android.jar",
 	}, patterns)
 }
 
 func TestSystemDroidstubs(t *testing.T) {
+	t.Parallel()
 	patterns := getAndroidJarPatternsForDroidstubs(t, "system")
 
 	android.AssertArrayString(t, "order of patterns", []string{
-		"--android-jar-pattern somedir/%/system/android.jar",
-		"--android-jar-pattern someotherdir/%/system/android.jar",
-		"--android-jar-pattern somedir/%/public/android.jar",
-		"--android-jar-pattern someotherdir/%/public/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/system/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/system/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/public/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/public/android.jar",
 	}, patterns)
 }
 
 func TestModuleLibDroidstubs(t *testing.T) {
+	t.Parallel()
 	patterns := getAndroidJarPatternsForDroidstubs(t, "module-lib")
 
 	android.AssertArrayString(t, "order of patterns", []string{
-		"--android-jar-pattern somedir/%/module-lib/android.jar",
-		"--android-jar-pattern someotherdir/%/module-lib/android.jar",
-		"--android-jar-pattern somedir/%/system/android.jar",
-		"--android-jar-pattern someotherdir/%/system/android.jar",
-		"--android-jar-pattern somedir/%/public/android.jar",
-		"--android-jar-pattern someotherdir/%/public/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/module-lib/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/module-lib/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/system/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/system/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/public/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/public/android.jar",
 	}, patterns)
 }
 
@@ -175,14 +179,14 @@
 	patterns := getAndroidJarPatternsForDroidstubs(t, "system-server")
 
 	android.AssertArrayString(t, "order of patterns", []string{
-		"--android-jar-pattern somedir/%/system-server/android.jar",
-		"--android-jar-pattern someotherdir/%/system-server/android.jar",
-		"--android-jar-pattern somedir/%/module-lib/android.jar",
-		"--android-jar-pattern someotherdir/%/module-lib/android.jar",
-		"--android-jar-pattern somedir/%/system/android.jar",
-		"--android-jar-pattern someotherdir/%/system/android.jar",
-		"--android-jar-pattern somedir/%/public/android.jar",
-		"--android-jar-pattern someotherdir/%/public/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/system-server/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/system-server/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/module-lib/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/module-lib/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/system/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/system/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/public/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/public/android.jar",
 	}, patterns)
 }
 
@@ -206,7 +210,7 @@
 			"bar-doc/a.java": nil,
 		})
 
-	m := ctx.ModuleForTests("bar-stubs", "android_common")
+	m := ctx.ModuleForTests(t, "bar-stubs", "android_common")
 	metalava := m.Rule("metalava")
 	if g, w := metalava.Inputs.Strings(), []string{"bar-doc/a.java"}; !reflect.DeepEqual(w, g) {
 		t.Errorf("Expected inputs %q, got %q", w, g)
@@ -267,7 +271,7 @@
 }
 
 func checkSystemModulesUseByDroidstubs(t *testing.T, ctx *android.TestContext, moduleName string, systemJar string) {
-	metalavaRule := ctx.ModuleForTests(moduleName, "android_common").Rule("metalava")
+	metalavaRule := ctx.ModuleForTests(t, moduleName, "android_common").Rule("metalava")
 	var systemJars []string
 	for _, i := range metalavaRule.Implicits {
 		systemJars = append(systemJars, i.Base())
@@ -300,10 +304,10 @@
 			"sdk/extensions/1/public/some-mainline-module-stubs.jar": nil,
 			"sdk/extensions/info.txt":                                nil,
 		})
-	m := ctx.ModuleForTests("baz-stubs", "android_common")
+	m := ctx.ModuleForTests(t, "baz-stubs", "android_common")
 	manifest := m.Output("metalava.sbox.textproto")
 	cmdline := String(android.RuleBuilderSboxProtoForTests(t, ctx, manifest).Commands[0].Command)
-	android.AssertStringDoesContain(t, "sdk-extensions-root present", cmdline, "--sdk-extensions-root sdk/extensions")
+	android.AssertStringDoesContain(t, "android-jar-pattern present", cmdline, "--android-jar-pattern sdk/extensions/{version:extension}/public/{module}.jar")
 	android.AssertStringDoesContain(t, "sdk-extensions-info present", cmdline, "--sdk-extensions-info sdk/extensions/info.txt")
 }
 
@@ -328,7 +332,7 @@
 		},
 	)
 
-	ctx.ModuleForTests("foo.api.contribution", "")
+	ctx.ModuleForTests(t, "foo.api.contribution", "")
 }
 
 func TestGeneratedApiContributionVisibilityTest(t *testing.T) {
@@ -362,7 +366,7 @@
 		},
 	)
 
-	ctx.ModuleForTests("bar", "android_common")
+	ctx.ModuleForTests(t, "bar", "android_common")
 }
 
 func TestAconfigDeclarations(t *testing.T) {
@@ -404,14 +408,14 @@
 	android.AssertBoolEquals(t, "foo expected to depend on bar",
 		CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar"), true)
 
-	m := result.ModuleForTests("foo", "android_common")
+	m := result.ModuleForTests(t, "foo", "android_common")
 	android.AssertStringDoesContain(t, "foo generates revert annotations file",
-		strings.Join(m.AllOutputs(), ""), "revert-annotations-exportable.txt")
+		strings.Join(m.AllOutputs(), ""), "flags-config-exportable.xml")
 
 	// revert-annotations.txt passed to exportable stubs generation metalava command
 	manifest := m.Output("metalava_exportable.sbox.textproto")
 	cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command)
-	android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "revert-annotations-exportable.txt")
+	android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "flags-config-exportable.xml")
 
 	android.AssertStringDoesContain(t, "foo generates exportable stubs jar",
 		strings.Join(m.AllOutputs(), ""), "exportable/foo-stubs.srcjar")
@@ -454,9 +458,9 @@
 	}
 	`)
 
-	m := result.ModuleForTests("foo", "android_common")
+	m := result.ModuleForTests(t, "foo", "android_common")
 
-	rule := m.Output("released-flagged-apis-exportable.txt")
+	rule := m.Output("released-flags-exportable.pb")
 	exposeWritableApisFilter := "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'"
 	android.AssertStringEquals(t, "Filter argument expected to contain READ_WRITE permissions", exposeWritableApisFilter, rule.Args["filter_args"])
 }
diff --git a/java/fuzz.go b/java/fuzz.go
index 90f09a8..0e239f0 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -63,7 +63,7 @@
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 	module.Module.sourceProperties.Test_only = proptools.BoolPtr(true)
 	module.Module.sourceProperties.Top_level_test_target = true
 
@@ -107,23 +107,7 @@
 }
 
 func (j *JavaFuzzTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	if j.fuzzPackagedModule.FuzzProperties.Corpus != nil {
-		j.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Corpus)
-	}
-	if j.fuzzPackagedModule.FuzzProperties.Device_common_corpus != nil {
-		j.fuzzPackagedModule.Corpus = append(j.fuzzPackagedModule.Corpus, android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Device_common_corpus)...)
-	}
-	if j.fuzzPackagedModule.FuzzProperties.Data != nil {
-		j.fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Data)
-	}
-	if j.fuzzPackagedModule.FuzzProperties.Dictionary != nil {
-		j.fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *j.fuzzPackagedModule.FuzzProperties.Dictionary)
-	}
-	if j.fuzzPackagedModule.FuzzProperties.Fuzz_config != nil {
-		configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
-		android.WriteFileRule(ctx, configPath, j.fuzzPackagedModule.FuzzProperties.Fuzz_config.String())
-		j.fuzzPackagedModule.Config = configPath
-	}
+	j.fuzzPackagedModule = cc.PackageFuzzModule(ctx, j.fuzzPackagedModule)
 
 	_, sharedDeps := cc.CollectAllSharedDependencies(ctx)
 	for _, dep := range sharedDeps {
@@ -148,6 +132,8 @@
 	}
 
 	j.Test.GenerateAndroidBuildActions(ctx)
+
+	fuzz.SetFuzzPackagedModuleInfo(ctx, &j.fuzzPackagedModule)
 }
 
 type javaFuzzPackager struct {
@@ -169,6 +155,10 @@
 		if !ok {
 			return
 		}
+		fuzzInfo, ok := android.OtherModuleProvider(ctx, module, fuzz.FuzzPackagedModuleInfoProvider)
+		if !ok {
+			return
+		}
 
 		hostOrTargetString := "target"
 		if javaFuzzModule.Target().HostCross {
@@ -195,7 +185,7 @@
 		builder := android.NewRuleBuilder(pctx, ctx)
 
 		// Package the artifacts (data, corpus, config and dictionary) into a zipfile.
-		files = s.PackageArtifacts(ctx, module, javaFuzzModule.fuzzPackagedModule, archDir, builder)
+		files = s.PackageArtifacts(ctx, module, &fuzzInfo, archDir, builder)
 
 		// Add .jar
 		if !javaFuzzModule.Host() {
@@ -209,7 +199,7 @@
 			files = append(files, fuzz.FileToZip{SourceFilePath: fPath})
 		}
 
-		archDirs[archOs], ok = s.BuildZipFile(ctx, module, javaFuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
+		archDirs[archOs], ok = s.BuildZipFile(ctx, module, &fuzzInfo, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
 		if !ok {
 			return
 		}
diff --git a/java/fuzz_test.go b/java/fuzz_test.go
index f29c913..735b8da 100644
--- a/java/fuzz_test.go
+++ b/java/fuzz_test.go
@@ -30,6 +30,7 @@
 )
 
 func TestJavaFuzz(t *testing.T) {
+	t.Parallel()
 	result := prepForJavaFuzzTest.RunTestWithBp(t, `
 		java_fuzz {
 			name: "foo",
@@ -63,16 +64,16 @@
 
 	osCommonTarget := result.Config.BuildOSCommonTarget.String()
 
-	javac := result.ModuleForTests("foo", osCommonTarget).Rule("javac")
-	combineJar := result.ModuleForTests("foo", osCommonTarget).Description("for javac")
+	javac := result.ModuleForTests(t, "foo", osCommonTarget).Rule("javac")
+	combineJar := result.ModuleForTests(t, "foo", osCommonTarget).Description("for javac")
 
 	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
 		t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
 	}
 
-	baz := result.ModuleForTests("baz", osCommonTarget).Rule("javac").Output.String()
-	barOut := filepath.Join("out", "soong", ".intermediates", "bar", osCommonTarget, "javac-header", "bar.jar")
-	bazOut := filepath.Join("out", "soong", ".intermediates", "baz", osCommonTarget, "javac-header", "baz.jar")
+	baz := result.ModuleForTests(t, "baz", osCommonTarget).Rule("javac").Output.String()
+	barOut := filepath.Join("out", "soong", ".intermediates", "bar", osCommonTarget, "local-javac-header", "bar.jar")
+	bazOut := filepath.Join("out", "soong", ".intermediates", "baz", osCommonTarget, "local-javac-header", "baz.jar")
 
 	android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], barOut)
 	android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], bazOut)
@@ -82,7 +83,7 @@
 	}
 
 	ctx := result.TestContext
-	foo := ctx.ModuleForTests("foo", osCommonTarget).Module().(*JavaFuzzTest)
+	foo := ctx.ModuleForTests(t, "foo", osCommonTarget).Module().(*JavaFuzzTest)
 
 	expected := "lib64/libjni.so"
 	if runtime.GOOS == "darwin" {
diff --git a/java/gen.go b/java/gen.go
index 1b4f4c7..aab8418 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -26,15 +26,14 @@
 )
 
 func init() {
-	pctx.SourcePathVariable("logtagsCmd", "build/make/tools/java-event-log-tags.py")
-	pctx.SourcePathVariable("logtagsLib", "build/make/tools/event_log_tags.py")
+	pctx.HostBinToolVariable("logtagsCmd", "java-event-log-tags")
 }
 
 var (
 	logtags = pctx.AndroidStaticRule("logtags",
 		blueprint.RuleParams{
 			Command:     "$logtagsCmd -o $out $in",
-			CommandDeps: []string{"$logtagsCmd", "$logtagsLib"},
+			CommandDeps: []string{"$logtagsCmd"},
 		})
 )
 
diff --git a/java/generated_java_library_test.go b/java/generated_java_library_test.go
index a5c4be1..e5ee586 100644
--- a/java/generated_java_library_test.go
+++ b/java/generated_java_library_test.go
@@ -52,6 +52,7 @@
 }
 
 func TestGenLib(t *testing.T) {
+	t.Parallel()
 	bp := `
 				test_java_gen_lib {
 					name: "javagenlibtest",
@@ -60,6 +61,6 @@
 			`
 	result := testGenLib(t, android.FixtureExpectsNoErrors, bp)
 
-	javagenlibtest := result.ModuleForTests("javagenlibtest", "android_common").Module().(*GeneratedJavaLibraryModule)
+	javagenlibtest := result.ModuleForTests(t, "javagenlibtest", "android_common").Module().(*GeneratedJavaLibraryModule)
 	android.AssertPathsEndWith(t, "Generated_srcjars", []string{"/blah.srcjar"}, javagenlibtest.Library.properties.Generated_srcjars)
 }
diff --git a/java/genrule_combiner.go b/java/genrule_combiner.go
new file mode 100644
index 0000000..357dc2c
--- /dev/null
+++ b/java/genrule_combiner.go
@@ -0,0 +1,252 @@
+// Copyright 2019 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 (
+	"fmt"
+	"io"
+
+	"android/soong/android"
+	"android/soong/dexpreopt"
+
+	"github.com/google/blueprint/depset"
+	"github.com/google/blueprint/proptools"
+)
+
+type GenruleCombiner struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	genruleCombinerProperties GenruleCombinerProperties
+
+	headerJars                    android.Paths
+	implementationJars            android.Paths
+	implementationAndResourceJars android.Paths
+	resourceJars                  android.Paths
+	aconfigProtoFiles             android.Paths
+
+	srcJarArgs []string
+	srcJarDeps android.Paths
+
+	headerDirs android.Paths
+
+	combinedHeaderJar         android.Path
+	combinedImplementationJar android.Path
+}
+
+type GenruleCombinerProperties struct {
+	// List of modules whose implementation (and resources) jars will be visible to modules
+	// that depend on this module.
+	Static_libs proptools.Configurable[[]string] `android:"arch_variant"`
+
+	// List of modules whose header jars will be visible to modules that depend on this module.
+	Headers proptools.Configurable[[]string] `android:"arch_variant"`
+}
+
+// java_genrule_combiner provides the implementation and resource jars from `static_libs`, with
+// the header jars from `headers`.
+//
+// This is useful when a java_genrule is used to change the implementation of a java library
+// without requiring a change in the header jars.
+func GenruleCombinerFactory() android.Module {
+	module := &GenruleCombiner{}
+
+	module.AddProperties(&module.genruleCombinerProperties)
+	InitJavaModule(module, android.HostAndDeviceSupported)
+	return module
+}
+
+var genruleCombinerHeaderDepTag = dependencyTag{name: "genrule_combiner_header"}
+
+func (j *GenruleCombiner) DepsMutator(ctx android.BottomUpMutatorContext) {
+	ctx.AddVariationDependencies(nil, staticLibTag,
+		j.genruleCombinerProperties.Static_libs.GetOrDefault(ctx, nil)...)
+	ctx.AddVariationDependencies(nil, genruleCombinerHeaderDepTag,
+		j.genruleCombinerProperties.Headers.GetOrDefault(ctx, nil)...)
+}
+
+func (j *GenruleCombiner) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if len(j.genruleCombinerProperties.Static_libs.GetOrDefault(ctx, nil)) < 1 {
+		ctx.PropertyErrorf("static_libs", "at least one dependency is required")
+	}
+
+	if len(j.genruleCombinerProperties.Headers.GetOrDefault(ctx, nil)) < 1 {
+		ctx.PropertyErrorf("headers", "at least one dependency is required")
+	}
+
+	var transitiveHeaderJars []depset.DepSet[android.Path]
+	var transitiveImplementationJars []depset.DepSet[android.Path]
+	var transitiveResourceJars []depset.DepSet[android.Path]
+	var sdkVersion android.SdkSpec
+	var stubsLinkType StubsLinkType
+	moduleWithSdkDepInfo := &ModuleWithSdkDepInfo{}
+
+	// Collect the headers first, so that aconfig flag values for the libraries will override
+	// values from the headers (if they are different).
+	ctx.VisitDirectDepsWithTag(genruleCombinerHeaderDepTag, func(m android.Module) {
+		if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
+			j.headerJars = append(j.headerJars, dep.HeaderJars...)
+
+			j.srcJarArgs = append(j.srcJarArgs, dep.SrcJarArgs...)
+			j.srcJarDeps = append(j.srcJarDeps, dep.SrcJarDeps...)
+			j.aconfigProtoFiles = append(j.aconfigProtoFiles, dep.AconfigIntermediateCacheOutputPaths...)
+			sdkVersion = dep.SdkVersion
+			stubsLinkType = dep.StubsLinkType
+			*moduleWithSdkDepInfo = *dep.ModuleWithSdkDepInfo
+
+			transitiveHeaderJars = append(transitiveHeaderJars, dep.TransitiveStaticLibsHeaderJars)
+		} else if dep, ok := android.OtherModuleProvider(ctx, m, android.CodegenInfoProvider); ok {
+			j.aconfigProtoFiles = append(j.aconfigProtoFiles, dep.IntermediateCacheOutputPaths...)
+		} else {
+			ctx.PropertyErrorf("headers", "module %q cannot be used as a dependency", ctx.OtherModuleName(m))
+		}
+	})
+	ctx.VisitDirectDepsWithTag(staticLibTag, func(m android.Module) {
+		if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
+			j.implementationJars = append(j.implementationJars, dep.ImplementationJars...)
+			j.implementationAndResourceJars = append(j.implementationAndResourceJars, dep.ImplementationAndResourcesJars...)
+			j.resourceJars = append(j.resourceJars, dep.ResourceJars...)
+
+			transitiveImplementationJars = append(transitiveImplementationJars, dep.TransitiveStaticLibsImplementationJars)
+			transitiveResourceJars = append(transitiveResourceJars, dep.TransitiveStaticLibsResourceJars)
+			j.aconfigProtoFiles = append(j.aconfigProtoFiles, dep.AconfigIntermediateCacheOutputPaths...)
+		} else if dep, ok := android.OtherModuleProvider(ctx, m, android.OutputFilesProvider); ok {
+			// This is provided by `java_genrule` modules.
+			j.implementationJars = append(j.implementationJars, dep.DefaultOutputFiles...)
+			j.implementationAndResourceJars = append(j.implementationAndResourceJars, dep.DefaultOutputFiles...)
+			stubsLinkType = Implementation
+		} else {
+			ctx.PropertyErrorf("static_libs", "module %q cannot be used as a dependency", ctx.OtherModuleName(m))
+		}
+	})
+
+	jarName := ctx.ModuleName() + ".jar"
+
+	if len(j.implementationAndResourceJars) > 1 {
+		outputFile := android.PathForModuleOut(ctx, "combined", jarName)
+		TransformJarsToJar(ctx, outputFile, "combine", j.implementationAndResourceJars,
+			android.OptionalPath{}, false, nil, nil)
+		j.combinedImplementationJar = outputFile
+	} else if len(j.implementationAndResourceJars) == 1 {
+		j.combinedImplementationJar = j.implementationAndResourceJars[0]
+	}
+
+	if len(j.headerJars) > 1 {
+		outputFile := android.PathForModuleOut(ctx, "turbine-combined", jarName)
+		TransformJarsToJar(ctx, outputFile, "turbine combine", j.headerJars,
+			android.OptionalPath{}, false, nil, []string{"META-INF/TRANSITIVE"})
+		j.combinedHeaderJar = outputFile
+		j.headerDirs = append(j.headerDirs, android.PathForModuleOut(ctx, "turbine-combined"))
+	} else if len(j.headerJars) == 1 {
+		j.combinedHeaderJar = j.headerJars[0]
+	}
+
+	javaInfo := &JavaInfo{
+		HeaderJars:                             android.Paths{j.combinedHeaderJar},
+		LocalHeaderJars:                        android.Paths{j.combinedHeaderJar},
+		TransitiveStaticLibsHeaderJars:         depset.New(depset.PREORDER, android.Paths{j.combinedHeaderJar}, transitiveHeaderJars),
+		TransitiveStaticLibsImplementationJars: depset.New(depset.PREORDER, android.Paths{j.combinedImplementationJar}, transitiveImplementationJars),
+		TransitiveStaticLibsResourceJars:       depset.New(depset.PREORDER, nil, transitiveResourceJars),
+		GeneratedSrcjars:                       android.Paths{j.combinedImplementationJar},
+		ImplementationAndResourcesJars:         android.Paths{j.combinedImplementationJar},
+		ImplementationJars:                     android.Paths{j.combinedImplementationJar},
+		ModuleWithSdkDepInfo:                   moduleWithSdkDepInfo,
+		ResourceJars:                           j.resourceJars,
+		OutputFile:                             j.combinedImplementationJar,
+		SdkVersion:                             sdkVersion,
+		SrcJarArgs:                             j.srcJarArgs,
+		SrcJarDeps:                             j.srcJarDeps,
+		StubsLinkType:                          stubsLinkType,
+		AconfigIntermediateCacheOutputPaths:    j.aconfigProtoFiles,
+	}
+	setExtraJavaInfo(ctx, j, javaInfo)
+	ctx.SetOutputFiles(android.Paths{javaInfo.OutputFile}, "")
+	ctx.SetOutputFiles(android.Paths{javaInfo.OutputFile}, android.DefaultDistTag)
+	ctx.SetOutputFiles(javaInfo.ImplementationAndResourcesJars, ".jar")
+	ctx.SetOutputFiles(javaInfo.HeaderJars, ".hjar")
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+
+}
+
+func (j *GenruleCombiner) GeneratedSourceFiles() android.Paths {
+	return append(android.Paths{}, j.combinedImplementationJar)
+}
+
+func (j *GenruleCombiner) GeneratedHeaderDirs() android.Paths {
+	return append(android.Paths{}, j.headerDirs...)
+}
+
+func (j *GenruleCombiner) GeneratedDeps() android.Paths {
+	return append(android.Paths{}, j.combinedImplementationJar)
+}
+
+func (j *GenruleCombiner) Srcs() android.Paths {
+	return append(android.Paths{}, j.implementationAndResourceJars...)
+}
+
+func (j *GenruleCombiner) HeaderJars() android.Paths {
+	return j.headerJars
+}
+
+func (j *GenruleCombiner) ImplementationAndResourcesJars() android.Paths {
+	return j.implementationAndResourceJars
+}
+
+func (j *GenruleCombiner) DexJarBuildPath(ctx android.ModuleErrorfContext) android.Path {
+	return nil
+}
+
+func (j *GenruleCombiner) DexJarInstallPath() android.Path {
+	return nil
+}
+
+func (j *GenruleCombiner) AidlIncludeDirs() android.Paths {
+	return nil
+}
+
+func (j *GenruleCombiner) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
+	return nil
+}
+
+func (j *GenruleCombiner) JacocoReportClassesFile() android.Path {
+	return nil
+}
+
+func (j *GenruleCombiner) AndroidMk() android.AndroidMkData {
+	return android.AndroidMkData{
+		Class:      "JAVA_LIBRARIES",
+		OutputFile: android.OptionalPathForPath(j.combinedImplementationJar),
+		// Make does not support Windows Java modules
+		Disabled: j.Os() == android.Windows,
+		Include:  "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+		Extra: []android.AndroidMkExtraFunc{
+			func(w io.Writer, outputFile android.Path) {
+				fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
+				fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", j.combinedHeaderJar.String())
+				fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", j.combinedImplementationJar.String())
+			},
+		},
+	}
+}
+
+// implement the following interface for IDE completion.
+var _ android.IDEInfo = (*GenruleCombiner)(nil)
+
+func (j *GenruleCombiner) IDEInfo(ctx android.BaseModuleContext, ideInfo *android.IdeInfo) {
+	ideInfo.Deps = append(ideInfo.Deps, j.genruleCombinerProperties.Static_libs.GetOrDefault(ctx, nil)...)
+	ideInfo.Libs = append(ideInfo.Libs, j.genruleCombinerProperties.Static_libs.GetOrDefault(ctx, nil)...)
+	ideInfo.Deps = append(ideInfo.Deps, j.genruleCombinerProperties.Headers.GetOrDefault(ctx, nil)...)
+	ideInfo.Libs = append(ideInfo.Libs, j.genruleCombinerProperties.Headers.GetOrDefault(ctx, nil)...)
+}
diff --git a/java/genrule_combiner_test.go b/java/genrule_combiner_test.go
new file mode 100644
index 0000000..7d024cf
--- /dev/null
+++ b/java/genrule_combiner_test.go
@@ -0,0 +1,237 @@
+// Copyright 2018 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 (
+	"reflect"
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestJarGenruleCombinerSingle(t *testing.T) {
+	t.Parallel()
+	t.Helper()
+	ctx := prepareForJavaTest.RunTestWithBp(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+		}
+
+		java_genrule {
+			name: "gen",
+			tool_files: ["b.java"],
+			cmd: "$(location b.java) $(in) $(out)",
+			out: ["gen.jar"],
+			srcs: [":foo"],
+		}
+
+		java_genrule_combiner {
+			name: "jarcomb",
+			static_libs: ["gen"],
+			headers: ["foo"],
+		}
+
+		java_library {
+			name: "bar",
+			static_libs: ["jarcomb"],
+			srcs: ["c.java"],
+		}
+
+		java_library {
+			name: "baz",
+			libs: ["jarcomb"],
+			srcs: ["c.java"],
+		}
+	`).TestContext
+
+	fooMod := ctx.ModuleForTests(t, "foo", "android_common")
+	fooCombined := fooMod.Output("turbine-combined/foo.jar")
+	fooOutputFiles, _ := android.OtherModuleProvider(ctx.OtherModuleProviderAdaptor(), fooMod.Module(), android.OutputFilesProvider)
+	fooHeaderJars := fooOutputFiles.TaggedOutputFiles[".hjar"]
+
+	genMod := ctx.ModuleForTests(t, "gen", "android_common")
+	gen := genMod.Output("gen.jar")
+
+	jarcombMod := ctx.ModuleForTests(t, "jarcomb", "android_common")
+	jarcombInfo, _ := android.OtherModuleProvider(ctx.OtherModuleProviderAdaptor(), jarcombMod.Module(), JavaInfoProvider)
+	jarcombOutputFiles, _ := android.OtherModuleProvider(ctx.OtherModuleProviderAdaptor(), jarcombMod.Module(), android.OutputFilesProvider)
+
+	// Confirm that jarcomb simply forwards the jarcomb implementation and the foo headers.
+	if len(jarcombOutputFiles.DefaultOutputFiles) != 1 ||
+		android.PathRelativeToTop(jarcombOutputFiles.DefaultOutputFiles[0]) != android.PathRelativeToTop(gen.Output) {
+		t.Errorf("jarcomb Implementation %v is not [%q]",
+			android.PathsRelativeToTop(jarcombOutputFiles.DefaultOutputFiles), android.PathRelativeToTop(gen.Output))
+	}
+	jarcombHeaderJars := jarcombOutputFiles.TaggedOutputFiles[".hjar"]
+	if !reflect.DeepEqual(jarcombHeaderJars, fooHeaderJars) {
+		t.Errorf("jarcomb Header jar %v is not %q",
+			jarcombHeaderJars, fooHeaderJars)
+	}
+
+	// Confirm that JavaInfoProvider agrees.
+	if len(jarcombInfo.ImplementationJars) != 1 ||
+		android.PathRelativeToTop(jarcombInfo.ImplementationJars[0]) != android.PathRelativeToTop(gen.Output) {
+		t.Errorf("jarcomb ImplementationJars %v is not [%q]",
+			android.PathsRelativeToTop(jarcombInfo.ImplementationJars), android.PathRelativeToTop(gen.Output))
+	}
+	if len(jarcombInfo.HeaderJars) != 1 ||
+		android.PathRelativeToTop(jarcombInfo.HeaderJars[0]) != android.PathRelativeToTop(fooCombined.Output) {
+		t.Errorf("jarcomb HeaderJars %v is not [%q]",
+			android.PathsRelativeToTop(jarcombInfo.HeaderJars), android.PathRelativeToTop(fooCombined.Output))
+	}
+
+	barMod := ctx.ModuleForTests(t, "bar", "android_common")
+	bar := barMod.Output("javac/bar.jar")
+	barCombined := barMod.Output("combined/bar.jar")
+
+	// Confirm that bar uses the Implementation from gen and headerJars from foo.
+	if len(barCombined.Inputs) != 2 ||
+		barCombined.Inputs[0].String() != bar.Output.String() ||
+		barCombined.Inputs[1].String() != gen.Output.String() {
+		t.Errorf("bar combined jar inputs %v is not [%q, %q]",
+			barCombined.Inputs.Strings(), bar.Output.String(), gen.Output.String())
+	}
+
+	bazMod := ctx.ModuleForTests(t, "baz", "android_common")
+	baz := bazMod.Output("javac/baz.jar")
+
+	string_in_list := func(s string, l []string) bool {
+		for _, v := range l {
+			if s == v {
+				return true
+			}
+		}
+		return false
+	}
+
+	// Confirm that baz uses the headerJars from foo.
+	bazImplicitsRel := android.PathsRelativeToTop(baz.Implicits)
+	for _, v := range android.PathsRelativeToTop(fooHeaderJars) {
+		if !string_in_list(v, bazImplicitsRel) {
+			t.Errorf("baz Implicits %v does not contain %q", bazImplicitsRel, v)
+		}
+	}
+}
+
+func TestJarGenruleCombinerMulti(t *testing.T) {
+	t.Parallel()
+	t.Helper()
+	ctx := prepareForJavaTest.RunTestWithBp(t, `
+		java_library {
+			name: "foo1",
+			srcs: ["foo1_a.java"],
+		}
+
+		java_library {
+			name: "foo2",
+			srcs: ["foo2_a.java"],
+		}
+
+		java_genrule {
+			name: "gen1",
+			tool_files: ["b.java"],
+			cmd: "$(location b.java) $(in) $(out)",
+			out: ["gen1.jar"],
+			srcs: [":foo1"],
+		}
+
+		java_genrule {
+			name: "gen2",
+			tool_files: ["b.java"],
+			cmd: "$(location b.java) $(in) $(out)",
+			out: ["gen2.jar"],
+			srcs: [":foo2"],
+		}
+
+		// Combine multiple java_genrule modules.
+		java_genrule_combiner {
+			name: "jarcomb",
+			static_libs: ["gen1", "gen2"],
+			headers: ["foo1", "foo2"],
+		}
+
+		java_library {
+			name: "bar",
+			static_libs: ["jarcomb"],
+			srcs: ["c.java"],
+		}
+
+		java_library {
+			name: "baz",
+			libs: ["jarcomb"],
+			srcs: ["c.java"],
+		}
+	`).TestContext
+
+	gen1Mod := ctx.ModuleForTests(t, "gen1", "android_common")
+	gen1 := gen1Mod.Output("gen1.jar")
+	gen2Mod := ctx.ModuleForTests(t, "gen2", "android_common")
+	gen2 := gen2Mod.Output("gen2.jar")
+
+	jarcombMod := ctx.ModuleForTests(t, "jarcomb", "android_common")
+	jarcomb := jarcombMod.Output("combined/jarcomb.jar")
+	jarcombTurbine := jarcombMod.Output("turbine-combined/jarcomb.jar")
+	_ = jarcombTurbine
+	jarcombInfo, _ := android.OtherModuleProvider(ctx.OtherModuleProviderAdaptor(), jarcombMod.Module(), JavaInfoProvider)
+	_ = jarcombInfo
+	jarcombOutputFiles, _ := android.OtherModuleProvider(ctx.OtherModuleProviderAdaptor(), jarcombMod.Module(), android.OutputFilesProvider)
+	jarcombHeaderJars := jarcombOutputFiles.TaggedOutputFiles[".hjar"]
+
+	if len(jarcomb.Inputs) != 2 ||
+		jarcomb.Inputs[0].String() != gen1.Output.String() ||
+		jarcomb.Inputs[1].String() != gen2.Output.String() {
+		t.Errorf("jarcomb inputs %v are not [%q, %q]",
+			jarcomb.Inputs.Strings(), gen1.Output.String(), gen2.Output.String())
+	}
+
+	if len(jarcombHeaderJars) != 1 ||
+		android.PathRelativeToTop(jarcombHeaderJars[0]) != android.PathRelativeToTop(jarcombTurbine.Output) {
+		t.Errorf("jarcomb Header jars %v is not [%q]",
+			android.PathsRelativeToTop(jarcombHeaderJars), android.PathRelativeToTop(jarcombTurbine.Output))
+	}
+
+	barMod := ctx.ModuleForTests(t, "bar", "android_common")
+	bar := barMod.Output("javac/bar.jar")
+	barCombined := barMod.Output("combined/bar.jar")
+
+	// Confirm that bar uses the Implementation and Headers from jarcomb.
+	if len(barCombined.Inputs) != 2 ||
+		barCombined.Inputs[0].String() != bar.Output.String() ||
+		barCombined.Inputs[1].String() != jarcomb.Output.String() {
+		t.Errorf("bar combined jar inputs %v is not [%q, %q]",
+			barCombined.Inputs.Strings(), bar.Output.String(), jarcomb.Output.String())
+	}
+
+	bazMod := ctx.ModuleForTests(t, "baz", "android_common")
+	baz := bazMod.Output("javac/baz.jar")
+
+	string_in_list := func(s string, l []string) bool {
+		for _, v := range l {
+			if s == v {
+				return true
+			}
+		}
+		return false
+	}
+
+	// Confirm that baz uses the headerJars from foo.
+	bazImplicitsRel := android.PathsRelativeToTop(baz.Implicits)
+	for _, v := range android.PathsRelativeToTop(jarcombHeaderJars) {
+		if !string_in_list(v, bazImplicitsRel) {
+			t.Errorf("baz Implicits %v does not contain %q", bazImplicitsRel, v)
+		}
+	}
+}
diff --git a/java/genrule_test.go b/java/genrule_test.go
index 1c294b2..c112e45 100644
--- a/java/genrule_test.go
+++ b/java/genrule_test.go
@@ -31,6 +31,7 @@
 }
 
 func TestGenruleCmd(t *testing.T) {
+	t.Parallel()
 	fs := map[string][]byte{
 		"tool": nil,
 		"foo":  nil,
@@ -56,7 +57,7 @@
 		t.Fatal(errs)
 	}
 
-	gen := ctx.ModuleForTests("gen", "android_common").Output("out")
+	gen := ctx.ModuleForTests(t, "gen", "android_common").Output("out")
 	expected := []string{"foo"}
 	if !reflect.DeepEqual(expected, gen.Implicits.Strings()[:len(expected)]) {
 		t.Errorf(`want arm inputs %v, got %v`, expected, gen.Implicits.Strings())
@@ -64,6 +65,7 @@
 }
 
 func TestJarGenrules(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -91,11 +93,11 @@
 		}
 	`)
 
-	foo := ctx.ModuleForTests("foo", "android_common").Output("javac/foo.jar")
-	jargen := ctx.ModuleForTests("jargen", "android_common").Output("jargen.jar")
-	bar := ctx.ModuleForTests("bar", "android_common").Output("javac/bar.jar")
-	baz := ctx.ModuleForTests("baz", "android_common").Output("javac/baz.jar")
-	barCombined := ctx.ModuleForTests("bar", "android_common").Output("combined/bar.jar")
+	foo := ctx.ModuleForTests(t, "foo", "android_common").Output("javac/foo.jar")
+	jargen := ctx.ModuleForTests(t, "jargen", "android_common").Output("jargen.jar")
+	bar := ctx.ModuleForTests(t, "bar", "android_common").Output("javac/bar.jar")
+	baz := ctx.ModuleForTests(t, "baz", "android_common").Output("javac/baz.jar")
+	barCombined := ctx.ModuleForTests(t, "bar", "android_common").Output("combined/bar.jar")
 
 	if g, w := jargen.Implicits.Strings(), foo.Output.String(); !android.InList(w, g) {
 		t.Errorf("expected jargen inputs [%q], got %q", w, g)
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index b1a9deb..c9a1f2b 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -97,7 +97,7 @@
 	// Save the classes jars even if this is not active as they may be used by modular hidden API
 	// processing.
 	classesJars := android.Paths{classesJar}
-	ctx.VisitDirectDepsWithTag(hiddenApiAnnotationsTag, func(dep android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(hiddenApiAnnotationsTag, func(dep android.ModuleProxy) {
 		if javaInfo, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
 			classesJars = append(classesJars, javaInfo.ImplementationJars...)
 		}
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 7d21b7a..2c86942 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -171,11 +171,11 @@
 	// Now match the apex part of the boot image configuration.
 	requiredApex := configuredBootJars.Apex(index)
 	if android.IsConfiguredJarForPlatform(requiredApex) {
-		if len(apexInfo.InApexVariants) != 0 {
+		if apexInfo.ApexVariationName != "" {
 			// A platform variant is required but this is for an apex so ignore it.
 			return false
 		}
-	} else if !apexInfo.InApexVariant(requiredApex) {
+	} else if apexInfo.BaseApexName != requiredApex {
 		// An apex variant for a specific apex is required but this is the wrong apex.
 		return false
 	}
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index afe8b4c..147f326 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -44,6 +44,7 @@
 )
 
 func TestHiddenAPISingleton(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
@@ -56,13 +57,14 @@
 		}
 	`)
 
-	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+	hiddenAPI := result.ModuleForTests(t, "platform-bootclasspath", "android_common")
 	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 	want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
 }
 
 func TestHiddenAPISingletonWithSourceAndPrebuiltPreferredButNoDex(t *testing.T) {
+	t.Parallel()
 	expectedErrorMessage := "module prebuilt_foo{os:android,arch:common} does not provide a dex jar"
 
 	android.GroupFixturePreparers(
@@ -86,6 +88,7 @@
 }
 
 func TestHiddenAPISingletonWithPrebuilt(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
@@ -98,13 +101,14 @@
 	}
 	`)
 
-	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+	hiddenAPI := result.ModuleForTests(t, "platform-bootclasspath", "android_common")
 	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 	want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
 }
 
 func TestHiddenAPISingletonWithPrebuiltUseSource(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
@@ -124,7 +128,7 @@
 		}
 	`)
 
-	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+	hiddenAPI := result.ModuleForTests(t, "platform-bootclasspath", "android_common")
 	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 	fromSourceJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, fromSourceJarArg)
@@ -134,6 +138,7 @@
 }
 
 func TestHiddenAPISingletonWithPrebuiltOverrideSource(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
@@ -153,7 +158,7 @@
 		}
 	`)
 
-	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+	hiddenAPI := result.ModuleForTests(t, "platform-bootclasspath", "android_common")
 	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 	prebuiltJarArg := "--boot-dex=out/soong/.intermediates/prebuilt_foo/android_common/dex/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, prebuiltJarArg)
@@ -163,6 +168,7 @@
 }
 
 func TestHiddenAPISingletonSdks(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name             string
 		unbundledBuild   bool
@@ -213,7 +219,7 @@
 		}
 		`)
 
-			hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+			hiddenAPI := result.ModuleForTests(t, "platform-bootclasspath", "android_common")
 			hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 			wantPublicStubs := "--public-stub-classpath=" + generateSdkDexPath(tc.publicStub, tc.unbundledBuild)
 			android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantPublicStubs)
@@ -246,6 +252,7 @@
 }
 
 func TestHiddenAPISingletonWithPrebuiltCsvFile(t *testing.T) {
+	t.Parallel()
 
 	// The idea behind this test is to ensure that when the build is
 	// confugured with a PrebuiltHiddenApiDir that the rules for the
@@ -272,9 +279,9 @@
 	expectedCpOutput := "out/soong/hiddenapi/hiddenapi-flags.csv"
 	expectedFlagsCsv := "out/soong/hiddenapi/hiddenapi-flags.csv"
 
-	foo := result.ModuleForTests("foo", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
 
-	hiddenAPI := result.SingletonForTests("hiddenapi")
+	hiddenAPI := result.SingletonForTests(t, "hiddenapi")
 	cpRule := hiddenAPI.Rule("Cp")
 	actualCpInput := cpRule.BuildParams.Input
 	actualCpOutput := cpRule.BuildParams.Output
@@ -289,6 +296,7 @@
 }
 
 func TestHiddenAPIEncoding_JavaSdkLibrary(t *testing.T) {
+	t.Parallel()
 
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
@@ -310,7 +318,7 @@
 	`)
 
 	checkDexEncoded := func(t *testing.T, name, unencodedDexJar, encodedDexJar string) {
-		moduleForTests := result.ModuleForTests(name+".impl", "android_common")
+		moduleForTests := result.ModuleForTests(t, name+".impl", "android_common")
 
 		encodeDexRule := moduleForTests.Rule("hiddenAPIEncodeDex")
 		actualUnencodedDexJar := encodeDexRule.Input
@@ -324,10 +332,7 @@
 		android.AssertPathRelativeToTopEquals(t, "encode embedded java_library", encodedDexJar, exportedDexJar)
 	}
 
-	// The java_library embedded with the java_sdk_library must be dex encoded.
-	t.Run("foo", func(t *testing.T) {
-		expectedUnencodedDexJar := "out/soong/.intermediates/foo.impl/android_common/aligned/foo.jar"
-		expectedEncodedDexJar := "out/soong/.intermediates/foo.impl/android_common/hiddenapi/foo.jar"
-		checkDexEncoded(t, "foo", expectedUnencodedDexJar, expectedEncodedDexJar)
-	})
+	expectedUnencodedDexJar := "out/soong/.intermediates/foo.impl/android_common/aligned/foo.jar"
+	expectedEncodedDexJar := "out/soong/.intermediates/foo.impl/android_common/hiddenapi/foo.jar"
+	checkDexEncoded(t, "foo", expectedUnencodedDexJar, expectedEncodedDexJar)
 }
diff --git a/java/jacoco_test.go b/java/jacoco_test.go
index 1882908..58a091e 100644
--- a/java/jacoco_test.go
+++ b/java/jacoco_test.go
@@ -17,6 +17,7 @@
 import "testing"
 
 func TestJacocoFilterToSpecs(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name, in, out string
 	}{
@@ -54,6 +55,7 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
 			got, err := jacocoFilterToSpec(testCase.in)
 			if err != nil {
 				t.Error(err)
@@ -66,6 +68,7 @@
 }
 
 func TestJacocoFiltersToZipCommand(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name               string
 		includes, excludes []string
@@ -96,6 +99,7 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
 			got := jacocoFiltersToZipCommand(testCase.includes, testCase.excludes)
 			if got != testCase.out {
 				t.Errorf("expected %q got %q", testCase.out, got)
diff --git a/java/jarjar_test.go b/java/jarjar_test.go
index 82bfa2b..b689761 100644
--- a/java/jarjar_test.go
+++ b/java/jarjar_test.go
@@ -22,7 +22,7 @@
 )
 
 func AssertJarJarRename(t *testing.T, result *android.TestResult, libName, original, expectedRename string) {
-	module := result.ModuleForTests(libName, "android_common")
+	module := result.ModuleForTests(t, libName, "android_common")
 
 	provider, found := android.OtherModuleProvider(result.OtherModuleProviderAdaptor(), module.Module(), JarJarProvider)
 	android.AssertBoolEquals(t, fmt.Sprintf("found provider (%s)", libName), true, found)
diff --git a/java/java.go b/java/java.go
index e1cfea5..2e14b82 100644
--- a/java/java.go
+++ b/java/java.go
@@ -64,6 +64,7 @@
 	ctx.RegisterModuleType("java_api_library", ApiLibraryFactory)
 	ctx.RegisterModuleType("java_api_contribution", ApiContributionFactory)
 	ctx.RegisterModuleType("java_api_contribution_import", ApiContributionImportFactory)
+	ctx.RegisterModuleType("java_genrule_combiner", GenruleCombinerFactory)
 
 	// This mutator registers dependencies on dex2oat for modules that should be
 	// dexpreopted. This is done late when the final variants have been
@@ -250,6 +251,36 @@
 
 var ProguardSpecInfoProvider = blueprint.NewProvider[ProguardSpecInfo]()
 
+type AndroidLibraryDependencyInfo struct {
+	ExportPackage       android.Path
+	ResourcesNodeDepSet depset.DepSet[*resourcesNode]
+	RRODirsDepSet       depset.DepSet[rroDir]
+	ManifestsDepSet     depset.DepSet[android.Path]
+}
+
+type UsesLibraryDependencyInfo struct {
+	DexJarInstallPath   android.Path
+	ClassLoaderContexts dexpreopt.ClassLoaderContextMap
+}
+
+type SdkLibraryComponentDependencyInfo struct {
+	// The name of the implementation library for the optional SDK library or nil, if there isn't one.
+	OptionalSdkLibraryImplementation *string
+}
+
+type ProvidesUsesLibInfo struct {
+	ProvidesUsesLib *string
+}
+
+type ModuleWithUsesLibraryInfo struct {
+	UsesLibrary *usesLibrary
+}
+
+type ModuleWithSdkDepInfo struct {
+	SdkLinkType sdkLinkType
+	Stubs       bool
+}
+
 // JavaInfo contains information about a java module for use by modules that depend on it.
 type JavaInfo struct {
 	// HeaderJars is a list of jars that can be passed as the javac classpath in order to link
@@ -328,10 +359,109 @@
 	AconfigIntermediateCacheOutputPaths android.Paths
 
 	SdkVersion android.SdkSpec
+
+	// output file of the module, which may be a classes jar or a dex jar
+	OutputFile android.Path
+
+	ExtraOutputFiles android.Paths
+
+	AndroidLibraryDependencyInfo *AndroidLibraryDependencyInfo
+
+	UsesLibraryDependencyInfo *UsesLibraryDependencyInfo
+
+	SdkLibraryComponentDependencyInfo *SdkLibraryComponentDependencyInfo
+
+	ProvidesUsesLibInfo *ProvidesUsesLibInfo
+
+	MissingOptionalUsesLibs []string
+
+	ModuleWithSdkDepInfo *ModuleWithSdkDepInfo
+
+	// output file containing classes.dex and resources
+	DexJarFile OptionalDexJarPath
+
+	// installed file for binary dependency
+	InstallFile android.Path
+
+	// The path to the dex jar that is in the boot class path. If this is unset then the associated
+	// module is not a boot jar, but could be one of the <x>-hiddenapi modules that provide additional
+	// annotations for the <x> boot dex jar but which do not actually provide a boot dex jar
+	// themselves.
+	//
+	// This must be the path to the unencoded dex jar as the encoded dex jar indirectly depends on
+	// this file so using the encoded dex jar here would result in a cycle in the ninja rules.
+	BootDexJarPath OptionalDexJarPath
+
+	// The compressed state of the dex file being encoded. This is used to ensure that the encoded
+	// dex file has the same state.
+	UncompressDexState *bool
+
+	// True if the module containing this structure contributes to the hiddenapi information or has
+	// that information encoded within it.
+	Active bool
+
+	BuiltInstalled string
+
+	// The config is used for two purposes:
+	// - Passing dexpreopt information about libraries from Soong to Make. This is needed when
+	//   a <uses-library> is defined in Android.bp, but used in Android.mk (see dex_preopt_config_merger.py).
+	//   Note that dexpreopt.config might be needed even if dexpreopt is disabled for the library itself.
+	// - Dexpreopt post-processing (using dexpreopt artifacts from a prebuilt system image to incrementally
+	//   dexpreopt another partition).
+	ConfigPath android.WritablePath
+
+	LogtagsSrcs android.Paths
+
+	ProguardDictionary android.OptionalPath
+
+	ProguardUsageZip android.OptionalPath
+
+	LinterReports android.Paths
+
+	// installed file for hostdex copy
+	HostdexInstallFile android.InstallPath
+
+	// Additional srcJars tacked in by GeneratedJavaLibraryModule
+	GeneratedSrcjars []android.Path
+
+	// True if profile-guided optimization is actually enabled.
+	ProfileGuided bool
+
+	Stem string
+
+	DexJarBuildPath OptionalDexJarPath
+
+	DexpreopterInfo *DexpreopterInfo
+
+	XrefJavaFiles   android.Paths
+	XrefKotlinFiles android.Paths
 }
 
 var JavaInfoProvider = blueprint.NewProvider[*JavaInfo]()
 
+type DexpreopterInfo struct {
+	// The path to the profile on host that dexpreopter generates. This is used as the input for
+	// dex2oat.
+	OutputProfilePathOnHost android.Path
+	// If the java module is to be installed into an APEX, this list contains information about the
+	// dexpreopt outputs to be installed on devices. Note that these dexpreopt outputs are installed
+	// outside of the APEX.
+	ApexSystemServerDexpreoptInstalls []DexpreopterInstall
+
+	// ApexSystemServerDexJars returns the list of dex jars if this is an apex system server jar.
+	ApexSystemServerDexJars android.Paths
+}
+
+type JavaLibraryInfo struct {
+	Prebuilt bool
+}
+
+var JavaLibraryInfoProvider = blueprint.NewProvider[JavaLibraryInfo]()
+
+type JavaDexImportInfo struct{}
+
+var JavaDexImportInfoProvider = blueprint.NewProvider[JavaDexImportInfo]()
+
 // SyspropPublicStubInfo contains info about the sysprop public stub library that corresponds to
 // the sysprop implementation library.
 type SyspropPublicStubInfo struct {
@@ -449,6 +579,7 @@
 	extraLintCheckTag       = dependencyTag{name: "extra-lint-check", toolchain: true}
 	jniLibTag               = dependencyTag{name: "jnilib", runtimeLinked: true}
 	r8LibraryJarTag         = dependencyTag{name: "r8-libraryjar", runtimeLinked: true}
+	traceReferencesTag      = dependencyTag{name: "trace-references"}
 	syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"}
 	javaApiContributionTag  = dependencyTag{name: "java-api-contribution"}
 	aconfigDeclarationTag   = dependencyTag{name: "aconfig-declaration"}
@@ -479,11 +610,12 @@
 		kotlinPluginTag,
 		syspropPublicStubDepTag,
 		instrumentationForTag,
+		traceReferencesTag,
 	}
 )
 
 func IsLibDepTag(depTag blueprint.DependencyTag) bool {
-	return depTag == libTag || depTag == sdkLibTag
+	return depTag == libTag
 }
 
 func IsStaticLibDepTag(depTag blueprint.DependencyTag) bool {
@@ -540,12 +672,12 @@
 		ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
 		ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
 		ctx.AddVariationDependencies(nil, sdkLibTag, sdkDep.classpath...)
-		if d.effectiveOptimizeEnabled() && sdkDep.hasStandardLibs() {
+		if d.effectiveOptimizeEnabled(ctx) && sdkDep.hasStandardLibs() {
 			ctx.AddVariationDependencies(nil, proguardRaiseTag,
 				config.LegacyCorePlatformBootclasspathLibraries...,
 			)
 		}
-		if d.effectiveOptimizeEnabled() && sdkDep.hasFrameworkLibs() {
+		if d.effectiveOptimizeEnabled(ctx) && sdkDep.hasFrameworkLibs() {
 			ctx.AddVariationDependencies(nil, proguardRaiseTag, config.FrameworkLibraries...)
 		}
 	}
@@ -599,11 +731,11 @@
 	transitiveStaticLibsResourceJars       []depset.DepSet[android.Path]
 }
 
-func checkProducesJars(ctx android.ModuleContext, dep android.SourceFileProducer) {
-	for _, f := range dep.Srcs() {
+func checkProducesJars(ctx android.ModuleContext, dep android.SourceFilesInfo, module android.ModuleProxy) {
+	for _, f := range dep.Srcs {
 		if f.Ext() != ".jar" {
 			ctx.ModuleErrorf("genrule %q must generate files ending with .jar to be used as a libs or static_libs dependency",
-				ctx.OtherModuleName(dep.(blueprint.Module)))
+				ctx.OtherModuleName(module))
 		}
 	}
 }
@@ -721,6 +853,8 @@
 	combinedExportedProguardFlagsFile android.Path
 
 	InstallMixin func(ctx android.ModuleContext, installPath android.Path) (extraInstallDeps android.InstallPaths)
+
+	apiXmlFile android.WritablePath
 }
 
 var _ android.ApexModule = (*Library)(nil)
@@ -1008,7 +1142,7 @@
 			j.dexpreopter.disableDexpreopt()
 		}
 	}
-	j.compile(ctx, nil, nil, nil, nil)
+	javaInfo := j.compile(ctx, nil, nil, nil, nil)
 
 	j.setInstallRules(ctx)
 
@@ -1017,7 +1151,97 @@
 		TopLevelTarget: j.sourceProperties.Top_level_test_target,
 	})
 
+	android.SetProvider(ctx, JavaLibraryInfoProvider, JavaLibraryInfo{
+		Prebuilt: false,
+	})
+
+	if javaInfo != nil {
+		setExtraJavaInfo(ctx, j, javaInfo)
+		javaInfo.ExtraOutputFiles = j.extraOutputFiles
+		javaInfo.DexJarFile = j.dexJarFile
+		javaInfo.InstallFile = j.installFile
+		javaInfo.BootDexJarPath = j.bootDexJarPath
+		javaInfo.UncompressDexState = j.uncompressDexState
+		javaInfo.Active = j.active
+		javaInfo.BuiltInstalled = j.builtInstalled
+		javaInfo.ConfigPath = j.configPath
+		javaInfo.LogtagsSrcs = j.logtagsSrcs
+		javaInfo.ProguardDictionary = j.proguardDictionary
+		javaInfo.ProguardUsageZip = j.proguardUsageZip
+		javaInfo.LinterReports = j.reports
+		javaInfo.HostdexInstallFile = j.hostdexInstallFile
+		javaInfo.GeneratedSrcjars = j.properties.Generated_srcjars
+		javaInfo.ProfileGuided = j.dexpreopter.dexpreoptProperties.Dex_preopt_result.Profile_guided
+
+		android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+	}
+
 	setOutputFiles(ctx, j.Module)
+
+	j.javaLibraryModuleInfoJSON(ctx)
+
+	buildComplianceMetadata(ctx)
+
+	j.createApiXmlFile(ctx)
+
+	if j.dexer.proguardDictionary.Valid() {
+		android.SetProvider(ctx, ProguardProvider, ProguardInfo{
+			ModuleName:         ctx.ModuleName(),
+			Class:              "JAVA_LIBRARIES",
+			ProguardDictionary: j.dexer.proguardDictionary.Path(),
+			ProguardUsageZip:   j.dexer.proguardUsageZip.Path(),
+			ClassesJar:         j.implementationAndResourcesJar,
+		})
+	}
+}
+
+func (j *Library) javaLibraryModuleInfoJSON(ctx android.ModuleContext) *android.ModuleInfoJSON {
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Class = []string{"JAVA_LIBRARIES"}
+	if j.implementationAndResourcesJar != nil {
+		moduleInfoJSON.ClassesJar = []string{j.implementationAndResourcesJar.String()}
+	}
+	moduleInfoJSON.SystemSharedLibs = []string{"none"}
+
+	if j.hostDexNeeded() {
+		hostDexModuleInfoJSON := ctx.ExtraModuleInfoJSON()
+		hostDexModuleInfoJSON.SubName = "-hostdex"
+		hostDexModuleInfoJSON.Class = []string{"JAVA_LIBRARIES"}
+		if j.implementationAndResourcesJar != nil {
+			hostDexModuleInfoJSON.ClassesJar = []string{j.implementationAndResourcesJar.String()}
+		}
+		hostDexModuleInfoJSON.SystemSharedLibs = []string{"none"}
+		hostDexModuleInfoJSON.SupportedVariantsOverride = []string{"HOST"}
+	}
+
+	if j.hideApexVariantFromMake {
+		moduleInfoJSON.Disabled = true
+	}
+	return moduleInfoJSON
+}
+
+func buildComplianceMetadata(ctx android.ModuleContext) {
+	// Dump metadata that can not be done in android/compliance-metadata.go
+	complianceMetadataInfo := ctx.ComplianceMetadataInfo()
+	builtFiles := ctx.GetOutputFiles().DefaultOutputFiles.Strings()
+	for _, paths := range ctx.GetOutputFiles().TaggedOutputFiles {
+		builtFiles = append(builtFiles, paths.Strings()...)
+	}
+	complianceMetadataInfo.SetListValue(android.ComplianceMetadataProp.BUILT_FILES, android.SortedUniqueStrings(builtFiles))
+
+	// Static deps
+	staticDepNames := make([]string, 0)
+	staticDepFiles := android.Paths{}
+	ctx.VisitDirectDepsWithTag(staticLibTag, func(module android.Module) {
+		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
+			staticDepNames = append(staticDepNames, module.Name())
+			staticDepFiles = append(staticDepFiles, dep.ImplementationJars...)
+			staticDepFiles = append(staticDepFiles, dep.HeaderJars...)
+			staticDepFiles = append(staticDepFiles, dep.ResourceJars...)
+		}
+	})
+	complianceMetadataInfo.SetListValue(android.ComplianceMetadataProp.STATIC_DEPS, android.SortedUniqueStrings(staticDepNames))
+	complianceMetadataInfo.SetListValue(android.ComplianceMetadataProp.STATIC_DEP_FILES, android.SortedUniqueStrings(staticDepFiles.Strings()))
 }
 
 func (j *Library) getJarInstallDir(ctx android.ModuleContext) android.InstallPath {
@@ -1073,6 +1297,28 @@
 	}
 }
 
+var apiXMLGeneratingApiSurfaces = []android.SdkKind{
+	android.SdkPublic,
+	android.SdkSystem,
+	android.SdkModule,
+	android.SdkSystemServer,
+	android.SdkTest,
+}
+
+func (j *Library) createApiXmlFile(ctx android.ModuleContext) {
+	if kind, ok := android.JavaLibraryNameToSdkKind(ctx.ModuleName()); ok && android.InList(kind, apiXMLGeneratingApiSurfaces) {
+		scopePrefix := AllApiScopes.matchingScopeFromSdkKind(kind).apiFilePrefix
+		j.apiXmlFile = android.PathForModuleOut(ctx, fmt.Sprintf("%sapi.xml", scopePrefix))
+		ctx.Build(pctx, android.BuildParams{
+			Rule: generateApiXMLRule,
+			// LOCAL_SOONG_CLASSES_JAR
+			Input:  j.implementationAndResourcesJar,
+			Output: j.apiXmlFile,
+		})
+		ctx.DistForGoal("dist_files", j.apiXmlFile)
+	}
+}
+
 const (
 	aidlIncludeDir   = "aidl"
 	javaDir          = "java"
@@ -1314,6 +1560,11 @@
 	// host test.
 	Device_first_prefer32_data []string `android:"path_device_first_prefer32"`
 
+	// Same as data, but will add dependencies on modules using the host's os variation and
+	// the common arch variation. Useful for a device test that wants to depend on a host
+	// module, for example to include a custom Tradefed test runner.
+	Host_common_data []string `android:"path_host_common"`
+
 	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
 	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
 	// explicitly.
@@ -1576,6 +1827,11 @@
 		MkInclude:            "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
 		MkAppClass:           "JAVA_LIBRARIES",
 	})
+
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	if proptools.Bool(j.testProperties.Test_options.Unit_test) {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "host-unit-tests")
+	}
 }
 
 func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -1607,20 +1863,21 @@
 	j.data = append(j.data, android.PathsForModuleSrc(ctx, j.testProperties.Device_common_data)...)
 	j.data = append(j.data, android.PathsForModuleSrc(ctx, j.testProperties.Device_first_data)...)
 	j.data = append(j.data, android.PathsForModuleSrc(ctx, j.testProperties.Device_first_prefer32_data)...)
+	j.data = append(j.data, android.PathsForModuleSrc(ctx, j.testProperties.Host_common_data)...)
 
 	j.extraTestConfigs = android.PathsForModuleSrc(ctx, j.testProperties.Test_options.Extra_test_configs)
 
-	ctx.VisitDirectDepsWithTag(dataNativeBinsTag, func(dep android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(dataNativeBinsTag, func(dep android.ModuleProxy) {
 		j.data = append(j.data, android.OutputFileForModule(ctx, dep, ""))
 	})
 
-	ctx.VisitDirectDepsWithTag(dataDeviceBinsTag, func(dep android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(dataDeviceBinsTag, func(dep android.ModuleProxy) {
 		j.data = append(j.data, android.OutputFileForModule(ctx, dep, ""))
 	})
 
 	var directImplementationDeps android.Paths
 	var transitiveImplementationDeps []depset.DepSet[android.Path]
-	ctx.VisitDirectDepsWithTag(jniLibTag, func(dep android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(jniLibTag, func(dep android.ModuleProxy) {
 		sharedLibInfo, _ := android.OtherModuleProvider(ctx, dep, cc.SharedLibraryInfoProvider)
 		if sharedLibInfo.SharedLibrary != nil {
 			// Copy to an intermediate output directory to append "lib[64]" to the path,
@@ -1653,10 +1910,82 @@
 	})
 
 	j.Library.GenerateAndroidBuildActions(ctx)
+
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	// LOCAL_MODULE_TAGS
+	moduleInfoJSON.Tags = append(moduleInfoJSON.Tags, "tests")
+	var allTestConfigs android.Paths
+	if j.testConfig != nil {
+		allTestConfigs = append(allTestConfigs, j.testConfig)
+	}
+	allTestConfigs = append(allTestConfigs, j.extraTestConfigs...)
+	if len(allTestConfigs) > 0 {
+		moduleInfoJSON.TestConfig = allTestConfigs.Strings()
+	} else {
+		optionalConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "AndroidTest.xml")
+		if optionalConfig.Valid() {
+			moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, optionalConfig.String())
+		}
+	}
+	if len(j.testProperties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, j.testProperties.Test_suites...)
+	} else {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
+	}
+	if _, ok := j.testConfig.(android.WritablePath); ok {
+		moduleInfoJSON.AutoTestConfig = []string{"true"}
+	}
+	if proptools.Bool(j.testProperties.Test_options.Unit_test) {
+		moduleInfoJSON.IsUnitTest = "true"
+		if ctx.Host() {
+			moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "host-unit-tests")
+		}
+	}
+	moduleInfoJSON.TestMainlineModules = append(moduleInfoJSON.TestMainlineModules, j.testProperties.Test_mainline_modules...)
+
+	// Install test deps
+	if !ctx.Config().KatiEnabled() {
+		pathInTestCases := android.PathForModuleInstall(ctx, "testcases", ctx.ModuleName())
+		if j.testConfig != nil {
+			ctx.InstallFile(pathInTestCases, ctx.ModuleName()+".config", j.testConfig)
+		}
+		dynamicConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "DynamicConfig.xml")
+		if dynamicConfig.Valid() {
+			ctx.InstallFile(pathInTestCases, ctx.ModuleName()+".dynamic", dynamicConfig.Path())
+		}
+		testDeps := append(j.data, j.extraTestConfigs...)
+		for _, data := range android.SortedUniquePaths(testDeps) {
+			dataPath := android.DataPath{SrcPath: data}
+			ctx.InstallTestData(pathInTestCases, []android.DataPath{dataPath})
+		}
+		if j.outputFile != nil {
+			ctx.InstallFile(pathInTestCases, ctx.ModuleName()+".jar", j.outputFile)
+		}
+	}
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: j.testProperties.Test_suites,
+	})
 }
 
 func (j *TestHelperLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	j.Library.GenerateAndroidBuildActions(ctx)
+
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Tags = append(moduleInfoJSON.Tags, "tests")
+	if len(j.testHelperLibraryProperties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, j.testHelperLibraryProperties.Test_suites...)
+	} else {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
+	}
+	optionalConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "AndroidTest.xml")
+	if optionalConfig.Valid() {
+		moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, optionalConfig.String())
+	}
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: j.testHelperLibraryProperties.Test_suites,
+	})
 }
 
 func (j *JavaTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -1746,7 +2075,7 @@
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 	module.Module.sourceProperties.Test_only = proptools.BoolPtr(true)
 	module.Module.sourceProperties.Top_level_test_target = true
 
@@ -1763,7 +2092,7 @@
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 	module.Module.sourceProperties.Test_only = proptools.BoolPtr(true)
 
 	InitJavaModule(module, android.HostAndDeviceSupported)
@@ -1910,13 +2239,13 @@
 	// 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) {
+	ctx.VisitDirectDepsProxyWithTag(jniInstallTag, func(jni android.ModuleProxy) {
 		// 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())
+		commonInfo := android.OtherModulePointerProviderOrDefault(ctx, jni, android.CommonModuleInfoProvider)
+		j.androidMkNamesOfJniLibs = append(j.androidMkNamesOfJniLibs, commonInfo.BaseModuleName+":"+commonInfo.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) {
+	ctx.VisitDirectDepsProxyWithTag(android.RequiredDepTag, func(dep android.ModuleProxy) {
 		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())
 		}
@@ -2135,7 +2464,7 @@
 
 func metalavaStubCmd(ctx android.ModuleContext, rule *android.RuleBuilder,
 	srcs android.Paths, homeDir android.WritablePath,
-	classpath android.Paths, configFiles android.Paths) *android.RuleBuilderCommand {
+	classpath android.Paths, configFiles android.Paths, apiSurface *string) *android.RuleBuilderCommand {
 	rule.Command().Text("rm -rf").Flag(homeDir.String())
 	rule.Command().Text("mkdir -p").Flag(homeDir.String())
 
@@ -2176,6 +2505,8 @@
 
 	addMetalavaConfigFilesToCmd(cmd, configFiles)
 
+	addOptionalApiSurfaceToCmd(cmd, apiSurface)
+
 	if len(classpath) == 0 {
 		// The main purpose of the `--api-class-resolution api` option is to force metalava to ignore
 		// classes on the classpath when an API file contains missing classes. However, as this command
@@ -2215,6 +2546,7 @@
 	apiContributions := al.properties.Api_contributions
 	addValidations := !ctx.Config().IsEnvTrue("DISABLE_STUB_VALIDATION") &&
 		!ctx.Config().IsEnvTrue("WITHOUT_CHECK_API") &&
+		!ctx.Config().PartialCompileFlags().Disable_stub_validation &&
 		proptools.BoolDefault(al.properties.Enable_validation, true)
 	for _, apiContributionName := range apiContributions {
 		ctx.AddDependency(ctx.Module(), javaApiContributionTag, apiContributionName)
@@ -2259,6 +2591,17 @@
 var scopeOrderMap = AllApiScopes.MapToIndex(
 	func(s *apiScope) string { return s.name })
 
+// Add some extra entries into scopeOrderMap for some special api surface names needed by libcore,
+// external/conscrypt and external/icu and java/core-libraries.
+func init() {
+	count := len(scopeOrderMap)
+	scopeOrderMap["core"] = count + 1
+	scopeOrderMap["core-platform"] = count + 2
+	scopeOrderMap["intra-core"] = count + 3
+	scopeOrderMap["core-platform-plus-public"] = count + 4
+	scopeOrderMap["core-platform-legacy"] = count + 5
+}
+
 func (al *ApiLibrary) sortApiFilesByApiScope(ctx android.ModuleContext, srcFilesInfo []JavaApiImportInfo) []JavaApiImportInfo {
 	for _, srcFileInfo := range srcFilesInfo {
 		if srcFileInfo.ApiSurface == "" {
@@ -2307,7 +2650,7 @@
 	var bootclassPaths android.Paths
 	var staticLibs android.Paths
 	var systemModulesPaths android.Paths
-	ctx.VisitDirectDeps(func(dep android.Module) {
+	ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(dep)
 		switch tag {
 		case javaApiContributionTag:
@@ -2336,8 +2679,8 @@
 				systemModulesPaths = append(systemModulesPaths, sm.HeaderJars...)
 			}
 		case metalavaCurrentApiTimestampTag:
-			if currentApiTimestampProvider, ok := dep.(currentApiTimestampProvider); ok {
-				al.validationPaths = append(al.validationPaths, currentApiTimestampProvider.CurrentApiTimestamp())
+			if currentApiTimestampProvider, ok := android.OtherModuleProvider(ctx, dep, DroidStubsInfoProvider); ok {
+				al.validationPaths = append(al.validationPaths, currentApiTimestampProvider.CurrentApiTimestamp)
 			}
 		case aconfigDeclarationTag:
 			if provider, ok := android.OtherModuleProvider(ctx, dep, android.AconfigDeclarationsProviderKey); ok {
@@ -2369,7 +2712,7 @@
 	combinedPaths := append(([]android.Path)(nil), systemModulesPaths...)
 	combinedPaths = append(combinedPaths, classPaths...)
 	combinedPaths = append(combinedPaths, bootclassPaths...)
-	cmd := metalavaStubCmd(ctx, rule, srcFiles, homeDir, combinedPaths, configFiles)
+	cmd := metalavaStubCmd(ctx, rule, srcFiles, homeDir, combinedPaths, configFiles, al.properties.Api_surface)
 
 	al.stubsFlags(ctx, cmd, stubsDir)
 
@@ -2433,7 +2776,7 @@
 
 	ctx.Phony(ctx.ModuleName(), al.stubsJar)
 
-	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+	javaInfo := &JavaInfo{
 		HeaderJars:                             android.PathsIfNonNil(al.stubsJar),
 		LocalHeaderJars:                        android.PathsIfNonNil(al.stubsJar),
 		TransitiveStaticLibsHeaderJars:         depset.New(depset.PREORDER, android.PathsIfNonNil(al.stubsJar), nil),
@@ -2443,7 +2786,9 @@
 		AidlIncludeDirs:                        android.Paths{},
 		StubsLinkType:                          Stubs,
 		// No aconfig libraries on api libraries
-	})
+	}
+	setExtraJavaInfo(ctx, al, javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
 }
 
 func (al *ApiLibrary) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
@@ -2711,7 +3056,7 @@
 	var staticJars android.Paths
 	var staticResourceJars android.Paths
 	var staticHeaderJars android.Paths
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(module)
 		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 			switch tag {
@@ -2763,12 +3108,7 @@
 	// file of the module to be named jarName.
 	var outputFile android.Path
 	combinedImplementationJar := android.PathForModuleOut(ctx, "combined", jarName)
-	var implementationJars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		implementationJars = completeStaticLibsImplementationJars.ToList()
-	} else {
-		implementationJars = append(slices.Clone(localJars), staticJars...)
-	}
+	implementationJars := completeStaticLibsImplementationJars.ToList()
 	TransformJarsToJar(ctx, combinedImplementationJar, "combine prebuilt implementation jars", implementationJars, android.OptionalPath{},
 		false, j.properties.Exclude_files, j.properties.Exclude_dirs)
 	outputFile = combinedImplementationJar
@@ -2791,12 +3131,7 @@
 	if reuseImplementationJarAsHeaderJar {
 		headerJar = outputFile
 	} else {
-		var headerJars android.Paths
-		if ctx.Config().UseTransitiveJarsInClasspath() {
-			headerJars = completeStaticLibsHeaderJars.ToList()
-		} else {
-			headerJars = append(slices.Clone(localJars), staticHeaderJars...)
-		}
+		headerJars := completeStaticLibsHeaderJars.ToList()
 		headerOutputFile := android.PathForModuleOut(ctx, "turbine-combined", jarName)
 		TransformJarsToJar(ctx, headerOutputFile, "combine prebuilt header jars", headerJars, android.OptionalPath{},
 			false, j.properties.Exclude_files, j.properties.Exclude_dirs)
@@ -2833,6 +3168,23 @@
 		outputFile = combinedJar
 	}
 
+	proguardFlags := android.PathForModuleOut(ctx, "proguard_flags")
+	TransformJarToR8Rules(ctx, proguardFlags, outputFile)
+
+	transitiveProguardFlags, transitiveUnconditionalExportedFlags := collectDepProguardSpecInfo(ctx)
+	android.SetProvider(ctx, ProguardSpecInfoProvider, ProguardSpecInfo{
+		ProguardFlagsFiles: depset.New[android.Path](
+			depset.POSTORDER,
+			android.Paths{proguardFlags},
+			transitiveProguardFlags,
+		),
+		UnconditionallyExportedProguardFlags: depset.New[android.Path](
+			depset.POSTORDER,
+			nil,
+			transitiveUnconditionalExportedFlags,
+		),
+	})
+
 	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource.
 	// Also strip the relative path from the header output file so that the reuseImplementationJarAsHeaderJar check
 	// in a module that depends on this module considers them equal.
@@ -2843,11 +3195,7 @@
 
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
 
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		ctx.CheckbuildFile(localJars...)
-	} else {
-		ctx.CheckbuildFile(outputFile)
-	}
+	ctx.CheckbuildFile(localJars...)
 
 	if ctx.Device() {
 		// Shared libraries deapexed from prebuilt apexes are no longer supported.
@@ -2901,7 +3249,7 @@
 		}
 	}
 
-	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+	javaInfo := &JavaInfo{
 		HeaderJars:                             android.PathsIfNonNil(j.combinedHeaderFile),
 		LocalHeaderJars:                        android.PathsIfNonNil(j.combinedHeaderFile),
 		TransitiveLibsHeaderJarsForR8:          j.transitiveLibsHeaderJarsForR8,
@@ -2915,10 +3263,18 @@
 		AidlIncludeDirs:                        j.exportAidlIncludeDirs,
 		StubsLinkType:                          j.stubsLinkType,
 		// TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
+	}
+	setExtraJavaInfo(ctx, j, javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+
+	android.SetProvider(ctx, JavaLibraryInfoProvider, JavaLibraryInfo{
+		Prebuilt: true,
 	})
 
 	ctx.SetOutputFiles(android.Paths{j.combinedImplementationFile}, "")
 	ctx.SetOutputFiles(android.Paths{j.combinedImplementationFile}, ".jar")
+
+	buildComplianceMetadata(ctx)
 }
 
 func (j *Import) maybeInstall(ctx android.ModuleContext, jarName string, outputFile android.Path) {
@@ -2965,26 +3321,29 @@
 var _ android.ApexModule = (*Import)(nil)
 
 // Implements android.ApexModule
-func (j *Import) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
-	return j.depIsInSameApex(ctx, dep)
+func (m *Import) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return JavaImportDepInSameApexChecker{}
+}
+
+type JavaImportDepInSameApexChecker struct {
+	android.BaseDepInSameApexChecker
+}
+
+func (m JavaImportDepInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool {
+	return depIsInSameApex(tag)
 }
 
 // Implements android.ApexModule
-func (j *Import) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
-	sdkVersion android.ApiLevel) error {
+func (j *Import) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
 	sdkVersionSpec := j.SdkVersion(ctx)
 	minSdkVersion := j.MinSdkVersion(ctx)
-	if !minSdkVersion.Specified() {
-		return fmt.Errorf("min_sdk_version is not specified")
-	}
+
 	// If the module is compiling against core (via sdk_version), skip comparison check.
 	if sdkVersionSpec.Kind == android.SdkCore {
-		return nil
+		return android.MinApiLevel
 	}
-	if minSdkVersion.GreaterThan(sdkVersion) {
-		return fmt.Errorf("newer SDK(%v)", minSdkVersion)
-	}
-	return nil
+
+	return minSdkVersion
 }
 
 // requiredFilesFromPrebuiltApexForImport returns information about the files that a java_import or
@@ -3021,19 +3380,10 @@
 
 // Add compile time check for interface implementation
 var _ android.IDEInfo = (*Import)(nil)
-var _ android.IDECustomizedModuleName = (*Import)(nil)
 
 // Collect information for opening IDE project files in java/jdeps.go.
-
 func (j *Import) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
-	dpInfo.Jars = append(dpInfo.Jars, j.combinedHeaderFile.String())
-}
-
-func (j *Import) IDECustomizedModuleName() string {
-	// TODO(b/113562217): Extract the base module name from the Import name, often the Import name
-	// has a prefix "prebuilt_". Remove the prefix explicitly if needed until we find a better
-	// solution to get the Import name.
-	return android.RemoveOptionalPrebuiltPrefix(j.Name())
+	dpInfo.Jars = append(dpInfo.Jars, j.combinedImplementationFile.String())
 }
 
 var _ android.PrebuiltInterface = (*Import)(nil)
@@ -3191,6 +3541,12 @@
 		ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
 			j.Stem()+".jar", dexOutputFile)
 	}
+
+	javaInfo := &JavaInfo{}
+	setExtraJavaInfo(ctx, j, javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+
+	android.SetProvider(ctx, JavaDexImportInfoProvider, JavaDexImportInfo{})
 }
 
 func (j *DexImport) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
@@ -3200,10 +3556,8 @@
 var _ android.ApexModule = (*DexImport)(nil)
 
 // Implements android.ApexModule
-func (j *DexImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
-	sdkVersion android.ApiLevel) error {
-	// we don't check prebuilt modules for sdk_version
-	return nil
+func (m *DexImport) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
+	return android.MinApiLevel
 }
 
 // dex_import imports a `.jar` file containing classes.dex files.
@@ -3225,7 +3579,6 @@
 type Defaults struct {
 	android.ModuleBase
 	android.DefaultsModuleBase
-	android.ApexModuleBase
 }
 
 // java_defaults provides a set of properties that can be inherited by other java or android modules.
@@ -3289,6 +3642,8 @@
 		&bootclasspathFragmentProperties{},
 		&SourceOnlyBootclasspathProperties{},
 		&ravenwoodTestProperties{},
+		&AndroidAppImportProperties{},
+		&UsesLibraryProperties{},
 	)
 
 	android.InitDefaultsModule(module)
@@ -3305,10 +3660,10 @@
 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()...)
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+		if javaInfo, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
+			xrefTargets = append(xrefTargets, javaInfo.XrefJavaFiles...)
+			xrefKotlinTargets = append(xrefKotlinTargets, javaInfo.XrefKotlinFiles...)
 		}
 	})
 	// TODO(asmundak): perhaps emit a rule to output a warning if there were no xrefTargets
@@ -3326,11 +3681,11 @@
 var inList = android.InList[string]
 
 // Add class loader context (CLC) of a given dependency to the current CLC.
-func addCLCFromDep(ctx android.ModuleContext, depModule android.Module,
+func addCLCFromDep(ctx android.ModuleContext, depModule android.ModuleProxy,
 	clcMap dexpreopt.ClassLoaderContextMap) {
 
-	dep, ok := depModule.(UsesLibraryDependency)
-	if !ok {
+	dep, ok := android.OtherModuleProvider(ctx, depModule, JavaInfoProvider)
+	if !ok || dep.UsesLibraryDependencyInfo == nil {
 		return
 	}
 
@@ -3340,14 +3695,14 @@
 	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 lib := dep.SdkLibraryComponentDependencyInfo; lib != nil && lib.OptionalSdkLibraryImplementation != nil {
+		if depModule.Name() == proptools.String(lib.OptionalSdkLibraryImplementation)+".impl" {
+			sdkLib = lib.OptionalSdkLibraryImplementation
 		}
-	} else if ulib, ok := depModule.(ProvidesUsesLib); ok {
+	} else if ulib := dep.ProvidesUsesLibInfo; ulib != nil {
 		// 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.
-		sdkLib = ulib.ProvidesUsesLib()
+		sdkLib = ulib.ProvidesUsesLib
 	}
 
 	depTag := ctx.OtherModuleDependencyTag(depModule)
@@ -3379,21 +3734,22 @@
 			}
 		}
 		clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, optional,
-			dep.DexJarBuildPath(ctx).PathOrNil(), dep.DexJarInstallPath(), dep.ClassLoaderContexts())
+			dep.DexJarBuildPath.PathOrNil(),
+			dep.UsesLibraryDependencyInfo.DexJarInstallPath, dep.UsesLibraryDependencyInfo.ClassLoaderContexts)
 	} else {
-		clcMap.AddContextMap(dep.ClassLoaderContexts(), depName)
+		clcMap.AddContextMap(dep.UsesLibraryDependencyInfo.ClassLoaderContexts, depName)
 	}
 }
 
-func addMissingOptionalUsesLibsFromDep(ctx android.ModuleContext, depModule android.Module,
+func addMissingOptionalUsesLibsFromDep(ctx android.ModuleContext, depModule android.ModuleProxy,
 	usesLibrary *usesLibrary) {
 
-	dep, ok := depModule.(ModuleWithUsesLibrary)
+	dep, ok := android.OtherModuleProvider(ctx, depModule, JavaInfoProvider)
 	if !ok {
 		return
 	}
 
-	for _, lib := range dep.UsesLibrary().usesLibraryProperties.Missing_optional_uses_libs {
+	for _, lib := range dep.MissingOptionalUsesLibs {
 		if !android.InList(lib, usesLibrary.usesLibraryProperties.Missing_optional_uses_libs) {
 			usesLibrary.usesLibraryProperties.Missing_optional_uses_libs =
 				append(usesLibrary.usesLibraryProperties.Missing_optional_uses_libs, lib)
@@ -3449,3 +3805,68 @@
 func (ap *JavaApiContributionImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	ap.JavaApiContribution.GenerateAndroidBuildActions(ctx)
 }
+
+func setExtraJavaInfo(ctx android.ModuleContext, module android.Module, javaInfo *JavaInfo) {
+	if alDep, ok := module.(AndroidLibraryDependency); ok {
+		javaInfo.AndroidLibraryDependencyInfo = &AndroidLibraryDependencyInfo{
+			ExportPackage:       alDep.ExportPackage(),
+			ResourcesNodeDepSet: alDep.ResourcesNodeDepSet(),
+			RRODirsDepSet:       alDep.RRODirsDepSet(),
+			ManifestsDepSet:     alDep.ManifestsDepSet(),
+		}
+	}
+
+	if ulDep, ok := module.(UsesLibraryDependency); ok {
+		javaInfo.UsesLibraryDependencyInfo = &UsesLibraryDependencyInfo{
+			DexJarInstallPath:   ulDep.DexJarInstallPath(),
+			ClassLoaderContexts: ulDep.ClassLoaderContexts(),
+		}
+	}
+
+	if slcDep, ok := module.(SdkLibraryComponentDependency); ok {
+		javaInfo.SdkLibraryComponentDependencyInfo = &SdkLibraryComponentDependencyInfo{
+			OptionalSdkLibraryImplementation: slcDep.OptionalSdkLibraryImplementation(),
+		}
+	}
+
+	if pul, ok := module.(ProvidesUsesLib); ok {
+		javaInfo.ProvidesUsesLibInfo = &ProvidesUsesLibInfo{
+			ProvidesUsesLib: pul.ProvidesUsesLib(),
+		}
+	}
+
+	if mwul, ok := module.(ModuleWithUsesLibrary); ok {
+		javaInfo.MissingOptionalUsesLibs = mwul.UsesLibrary().usesLibraryProperties.Missing_optional_uses_libs
+	}
+
+	if mwsd, ok := module.(moduleWithSdkDep); ok {
+		linkType, stubs := mwsd.getSdkLinkType(ctx, ctx.ModuleName())
+		javaInfo.ModuleWithSdkDepInfo = &ModuleWithSdkDepInfo{
+			SdkLinkType: linkType,
+			Stubs:       stubs,
+		}
+	}
+
+	if st, ok := module.(ModuleWithStem); ok {
+		javaInfo.Stem = st.Stem()
+	}
+
+	if mm, ok := module.(interface {
+		DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath
+	}); ok {
+		javaInfo.DexJarBuildPath = mm.DexJarBuildPath(ctx)
+	}
+
+	if di, ok := module.(DexpreopterInterface); ok {
+		javaInfo.DexpreopterInfo = &DexpreopterInfo{
+			OutputProfilePathOnHost:           di.OutputProfilePathOnHost(),
+			ApexSystemServerDexpreoptInstalls: di.ApexSystemServerDexpreoptInstalls(),
+			ApexSystemServerDexJars:           di.ApexSystemServerDexJars(),
+		}
+	}
+
+	if xr, ok := module.(xref); ok {
+		javaInfo.XrefJavaFiles = xr.XrefJavaFiles()
+		javaInfo.XrefKotlinFiles = xr.XrefKotlinFiles()
+	}
+}
diff --git a/java/java_test.go b/java/java_test.go
index d415679..1ba78d4 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -110,13 +110,14 @@
 	case strings.HasSuffix(name, ".jar"):
 		return name
 	default:
-		return filepath.Join("out", "soong", ".intermediates", defaultJavaDir, name, "android_common", "turbine-combined", name+".jar")
+		return filepath.Join("out", "soong", ".intermediates", defaultJavaDir, name, "android_common", "turbine", name+".jar")
 	}
 }
 
 // Test that the PrepareForTestWithJavaDefaultModules provides all the files that it uses by
 // running it in a fixture that requires all source files to exist.
 func TestPrepareForTestWithJavaDefaultModules(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.PrepareForTestDisallowNonExistentPaths,
@@ -124,6 +125,7 @@
 }
 
 func TestJavaLinkType(t *testing.T) {
+	t.Parallel()
 	testJava(t, `
 		java_library {
 			name: "foo",
@@ -212,6 +214,7 @@
 }
 
 func TestSimple(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "foo",
@@ -237,11 +240,6 @@
 			srcs: ["d.java"],
 		}`
 
-	frameworkTurbineCombinedJars := []string{
-		"out/soong/.intermediates/default/java/ext/android_common/turbine-combined/ext.jar",
-		"out/soong/.intermediates/default/java/framework/android_common/turbine-combined/framework.jar",
-	}
-
 	frameworkTurbineJars := []string{
 		"out/soong/.intermediates/default/java/ext/android_common/turbine/ext.jar",
 		"out/soong/.intermediates/default/java/framework/android_common/turbine/framework.jar",
@@ -267,43 +265,6 @@
 			preparer:       android.NullFixturePreparer,
 			fooJavacInputs: []string{"a.java"},
 			fooJavacClasspath: slices.Concat(
-				frameworkTurbineCombinedJars,
-				[]string{
-					"out/soong/.intermediates/bar/android_common/turbine-combined/bar.jar",
-					"out/soong/.intermediates/baz/android_common/turbine-combined/baz.jar",
-				},
-			),
-			fooCombinedInputs: []string{
-				"out/soong/.intermediates/foo/android_common/javac/foo.jar",
-				"out/soong/.intermediates/baz/android_common/combined/baz.jar",
-			},
-
-			fooHeaderCombinedInputs: []string{
-				"out/soong/.intermediates/foo/android_common/turbine/foo.jar",
-				"out/soong/.intermediates/baz/android_common/turbine-combined/baz.jar",
-			},
-
-			barJavacInputs: []string{"b.java"},
-			barJavacClasspath: slices.Concat(
-				frameworkTurbineCombinedJars,
-				[]string{
-					"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar",
-				},
-			),
-			barCombinedInputs: []string{
-				"out/soong/.intermediates/bar/android_common/javac/bar.jar",
-				"out/soong/.intermediates/quz/android_common/javac/quz.jar",
-			},
-			barHeaderCombinedInputs: []string{
-				"out/soong/.intermediates/bar/android_common/turbine/bar.jar",
-				"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar",
-			},
-		},
-		{
-			name:           "transitive classpath",
-			preparer:       PrepareForTestWithTransitiveClasspathEnabled,
-			fooJavacInputs: []string{"a.java"},
-			fooJavacClasspath: slices.Concat(
 				frameworkTurbineJars,
 				[]string{
 					"out/soong/.intermediates/bar/android_common/turbine/bar.jar",
@@ -341,11 +302,12 @@
 
 	for _, tt := range testCases {
 		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 				tt.preparer,
 			).RunTestWithBp(t, bp)
-			foo := result.ModuleForTests("foo", "android_common")
+			foo := result.ModuleForTests(t, "foo", "android_common")
 
 			fooJavac := foo.Rule("javac")
 			android.AssertPathsRelativeToTopEquals(t, "foo javac inputs", tt.fooJavacInputs, fooJavac.Inputs)
@@ -360,7 +322,7 @@
 			fooCombinedHeaderJar := foo.Output("turbine-combined/foo.jar")
 			android.AssertPathsRelativeToTopEquals(t, "foo header combined inputs", tt.fooHeaderCombinedInputs, fooCombinedHeaderJar.Inputs)
 
-			bar := result.ModuleForTests("bar", "android_common")
+			bar := result.ModuleForTests(t, "bar", "android_common")
 			barJavac := bar.Rule("javac")
 			android.AssertPathsRelativeToTopEquals(t, "bar javac inputs", tt.barJavacInputs, barJavac.Inputs)
 
@@ -378,6 +340,7 @@
 }
 
 func TestExportedPlugins(t *testing.T) {
+	t.Parallel()
 	type Result struct {
 		library        string
 		processors     string
@@ -456,6 +419,7 @@
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			ctx, _ := testJava(t, `
 				java_plugin {
 					name: "plugin",
@@ -469,11 +433,11 @@
 			`+test.extra)
 
 			for _, want := range test.results {
-				javac := ctx.ModuleForTests(want.library, "android_common").Rule("javac")
+				javac := ctx.ModuleForTests(t, want.library, "android_common").Rule("javac")
 				if javac.Args["processor"] != want.processors {
 					t.Errorf("For library %v, expected %v, found %v", want.library, want.processors, javac.Args["processor"])
 				}
-				turbine := ctx.ModuleForTests(want.library, "android_common").MaybeRule("turbine")
+				turbine := ctx.ModuleForTests(t, want.library, "android_common").MaybeRule("turbine")
 				disableTurbine := turbine.BuildParams.Rule == nil
 				if disableTurbine != want.disableTurbine {
 					t.Errorf("For library %v, expected disableTurbine %v, found %v", want.library, want.disableTurbine, disableTurbine)
@@ -484,6 +448,7 @@
 }
 
 func TestSdkVersionByPartition(t *testing.T) {
+	t.Parallel()
 	testJavaError(t, "sdk_version must have a value when the module is located at vendor or product", `
 		java_library {
 			name: "foo",
@@ -525,6 +490,7 @@
 }
 
 func TestArchSpecific(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -537,13 +503,14 @@
 		}
 	`)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Rule("javac")
 	if len(javac.Inputs) != 2 || javac.Inputs[0].String() != "a.java" || javac.Inputs[1].String() != "b.java" {
 		t.Errorf(`foo inputs %v != ["a.java", "b.java"]`, javac.Inputs)
 	}
 }
 
 func TestBinary(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library_host {
 			name: "foo",
@@ -567,11 +534,11 @@
 
 	buildOS := ctx.Config().BuildOS.String()
 
-	bar := ctx.ModuleForTests("bar", buildOS+"_common")
+	bar := ctx.ModuleForTests(t, "bar", buildOS+"_common")
 	barJar := bar.Output("bar.jar").Output.String()
 	barWrapperDeps := bar.Output("bar").Implicits.Strings()
 
-	libjni := ctx.ModuleForTests("libjni", buildOS+"_x86_64_shared")
+	libjni := ctx.ModuleForTests(t, "libjni", buildOS+"_x86_64_shared")
 	libjniSO := libjni.Rule("Cp").Output.String()
 
 	// Test that the install binary wrapper depends on the installed jar file
@@ -586,6 +553,7 @@
 }
 
 func TestTest(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_test_host {
 			name: "foo",
@@ -603,7 +571,7 @@
 
 	buildOS := ctx.Config().BuildOS.String()
 
-	foo := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
+	foo := ctx.ModuleForTests(t, "foo", buildOS+"_common").Module().(*TestHost)
 
 	expected := "lib64/libjni.so"
 	if runtime.GOOS == "darwin" {
@@ -617,7 +585,31 @@
 	}
 }
 
+func TestHostCommonData(t *testing.T) {
+	t.Parallel()
+	ctx, _ := testJava(t, `
+		java_library_host {
+			name: "host",
+			srcs: ["a.java"],
+		}
+
+		java_test {
+			name: "foo",
+			srcs: ["a.java"],
+			host_common_data: [":host"],
+		}
+	`)
+
+	foo := ctx.ModuleForTests(t, "foo", "android_common").Module().(*Test)
+	host := ctx.ModuleForTests(t, "host", ctx.Config().BuildOSCommonTarget.String()).Module().(*Library)
+
+	if g, w := foo.data.RelativeToTop().Strings(), []string{host.outputFile.RelativeToTop().String()}; !slices.Equal(g, w) {
+		t.Errorf("expected test data %q, got %q\n", w, g)
+	}
+}
+
 func TestHostBinaryNoJavaDebugInfoOverride(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "target_library",
@@ -638,7 +630,7 @@
 	).RunTestWithBp(t, bp)
 
 	// first, check that the -g flag is added to target modules
-	targetLibrary := result.ModuleForTests("target_library", "android_common")
+	targetLibrary := result.ModuleForTests(t, "target_library", "android_common")
 	targetJavaFlags := targetLibrary.Module().VariablesForTests()["javacFlags"]
 	if !strings.Contains(targetJavaFlags, "-g:source,lines") {
 		t.Errorf("target library javac flags %v should contain "+
@@ -647,7 +639,7 @@
 
 	// check that -g is not overridden for host modules
 	buildOS := result.Config.BuildOS.String()
-	hostBinary := result.ModuleForTests("host_binary", buildOS+"_common")
+	hostBinary := result.ModuleForTests(t, "host_binary", buildOS+"_common")
 	hostJavaFlags := hostBinary.Module().VariablesForTests()["javacFlags"]
 	if strings.Contains(hostJavaFlags, "-g:source,lines") {
 		t.Errorf("java_binary_host javac flags %v should not have "+
@@ -665,6 +657,7 @@
 var _ android.ModuleErrorfContext = (*moduleErrorfTestCtx)(nil)
 
 func TestPrebuilts(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -710,15 +703,15 @@
 		}
 		`)
 
-	fooModule := ctx.ModuleForTests("foo", "android_common")
+	fooModule := ctx.ModuleForTests(t, "foo", "android_common")
 	javac := fooModule.Rule("javac")
-	combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac")
-	barModule := ctx.ModuleForTests("bar", "android_common")
-	barJar := barModule.Output("combined/bar.jar").Output
-	bazModule := ctx.ModuleForTests("baz", "android_common")
-	bazJar := bazModule.Output("combined/baz.jar").Output
-	sdklibStubsJar := ctx.ModuleForTests("sdklib.stubs", "android_common").
-		Output("combined/sdklib.stubs.jar").Output
+	combineJar := ctx.ModuleForTests(t, "foo", "android_common").Description("for javac")
+	barModule := ctx.ModuleForTests(t, "bar", "android_common")
+	barJar := barModule.Output("local-combined/bar.jar").Output
+	bazModule := ctx.ModuleForTests(t, "baz", "android_common")
+	bazJar := bazModule.Output("local-combined/baz.jar").Output
+	sdklibStubsJar := ctx.ModuleForTests(t, "sdklib.stubs", "android_common").
+		Output("local-combined/sdklib.stubs.jar").Output
 
 	fooLibrary := fooModule.Module().(*Library)
 	assertDeepEquals(t, "foo unique sources incorrect",
@@ -750,7 +743,7 @@
 	expectedDexJar := "out/soong/.intermediates/baz/android_common/dex/baz.jar"
 	android.AssertPathRelativeToTopEquals(t, "baz dex jar build path", expectedDexJar, bazDexJar)
 
-	ctx.ModuleForTests("qux", "android_common").Rule("Cp")
+	ctx.ModuleForTests(t, "qux", "android_common").Rule("Cp")
 
 	entries := android.AndroidMkEntriesForTest(t, ctx, fooModule.Module())[0]
 	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "java_library", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
@@ -765,6 +758,7 @@
 }
 
 func TestPrebuiltStubsSources(t *testing.T) {
+	t.Parallel()
 	test := func(t *testing.T, sourcesPath string, expectedInputs []string) {
 		ctx, _ := testJavaWithFS(t, fmt.Sprintf(`
 prebuilt_stubs_sources {
@@ -775,17 +769,19 @@
 			"stubs/sources/pkg/B.java": nil,
 		})
 
-		zipSrc := ctx.ModuleForTests("stubs-source", "android_common").Rule("zip_src")
+		zipSrc := ctx.ModuleForTests(t, "stubs-source", "android_common").Rule("zip_src")
 		if expected, actual := expectedInputs, zipSrc.Inputs.Strings(); !reflect.DeepEqual(expected, actual) {
 			t.Errorf("mismatch of inputs to soong_zip: expected %q, actual %q", expected, actual)
 		}
 	}
 
 	t.Run("empty/missing directory", func(t *testing.T) {
+		t.Parallel()
 		test(t, "empty-directory", nil)
 	})
 
 	t.Run("non-empty set of sources", func(t *testing.T) {
+		t.Parallel()
 		test(t, "stubs/sources", []string{
 			"stubs/sources/pkg/A.java",
 			"stubs/sources/pkg/B.java",
@@ -794,6 +790,7 @@
 }
 
 func TestDefaults(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_defaults {
 			name: "defaults",
@@ -835,40 +832,41 @@
 		}
 		`)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
-	combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Rule("javac")
+	combineJar := ctx.ModuleForTests(t, "foo", "android_common").Description("for javac")
 
 	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
 		t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
 	}
 
-	barTurbine := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
+	barTurbine := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine", "bar.jar")
 	if !strings.Contains(javac.Args["classpath"], barTurbine) {
 		t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], barTurbine)
 	}
 
-	baz := ctx.ModuleForTests("baz", "android_common").Rule("javac").Output.String()
+	baz := ctx.ModuleForTests(t, "baz", "android_common").Rule("javac").Output.String()
 	if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != baz {
 		t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, baz)
 	}
 
-	atestOptimize := ctx.ModuleForTests("atestOptimize", "android_common").MaybeRule("r8")
+	atestOptimize := ctx.ModuleForTests(t, "atestOptimize", "android_common").MaybeRule("r8")
 	if atestOptimize.Output == nil {
 		t.Errorf("atestOptimize should optimize APK")
 	}
 
-	atestNoOptimize := ctx.ModuleForTests("atestNoOptimize", "android_common").MaybeRule("d8")
+	atestNoOptimize := ctx.ModuleForTests(t, "atestNoOptimize", "android_common").MaybeRule("d8")
 	if atestNoOptimize.Output == nil {
 		t.Errorf("atestNoOptimize should not optimize APK")
 	}
 
-	atestDefault := ctx.ModuleForTests("atestDefault", "android_common").MaybeRule("d8")
+	atestDefault := ctx.ModuleForTests(t, "atestDefault", "android_common").MaybeRule("d8")
 	if atestDefault.Output == nil {
 		t.Errorf("atestDefault should not optimize APK")
 	}
 }
 
 func TestResources(t *testing.T) {
+	t.Parallel()
 	var table = []struct {
 		name  string
 		prop  string
@@ -940,6 +938,7 @@
 
 	for _, test := range table {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			ctx, _ := testJavaWithFS(t, `
 				java_library {
 					name: "foo",
@@ -958,8 +957,8 @@
 				},
 			)
 
-			foo := ctx.ModuleForTests("foo", "android_common").Output("withres/foo.jar")
-			fooRes := ctx.ModuleForTests("foo", "android_common").Output("res/foo.jar")
+			foo := ctx.ModuleForTests(t, "foo", "android_common").Output("withres/foo.jar")
+			fooRes := ctx.ModuleForTests(t, "foo", "android_common").Output("res/foo.jar")
 
 			if !inList(fooRes.Output.String(), foo.Inputs.Strings()) {
 				t.Errorf("foo combined jars %v does not contain %q",
@@ -975,6 +974,7 @@
 }
 
 func TestIncludeSrcs(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJavaWithFS(t, `
 		java_library {
 			name: "foo",
@@ -1003,8 +1003,8 @@
 	})
 
 	// Test a library with include_srcs: true
-	foo := ctx.ModuleForTests("foo", "android_common").Output("withres/foo.jar")
-	fooSrcJar := ctx.ModuleForTests("foo", "android_common").Output("foo.srcjar")
+	foo := ctx.ModuleForTests(t, "foo", "android_common").Output("withres/foo.jar")
+	fooSrcJar := ctx.ModuleForTests(t, "foo", "android_common").Output("foo.srcjar")
 
 	if g, w := fooSrcJar.Output.String(), foo.Inputs.Strings(); !inList(g, w) {
 		t.Errorf("foo combined jars %v does not contain %q", w, g)
@@ -1015,10 +1015,10 @@
 	}
 
 	// Test a library with include_srcs: true and resources
-	bar := ctx.ModuleForTests("bar", "android_common").Output("withres/bar.jar")
-	barResCombined := ctx.ModuleForTests("bar", "android_common").Output("res-combined/bar.jar")
-	barRes := ctx.ModuleForTests("bar", "android_common").Output("res/bar.jar")
-	barSrcJar := ctx.ModuleForTests("bar", "android_common").Output("bar.srcjar")
+	bar := ctx.ModuleForTests(t, "bar", "android_common").Output("withres/bar.jar")
+	barResCombined := ctx.ModuleForTests(t, "bar", "android_common").Output("res-combined/bar.jar")
+	barRes := ctx.ModuleForTests(t, "bar", "android_common").Output("res/bar.jar")
+	barSrcJar := ctx.ModuleForTests(t, "bar", "android_common").Output("bar.srcjar")
 
 	if g, w := barSrcJar.Output.String(), barResCombined.Inputs.Strings(); !inList(g, w) {
 		t.Errorf("bar combined resource jars %v does not contain %q", w, g)
@@ -1028,7 +1028,7 @@
 		t.Errorf("bar combined resource jars %v does not contain %q", w, g)
 	}
 
-	if g, w := barResCombined.Output.String(), bar.Inputs.Strings(); !inList(g, w) {
+	if g, w := barRes.Output.String(), bar.Inputs.Strings(); !inList(g, w) {
 		t.Errorf("bar combined jars %v does not contain %q", w, g)
 	}
 
@@ -1042,6 +1042,7 @@
 }
 
 func TestGeneratedSources(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJavaWithFS(t, `
 		java_library {
 			name: "foo",
@@ -1062,8 +1063,8 @@
 		"b.java": nil,
 	})
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
-	genrule := ctx.ModuleForTests("gen", "").Rule("generator")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Rule("javac")
+	genrule := ctx.ModuleForTests(t, "gen", "").Rule("generator")
 
 	if filepath.Base(genrule.Output.String()) != "gen.java" {
 		t.Fatalf(`gen output file %v is not ".../gen.java"`, genrule.Output.String())
@@ -1078,6 +1079,7 @@
 }
 
 func TestTurbine(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{"14": {"foo"}})).
 		RunTestWithBp(t, `
@@ -1102,15 +1104,15 @@
 		}
 		`)
 
-	fooTurbine := result.ModuleForTests("foo", "android_common").Rule("turbine")
-	barTurbine := result.ModuleForTests("bar", "android_common").Rule("turbine")
-	barJavac := result.ModuleForTests("bar", "android_common").Rule("javac")
-	barTurbineCombined := result.ModuleForTests("bar", "android_common").Description("for turbine")
-	bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac")
+	fooTurbine := result.ModuleForTests(t, "foo", "android_common").Rule("turbine")
+	barTurbine := result.ModuleForTests(t, "bar", "android_common").Rule("turbine")
+	barJavac := result.ModuleForTests(t, "bar", "android_common").Rule("javac")
+	barTurbineCombined := result.ModuleForTests(t, "bar", "android_common").Description("for turbine")
+	bazJavac := result.ModuleForTests(t, "baz", "android_common").Rule("javac")
 
 	android.AssertPathsRelativeToTopEquals(t, "foo inputs", []string{"a.java"}, fooTurbine.Inputs)
 
-	fooHeaderJar := filepath.Join("out", "soong", ".intermediates", "foo", "android_common", "turbine-combined", "foo.jar")
+	fooHeaderJar := filepath.Join("out", "soong", ".intermediates", "foo", "android_common", "turbine", "foo.jar")
 	barTurbineJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine", "bar.jar")
 	android.AssertStringDoesContain(t, "bar turbine classpath", barTurbine.Args["turbineFlags"], fooHeaderJar)
 	android.AssertStringDoesContain(t, "bar javac classpath", barJavac.Args["classpath"], fooHeaderJar)
@@ -1119,6 +1121,7 @@
 }
 
 func TestSharding(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "bar",
@@ -1129,7 +1132,7 @@
 
 	barHeaderJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine", "bar.jar")
 	for i := 0; i < 3; i++ {
-		barJavac := ctx.ModuleForTests("bar", "android_common").Description("javac" + strconv.Itoa(i))
+		barJavac := ctx.ModuleForTests(t, "bar", "android_common").Description("javac" + strconv.Itoa(i))
 		if !strings.HasPrefix(barJavac.Args["classpath"], "-classpath "+barHeaderJar+":") {
 			t.Errorf("bar javac classpath %v does start with %q", barJavac.Args["classpath"], barHeaderJar)
 		}
@@ -1137,6 +1140,7 @@
 }
 
 func TestExcludeFileGroupInSrcs(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -1155,7 +1159,7 @@
 		}
 	`)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Rule("javac")
 
 	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "java-fg/c.java" {
 		t.Errorf(`foo inputs %v != ["java-fg/c.java"]`, javac.Inputs)
@@ -1163,6 +1167,7 @@
 }
 
 func TestJavaLibraryOutputFiles(t *testing.T) {
+	t.Parallel()
 	testJavaWithFS(t, "", map[string][]byte{
 		"libcore/Android.bp": []byte(`
 				java_library {
@@ -1180,6 +1185,7 @@
 }
 
 func TestJavaImportOutputFiles(t *testing.T) {
+	t.Parallel()
 	testJavaWithFS(t, "", map[string][]byte{
 		"libcore/Android.bp": []byte(`
 				java_import {
@@ -1196,6 +1202,7 @@
 }
 
 func TestJavaImport(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "source_library",
@@ -1223,18 +1230,20 @@
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, bp)
 
-	source := ctx.ModuleForTests("source_library", "android_common")
+	source := ctx.ModuleForTests(t, "source_library", "android_common")
 	sourceJar := source.Output("javac/source_library.jar")
-	sourceHeaderJar := source.Output("turbine-combined/source_library.jar")
+	sourceHeaderJar := source.Output("turbine/source_library.jar")
+	sourceCombinedHeaderJar := source.Output("turbine-combined/source_library.jar")
 	sourceJavaInfo, _ := android.OtherModuleProvider(ctx, source.Module(), JavaInfoProvider)
 
 	// The source library produces separate implementation and header jars
 	android.AssertPathsRelativeToTopEquals(t, "source library implementation jar",
 		[]string{sourceJar.Output.String()}, sourceJavaInfo.ImplementationAndResourcesJars)
 	android.AssertPathsRelativeToTopEquals(t, "source library header jar",
-		[]string{sourceHeaderJar.Output.String()}, sourceJavaInfo.HeaderJars)
+		[]string{sourceCombinedHeaderJar.Output.String()}, sourceJavaInfo.HeaderJars)
 
-	importWithNoDeps := ctx.ModuleForTests("import_with_no_deps", "android_common")
+	importWithNoDeps := ctx.ModuleForTests(t, "import_with_no_deps", "android_common")
+	importWithNoDepsLocalJar := importWithNoDeps.Output("local-combined/import_with_no_deps.jar")
 	importWithNoDepsJar := importWithNoDeps.Output("combined/import_with_no_deps.jar")
 	importWithNoDepsJavaInfo, _ := android.OtherModuleProvider(ctx, importWithNoDeps.Module(), JavaInfoProvider)
 
@@ -1244,10 +1253,14 @@
 	android.AssertPathsRelativeToTopEquals(t, "import with no deps header jar",
 		[]string{importWithNoDepsJar.Output.String()}, importWithNoDepsJavaInfo.HeaderJars)
 	android.AssertPathsRelativeToTopEquals(t, "import with no deps combined inputs",
-		[]string{"no_deps.jar"}, importWithNoDepsJar.Inputs)
+		[]string{importWithNoDepsLocalJar.Output.String()}, importWithNoDepsJar.Inputs)
+	android.AssertPathsRelativeToTopEquals(t, "import with no deps local combined inputs",
+		[]string{"no_deps.jar"}, importWithNoDepsLocalJar.Inputs)
 
-	importWithSourceDeps := ctx.ModuleForTests("import_with_source_deps", "android_common")
+	importWithSourceDeps := ctx.ModuleForTests(t, "import_with_source_deps", "android_common")
+	importWithSourceDepsLocalJar := importWithSourceDeps.Output("local-combined/import_with_source_deps.jar")
 	importWithSourceDepsJar := importWithSourceDeps.Output("combined/import_with_source_deps.jar")
+	importWithSourceDepsLocalHeaderJar := importWithSourceDeps.Output("local-combined/import_with_source_deps.jar")
 	importWithSourceDepsHeaderJar := importWithSourceDeps.Output("turbine-combined/import_with_source_deps.jar")
 	importWithSourceDepsJavaInfo, _ := android.OtherModuleProvider(ctx, importWithSourceDeps.Module(), JavaInfoProvider)
 
@@ -1257,11 +1270,16 @@
 	android.AssertPathsRelativeToTopEquals(t, "import with source deps header jar",
 		[]string{importWithSourceDepsHeaderJar.Output.String()}, importWithSourceDepsJavaInfo.HeaderJars)
 	android.AssertPathsRelativeToTopEquals(t, "import with source deps combined implementation jar inputs",
-		[]string{"source_deps.jar", sourceJar.Output.String()}, importWithSourceDepsJar.Inputs)
+		[]string{importWithSourceDepsLocalJar.Output.String(), sourceJar.Output.String()}, importWithSourceDepsJar.Inputs)
 	android.AssertPathsRelativeToTopEquals(t, "import with source deps combined header jar inputs",
-		[]string{"source_deps.jar", sourceHeaderJar.Output.String()}, importWithSourceDepsHeaderJar.Inputs)
+		[]string{importWithSourceDepsLocalHeaderJar.Output.String(), sourceHeaderJar.Output.String()}, importWithSourceDepsHeaderJar.Inputs)
+	android.AssertPathsRelativeToTopEquals(t, "import with source deps local combined implementation jar inputs",
+		[]string{"source_deps.jar"}, importWithSourceDepsLocalJar.Inputs)
+	android.AssertPathsRelativeToTopEquals(t, "import with source deps local combined header jar inputs",
+		[]string{"source_deps.jar"}, importWithSourceDepsLocalHeaderJar.Inputs)
 
-	importWithImportDeps := ctx.ModuleForTests("import_with_import_deps", "android_common")
+	importWithImportDeps := ctx.ModuleForTests(t, "import_with_import_deps", "android_common")
+	importWithImportDepsLocalJar := importWithImportDeps.Output("local-combined/import_with_import_deps.jar")
 	importWithImportDepsJar := importWithImportDeps.Output("combined/import_with_import_deps.jar")
 	importWithImportDepsJavaInfo, _ := android.OtherModuleProvider(ctx, importWithImportDeps.Module(), JavaInfoProvider)
 
@@ -1271,7 +1289,9 @@
 	android.AssertPathsRelativeToTopEquals(t, "import with import deps header jar",
 		[]string{importWithImportDepsJar.Output.String()}, importWithImportDepsJavaInfo.HeaderJars)
 	android.AssertPathsRelativeToTopEquals(t, "import with import deps combined implementation jar inputs",
-		[]string{"import_deps.jar", importWithNoDepsJar.Output.String()}, importWithImportDepsJar.Inputs)
+		[]string{importWithImportDepsLocalJar.Output.String(), importWithNoDepsLocalJar.Output.String()}, importWithImportDepsJar.Inputs)
+	android.AssertPathsRelativeToTopEquals(t, "import with import deps local combined implementation jar inputs",
+		[]string{"import_deps.jar"}, importWithImportDepsLocalJar.Inputs)
 }
 
 var compilerFlagsTestCases = []struct {
@@ -1323,6 +1343,7 @@
 }
 
 func TestCompilerFlags(t *testing.T) {
+	t.Parallel()
 	for _, testCase := range compilerFlagsTestCases {
 		ctx := &mockContext{result: true}
 		CheckKotlincFlags(ctx, []string{testCase.in})
@@ -1337,7 +1358,8 @@
 
 // TODO(jungjw): Consider making this more robust by ignoring path order.
 func checkPatchModuleFlag(t *testing.T, ctx *android.TestContext, moduleName string, expected string) {
-	variables := ctx.ModuleForTests(moduleName, "android_common").VariablesForTestsRelativeToTop()
+	t.Helper()
+	variables := ctx.ModuleForTests(t, moduleName, "android_common").VariablesForTestsRelativeToTop()
 	flags := strings.Split(variables["javacFlags"], " ")
 	got := ""
 	for _, flag := range flags {
@@ -1353,7 +1375,9 @@
 }
 
 func TestPatchModule(t *testing.T) {
+	t.Parallel()
 	t.Run("Java language level 8", func(t *testing.T) {
+		t.Parallel()
 		// Test with legacy javac -source 1.8 -target 1.8
 		bp := `
 			java_library {
@@ -1386,6 +1410,7 @@
 	})
 
 	t.Run("Java language level 9", func(t *testing.T) {
+		t.Parallel()
 		// Test with default javac -source 9 -target 9
 		bp := `
 			java_library {
@@ -1426,6 +1451,7 @@
 }
 
 func TestJavaLibraryWithSystemModules(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 		    name: "lib-with-source-system-modules",
@@ -1474,7 +1500,7 @@
 }
 
 func checkBootClasspathForLibWithSystemModule(t *testing.T, ctx *android.TestContext, moduleName string, expectedSuffix string) {
-	javacRule := ctx.ModuleForTests(moduleName, "android_common").Rule("javac")
+	javacRule := ctx.ModuleForTests(t, moduleName, "android_common").Rule("javac")
 	bootClasspath := javacRule.Args["bootClasspath"]
 	if strings.HasPrefix(bootClasspath, "--system ") && strings.HasSuffix(bootClasspath, expectedSuffix) {
 		t.Errorf("bootclasspath of %q must start with --system and end with %q, but was %#v.", moduleName, expectedSuffix, bootClasspath)
@@ -1482,6 +1508,7 @@
 }
 
 func TestAidlExportIncludeDirsFromImports(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -1498,7 +1525,7 @@
 		}
 	`)
 
-	aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+	aidlCommand := ctx.ModuleForTests(t, "foo", "android_common").Rule("aidl").RuleParams.Command
 	expectedAidlFlag := "-Iaidl/bar"
 	if !strings.Contains(aidlCommand, expectedAidlFlag) {
 		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
@@ -1506,6 +1533,7 @@
 }
 
 func TestAidlFlagsArePassedToTheAidlCompiler(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -1514,7 +1542,7 @@
 		}
 	`)
 
-	aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+	aidlCommand := ctx.ModuleForTests(t, "foo", "android_common").Rule("aidl").RuleParams.Command
 	expectedAidlFlag := "-Werror"
 	if !strings.Contains(aidlCommand, expectedAidlFlag) {
 		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
@@ -1522,6 +1550,7 @@
 }
 
 func TestAidlFlagsWithMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	fixture := android.GroupFixturePreparers(
 		prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{"14": {"foo"}}))
 
@@ -1535,6 +1564,7 @@
 		{"system_current", `sdk_version: "system_current"`, "current"},
 	} {
 		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
 			ctx := fixture.RunTestWithBp(t, `
 				java_library {
 					name: "foo",
@@ -1542,7 +1572,7 @@
 					`+tc.sdkVersion+`
 				}
 			`)
-			aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+			aidlCommand := ctx.ModuleForTests(t, "foo", "android_common").Rule("aidl").RuleParams.Command
 			expectedAidlFlag := "--min_sdk_version=" + tc.expected
 			if !strings.Contains(aidlCommand, expectedAidlFlag) {
 				t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
@@ -1552,6 +1582,7 @@
 }
 
 func TestAidlFlagsMinSdkVersionDroidstubs(t *testing.T) {
+	t.Parallel()
 	bpTemplate := `
 	droidstubs {
 		name: "foo-stubs",
@@ -1578,13 +1609,14 @@
 	}
 	for _, tc := range testCases {
 		ctx := prepareForJavaTest.RunTestWithBp(t, fmt.Sprintf(bpTemplate, tc.sdkVersionBp))
-		aidlCmd := ctx.ModuleForTests("foo-stubs", "android_common").Rule("aidl").RuleParams.Command
+		aidlCmd := ctx.ModuleForTests(t, "foo-stubs", "android_common").Rule("aidl").RuleParams.Command
 		expected := "--min_sdk_version=" + tc.minSdkVersionExpected
 		android.AssertStringDoesContain(t, "aidl command conatins incorrect min_sdk_version for testCse: "+tc.desc, aidlCmd, expected)
 	}
 }
 
 func TestAidlEnforcePermissions(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -1593,7 +1625,7 @@
 		}
 	`)
 
-	aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+	aidlCommand := ctx.ModuleForTests(t, "foo", "android_common").Rule("aidl").RuleParams.Command
 	expectedAidlFlag := "-Wmissing-permission-annotation -Werror"
 	if !strings.Contains(aidlCommand, expectedAidlFlag) {
 		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
@@ -1601,6 +1633,7 @@
 }
 
 func TestAidlEnforcePermissionsException(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -1609,7 +1642,7 @@
 		}
 	`)
 
-	aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+	aidlCommand := ctx.ModuleForTests(t, "foo", "android_common").Rule("aidl").RuleParams.Command
 	expectedAidlFlag := "$$FLAGS -Wmissing-permission-annotation -Werror aidl/foo/IFoo.aidl"
 	if !strings.Contains(aidlCommand, expectedAidlFlag) {
 		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
@@ -1621,6 +1654,7 @@
 }
 
 func TestDataNativeBinaries(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		android.PrepareForTestWithAllowMissingDependencies).RunTestWithBp(t, `
@@ -1638,7 +1672,7 @@
 
 	buildOS := ctx.Config().BuildOS.String()
 
-	test := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
+	test := ctx.ModuleForTests(t, "foo", buildOS+"_common").Module().(*TestHost)
 	entries := android.AndroidMkEntriesForTest(t, ctx, test)[0]
 	expected := []string{"out/soong/.intermediates/bin/" + buildOS + "_x86_64/bin:bin"}
 	actual := entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
@@ -1646,6 +1680,7 @@
 }
 
 func TestDefaultInstallable(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_test_host {
 			name: "foo"
@@ -1653,12 +1688,13 @@
 	`)
 
 	buildOS := ctx.Config().BuildOS.String()
-	module := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
+	module := ctx.ModuleForTests(t, "foo", buildOS+"_common").Module().(*TestHost)
 	assertDeepEquals(t, "Default installable value should be true.", proptools.BoolPtr(true),
 		module.properties.Installable)
 }
 
 func TestErrorproneEnabled(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -1669,7 +1705,7 @@
 		}
 	`)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Description("javac")
 
 	// Test that the errorprone plugins are passed to javac
 	expectedSubstring := "-Xplugin:ErrorProne"
@@ -1680,13 +1716,14 @@
 	// Modules with errorprone { enabled: true } will include errorprone checks
 	// in the main javac build rule. Only when RUN_ERROR_PRONE is true will
 	// the explicit errorprone build rule be created.
-	errorprone := ctx.ModuleForTests("foo", "android_common").MaybeDescription("errorprone")
+	errorprone := ctx.ModuleForTests(t, "foo", "android_common").MaybeDescription("errorprone")
 	if errorprone.RuleParams.Description != "" {
 		t.Errorf("expected errorprone build rule to not exist, but it did")
 	}
 }
 
 func TestErrorproneDisabled(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "foo",
@@ -1703,7 +1740,7 @@
 		}),
 	).RunTestWithBp(t, bp)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Description("javac")
 
 	// Test that the errorprone plugins are not passed to javac, like they would
 	// be if enabled was true.
@@ -1714,13 +1751,14 @@
 
 	// Check that no errorprone build rule is created, like there would be
 	// if enabled was unset and RUN_ERROR_PRONE was true.
-	errorprone := ctx.ModuleForTests("foo", "android_common").MaybeDescription("errorprone")
+	errorprone := ctx.ModuleForTests(t, "foo", "android_common").MaybeDescription("errorprone")
 	if errorprone.RuleParams.Description != "" {
 		t.Errorf("expected errorprone build rule to not exist, but it did")
 	}
 }
 
 func TestErrorproneEnabledOnlyByEnvironmentVariable(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "foo",
@@ -1734,8 +1772,8 @@
 		}),
 	).RunTestWithBp(t, bp)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
-	errorprone := ctx.ModuleForTests("foo", "android_common").Description("errorprone")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Description("javac")
+	errorprone := ctx.ModuleForTests(t, "foo", "android_common").Description("errorprone")
 
 	// Check that the errorprone plugins are not passed to javac, because they
 	// will instead be passed to the separate errorprone compilation
@@ -1751,6 +1789,7 @@
 }
 
 func TestDataDeviceBinsBuildsDeviceBinary(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		dataDeviceBinType  string
 		depCompileMultilib string
@@ -1887,6 +1926,7 @@
 
 		testName := fmt.Sprintf(`data_device_bins_%s with compile_multilib:"%s"`, tc.dataDeviceBinType, tc.depCompileMultilib)
 		t.Run(testName, func(t *testing.T) {
+			t.Parallel()
 			ctx := android.GroupFixturePreparers(PrepareForIntegrationTestWithJava).
 				ExtendWithErrorHandler(errorHandler).
 				RunTestWithBp(t, bp)
@@ -1895,7 +1935,7 @@
 			}
 
 			buildOS := ctx.Config.BuildOS.String()
-			fooVariant := ctx.ModuleForTests("foo", buildOS+"_common")
+			fooVariant := ctx.ModuleForTests(t, "foo", buildOS+"_common")
 			fooMod := fooVariant.Module().(*TestHost)
 			entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, fooMod)[0]
 
@@ -1907,7 +1947,7 @@
 
 			expectedData := []string{}
 			for _, variant := range tc.variants {
-				barVariant := ctx.ModuleForTests("bar", variant)
+				barVariant := ctx.ModuleForTests(t, "bar", variant)
 				relocated := barVariant.Output("bar")
 				expectedInput := fmt.Sprintf("out/soong/.intermediates/bar/%s/unstripped/bar", variant)
 				android.AssertPathRelativeToTopEquals(t, "relocation input", expectedInput, relocated.Input)
@@ -1916,12 +1956,13 @@
 			}
 
 			actualData := entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
-			android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_TEST_DATA", ctx.Config, expectedData, actualData)
+			android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_TEST_DATA", ctx.Config, android.SortedUniqueStrings(expectedData), android.SortedUniqueStrings(actualData))
 		})
 	}
 }
 
 func TestDeviceBinaryWrapperGeneration(t *testing.T) {
+	t.Parallel()
 	// Scenario 1: java_binary has main_class property in its bp
 	ctx, _ := testJava(t, `
 		java_binary {
@@ -1930,7 +1971,7 @@
 			main_class: "foo.bar.jb",
 		}
 	`)
-	wrapperPath := fmt.Sprint(ctx.ModuleForTests("foo", "android_common").AllOutputs())
+	wrapperPath := fmt.Sprint(ctx.ModuleForTests(t, "foo", "android_common").AllOutputs())
 	if !strings.Contains(wrapperPath, "foo.sh") {
 		t.Errorf("wrapper file foo.sh is not generated")
 	}
@@ -1945,6 +1986,7 @@
 }
 
 func TestJavaApiContributionEmptyApiFile(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		android.FixtureMergeEnv(
@@ -1968,6 +2010,7 @@
 }
 
 func TestJavaApiLibraryAndProviderLink(t *testing.T) {
+	t.Parallel()
 	provider_bp_a := `
 	java_api_contribution {
 		name: "foo1",
@@ -2024,7 +2067,7 @@
 		},
 	}
 	for _, c := range testcases {
-		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		m := ctx.ModuleForTests(t, c.moduleName, "android_common")
 		manifest := m.Output("metalava.sbox.textproto")
 		sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, manifest)
 		manifestCommand := sboxProto.Commands[0].GetCommand()
@@ -2034,6 +2077,7 @@
 }
 
 func TestJavaApiLibraryAndDefaultsLink(t *testing.T) {
+	t.Parallel()
 	provider_bp_a := `
 	java_api_contribution {
 		name: "foo1",
@@ -2132,7 +2176,7 @@
 		},
 	}
 	for _, c := range testcases {
-		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		m := ctx.ModuleForTests(t, c.moduleName, "android_common")
 		manifest := m.Output("metalava.sbox.textproto")
 		sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, manifest)
 		manifestCommand := sboxProto.Commands[0].GetCommand()
@@ -2142,6 +2186,7 @@
 }
 
 func TestJavaApiLibraryJarGeneration(t *testing.T) {
+	t.Parallel()
 	provider_bp_a := `
 	java_api_contribution {
 		name: "foo1",
@@ -2199,7 +2244,7 @@
 		},
 	}
 	for _, c := range testcases {
-		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		m := ctx.ModuleForTests(t, c.moduleName, "android_common")
 		outputs := fmt.Sprint(m.AllOutputs())
 		if !strings.Contains(outputs, c.outputJarName) {
 			t.Errorf("Module output does not contain expected jar %s", c.outputJarName)
@@ -2208,6 +2253,7 @@
 }
 
 func TestJavaApiLibraryLibsLink(t *testing.T) {
+	t.Parallel()
 	provider_bp_a := `
 	java_api_contribution {
 		name: "foo1",
@@ -2284,7 +2330,7 @@
 		},
 	}
 	for _, c := range testcases {
-		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		m := ctx.ModuleForTests(t, c.moduleName, "android_common")
 		javacRules := m.Rule("javac")
 		classPathArgs := javacRules.Args["classpath"]
 		for _, jarName := range c.classPathJarNames {
@@ -2296,6 +2342,7 @@
 }
 
 func TestJavaApiLibraryStaticLibsLink(t *testing.T) {
+	t.Parallel()
 	provider_bp_a := `
 	java_api_contribution {
 		name: "foo1",
@@ -2372,7 +2419,7 @@
 		},
 	}
 	for _, c := range testcases {
-		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		m := ctx.ModuleForTests(t, c.moduleName, "android_common")
 		mergeZipsCommand := m.Rule("merge_zips").RuleParams.Command
 		for _, jarName := range c.staticLibJarNames {
 			if !strings.Contains(mergeZipsCommand, jarName) {
@@ -2383,6 +2430,7 @@
 }
 
 func TestTransitiveSrcFiles(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "a",
@@ -2399,13 +2447,14 @@
 			static_libs: ["b"],
 		}
 	`)
-	c := ctx.ModuleForTests("c", "android_common").Module()
+	c := ctx.ModuleForTests(t, "c", "android_common").Module()
 	javaInfo, _ := android.OtherModuleProvider(ctx, c, JavaInfoProvider)
 	transitiveSrcFiles := android.Paths(javaInfo.TransitiveSrcFiles.ToList())
 	android.AssertArrayString(t, "unexpected jar deps", []string{"b.java", "c.java"}, transitiveSrcFiles.Strings())
 }
 
 func TestTradefedOptions(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaBuildComponents.RunTestWithBp(t, `
 java_test_host {
 	name: "foo",
@@ -2421,7 +2470,7 @@
 `)
 
 	buildOS := result.Config.BuildOS.String()
-	args := result.ModuleForTests("foo", buildOS+"_common").
+	args := result.ModuleForTests(t, "foo", buildOS+"_common").
 		Output("out/soong/.intermediates/foo/" + buildOS + "_common/foo.config").Args
 	expected := proptools.NinjaAndShellEscape("<option name=\"exclude-path\" value=\"org/apache\" />")
 	if args["extraConfigs"] != expected {
@@ -2430,6 +2479,7 @@
 }
 
 func TestTestRunnerOptions(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaBuildComponents.RunTestWithBp(t, `
 java_test_host {
 	name: "foo",
@@ -2445,7 +2495,7 @@
 `)
 
 	buildOS := result.Config.BuildOS.String()
-	args := result.ModuleForTests("foo", buildOS+"_common").
+	args := result.ModuleForTests(t, "foo", buildOS+"_common").
 		Output("out/soong/.intermediates/foo/" + buildOS + "_common/foo.config").Args
 	expected := proptools.NinjaAndShellEscape("<option name=\"test-timeout\" value=\"10m\" />\\n        ")
 	if args["extraTestRunnerConfigs"] != expected {
@@ -2454,6 +2504,7 @@
 }
 
 func TestJavaLibraryWithResourcesStem(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJavaWithFS(t, `
     java_library {
         name: "foo",
@@ -2465,7 +2516,7 @@
 			"test-jar/test/resource.txt": nil,
 		})
 
-	m := ctx.ModuleForTests("foo", "android_common")
+	m := ctx.ModuleForTests(t, "foo", "android_common")
 	outputs := fmt.Sprint(m.AllOutputs())
 	if !strings.Contains(outputs, "test.jar") {
 		t.Errorf("Module output does not contain expected jar %s", "test.jar")
@@ -2473,6 +2524,7 @@
 }
 
 func TestHeadersOnly(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -2481,16 +2533,17 @@
 		}
 	`)
 
-	turbine := ctx.ModuleForTests("foo", "android_common").Rule("turbine")
+	turbine := ctx.ModuleForTests(t, "foo", "android_common").Rule("turbine")
 	if len(turbine.Inputs) != 1 || turbine.Inputs[0].String() != "a.java" {
 		t.Errorf(`foo inputs %v != ["a.java"]`, turbine.Inputs)
 	}
 
-	javac := ctx.ModuleForTests("foo", "android_common").MaybeRule("javac")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").MaybeRule("javac")
 	android.AssertDeepEquals(t, "javac rule", nil, javac.Rule)
 }
 
 func TestJavaApiContributionImport(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		android.FixtureMergeEnv(
@@ -2510,7 +2563,7 @@
 			api_surface: "public",
 		}
 	`)
-	m := ctx.ModuleForTests("foo", "android_common")
+	m := ctx.ModuleForTests(t, "foo", "android_common")
 	manifest := m.Output("metalava.sbox.textproto")
 	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, manifest)
 	manifestCommand := sboxProto.Commands[0].GetCommand()
@@ -2519,6 +2572,7 @@
 }
 
 func TestJavaApiLibraryApiFilesSorting(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_api_library {
 			name: "foo",
@@ -2532,7 +2586,7 @@
 			stubs_type: "everything",
 		}
 	`)
-	m := ctx.ModuleForTests("foo", "android_common")
+	m := ctx.ModuleForTests(t, "foo", "android_common")
 	manifest := m.Output("metalava.sbox.textproto")
 	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, manifest)
 	manifestCommand := sboxProto.Commands[0].GetCommand()
@@ -2547,6 +2601,7 @@
 }
 
 func TestSdkLibraryProvidesSystemModulesToApiLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -2576,6 +2631,7 @@
 }
 
 func TestApiLibraryDroidstubsDependency(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -2605,9 +2661,9 @@
 	`)
 
 	currentApiTimestampPath := "api-stubs-docs-non-updatable/android_common/everything/check_current_api.timestamp"
-	foo := result.ModuleForTests("foo", "android_common").Module().(*ApiLibrary)
+	foo := result.ModuleForTests(t, "foo", "android_common").Module().(*ApiLibrary)
 	fooValidationPathsString := strings.Join(foo.validationPaths.Strings(), " ")
-	bar := result.ModuleForTests("bar", "android_common").Module().(*ApiLibrary)
+	bar := result.ModuleForTests(t, "bar", "android_common").Module().(*ApiLibrary)
 	barValidationPathsString := strings.Join(bar.validationPaths.Strings(), " ")
 	android.AssertStringDoesContain(t,
 		"Module expected to have validation",
@@ -2622,6 +2678,7 @@
 }
 
 func TestDisableFromTextStubForCoverageBuild(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -2651,6 +2708,7 @@
 }
 
 func TestMultiplePrebuilts(t *testing.T) {
+	t.Parallel()
 	bp := `
 		// an rdep
 		java_library {
@@ -2733,8 +2791,8 @@
 		).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName))
 
 		// check that rdep gets the correct variation of dep
-		foo := ctx.ModuleForTests("foo", "android_common")
-		expectedDependency := ctx.ModuleForTests(tc.expectedDependencyName, "android_common")
+		foo := ctx.ModuleForTests(t, "foo", "android_common")
+		expectedDependency := ctx.ModuleForTests(t, tc.expectedDependencyName, "android_common")
 		android.AssertBoolEquals(t, fmt.Sprintf("expected dependency from %s to %s\n", foo.Module().Name(), tc.expectedDependencyName), true, hasDep(ctx, foo.Module(), expectedDependency.Module()))
 
 		// check that output file of dep is always bar.jar
@@ -2749,6 +2807,7 @@
 }
 
 func TestMultiplePlatformCompatConfigPrebuilts(t *testing.T) {
+	t.Parallel()
 	bp := `
 		// multiple variations of platform_compat_config
 		// source
@@ -2802,13 +2861,14 @@
 			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"),
 		).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName))
 
-		mergedGlobalConfig := ctx.SingletonForTests("platform_compat_config_singleton").Output("compat_config/merged_compat_config.xml")
+		mergedGlobalConfig := ctx.SingletonForTests(t, "platform_compat_config_singleton").Output("compat_config/merged_compat_config.xml")
 		android.AssertIntEquals(t, "The merged compat config file should only have a single dependency", 1, len(mergedGlobalConfig.Implicits))
 		android.AssertStringEquals(t, "The merged compat config file is missing the appropriate platform compat config", mergedGlobalConfig.Implicits[0].String(), tc.expectedPlatformCompatConfigXml)
 	}
 }
 
 func TestApiLibraryAconfigDeclarations(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -2850,14 +2910,14 @@
 	android.AssertBoolEquals(t, "foo expected to depend on bar",
 		CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar"), true)
 
-	m := result.ModuleForTests("foo", "android_common")
+	m := result.ModuleForTests(t, "foo", "android_common")
 	android.AssertStringDoesContain(t, "foo generates revert annotations file",
-		strings.Join(m.AllOutputs(), ""), "revert-annotations-exportable.txt")
+		strings.Join(m.AllOutputs(), ""), "flags-config-exportable.xml")
 
 	// revert-annotations.txt passed to exportable stubs generation metalava command
 	manifest := m.Output("metalava.sbox.textproto")
 	cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command)
-	android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "revert-annotations-exportable.txt")
+	android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "flags-config-exportable.xml")
 }
 
 func TestTestOnly(t *testing.T) {
@@ -2919,6 +2979,7 @@
 
 // Don't allow setting test-only on things that are always tests or never tests.
 func TestInvalidTestOnlyTargets(t *testing.T) {
+	t.Parallel()
 	testCases := []string{
 		` java_test {  name: "java-test", test_only: true, srcs: ["foo.java"],  } `,
 		` java_test_host {  name: "java-test-host", test_only: true, srcs: ["foo.java"],  } `,
@@ -2954,6 +3015,7 @@
 }
 
 func TestJavaLibHostWithStem(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library_host {
 			name: "foo",
@@ -2963,7 +3025,7 @@
 	`)
 
 	buildOS := ctx.Config().BuildOS.String()
-	foo := ctx.ModuleForTests("foo", buildOS+"_common")
+	foo := ctx.ModuleForTests(t, "foo", buildOS+"_common")
 
 	outputs := fmt.Sprint(foo.AllOutputs())
 	if !strings.Contains(outputs, "foo-new.jar") {
@@ -2972,6 +3034,7 @@
 }
 
 func TestJavaLibWithStem(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -2980,7 +3043,7 @@
 		}
 	`)
 
-	foo := ctx.ModuleForTests("foo", "android_common")
+	foo := ctx.ModuleForTests(t, "foo", "android_common")
 
 	outputs := fmt.Sprint(foo.AllOutputs())
 	if !strings.Contains(outputs, "foo-new.jar") {
@@ -2989,6 +3052,7 @@
 }
 
 func TestJavaLibraryOutputFilesRel(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -3010,9 +3074,9 @@
 		}
 	`)
 
-	foo := result.ModuleForTests("foo", "android_common")
-	bar := result.ModuleForTests("bar", "android_common")
-	baz := result.ModuleForTests("baz", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
+	bar := result.ModuleForTests(t, "bar", "android_common")
+	baz := result.ModuleForTests(t, "baz", "android_common")
 
 	fooOutputPaths := foo.OutputFiles(result.TestContext, t, "")
 	barOutputPaths := bar.OutputFiles(result.TestContext, t, "")
@@ -3034,10 +3098,10 @@
 }
 
 func TestCoverage(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		prepareForTestWithFrameworkJacocoInstrumentation,
-		PrepareForTestWithTransitiveClasspathEnabled,
 	).RunTestWithBp(t, `
 		android_app {
 			name: "foo",
@@ -3054,8 +3118,8 @@
 		}
 	`)
 
-	foo := result.ModuleForTests("foo", "android_common")
-	androidCar := result.ModuleForTests("android.car", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
+	androidCar := result.ModuleForTests(t, "android.car", "android_common")
 
 	fooJacoco := foo.Rule("jacoco")
 	fooCombine := foo.Description("for javac")
@@ -3104,6 +3168,7 @@
 
 // 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) {
+	t.Parallel()
 	findDepsOfModule := func(ctx *android.TestContext, module android.Module, depName string) []blueprint.Module {
 		var ret []blueprint.Module
 		ctx.VisitDirectDeps(module, func(dep blueprint.Module) {
@@ -3125,6 +3190,75 @@
 }
 `
 	res, _ := testJava(t, bp)
-	deps := findDepsOfModule(res, res.ModuleForTests("myjavabin", "android_common").Module(), "mynativelib")
+	deps := findDepsOfModule(res, res.ModuleForTests(t, "myjavabin", "android_common").Module(), "mynativelib")
 	android.AssertIntEquals(t, "Create a dep on the first variant", 1, len(deps))
 }
+
+func TestBootJarNotInUsesLibs(t *testing.T) {
+	t.Parallel()
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary"),
+		FixtureConfigureApexBootJars("myapex:mysdklibrary"),
+	).RunTestWithBp(t, `
+		bootclasspath_fragment {
+			name: "myfragment",
+			contents: ["mysdklibrary"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+
+		java_sdk_library {
+			name: "mysdklibrary",
+			srcs: ["Test.java"],
+			compile_dex: true,
+			public: {enabled: true},
+			min_sdk_version: "2",
+			permitted_packages: ["mysdklibrary"],
+			sdk_version: "current",
+		}
+
+		java_sdk_library {
+			name: "myothersdklibrary",
+			srcs: ["Test.java"],
+			compile_dex: true,
+			public: {enabled: true},
+			min_sdk_version: "2",
+			permitted_packages: ["myothersdklibrary"],
+			sdk_version: "current",
+		}
+
+		java_library {
+			name: "foo",
+			libs: [
+				"bar",
+				"mysdklibrary.stubs",
+			],
+			srcs: ["A.java"],
+		}
+
+		java_library {
+			name: "bar",
+			libs: [
+				"myothersdklibrary.stubs"
+			],
+		}
+	`)
+	ctx := result.TestContext
+	fooModule := ctx.ModuleForTests(t, "foo", "android_common")
+
+	androidMkEntries := android.AndroidMkEntriesForTest(t, ctx, fooModule.Module())[0]
+	localExportSdkLibraries := androidMkEntries.EntryMap["LOCAL_EXPORT_SDK_LIBRARIES"]
+	android.AssertStringListDoesNotContain(t,
+		"boot jar should not be included in uses libs entries",
+		localExportSdkLibraries,
+		"mysdklibrary",
+	)
+	android.AssertStringListContains(t,
+		"non boot jar is included in uses libs entries",
+		localExportSdkLibraries,
+		"myothersdklibrary",
+	)
+}
diff --git a/java/jdeps.go b/java/jdeps.go
index c2ce503..56142c8 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -37,8 +37,6 @@
 	outputPath android.Path
 }
 
-var _ android.SingletonMakeVarsProvider = (*jdepsGeneratorSingleton)(nil)
-
 const (
 	jdepsJsonFileName = "module_bp_java_deps.json"
 )
@@ -47,13 +45,13 @@
 	// (b/204397180) Generate module_bp_java_deps.json by default.
 	moduleInfos := make(map[string]android.IdeInfo)
 
-	ctx.VisitAllModules(func(module android.Module) {
-		if !module.Enabled(ctx) {
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+		if !android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider).Enabled {
 			return
 		}
 
 		// Prevent including both prebuilts and matching source modules when one replaces the other.
-		if !android.IsModulePreferred(module) {
+		if !android.IsModulePreferredProxy(ctx, module) {
 			return
 		}
 
@@ -62,9 +60,11 @@
 			return
 		}
 		name := ideInfoProvider.BaseModuleName
-		ideModuleNameProvider, ok := module.(android.IDECustomizedModuleName)
-		if ok {
-			name = ideModuleNameProvider.IDECustomizedModuleName()
+		if info, ok := android.OtherModuleProvider(ctx, module, JavaLibraryInfoProvider); ok && info.Prebuilt {
+			// TODO(b/113562217): Extract the base module name from the Import name, often the Import name
+			// has a prefix "prebuilt_". Remove the prefix explicitly if needed until we find a better
+			// solution to get the Import name.
+			name = android.RemoveOptionalPrebuiltPrefix(module.Name())
 		}
 
 		dpInfo := moduleInfos[name]
@@ -72,13 +72,12 @@
 		dpInfo.Paths = []string{ctx.ModuleDir(module)}
 		moduleInfos[name] = dpInfo
 
-		mkProvider, ok := module.(android.AndroidMkDataProvider)
+		mkProvider, ok := android.OtherModuleProvider(ctx, module, android.AndroidMkDataInfoProvider)
 		if !ok {
 			return
 		}
-		data := mkProvider.AndroidMk()
-		if data.Class != "" {
-			dpInfo.Classes = append(dpInfo.Classes, data.Class)
+		if mkProvider.Class != "" {
+			dpInfo.Classes = append(dpInfo.Classes, mkProvider.Class)
 		}
 
 		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
@@ -101,14 +100,7 @@
 		Rule:   android.Touch,
 		Output: jfpath,
 	})
-}
-
-func (j *jdepsGeneratorSingleton) MakeVars(ctx android.MakeVarsContext) {
-	if j.outputPath == nil {
-		return
-	}
-
-	ctx.DistForGoal("general-tests", j.outputPath)
+	ctx.DistForGoals([]string{"general-tests", "dist_files"}, j.outputPath)
 }
 
 func createJsonFile(moduleInfos map[string]android.IdeInfo, jfpath android.WritablePath) error {
diff --git a/java/jdeps_test.go b/java/jdeps_test.go
index 1435000..00f3839 100644
--- a/java/jdeps_test.go
+++ b/java/jdeps_test.go
@@ -22,6 +22,7 @@
 )
 
 func TestCollectJavaLibraryPropertiesAddLibsDeps(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t,
 		`
 		java_library {name: "Foo"}
@@ -31,7 +32,7 @@
 			libs: ["Foo", "Bar"],
 		}
 	`)
-	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	module := ctx.ModuleForTests(t, "javalib", "android_common").Module().(*Library)
 	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 
 	for _, expected := range []string{"Foo", "Bar"} {
@@ -42,6 +43,7 @@
 }
 
 func TestCollectJavaLibraryPropertiesAddStaticLibsDeps(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t,
 		`
 		java_library {name: "Foo"}
@@ -51,7 +53,7 @@
 			static_libs: ["Foo", "Bar"],
 		}
 	`)
-	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	module := ctx.ModuleForTests(t, "javalib", "android_common").Module().(*Library)
 	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 
 	for _, expected := range []string{"Foo", "Bar"} {
@@ -62,6 +64,7 @@
 }
 
 func TestCollectJavaLibraryPropertiesAddScrs(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t,
 		`
 		java_library {
@@ -69,7 +72,7 @@
 			srcs: ["Foo.java", "Bar.java"],
 		}
 	`)
-	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	module := ctx.ModuleForTests(t, "javalib", "android_common").Module().(*Library)
 	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 
 	expected := []string{"Foo.java", "Bar.java"}
@@ -79,6 +82,7 @@
 }
 
 func TestCollectJavaLibraryPropertiesAddAidlIncludeDirs(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t,
 		`
 		java_library {
@@ -88,7 +92,7 @@
 			},
 		}
 	`)
-	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	module := ctx.ModuleForTests(t, "javalib", "android_common").Module().(*Library)
 	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 
 	expected := []string{"Foo", "Bar"}
@@ -98,6 +102,7 @@
 }
 
 func TestCollectJavaLibraryWithJarJarRules(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t,
 		`
 		java_library {
@@ -106,7 +111,7 @@
 			jarjar_rules: "jarjar_rules.txt",
 		}
 	`)
-	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	module := ctx.ModuleForTests(t, "javalib", "android_common").Module().(*Library)
 	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 
 	android.AssertStringEquals(t, "IdeInfo.Srcs of repackaged library should not be empty", "foo.java", dpInfo.Srcs[0])
@@ -117,6 +122,7 @@
 }
 
 func TestCollectJavaLibraryLinkingAgainstVersionedSdk(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		FixtureWithPrebuiltApis(map[string][]string{
@@ -129,7 +135,7 @@
 			sdk_version: "29",
 		}
 	`)
-	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	module := ctx.ModuleForTests(t, "javalib", "android_common").Module().(*Library)
 	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 
 	android.AssertStringListContains(t, "IdeInfo.Deps should contain versioned sdk module", dpInfo.Deps, "sdk_public_29_android")
@@ -165,11 +171,11 @@
 			api_surface: "public",
 		}
 	`)
-	javalib := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	javalib := ctx.ModuleForTests(t, "javalib", "android_common").Module().(*Library)
 	dpInfo, _ := android.OtherModuleProvider(ctx, javalib, android.IdeInfoProviderKey)
 	android.AssertStringListDoesNotContain(t, "IdeInfo.Deps should contain not contain `none`", dpInfo.Deps, "none")
 
-	javalib_stubs := ctx.ModuleForTests("javalib.stubs", "android_common").Module().(*ApiLibrary)
+	javalib_stubs := ctx.ModuleForTests(t, "javalib.stubs", "android_common").Module().(*ApiLibrary)
 	dpInfo, _ = android.OtherModuleProvider(ctx, javalib_stubs, android.IdeInfoProviderKey)
 	android.AssertStringListDoesNotContain(t, "IdeInfo.Deps should contain not contain `none`", dpInfo.Deps, "none")
 }
diff --git a/java/kotlin.go b/java/kotlin.go
index f42d163..308bb03 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -181,6 +181,7 @@
 		Command: `rm -rf "$srcJarDir" "$kotlinBuildFile" "$kaptDir" && ` +
 			`mkdir -p "$srcJarDir" "$kaptDir/sources" "$kaptDir/classes" && ` +
 			`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
+			`${config.FindInputDeltaCmd} --template '' --target "$out" --inputs_file "$out.rsp" && ` +
 			`${config.GenKotlinBuildFileCmd} --classpath "$classpath" --name "$name"` +
 			` --srcs "$out.rsp" --srcs "$srcJarDir/list"` +
 			` $commonSrcFilesArg --out "$kotlinBuildFile" && ` +
@@ -197,8 +198,10 @@
 			`$kaptProcessor ` +
 			`-Xbuild-file=$kotlinBuildFile && ` +
 			`${config.SoongZipCmd} -jar -write_if_changed -o $out -C $kaptDir/stubs -D $kaptDir/stubs && ` +
+			`if [ -f "$out.pc_state.new" ]; then mv "$out.pc_state.new" "$out.pc_state"; fi && ` +
 			`rm -rf "$srcJarDir"`,
 		CommandDeps: []string{
+			"${config.FindInputDeltaCmd}",
 			"${config.KotlincCmd}",
 			"${config.KotlinCompilerJar}",
 			"${config.KotlinKaptJar}",
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index 45eac01..4b56cff 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -24,6 +24,7 @@
 )
 
 func TestKotlin(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "foo",
@@ -49,13 +50,6 @@
 			srcs: ["d.kt"],
 		}`
 
-	kotlinStdlibTurbineCombinedJars := []string{
-		"out/soong/.intermediates/default/java/kotlin-stdlib/android_common/turbine-combined/kotlin-stdlib.jar",
-		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk7/android_common/turbine-combined/kotlin-stdlib-jdk7.jar",
-		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk8/android_common/turbine-combined/kotlin-stdlib-jdk8.jar",
-		"out/soong/.intermediates/default/java/kotlin-annotations/android_common/turbine-combined/kotlin-annotations.jar",
-	}
-
 	kotlinStdlibTurbineJars := []string{
 		"out/soong/.intermediates/default/java/kotlin-stdlib/android_common/turbine/kotlin-stdlib.jar",
 		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk7/android_common/turbine/kotlin-stdlib-jdk7.jar",
@@ -70,21 +64,11 @@
 		"out/soong/.intermediates/default/java/kotlin-annotations/android_common/javac/kotlin-annotations.jar",
 	}
 
-	bootclasspathTurbineCombinedJars := []string{
-		"out/soong/.intermediates/default/java/stable.core.platform.api.stubs/android_common/turbine-combined/stable.core.platform.api.stubs.jar",
-		"out/soong/.intermediates/default/java/core-lambda-stubs/android_common/turbine-combined/core-lambda-stubs.jar",
-	}
-
 	bootclasspathTurbineJars := []string{
 		"out/soong/.intermediates/default/java/stable.core.platform.api.stubs/android_common/turbine/stable.core.platform.api.stubs.jar",
 		"out/soong/.intermediates/default/java/core-lambda-stubs/android_common/turbine/core-lambda-stubs.jar",
 	}
 
-	frameworkTurbineCombinedJars := []string{
-		"out/soong/.intermediates/default/java/ext/android_common/turbine-combined/ext.jar",
-		"out/soong/.intermediates/default/java/framework/android_common/turbine-combined/framework.jar",
-	}
-
 	frameworkTurbineJars := []string{
 		"out/soong/.intermediates/default/java/ext/android_common/turbine/ext.jar",
 		"out/soong/.intermediates/default/java/framework/android_common/turbine/framework.jar",
@@ -108,68 +92,8 @@
 		barHeaderCombinedInputs []string
 	}{
 		{
-			name:             "normal",
-			preparer:         android.NullFixturePreparer,
-			fooKotlincInputs: []string{"a.java", "b.kt"},
-			fooJavacInputs:   []string{"a.java"},
-			fooKotlincClasspath: slices.Concat(
-				bootclasspathTurbineCombinedJars,
-				frameworkTurbineCombinedJars,
-				[]string{"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar"},
-				kotlinStdlibTurbineCombinedJars,
-			),
-			fooJavacClasspath: slices.Concat(
-				[]string{"out/soong/.intermediates/foo/android_common/kotlin_headers/foo.jar"},
-				frameworkTurbineCombinedJars,
-				[]string{"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar"},
-				kotlinStdlibTurbineCombinedJars,
-			),
-			fooCombinedInputs: slices.Concat(
-				[]string{
-					"out/soong/.intermediates/foo/android_common/kotlin/foo.jar",
-					"out/soong/.intermediates/foo/android_common/javac/foo.jar",
-					"out/soong/.intermediates/quz/android_common/combined/quz.jar",
-				},
-				kotlinStdlibJavacJars,
-			),
-			fooHeaderCombinedInputs: slices.Concat(
-				[]string{
-					"out/soong/.intermediates/foo/android_common/turbine/foo.jar",
-					"out/soong/.intermediates/foo/android_common/kotlin_headers/foo.jar",
-					"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar",
-				},
-				kotlinStdlibTurbineCombinedJars,
-			),
-
-			barKotlincInputs: []string{"b.kt"},
-			barKotlincClasspath: slices.Concat(
-				bootclasspathTurbineCombinedJars,
-				frameworkTurbineCombinedJars,
-				[]string{
-					"out/soong/.intermediates/foo/android_common/turbine-combined/foo.jar",
-					"out/soong/.intermediates/baz/android_common/turbine-combined/baz.jar",
-				},
-				kotlinStdlibTurbineCombinedJars,
-			),
-			barCombinedInputs: slices.Concat(
-				[]string{
-					"out/soong/.intermediates/bar/android_common/kotlin/bar.jar",
-					"out/soong/.intermediates/baz/android_common/combined/baz.jar",
-				},
-				kotlinStdlibJavacJars,
-				[]string{},
-			),
-			barHeaderCombinedInputs: slices.Concat(
-				[]string{
-					"out/soong/.intermediates/bar/android_common/kotlin_headers/bar.jar",
-					"out/soong/.intermediates/baz/android_common/turbine-combined/baz.jar",
-				},
-				kotlinStdlibTurbineCombinedJars,
-			),
-		},
-		{
 			name:             "transitive classpath",
-			preparer:         PrepareForTestWithTransitiveClasspathEnabled,
+			preparer:         android.NullFixturePreparer,
 			fooKotlincInputs: []string{"a.java", "b.kt"},
 			fooJavacInputs:   []string{"a.java"},
 			fooKotlincClasspath: slices.Concat(
@@ -234,11 +158,12 @@
 
 	for _, tt := range testCases {
 		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 				tt.preparer,
 			).RunTestWithBp(t, bp)
-			foo := result.ModuleForTests("foo", "android_common")
+			foo := result.ModuleForTests(t, "foo", "android_common")
 			fooKotlinc := foo.Rule("kotlinc")
 			android.AssertPathsRelativeToTopEquals(t, "foo kotlinc inputs", tt.fooKotlincInputs, fooKotlinc.Inputs)
 
@@ -258,7 +183,7 @@
 			fooCombinedHeaderJar := foo.Output("turbine-combined/foo.jar")
 			android.AssertPathsRelativeToTopEquals(t, "foo header combined inputs", tt.fooHeaderCombinedInputs, fooCombinedHeaderJar.Inputs)
 
-			bar := result.ModuleForTests("bar", "android_common")
+			bar := result.ModuleForTests(t, "bar", "android_common")
 			barKotlinc := bar.Rule("kotlinc")
 			android.AssertPathsRelativeToTopEquals(t, "bar kotlinc inputs", tt.barKotlincInputs, barKotlinc.Inputs)
 
@@ -275,6 +200,7 @@
 }
 
 func TestKapt(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "foo",
@@ -303,18 +229,19 @@
 		}
 	`
 	t.Run("", func(t *testing.T) {
+		t.Parallel()
 		ctx, _ := testJava(t, bp)
 
 		buildOS := ctx.Config().BuildOS.String()
 
-		foo := ctx.ModuleForTests("foo", "android_common")
+		foo := ctx.ModuleForTests(t, "foo", "android_common")
 		kaptStubs := foo.Rule("kapt")
 		turbineApt := foo.Description("turbine apt")
 		kotlinc := foo.Rule("kotlinc")
 		javac := foo.Rule("javac")
 
-		bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String()
-		baz := ctx.ModuleForTests("baz", buildOS+"_common").Rule("javac").Output.String()
+		bar := ctx.ModuleForTests(t, "bar", buildOS+"_common").Rule("javac").Output.String()
+		baz := ctx.ModuleForTests(t, "baz", buildOS+"_common").Rule("javac").Output.String()
 
 		// Test that the kotlin and java sources are passed to kapt and kotlinc
 		if len(kaptStubs.Inputs) != 2 || kaptStubs.Inputs[0].String() != "a.java" || kaptStubs.Inputs[1].String() != "b.kt" {
@@ -384,6 +311,7 @@
 	})
 
 	t.Run("errorprone", func(t *testing.T) {
+		t.Parallel()
 		env := map[string]string{
 			"RUN_ERROR_PRONE": "true",
 		}
@@ -395,13 +323,13 @@
 
 		buildOS := result.Config.BuildOS.String()
 
-		kapt := result.ModuleForTests("foo", "android_common").Rule("kapt")
-		javac := result.ModuleForTests("foo", "android_common").Description("javac")
-		errorprone := result.ModuleForTests("foo", "android_common").Description("errorprone")
+		kapt := result.ModuleForTests(t, "foo", "android_common").Rule("kapt")
+		javac := result.ModuleForTests(t, "foo", "android_common").Description("javac")
+		errorprone := result.ModuleForTests(t, "foo", "android_common").Description("errorprone")
 
-		bar := result.ModuleForTests("bar", buildOS+"_common").Description("javac").Output.String()
-		baz := result.ModuleForTests("baz", buildOS+"_common").Description("javac").Output.String()
-		myCheck := result.ModuleForTests("my_check", buildOS+"_common").Description("javac").Output.String()
+		bar := result.ModuleForTests(t, "bar", buildOS+"_common").Description("javac").Output.String()
+		baz := result.ModuleForTests(t, "baz", buildOS+"_common").Description("javac").Output.String()
+		myCheck := result.ModuleForTests(t, "my_check", buildOS+"_common").Description("javac").Output.String()
 
 		// Test that the errorprone plugins are not passed to kapt
 		expectedProcessorPath := "-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + bar +
@@ -434,6 +362,7 @@
 }
 
 func TestKaptEncodeFlags(t *testing.T) {
+	t.Parallel()
 	// Compares the kaptEncodeFlags against the results of the example implementation at
 	// https://kotlinlang.org/docs/reference/kapt.html#apjavac-options-encoding
 	tests := []struct {
@@ -484,6 +413,7 @@
 
 	for i, test := range tests {
 		t.Run(strconv.Itoa(i), func(t *testing.T) {
+			t.Parallel()
 			got := kaptEncodeFlags(test.in)
 			if got != test.out {
 				t.Errorf("\nwant %q\n got %q", test.out, got)
@@ -493,6 +423,7 @@
 }
 
 func TestKotlinCompose(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -501,7 +432,7 @@
 		}
 
 		kotlin_plugin {
-			name: "androidx.compose.compiler_compiler-hosted-plugin",
+			name: "kotlin-compose-compiler-plugin",
 		}
 
 		java_library {
@@ -523,9 +454,9 @@
 
 	buildOS := result.Config.BuildOS.String()
 
-	composeCompiler := result.ModuleForTests("androidx.compose.compiler_compiler-hosted-plugin", buildOS+"_common").Rule("combineJar").Output
-	withCompose := result.ModuleForTests("withcompose", "android_common")
-	noCompose := result.ModuleForTests("nocompose", "android_common")
+	composeCompiler := result.ModuleForTests(t, "kotlin-compose-compiler-plugin", buildOS+"_common").Rule("combineJar").Output
+	withCompose := result.ModuleForTests(t, "withcompose", "android_common")
+	noCompose := result.ModuleForTests(t, "nocompose", "android_common")
 
 	android.AssertStringListContains(t, "missing compose compiler dependency",
 		withCompose.Rule("kotlinc").Implicits.Strings(), composeCompiler.String())
@@ -544,6 +475,7 @@
 }
 
 func TestKotlinPlugin(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -570,9 +502,9 @@
 
 	buildOS := result.Config.BuildOS.String()
 
-	kotlinPlugin := result.ModuleForTests("kotlin_plugin", buildOS+"_common").Rule("combineJar").Output
-	withKotlinPlugin := result.ModuleForTests("with_kotlin_plugin", "android_common")
-	noKotlinPlugin := result.ModuleForTests("no_kotlin_plugin", "android_common")
+	kotlinPlugin := result.ModuleForTests(t, "kotlin_plugin", buildOS+"_common").Rule("combineJar").Output
+	withKotlinPlugin := result.ModuleForTests(t, "with_kotlin_plugin", "android_common")
+	noKotlinPlugin := result.ModuleForTests(t, "no_kotlin_plugin", "android_common")
 
 	android.AssertStringListContains(t, "missing plugin compiler dependency",
 		withKotlinPlugin.Rule("kotlinc").Implicits.Strings(), kotlinPlugin.String())
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
index 4be7d04..21895d4 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -47,8 +47,8 @@
 	"sam",
 	"saminterfacelibrary",
 	"sammanagerlibrary",
-	"services",
 	"services.core.unboosted",
+	"services.impl",
 	"Settings-core",
 	"SettingsGoogle",
 	"SettingsGoogleOverlayCoral",
diff --git a/java/lint.go b/java/lint.go
index ac90e19..dc1e51f 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -69,6 +69,11 @@
 		// If soong gets support for testonly, this flag should be replaced with that.
 		Test *bool
 
+		// Same as the regular Test property, but set by internal soong code based on if the module
+		// type is a test module type. This will act as the default value for the test property,
+		// but can be overridden by the user.
+		Test_module_type *bool `blueprint:"mutated"`
+
 		// Whether to ignore the exit code of Android lint. This is the --exit_code
 		// option. Defaults to false.
 		Suppress_exit_code *bool
@@ -257,7 +262,12 @@
 	if l.library {
 		cmd.Flag("--library")
 	}
-	if proptools.BoolDefault(l.properties.Lint.Test, false) {
+
+	test := proptools.BoolDefault(l.properties.Lint.Test_module_type, false)
+	if l.properties.Lint.Test != nil {
+		test = *l.properties.Lint.Test
+	}
+	if test {
 		cmd.Flag("--test")
 	}
 	if l.manifest != nil {
@@ -388,7 +398,7 @@
 		}
 	}
 
-	extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag)
+	extraLintCheckModules := ctx.GetDirectDepsProxyWithTag(extraLintCheckTag)
 	for _, extraLintCheckModule := range extraLintCheckModules {
 		if dep, ok := android.OtherModuleProvider(ctx, extraLintCheckModule, JavaInfoProvider); ok {
 			l.extraLintCheckJars = append(l.extraLintCheckJars, dep.ImplementationAndResourcesJars...)
@@ -413,7 +423,7 @@
 
 	depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml, baseline)
 
-	ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(staticLibTag, func(dep android.ModuleProxy) {
 		if info, ok := android.OtherModuleProvider(ctx, dep, LintProvider); ok {
 			depSetsBuilder.Transitive(info)
 		}
@@ -470,7 +480,7 @@
 
 	cmd := rule.Command()
 
-	cmd.Flag(`JAVA_OPTS="-Xmx3072m --add-opens java.base/java.util=ALL-UNNAMED"`).
+	cmd.Flag(`JAVA_OPTS="-Xmx4096m --add-opens java.base/java.util=ALL-UNNAMED"`).
 		FlagWithArg("ANDROID_SDK_HOME=", lintPaths.homeDir.String()).
 		FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath).
 		FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath)
@@ -585,12 +595,12 @@
 	l.copyLintDependencies(ctx)
 }
 
-func findModuleOrErr(ctx android.SingletonContext, moduleName string) android.Module {
-	var res android.Module
-	ctx.VisitAllModules(func(m android.Module) {
+func findModuleOrErr(ctx android.SingletonContext, moduleName string) *android.ModuleProxy {
+	var res *android.ModuleProxy
+	ctx.VisitAllModuleProxies(func(m android.ModuleProxy) {
 		if ctx.ModuleName(m) == moduleName {
 			if res == nil {
-				res = m
+				res = &m
 			} else {
 				ctx.Errorf("lint: multiple %s modules found: %s and %s", moduleName,
 					ctx.ModuleSubDir(m), ctx.ModuleSubDir(res))
@@ -625,13 +635,13 @@
 
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   android.CpIfChanged,
-			Input:  android.OutputFileForModule(ctx, sdkAnnotations, ""),
+			Input:  android.OutputFileForModule(ctx, *sdkAnnotations, ""),
 			Output: copiedLintDatabaseFilesPath(ctx, files.annotationCopiedName),
 		})
 
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   android.CpIfChanged,
-			Input:  android.OutputFileForModule(ctx, apiVersionsDb, ".api_versions.xml"),
+			Input:  android.OutputFileForModule(ctx, *apiVersionsDb, ".api_versions.xml"),
 			Output: copiedLintDatabaseFilesPath(ctx, files.apiVersionsCopiedName),
 		})
 	}
@@ -648,12 +658,13 @@
 
 	var outputs []*LintInfo
 	var dirs []string
-	ctx.VisitAllModules(func(m android.Module) {
-		if ctx.Config().KatiEnabled() && !m.ExportedToMake() {
+	ctx.VisitAllModuleProxies(func(m android.ModuleProxy) {
+		commonInfo := android.OtherModulePointerProviderOrDefault(ctx, m, android.CommonModuleInfoProvider)
+		if ctx.Config().KatiEnabled() && !commonInfo.ExportedToMake {
 			return
 		}
 
-		if apex, ok := m.(android.ApexModule); ok && apex.NotAvailableForPlatform() {
+		if commonInfo.IsApexModule && commonInfo.NotAvailableForPlatform {
 			apexInfo, _ := android.OtherModuleProvider(ctx, m, android.ApexInfoProvider)
 			if apexInfo.IsForPlatform() {
 				// There are stray platform variants of modules in apexes that are not available for
@@ -694,16 +705,12 @@
 	zip(l.referenceBaselineZip, func(l *LintInfo) android.Path { return l.ReferenceBaseline })
 
 	ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip)
-}
 
-func (l *lintSingleton) MakeVars(ctx android.MakeVarsContext) {
 	if !ctx.Config().UnbundledBuild() {
 		ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip)
 	}
 }
 
-var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil)
-
 func init() {
 	android.RegisterParallelSingletonType("lint",
 		func() android.Singleton { return &lintSingleton{} })
diff --git a/java/lint_test.go b/java/lint_test.go
index afe3914..236fa63 100644
--- a/java/lint_test.go
+++ b/java/lint_test.go
@@ -22,6 +22,7 @@
 )
 
 func TestJavaLintDoesntUseBaselineImplicitly(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJavaWithFS(t, `
 		java_library {
 			name: "foo",
@@ -37,7 +38,7 @@
 		"lint-baseline.xml": nil,
 	})
 
-	foo := ctx.ModuleForTests("foo", "android_common")
+	foo := ctx.ModuleForTests(t, "foo", "android_common")
 
 	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, foo.Output("lint.sbox.textproto"))
 	if strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml") {
@@ -46,6 +47,7 @@
 }
 
 func TestJavaLintRequiresCustomLintFileToExist(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.PrepareForTestDisallowNonExistentPaths,
@@ -65,6 +67,7 @@
 }
 
 func TestJavaLintUsesCorrectBpConfig(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJavaWithFS(t, `
 		java_library {
 			name: "foo",
@@ -84,7 +87,7 @@
 		"mybaseline.xml": nil,
 	})
 
-	foo := ctx.ModuleForTests("foo", "android_common")
+	foo := ctx.ModuleForTests(t, "foo", "android_common")
 
 	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, foo.Output("lint.sbox.textproto"))
 	if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline mybaseline.xml") {
@@ -101,6 +104,7 @@
 }
 
 func TestJavaLintBypassUpdatableChecks(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name  string
 		bp    string
@@ -144,6 +148,7 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
 			errorHandler := android.FixtureExpectsAtLeastOneErrorMatchingPattern(testCase.error)
 			android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules).
 				ExtendWithErrorHandler(errorHandler).
@@ -153,6 +158,7 @@
 }
 
 func TestJavaLintStrictUpdatabilityLinting(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "foo",
@@ -187,7 +193,7 @@
 	result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules, fs.AddToFixture()).
 		RunTestWithBp(t, bp)
 
-	foo := result.ModuleForTests("foo", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
 	strictUpdatabilityCheck := foo.Output("lint_strict_updatability_check.stamp")
 	if !strings.Contains(strictUpdatabilityCheck.RuleParams.Command,
 		"--disallowed_issues NewApi") {
@@ -250,7 +256,7 @@
 		})).
 			RunTestWithBp(t, thisBp)
 
-		foo := result.ModuleForTests("foo", "android_common")
+		foo := result.ModuleForTests(t, "foo", "android_common")
 		sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, foo.Output("lint.sbox.textproto"))
 		if !strings.Contains(*sboxProto.Commands[0].Command, "/"+testCase.expected_file) {
 			t.Error("did not use full api database for case", testCase)
@@ -276,3 +282,50 @@
 		ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern("Don't use --disable, --enable, or --check in the flags field, instead use the dedicated disabled_checks, warning_checks, error_checks, or fatal_checks fields")).
 		RunTestWithBp(t, bp)
 }
+
+// b/358643466
+func TestNotTestViaDefault(t *testing.T) {
+	bp := `
+		java_defaults {
+			name: "mydefaults",
+			lint: {
+				test: false,
+			},
+		}
+		android_test {
+			name: "foo",
+			srcs: [
+				"a.java",
+			],
+			min_sdk_version: "29",
+			sdk_version: "current",
+			defaults: ["mydefaults"],
+		}
+		android_test {
+			name: "foo2",
+			srcs: [
+				"a.java",
+			],
+			min_sdk_version: "29",
+			sdk_version: "current",
+		}
+	`
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, bp)
+	ctx := result.TestContext
+
+	foo := ctx.ModuleForTests(t, "foo", "android_common")
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, foo.Output("lint.sbox.textproto"))
+	command := *sboxProto.Commands[0].Command
+
+	if strings.Contains(command, "--test") {
+		t.Fatalf("Expected command to not contain --test")
+	}
+
+	foo2 := ctx.ModuleForTests(t, "foo2", "android_common")
+	sboxProto2 := android.RuleBuilderSboxProtoForTests(t, ctx, foo2.Output("lint.sbox.textproto"))
+	command2 := *sboxProto2.Commands[0].Command
+
+	if !strings.Contains(command2, "--test") {
+		t.Fatalf("Expected command to contain --test")
+	}
+}
diff --git a/java/metalava/main-config.xml b/java/metalava/main-config.xml
index c61196f..c2881ac 100644
--- a/java/metalava/main-config.xml
+++ b/java/metalava/main-config.xml
@@ -16,4 +16,20 @@
 
 <config xmlns="http://www.google.com/tools/metalava/config"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://www.google.com/tools/metalava/config ../../../../tools/metalava/metalava/src/main/resources/schemas/config.xsd"/>
+    xsi:schemaLocation="http://www.google.com/tools/metalava/config ../../../../tools/metalava/metalava/src/main/resources/schemas/config.xsd">
+  <api-surfaces>
+    <!-- These are hard coded into java_sdk_library. -->
+    <api-surface name="public"/>
+    <api-surface name="system" extends="public"/>
+    <api-surface name="module-lib" extends="system"/>
+    <api-surface name="test" extends="system"/>
+    <api-surface name="system-server" extends="public"/>
+    <!-- This is used in java/core-libraries/Android.bp. -->
+    <api-surface name="core"/>
+    <!-- These are used in libcore, external/conscrypt and external/icu. -->
+    <api-surface name="core-platform" extends="public"/>
+    <api-surface name="core-platform-legacy" extends="public"/>
+    <api-surface name="core-platform-plus-public"/>
+    <api-surface name="intra-core" extends="public"/>
+  </api-surfaces>
+</config>
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index d09a02e..155afc6 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -15,6 +15,11 @@
 package java
 
 import (
+	"maps"
+	"slices"
+
+	"github.com/google/blueprint"
+
 	"android/soong/android"
 	"android/soong/dexpreopt"
 )
@@ -29,12 +34,15 @@
 
 // The tags used for the dependencies between the platform bootclasspath and any configured boot
 // jars.
-var (
-	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 platformBootclasspathImplLibDepTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+func (p platformBootclasspathImplLibDepTagType) ExcludeFromVisibilityEnforcement() {}
+
+var platformBootclasspathImplLibDepTag platformBootclasspathImplLibDepTagType
+
+var _ android.ExcludeFromVisibilityEnforcementTag = platformBootclasspathImplLibDepTag
 
 type platformBootclasspathModule struct {
 	android.ModuleBase
@@ -48,6 +56,12 @@
 	// The apex:module pairs obtained from the fragments.
 	fragments []android.Module
 
+	// The map of apex to the fragments they contain.
+	apexNameToFragment map[string]android.Module
+
+	// The map of library modules in the bootclasspath to the fragments that contain them.
+	libraryToApex map[android.Module]string
+
 	// Path to the monolithic hiddenapi-flags.csv file.
 	hiddenAPIFlagsCSV android.OutputPath
 
@@ -89,26 +103,12 @@
 
 	b.hiddenAPIDepsMutator(ctx)
 
-	if !dexpreopt.IsDex2oatNeeded(ctx) {
-		return
+	if dexpreopt.IsDex2oatNeeded(ctx) {
+		// Add a dependency onto the dex2oat tool which is needed for creating the boot image. The
+		// path is retrieved from the dependency by GetGlobalSoongConfig(ctx).
+		dexpreopt.RegisterToolDeps(ctx)
 	}
 
-	// Add a dependency onto the dex2oat tool which is needed for creating the boot image. The
-	// path is retrieved from the dependency by GetGlobalSoongConfig(ctx).
-	dexpreopt.RegisterToolDeps(ctx)
-}
-
-func (b *platformBootclasspathModule) hiddenAPIDepsMutator(ctx android.BottomUpMutatorContext) {
-	if ctx.Config().DisableHiddenApiChecks() {
-		return
-	}
-
-	// Add dependencies onto the stub lib modules.
-	apiLevelToStubLibModules := hiddenAPIComputeMonolithicStubLibModules(ctx.Config())
-	hiddenAPIAddStubLibDependencies(ctx, apiLevelToStubLibModules)
-}
-
-func (b *platformBootclasspathModule) BootclasspathDepsMutator(ctx android.BottomUpMutatorContext) {
 	// Add dependencies on all the ART jars.
 	global := dexpreopt.GetGlobalConfig(ctx)
 	addDependenciesOntoSelectedBootImageApexes(ctx, "com.android.art")
@@ -116,13 +116,13 @@
 	var bootImageModuleNames []string
 
 	// TODO: b/308174306 - Remove the mechanism of depending on the java_sdk_library(_import) directly
-	addDependenciesOntoBootImageModules(ctx, global.ArtApexJars, platformBootclasspathArtBootJarDepTag)
+	addDependenciesOntoBootImageModules(ctx, global.ArtApexJars, artBootJar)
 	bootImageModuleNames = append(bootImageModuleNames, global.ArtApexJars.CopyOfJars()...)
 
 	// Add dependencies on all the non-updatable jars, which are on the platform or in non-updatable
 	// APEXes.
 	platformJars := b.platformJars(ctx)
-	addDependenciesOntoBootImageModules(ctx, platformJars, platformBootclasspathBootJarDepTag)
+	addDependenciesOntoBootImageModules(ctx, platformJars, platformBootJar)
 	bootImageModuleNames = append(bootImageModuleNames, platformJars.CopyOfJars()...)
 
 	// Add dependencies on all the updatable jars, except the ART jars.
@@ -133,7 +133,7 @@
 	}
 	addDependenciesOntoSelectedBootImageApexes(ctx, android.FirstUniqueStrings(apexes)...)
 	// TODO: b/308174306 - Remove the mechanism of depending on the java_sdk_library(_import) directly
-	addDependenciesOntoBootImageModules(ctx, apexJars, platformBootclasspathApexBootJarDepTag)
+	addDependenciesOntoBootImageModules(ctx, apexJars, apexBootJar)
 	bootImageModuleNames = append(bootImageModuleNames, apexJars.CopyOfJars()...)
 
 	// Add dependencies on all the fragments.
@@ -147,32 +147,42 @@
 	}
 }
 
-func addDependenciesOntoBootImageModules(ctx android.BottomUpMutatorContext, modules android.ConfiguredJarList, tag bootclasspathDependencyTag) {
+func (b *platformBootclasspathModule) hiddenAPIDepsMutator(ctx android.BottomUpMutatorContext) {
+	if ctx.Config().DisableHiddenApiChecks() {
+		return
+	}
+
+	// Add dependencies onto the stub lib modules.
+	apiLevelToStubLibModules := hiddenAPIComputeMonolithicStubLibModules(ctx.Config())
+	hiddenAPIAddStubLibDependencies(ctx, apiLevelToStubLibModules)
+}
+
+func addDependenciesOntoBootImageModules(ctx android.BottomUpMutatorContext, modules android.ConfiguredJarList, tagType bootclasspathDependencyTagType) {
 	for i := 0; i < modules.Len(); i++ {
 		apex := modules.Apex(i)
 		name := modules.Jar(i)
 
-		addDependencyOntoApexModulePair(ctx, apex, name, tag)
+		addDependencyOntoApexModulePair(ctx, apex, name, tagType)
 	}
 }
 
 func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// Gather all the dependencies from the art, platform, and apex boot jars.
-	artModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathArtBootJarDepTag)
-	platformModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathBootJarDepTag)
-	apexModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathApexBootJarDepTag)
+	artModules, artModulesToApex := gatherApexModulePairDepsWithTag(ctx, artBootJar)
+	platformModules, platformModulesToApex := gatherApexModulePairDepsWithTag(ctx, platformBootJar)
+	apexModules, apexModulesToApex := gatherApexModulePairDepsWithTag(ctx, apexBootJar)
 
 	// Concatenate them all, in order as they would appear on the bootclasspath.
-	var allModules []android.Module
-	allModules = append(allModules, artModules...)
-	allModules = append(allModules, platformModules...)
-	allModules = append(allModules, apexModules...)
+	allModules := slices.Concat(artModules, platformModules, apexModules)
 	b.configuredModules = allModules
+	b.libraryToApex = maps.Clone(artModulesToApex)
+	maps.Copy(b.libraryToApex, platformModulesToApex)
+	maps.Copy(b.libraryToApex, apexModulesToApex)
 
 	// 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) {
+	ctx.VisitDirectDepsWithTag(platformBootclasspathImplLibDepTag, func(m android.Module) {
 		implLibModule = append(implLibModule, m)
 	})
 
@@ -188,7 +198,7 @@
 	TransformResourcesToJar(ctx, srcjar, jarArgs, transitiveSrcFiles)
 
 	// Gather all the fragments dependencies.
-	b.fragments = gatherApexModulePairDepsWithTag(ctx, bootclasspathFragmentDepTag)
+	b.fragments, b.apexNameToFragment = gatherFragments(ctx)
 
 	// Check the configuration of the boot modules.
 	// ART modules are checked by the art-bootclasspath-fragment.
@@ -197,7 +207,7 @@
 
 	b.generateClasspathProtoBuildActions(ctx)
 
-	bootDexJarByModule := b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
+	bootDexJarByModule := b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments, b.libraryToApex, b.apexNameToFragment)
 	buildRuleForBootJarsPackageCheck(ctx, bootDexJarByModule)
 
 	ctx.SetOutputFiles(android.Paths{b.hiddenAPIFlagsCSV}, "hiddenapi-flags.csv")
@@ -248,7 +258,7 @@
 		fromUpdatableApex := apexInfo.Updatable
 		if fromUpdatableApex {
 			// error: this jar is part of an updatable apex
-			ctx.ModuleErrorf("module %q from updatable apexes %q is not allowed in the platform bootclasspath", ctx.OtherModuleName(m), apexInfo.InApexVariants)
+			ctx.ModuleErrorf("module %q from updatable apex %q is not allowed in the platform bootclasspath", ctx.OtherModuleName(m), apexInfo.BaseApexName)
 		} else {
 			// ok: this jar is part of the platform or a non-updatable apex
 		}
@@ -272,7 +282,11 @@
 				//  modules is complete.
 				if !ctx.Config().AlwaysUsePrebuiltSdks() {
 					// error: this jar is part of the platform
-					ctx.ModuleErrorf("module %q from platform is not allowed in the apex boot jars list", name)
+					if ctx.Config().AllowMissingDependencies() {
+						ctx.AddMissingDependencies([]string{"module_" + name + "_from_platform_is_not_allowed_in_the_apex_boot_jars_list"})
+					} else {
+						ctx.ModuleErrorf("module %q from platform is not allowed in the apex boot jars list", name)
+					}
 				}
 			} else {
 				// TODO(b/177892522): Treat this as an error.
@@ -284,7 +298,8 @@
 }
 
 // generateHiddenAPIBuildActions generates all the hidden API related build rules.
-func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) bootDexJarByModule {
+func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module,
+	fragments []android.Module, libraryToApex map[android.Module]string, apexNameToFragment map[string]android.Module) bootDexJarByModule {
 	createEmptyHiddenApiFiles := func() {
 		paths := android.OutputPaths{b.hiddenAPIFlagsCSV, b.hiddenAPIIndexCSV, b.hiddenAPIMetadataCSV}
 		for _, path := range paths {
@@ -311,7 +326,7 @@
 	}
 
 	// Construct a list of ClasspathElement objects from the modules and fragments.
-	classpathElements := CreateClasspathElements(ctx, modules, fragments)
+	classpathElements := CreateClasspathElements(ctx, modules, fragments, libraryToApex, apexNameToFragment)
 
 	monolithicInfo := b.createAndProvideMonolithicHiddenAPIInfo(ctx, classpathElements)
 
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index 7fa6ddb..727e306 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -27,21 +27,27 @@
 )
 
 func TestPlatformBootclasspath(t *testing.T) {
+	t.Parallel()
 	preparer := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		FixtureConfigureBootJars("platform:foo", "system_ext:bar"),
+		android.FixtureMergeMockFs(android.MockFS{
+			"api/current.txt": nil,
+			"api/removed.txt": nil,
+		}),
 		android.FixtureWithRootAndroidBp(`
 			platform_bootclasspath {
 				name: "platform-bootclasspath",
 			}
 
-			java_library {
+			java_sdk_library {
 				name: "bar",
 				srcs: ["a.java"],
 				system_modules: "none",
 				sdk_version: "none",
 				compile_dex: true,
 				system_ext_specific: true,
+				unsafe_ignore_missing_latest_api: true,
 			}
 		`),
 	)
@@ -76,6 +82,7 @@
 	`)
 
 	t.Run("missing", func(t *testing.T) {
+		t.Parallel()
 		preparer.
 			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`"platform-bootclasspath" depends on undefined module "foo"`)).
 			RunTest(t)
@@ -86,11 +93,12 @@
 
 	checkSrcJarInputs := func(t *testing.T, result *android.TestResult, name string, expected []string) {
 		t.Helper()
-		srcjar := result.ModuleForTests(name, "android_common").Output(name + "-transitive.srcjar")
+		srcjar := result.ModuleForTests(t, name, "android_common").Output(name + "-transitive.srcjar")
 		android.AssertStringDoesContain(t, "srcjar arg", srcjar.Args["jarArgs"], "-srcjar")
 		android.AssertArrayString(t, "srcjar inputs", expected, srcjar.Implicits.Strings())
 	}
 	t.Run("source", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			preparer,
 			addSourceBootclassPathModule,
@@ -107,6 +115,7 @@
 	})
 
 	t.Run("prebuilt", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			preparer,
 			addPrebuiltBootclassPathModule,
@@ -123,6 +132,7 @@
 	})
 
 	t.Run("source+prebuilt - source preferred", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			preparer,
 			addSourceBootclassPathModule,
@@ -140,6 +150,7 @@
 	})
 
 	t.Run("source+prebuilt - prebuilt preferred", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			preparer,
 			addSourceBootclassPathModule,
@@ -157,6 +168,7 @@
 	})
 
 	t.Run("dex import", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			preparer,
 			android.FixtureAddTextFile("deximport/Android.bp", `
@@ -179,6 +191,7 @@
 }
 
 func TestPlatformBootclasspathVariant(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		android.FixtureWithRootAndroidBp(`
@@ -193,6 +206,7 @@
 }
 
 func TestPlatformBootclasspath_ClasspathFragmentPaths(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		android.FixtureWithRootAndroidBp(`
@@ -204,10 +218,11 @@
 
 	p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
 	android.AssertStringEquals(t, "output filepath", "bootclasspath.pb", p.ClasspathFragmentBase.outputFilepath.Base())
-	android.AssertPathRelativeToTopEquals(t, "install filepath", "out/soong/target/product/test_device/system/etc/classpaths", p.ClasspathFragmentBase.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install filepath", "out/target/product/test_device/system/etc/classpaths", p.ClasspathFragmentBase.installDirPath)
 }
 
 func TestPlatformBootclasspathModule_AndroidMkEntries(t *testing.T) {
+	t.Parallel()
 	preparer := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		android.FixtureWithRootAndroidBp(`
@@ -218,6 +233,7 @@
 	)
 
 	t.Run("AndroidMkEntries", func(t *testing.T) {
+		t.Parallel()
 		result := preparer.RunTest(t)
 
 		p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
@@ -227,6 +243,7 @@
 	})
 
 	t.Run("hiddenapi-flags-entry", func(t *testing.T) {
+		t.Parallel()
 		result := preparer.RunTest(t)
 
 		p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
@@ -238,6 +255,7 @@
 	})
 
 	t.Run("classpath-fragment-entry", func(t *testing.T) {
+		t.Parallel()
 		result := preparer.RunTest(t)
 
 		want := map[string][]string{
@@ -262,6 +280,7 @@
 }
 
 func TestPlatformBootclasspath_Dist(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		FixtureConfigureBootJars("platform:foo", "platform:bar"),
@@ -305,6 +324,7 @@
 }
 
 func TestPlatformBootclasspath_HiddenAPIMonolithicFiles(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -347,7 +367,7 @@
 
 	// Make sure that the foo-hiddenapi-annotations.jar is included in the inputs to the rules that
 	// creates the index.csv file.
-	platformBootclasspath := result.ModuleForTests("myplatform-bootclasspath", "android_common")
+	platformBootclasspath := result.ModuleForTests(t, "myplatform-bootclasspath", "android_common")
 
 	var rule android.TestingBuildParams
 
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 5b145c6..d2ec8bd 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -43,6 +43,13 @@
 	ctx.RegisterModuleType("global_compat_config", globalCompatConfigFactory)
 }
 
+type PlatformCompatConfigInfo struct {
+	CompatConfig android.OutputPath
+	SubDir       string
+}
+
+var PlatformCompatConfigInfoProvider = blueprint.NewProvider[PlatformCompatConfigInfo]()
+
 var PrepareForTestWithPlatformCompatConfig = android.FixtureRegisterWithContext(registerPlatformCompatConfigBuildComponents)
 
 func platformCompatConfigPath(ctx android.PathContext) android.OutputPath {
@@ -51,6 +58,10 @@
 
 type platformCompatConfigProperties struct {
 	Src *string `android:"path"`
+
+	// If true, we include it in the "merged" XML (merged_compat_config.xml).
+	// Default is true.
+	Include_in_merged_xml *bool
 }
 
 type platformCompatConfig struct {
@@ -60,6 +71,7 @@
 	installDirPath android.InstallPath
 	configFile     android.OutputPath
 	metadataFile   android.OutputPath
+	doMerge        bool
 
 	installConfigFile android.InstallPath
 }
@@ -68,6 +80,10 @@
 	return p.metadataFile
 }
 
+func (p *platformCompatConfig) includeInMergedXml() bool {
+	return p.doMerge
+}
+
 func (p *platformCompatConfig) CompatConfig() android.OutputPath {
 	return p.configFile
 }
@@ -78,8 +94,19 @@
 
 type platformCompatConfigMetadataProvider interface {
 	compatConfigMetadata() android.Path
+
+	// Whether to include it in the "merged" XML (merged_compat_config.xml) or not.
+	includeInMergedXml() bool
 }
 
+type PlatformCompatConfigMetadataInfo struct {
+	CompatConfigMetadata android.Path
+	// Whether to include it in the "merged" XML (merged_compat_config.xml) or not.
+	IncludeInMergedXml bool
+}
+
+var PlatformCompatConfigMetadataInfoProvider = blueprint.NewProvider[PlatformCompatConfigMetadataInfo]()
+
 type PlatformCompatConfigIntf interface {
 	android.Module
 
@@ -98,6 +125,7 @@
 	metadataFileName := p.Name() + "_meta.xml"
 	p.configFile = android.PathForModuleOut(ctx, configFileName).OutputPath
 	p.metadataFile = android.PathForModuleOut(ctx, metadataFileName).OutputPath
+	p.doMerge = proptools.BoolDefault(p.properties.Include_in_merged_xml, true)
 	path := android.PathForModuleSrc(ctx, String(p.properties.Src))
 
 	rule.Command().
@@ -111,6 +139,16 @@
 	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}, "")
+
+	android.SetProvider(ctx, PlatformCompatConfigInfoProvider, PlatformCompatConfigInfo{
+		CompatConfig: p.CompatConfig(),
+		SubDir:       p.SubDir(),
+	})
+
+	android.SetProvider(ctx, PlatformCompatConfigMetadataInfoProvider, PlatformCompatConfigMetadataInfo{
+		CompatConfigMetadata: p.compatConfigMetadata(),
+		IncludeInMergedXml:   p.includeInMergedXml(),
+	})
 }
 
 func (p *platformCompatConfig) AndroidMkEntries() []android.AndroidMkEntries {
@@ -201,6 +239,10 @@
 	return module.metadataFile
 }
 
+func (module *prebuiltCompatConfigModule) includeInMergedXml() bool {
+	return true // Always include in merged.xml
+}
+
 func (module *prebuiltCompatConfigModule) BaseModuleName() string {
 	return proptools.StringDefault(module.properties.Source_module_name, module.ModuleBase.Name())
 }
@@ -209,6 +251,11 @@
 
 func (module *prebuiltCompatConfigModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	module.metadataFile = module.prebuilt.SingleSourcePath(ctx)
+
+	android.SetProvider(ctx, PlatformCompatConfigMetadataInfoProvider, PlatformCompatConfigMetadataInfo{
+		CompatConfigMetadata: module.compatConfigMetadata(),
+		IncludeInMergedXml:   module.includeInMergedXml(),
+	})
 }
 
 // A prebuilt version of platform_compat_config that provides the metadata.
@@ -229,15 +276,18 @@
 
 	var compatConfigMetadata android.Paths
 
-	ctx.VisitAllModules(func(module android.Module) {
-		if !module.Enabled(ctx) {
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+		if !android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider).Enabled {
 			return
 		}
-		if c, ok := module.(platformCompatConfigMetadataProvider); ok {
-			if !android.IsModulePreferred(module) {
+		if c, ok := android.OtherModuleProvider(ctx, module, PlatformCompatConfigMetadataInfoProvider); ok {
+			if !android.IsModulePreferredProxy(ctx, module) {
 				return
 			}
-			metadata := c.compatConfigMetadata()
+			if !c.IncludeInMergedXml {
+				return
+			}
+			metadata := c.CompatConfigMetadata
 			compatConfigMetadata = append(compatConfigMetadata, metadata)
 		}
 	})
@@ -258,12 +308,7 @@
 	rule.Build("merged-compat-config", "Merge compat config")
 
 	p.metadata = outputPath
-}
-
-func (p *platformCompatConfigSingleton) MakeVars(ctx android.MakeVarsContext) {
-	if p.metadata != nil {
-		ctx.DistForGoal("droidcore", p.metadata)
-	}
+	ctx.DistForGoal("droidcore", p.metadata)
 }
 
 func platformCompatConfigSingletonFactory() android.Singleton {
diff --git a/java/platform_compat_config_test.go b/java/platform_compat_config_test.go
index 80d991c..72f81e0 100644
--- a/java/platform_compat_config_test.go
+++ b/java/platform_compat_config_test.go
@@ -21,11 +21,13 @@
 )
 
 func TestPlatformCompatConfig(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithPlatformCompatConfig,
 		android.FixtureWithRootAndroidBp(`
 			platform_compat_config {
 				name: "myconfig2",
+				include_in_merged_xml: false,
 			}
 			platform_compat_config {
 				name: "myconfig1",
@@ -38,7 +40,6 @@
 
 	CheckMergedCompatConfigInputs(t, result, "myconfig",
 		"out/soong/.intermediates/myconfig1/myconfig1_meta.xml",
-		"out/soong/.intermediates/myconfig2/myconfig2_meta.xml",
 		"out/soong/.intermediates/myconfig3/myconfig3_meta.xml",
 	)
 }
diff --git a/java/plugin.go b/java/plugin.go
index 610c9fd..3534c7b 100644
--- a/java/plugin.go
+++ b/java/plugin.go
@@ -16,8 +16,21 @@
 
 import (
 	"android/soong/android"
+	"github.com/google/blueprint"
 )
 
+type JavaPluginInfo struct {
+	ProcessorClass *string
+	GeneratesApi   bool
+}
+
+var JavaPluginInfoProvider = blueprint.NewProvider[JavaPluginInfo]()
+
+type KotlinPluginInfo struct {
+}
+
+var KotlinPluginInfoProvider = blueprint.NewProvider[KotlinPluginInfo]()
+
 func init() {
 	registerJavaPluginBuildComponents(android.InitRegistrationContext)
 }
@@ -65,7 +78,22 @@
 	Generates_api *bool
 }
 
+func (p *Plugin) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	p.Library.GenerateAndroidBuildActions(ctx)
+
+	android.SetProvider(ctx, JavaPluginInfoProvider, JavaPluginInfo{
+		ProcessorClass: p.pluginProperties.Processor_class,
+		GeneratesApi:   Bool(p.pluginProperties.Generates_api),
+	})
+}
+
 // Plugin describes a kotlin_plugin module, a host java/kotlin library that will be used by kotlinc as a compiler plugin.
 type KotlinPlugin struct {
 	Library
 }
+
+func (p *KotlinPlugin) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	p.Library.GenerateAndroidBuildActions(ctx)
+
+	android.SetProvider(ctx, KotlinPluginInfoProvider, KotlinPluginInfo{})
+}
diff --git a/java/plugin_test.go b/java/plugin_test.go
index dc29b1c..007b74a 100644
--- a/java/plugin_test.go
+++ b/java/plugin_test.go
@@ -19,6 +19,7 @@
 )
 
 func TestNoPlugin(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -26,8 +27,8 @@
 		}
 	`)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
-	turbine := ctx.ModuleForTests("foo", "android_common").MaybeRule("turbine")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Rule("javac")
+	turbine := ctx.ModuleForTests(t, "foo", "android_common").MaybeRule("turbine")
 
 	if turbine.Rule == nil {
 		t.Errorf("expected turbine to be enabled")
@@ -43,6 +44,7 @@
 }
 
 func TestPlugin(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -59,14 +61,14 @@
 
 	buildOS := ctx.Config().BuildOS.String()
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
-	turbine := ctx.ModuleForTests("foo", "android_common").MaybeRule("turbine")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Rule("javac")
+	turbine := ctx.ModuleForTests(t, "foo", "android_common").MaybeRule("turbine")
 
 	if turbine.Rule == nil {
 		t.Errorf("expected turbine to be enabled")
 	}
 
-	bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String()
+	bar := ctx.ModuleForTests(t, "bar", buildOS+"_common").Rule("javac").Output.String()
 
 	if !inList(bar, javac.Implicits.Strings()) {
 		t.Errorf("foo implicits %v does not contain %q", javac.Implicits.Strings(), bar)
@@ -82,6 +84,7 @@
 }
 
 func TestPluginGeneratesApi(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -99,14 +102,14 @@
 
 	buildOS := ctx.Config().BuildOS.String()
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
-	turbine := ctx.ModuleForTests("foo", "android_common").MaybeRule("turbine")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Rule("javac")
+	turbine := ctx.ModuleForTests(t, "foo", "android_common").MaybeRule("turbine")
 
 	if turbine.Rule != nil {
 		t.Errorf("expected turbine to be disabled")
 	}
 
-	bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String()
+	bar := ctx.ModuleForTests(t, "bar", buildOS+"_common").Rule("javac").Output.String()
 
 	if !inList(bar, javac.Implicits.Strings()) {
 		t.Errorf("foo implicits %v does not contain %q", javac.Implicits.Strings(), bar)
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 527e479..31f149e 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -55,11 +55,6 @@
 
 	// If set to true, compile dex for java_import modules. Defaults to false.
 	Imports_compile_dex *bool
-
-	// If set to true, allow incremental platform API of the form MM.m where MM is the major release
-	// version corresponding to the API level/SDK_INT and m is an incremental release version
-	// (e.g. API changes associated with QPR). Defaults to false.
-	Allow_incremental_platform_api *bool
 }
 
 type prebuiltApis struct {
@@ -97,28 +92,28 @@
 }
 
 // parseFinalizedPrebuiltPath is like parsePrebuiltPath, but verifies the version is numeric (a finalized version).
-func parseFinalizedPrebuiltPath(ctx android.LoadHookContext, p string, allowIncremental bool) (module string, version int, release int, scope string) {
+func parseFinalizedPrebuiltPath(ctx android.LoadHookContext, p string) (module string, version int, release int, scope string) {
 	module, v, scope := parsePrebuiltPath(ctx, p)
-	if allowIncremental {
-		parts := strings.Split(v, ".")
-		if len(parts) != 2 {
-			ctx.ModuleErrorf("Found unexpected version '%v' for incremental prebuilts - expect MM.m format for incremental API with both major (MM) an minor (m) revision.", v)
-			return
-		}
+
+	// assume a major.minor version code
+	parts := strings.Split(v, ".")
+	if len(parts) == 2 {
 		sdk, sdk_err := strconv.Atoi(parts[0])
 		qpr, qpr_err := strconv.Atoi(parts[1])
 		if sdk_err != nil || qpr_err != nil {
-			ctx.ModuleErrorf("Unable to read version number for incremental prebuilt api '%v'", v)
+			ctx.ModuleErrorf("Unable to read major.minor version for prebuilt api '%v'", v)
 			return
 		}
 		version = sdk
 		release = qpr
 		return
 	}
+
+	// assume a legacy integer only api level
 	release = 0
 	version, err := strconv.Atoi(v)
 	if err != nil {
-		ctx.ModuleErrorf("Found finalized API files in non-numeric dir '%v'", v)
+		ctx.ModuleErrorf("Unable to read API level for prebuilt api '%v'", v)
 		return
 	}
 	return
@@ -279,12 +274,11 @@
 	}
 
 	// Create modules for all (<module>, <scope, <version>) triplets,
-	allowIncremental := proptools.BoolDefault(p.properties.Allow_incremental_platform_api, false)
 	for _, f := range apiLevelFiles {
-		module, version, release, scope := parseFinalizedPrebuiltPath(mctx, f, allowIncremental)
-		if allowIncremental {
-			incrementalVersion := strconv.Itoa(version) + "." + strconv.Itoa(release)
-			createApiModule(mctx, PrebuiltApiModuleName(module, scope, incrementalVersion), f)
+		module, version, release, scope := parseFinalizedPrebuiltPath(mctx, f)
+		if release != 0 {
+			majorDotMinorVersion := strconv.Itoa(version) + "." + strconv.Itoa(release)
+			createApiModule(mctx, PrebuiltApiModuleName(module, scope, majorDotMinorVersion), f)
 		} else {
 			createApiModule(mctx, PrebuiltApiModuleName(module, scope, strconv.Itoa(version)), f)
 		}
@@ -300,7 +294,7 @@
 	getLatest := func(files []string, isExtensionApiFile bool) map[string]latestApiInfo {
 		m := make(map[string]latestApiInfo)
 		for _, f := range files {
-			module, version, release, scope := parseFinalizedPrebuiltPath(mctx, f, allowIncremental)
+			module, version, release, scope := parseFinalizedPrebuiltPath(mctx, f)
 			if strings.HasSuffix(module, "incompatibilities") {
 				continue
 			}
diff --git a/java/prebuilt_apis_test.go b/java/prebuilt_apis_test.go
index b6fb2c6..17fdae9 100644
--- a/java/prebuilt_apis_test.go
+++ b/java/prebuilt_apis_test.go
@@ -29,6 +29,7 @@
 }
 
 func TestPrebuiltApis_SystemModulesCreation(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		FixtureWithPrebuiltApis(map[string][]string{
@@ -61,6 +62,7 @@
 }
 
 func TestPrebuiltApis_WithExtensions(t *testing.T) {
+	t.Parallel()
 	runTestWithBaseExtensionLevel := func(v int) (foo_input, bar_input, baz_input string) {
 		result := android.GroupFixturePreparers(
 			prepareForJavaTest,
@@ -76,9 +78,9 @@
 				"2": {"foo", "bar"},
 			}),
 		).RunTest(t)
-		foo_input = result.ModuleForTests("foo.api.public.latest", "").Rule("generator").Implicits[0].String()
-		bar_input = result.ModuleForTests("bar.api.public.latest", "").Rule("generator").Implicits[0].String()
-		baz_input = result.ModuleForTests("baz.api.public.latest", "").Rule("generator").Implicits[0].String()
+		foo_input = result.ModuleForTests(t, "foo.api.public.latest", "").Rule("generator").Implicits[0].String()
+		bar_input = result.ModuleForTests(t, "bar.api.public.latest", "").Rule("generator").Implicits[0].String()
+		baz_input = result.ModuleForTests(t, "baz.api.public.latest", "").Rule("generator").Implicits[0].String()
 		return
 	}
 	// Extension 2 is the latest for both foo and bar, finalized after the base extension version.
@@ -100,25 +102,26 @@
 	android.AssertStringEquals(t, "Expected latest baz = api level 32", "prebuilts/sdk/32/public/api/baz.txt", baz_input)
 }
 
-func TestPrebuiltApis_WithIncrementalApi(t *testing.T) {
+func TestPrebuiltApis_WithMixedVersionCodes(t *testing.T) {
+	t.Parallel()
 	runTestWithIncrementalApi := func() (foo_input, bar_input, baz_input string) {
 		result := android.GroupFixturePreparers(
 			prepareForJavaTest,
-			FixtureWithPrebuiltIncrementalApis(map[string][]string{
+			FixtureWithPrebuiltApis(map[string][]string{
 				"33.0":    {"foo"},
-				"33.1":    {"foo", "bar", "baz"},
-				"33.2":    {"foo", "bar"},
+				"34":      {"foo", "bar", "baz"},
+				"34.1":    {"foo", "bar"},
 				"current": {"foo", "bar"},
 			}),
 		).RunTest(t)
-		foo_input = result.ModuleForTests("foo.api.public.latest", "").Rule("generator").Implicits[0].String()
-		bar_input = result.ModuleForTests("bar.api.public.latest", "").Rule("generator").Implicits[0].String()
-		baz_input = result.ModuleForTests("baz.api.public.latest", "").Rule("generator").Implicits[0].String()
+		foo_input = result.ModuleForTests(t, "foo.api.public.latest", "").Rule("generator").Implicits[0].String()
+		bar_input = result.ModuleForTests(t, "bar.api.public.latest", "").Rule("generator").Implicits[0].String()
+		baz_input = result.ModuleForTests(t, "baz.api.public.latest", "").Rule("generator").Implicits[0].String()
 		return
 	}
-	// 33.1 is the latest for baz, 33.2 is the latest for both foo & bar
+	// 34 is the latest for baz, 34.1 is the latest for both foo & bar
 	foo_input, bar_input, baz_input := runTestWithIncrementalApi()
-	android.AssertStringEquals(t, "Expected latest foo = api level 33.2", "prebuilts/sdk/33.2/public/api/foo.txt", foo_input)
-	android.AssertStringEquals(t, "Expected latest bar = api level 33.2", "prebuilts/sdk/33.2/public/api/bar.txt", bar_input)
-	android.AssertStringEquals(t, "Expected latest baz = api level 33.1", "prebuilts/sdk/33.1/public/api/baz.txt", baz_input)
+	android.AssertStringEquals(t, "Expected latest foo = api level 34.1", "prebuilts/sdk/34.1/public/api/foo.txt", foo_input)
+	android.AssertStringEquals(t, "Expected latest bar = api level 34.1", "prebuilts/sdk/34.1/public/api/bar.txt", bar_input)
+	android.AssertStringEquals(t, "Expected latest baz = api level 34", "prebuilts/sdk/34/public/api/baz.txt", baz_input)
 }
diff --git a/java/proto_test.go b/java/proto_test.go
index d1cb714..3fbe3e6 100644
--- a/java/proto_test.go
+++ b/java/proto_test.go
@@ -28,6 +28,7 @@
 `
 
 func TestProtoStream(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "java-stream-protos",
@@ -45,7 +46,7 @@
 		PrepareForIntegrationTestWithJava,
 	).RunTestWithBp(t, protoModules+bp)
 
-	proto0 := ctx.ModuleForTests("java-stream-protos", "android_common").Output("proto/proto0.srcjar")
+	proto0 := ctx.ModuleForTests(t, "java-stream-protos", "android_common").Output("proto/proto0.srcjar")
 
 	if cmd := proto0.RuleParams.Command; !strings.Contains(cmd, "--javastream_out=") {
 		t.Errorf("expected '--javastream_out' in %q", cmd)
diff --git a/java/ravenwood.go b/java/ravenwood.go
index 4c43a9f..a942dc6 100644
--- a/java/ravenwood.go
+++ b/java/ravenwood.go
@@ -110,13 +110,15 @@
 	module.AddProperties(&module.testProperties, &module.ravenwoodTestProperties)
 
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 
 	module.testProperties.Test_suites = []string{
 		"general-tests",
 		"ravenwood-tests",
 	}
 	module.testProperties.Test_options.Unit_test = proptools.BoolPtr(false)
+	module.Module.sourceProperties.Test_only = proptools.BoolPtr(true)
+	module.Module.sourceProperties.Top_level_test_target = true
 
 	InitJavaModule(module, android.DeviceSupported)
 	android.InitDefaultableModule(module)
@@ -185,26 +187,24 @@
 	// All JNI libraries included in the runtime
 	var runtimeJniModuleNames map[string]bool
 
-	if utils := ctx.GetDirectDepsWithTag(ravenwoodUtilsTag)[0]; utils != nil {
-		for _, installFile := range android.OtherModuleProviderOrDefault(
-			ctx, utils, android.InstallFilesProvider).InstallFiles {
-			installDeps = append(installDeps, installFile)
-		}
-		jniDeps, ok := android.OtherModuleProvider(ctx, utils, ravenwoodLibgroupJniDepProvider)
-		if ok {
-			runtimeJniModuleNames = jniDeps.names
-		}
+	utils := ctx.GetDirectDepsProxyWithTag(ravenwoodUtilsTag)[0]
+	for _, installFile := range android.OtherModuleProviderOrDefault(
+		ctx, utils, android.InstallFilesProvider).InstallFiles {
+		installDeps = append(installDeps, installFile)
+	}
+	jniDeps, ok := android.OtherModuleProvider(ctx, utils, ravenwoodLibgroupJniDepProvider)
+	if ok {
+		runtimeJniModuleNames = jniDeps.names
 	}
 
-	if runtime := ctx.GetDirectDepsWithTag(ravenwoodRuntimeTag)[0]; runtime != nil {
-		for _, installFile := range android.OtherModuleProviderOrDefault(
-			ctx, runtime, android.InstallFilesProvider).InstallFiles {
-			installDeps = append(installDeps, installFile)
-		}
-		jniDeps, ok := android.OtherModuleProvider(ctx, runtime, ravenwoodLibgroupJniDepProvider)
-		if ok {
-			runtimeJniModuleNames = jniDeps.names
-		}
+	runtime := ctx.GetDirectDepsProxyWithTag(ravenwoodRuntimeTag)[0]
+	for _, installFile := range android.OtherModuleProviderOrDefault(
+		ctx, runtime, android.InstallFilesProvider).InstallFiles {
+		installDeps = append(installDeps, installFile)
+	}
+	jniDeps, ok = android.OtherModuleProvider(ctx, runtime, ravenwoodLibgroupJniDepProvider)
+	if ok {
+		runtimeJniModuleNames = jniDeps.names
 	}
 
 	// Also remember what JNI libs are in the runtime.
@@ -228,7 +228,7 @@
 	resApkInstallPath := installPath.Join(ctx, "ravenwood-res-apks")
 
 	copyResApk := func(tag blueprint.DependencyTag, toFileName string) {
-		if resApk := ctx.GetDirectDepsWithTag(tag); len(resApk) > 0 {
+		if resApk := ctx.GetDirectDepsProxyWithTag(tag); len(resApk) > 0 {
 			installFile := android.OutputFileForModule(ctx, resApk[0], "")
 			installResApk := ctx.InstallFile(resApkInstallPath, toFileName, installFile)
 			installDeps = append(installDeps, installResApk)
@@ -260,6 +260,19 @@
 
 	// Install our JAR with all dependencies
 	ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.outputFile, installDeps...)
+
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	if _, ok := r.testConfig.(android.WritablePath); ok {
+		moduleInfoJSON.AutoTestConfig = []string{"true"}
+	}
+	if r.testConfig != nil {
+		moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, r.testConfig.String())
+	}
+	moduleInfoJSON.CompatibilitySuites = []string{"general-tests", "ravenwood-tests"}
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: r.TestSuites(),
+	})
 }
 
 func (r *ravenwoodTest) AndroidMkEntries() []android.AndroidMkEntries {
@@ -345,7 +358,7 @@
 	// Install our runtime into expected location for packaging
 	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
 	for _, lib := range r.ravenwoodLibgroupProperties.Libs {
-		libModule := ctx.GetDirectDepWithTag(lib, ravenwoodLibContentTag)
+		libModule := ctx.GetDirectDepProxyWithTag(lib, ravenwoodLibContentTag)
 		if libModule == nil {
 			if ctx.Config().AllowMissingDependencies() {
 				ctx.AddMissingDependencies([]string{lib})
@@ -377,6 +390,10 @@
 
 	// Normal build should perform install steps
 	ctx.Phony(r.BaseModuleName(), android.PathForPhony(ctx, r.BaseModuleName()+"-install"))
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: r.TestSuites(),
+	})
 }
 
 // collectTransitiveJniDeps returns all JNI dependencies, including transitive
diff --git a/java/ravenwood_test.go b/java/ravenwood_test.go
index 6394a9a..d6493bc 100644
--- a/java/ravenwood_test.go
+++ b/java/ravenwood_test.go
@@ -100,9 +100,10 @@
 	`),
 )
 
-var installPathPrefix = "out/soong/host/linux-x86/testcases"
+var installPathPrefix = "out/host/linux-x86/testcases"
 
 func TestRavenwoodRuntime(t *testing.T) {
+	t.Parallel()
 	if runtime.GOOS != "linux" {
 		t.Skip("requires linux")
 	}
@@ -120,7 +121,7 @@
 	CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-utils", "android_common", "framework-rules.ravenwood")
 
 	// Verify that we've emitted artifacts in expected location
-	runtime := ctx.ModuleForTests("ravenwood-runtime", "android_common")
+	runtime := ctx.ModuleForTests(t, "ravenwood-runtime", "android_common")
 	runtime.Output(installPathPrefix + "/ravenwood-runtime/framework-minus-apex.ravenwood.jar")
 	runtime.Output(installPathPrefix + "/ravenwood-runtime/framework-services.ravenwood.jar")
 	runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/ravenwood-runtime-jni1.so")
@@ -128,11 +129,12 @@
 	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 := ctx.ModuleForTests(t, "ravenwood-utils", "android_common")
 	utils.Output(installPathPrefix + "/ravenwood-utils/framework-rules.ravenwood.jar")
 }
 
 func TestRavenwoodTest(t *testing.T) {
+	t.Parallel()
 	if runtime.GOOS != "linux" {
 		t.Skip("requires linux")
 	}
@@ -191,7 +193,7 @@
 	CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-test", "android_common", "ravenwood-utils")
 	CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-test", "android_common", "jni-lib")
 
-	module := ctx.ModuleForTests("ravenwood-test", "android_common")
+	module := ctx.ModuleForTests(t, "ravenwood-test", "android_common")
 	classpath := module.Rule("javac").Args["classpath"]
 
 	// Verify that we're linking against test_current
@@ -212,7 +214,7 @@
 	module.Output(installPathPrefix + "/ravenwood-test/ravenwood-res-apks/ravenwood-res.apk")
 	module.Output(installPathPrefix + "/ravenwood-test/ravenwood-res-apks/ravenwood-inst-res.apk")
 
-	module = ctx.ModuleForTests("ravenwood-test-empty", "android_common")
+	module = ctx.ModuleForTests(t, "ravenwood-test-empty", "android_common")
 	module.Output(installPathPrefix + "/ravenwood-test-empty/ravenwood.properties")
 
 	// ravenwood-runtime*.so are included in the runtime, so it shouldn't be emitted.
@@ -228,4 +230,15 @@
 	android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/lib64/libred.so")
 	android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/lib64/ravenwood-runtime-jni3.so")
 	android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-utils/framework-rules.ravenwood.jar")
+
+	// Ensure they are listed as "test" modules for code coverage
+	expectedTestOnlyModules := []string{
+		"ravenwood-test",
+		"ravenwood-test-empty",
+	}
+	expectedTopLevelTests := []string{
+		"ravenwood-test",
+		"ravenwood-test-empty",
+	}
+	assertTestOnlyAndTopLevel(t, ctx, expectedTestOnlyModules, expectedTopLevelTests)
 }
diff --git a/java/robolectric.go b/java/robolectric.go
index 5f46267..1d204a4 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -21,6 +21,7 @@
 	"android/soong/java/config"
 	"android/soong/tradefed"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -28,6 +29,23 @@
 	RegisterRobolectricBuildComponents(android.InitRegistrationContext)
 }
 
+type RobolectricRuntimesInfo struct {
+	Runtimes []android.InstallPath
+}
+
+var RobolectricRuntimesInfoProvider = blueprint.NewProvider[RobolectricRuntimesInfo]()
+
+type roboRuntimeOnlyDependencyTag struct {
+	blueprint.BaseDependencyTag
+}
+
+var roboRuntimeOnlyDepTag roboRuntimeOnlyDependencyTag
+
+// Mark this tag so dependencies that use it are excluded from visibility enforcement.
+func (t roboRuntimeOnlyDependencyTag) ExcludeFromVisibilityEnforcement() {}
+
+var _ android.ExcludeFromVisibilityEnforcementTag = roboRuntimeOnlyDepTag
+
 func RegisterRobolectricBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("android_robolectric_test", RobolectricTestFactory)
 	ctx.RegisterModuleType("android_robolectric_runtimes", robolectricRuntimesFactory)
@@ -41,12 +59,12 @@
 }
 
 const robolectricCurrentLib = "Robolectric_all-target"
+const clearcutJunitLib = "ClearcutJunitListenerAar"
 const robolectricPrebuiltLibPattern = "platform-robolectric-%s-prebuilt"
 
 var (
 	roboCoverageLibsTag = dependencyTag{name: "roboCoverageLibs"}
 	roboRuntimesTag     = dependencyTag{name: "roboRuntimes"}
-	roboRuntimeOnlyTag  = dependencyTag{name: "roboRuntimeOnlyTag"}
 )
 
 type robolectricProperties struct {
@@ -64,10 +82,6 @@
 		Shards *int64
 	}
 
-	// The version number of a robolectric prebuilt to use from prebuilts/misc/common/robolectric
-	// instead of the one built from source in external/robolectric-shadows.
-	Robolectric_prebuilt_version *string
-
 	// Use /external/robolectric rather than /external/robolectric-shadows as the version of robolectric
 	// to use.  /external/robolectric closely tracks github's master, and will fully replace /external/robolectric-shadows
 	Upstream *bool
@@ -106,21 +120,12 @@
 		ctx.PropertyErrorf("instrumentation_for", "missing required instrumented module")
 	}
 
-	if v := String(r.robolectricProperties.Robolectric_prebuilt_version); v != "" {
-		ctx.AddVariationDependencies(nil, staticLibTag, fmt.Sprintf(robolectricPrebuiltLibPattern, v))
-	} else if !proptools.BoolDefault(r.robolectricProperties.Strict_mode, true) {
-		if proptools.Bool(r.robolectricProperties.Upstream) {
-			ctx.AddVariationDependencies(nil, staticLibTag, robolectricCurrentLib+"_upstream")
-		} else {
-			ctx.AddVariationDependencies(nil, staticLibTag, robolectricCurrentLib)
-		}
-	}
+	ctx.AddVariationDependencies(nil, roboRuntimeOnlyDepTag, clearcutJunitLib)
 
 	if proptools.BoolDefault(r.robolectricProperties.Strict_mode, true) {
-		ctx.AddVariationDependencies(nil, roboRuntimeOnlyTag, robolectricCurrentLib+"_upstream")
+		ctx.AddVariationDependencies(nil, roboRuntimeOnlyDepTag, robolectricCurrentLib)
 	} else {
-		// opting out from strict mode, robolectric_non_strict_mode_permission lib should be added
-		ctx.AddVariationDependencies(nil, staticLibTag, "robolectric_non_strict_mode_permission")
+		ctx.AddVariationDependencies(nil, staticLibTag, robolectricCurrentLib)
 	}
 
 	ctx.AddVariationDependencies(nil, staticLibTag, robolectricDefaultLibs...)
@@ -139,39 +144,54 @@
 	r.forceOSType = ctx.Config().BuildOS
 	r.forceArchType = ctx.Config().BuildArch
 
+	var extraTestRunnerOptions []tradefed.Option
+	extraTestRunnerOptions = append(extraTestRunnerOptions, tradefed.Option{Name: "java-flags", Value: "-Drobolectric=true"})
+	if proptools.BoolDefault(r.robolectricProperties.Strict_mode, true) {
+		extraTestRunnerOptions = append(extraTestRunnerOptions, tradefed.Option{Name: "java-flags", Value: "-Drobolectric.strict.mode=true"})
+	}
+
+	var extraOptions []tradefed.Option
+	var javaHome = ctx.Config().Getenv("ANDROID_JAVA_HOME")
+	extraOptions = append(extraOptions, tradefed.Option{Name: "java-folder", Value: javaHome})
+
 	r.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
-		TestConfigProp:         r.testProperties.Test_config,
-		TestConfigTemplateProp: r.testProperties.Test_config_template,
-		TestSuites:             r.testProperties.Test_suites,
-		AutoGenConfig:          r.testProperties.Auto_gen_config,
-		DeviceTemplate:         "${RobolectricTestConfigTemplate}",
-		HostTemplate:           "${RobolectricTestConfigTemplate}",
+		TestConfigProp:          r.testProperties.Test_config,
+		TestConfigTemplateProp:  r.testProperties.Test_config_template,
+		TestSuites:              r.testProperties.Test_suites,
+		OptionsForAutogenerated: extraOptions,
+		TestRunnerOptions:       extraTestRunnerOptions,
+		AutoGenConfig:           r.testProperties.Auto_gen_config,
+		DeviceTemplate:          "${RobolectricTestConfigTemplate}",
+		HostTemplate:            "${RobolectricTestConfigTemplate}",
 	})
 	r.data = android.PathsForModuleSrc(ctx, r.testProperties.Data)
 	r.data = append(r.data, android.PathsForModuleSrc(ctx, r.testProperties.Device_common_data)...)
 	r.data = append(r.data, android.PathsForModuleSrc(ctx, r.testProperties.Device_first_data)...)
 	r.data = append(r.data, android.PathsForModuleSrc(ctx, r.testProperties.Device_first_prefer32_data)...)
+	r.data = append(r.data, android.PathsForModuleSrc(ctx, r.testProperties.Host_common_data)...)
 
 	var ok bool
-	var instrumentedApp *AndroidApp
+	var instrumentedApp *JavaInfo
+	var appInfo *AppInfo
 
 	// TODO: this inserts paths to built files into the test, it should really be inserting the contents.
-	instrumented := ctx.GetDirectDepsWithTag(instrumentationForTag)
+	instrumented := ctx.GetDirectDepsProxyWithTag(instrumentationForTag)
 
 	if len(instrumented) == 1 {
-		instrumentedApp, ok = instrumented[0].(*AndroidApp)
+		appInfo, ok = android.OtherModuleProvider(ctx, instrumented[0], AppInfoProvider)
 		if !ok {
 			ctx.PropertyErrorf("instrumentation_for", "dependency must be an android_app")
 		}
+		instrumentedApp = android.OtherModuleProviderOrDefault(ctx, instrumented[0], JavaInfoProvider)
 	} else if !ctx.Config().AllowMissingDependencies() {
 		panic(fmt.Errorf("expected exactly 1 instrumented dependency, got %d", len(instrumented)))
 	}
 
 	var resourceApk android.Path
 	var manifest android.Path
-	if instrumentedApp != nil {
-		manifest = instrumentedApp.mergedManifestFile
-		resourceApk = instrumentedApp.outputFile
+	if appInfo != nil {
+		manifest = appInfo.MergedManifestFile
+		resourceApk = instrumentedApp.OutputFile
 	}
 
 	roboTestConfigJar := android.PathForModuleOut(ctx, "robolectric_samedir", "samedir_config.jar")
@@ -179,7 +199,7 @@
 
 	extraCombinedJars := android.Paths{roboTestConfigJar}
 
-	handleLibDeps := func(dep android.Module) {
+	handleLibDeps := func(dep android.ModuleProxy) {
 		if !android.InList(ctx.OtherModuleName(dep), config.FrameworkLibraries) {
 			if m, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
 				extraCombinedJars = append(extraCombinedJars, m.ImplementationAndResourcesJars...)
@@ -187,25 +207,25 @@
 		}
 	}
 
-	for _, dep := range ctx.GetDirectDepsWithTag(libTag) {
+	for _, dep := range ctx.GetDirectDepsProxyWithTag(libTag) {
 		handleLibDeps(dep)
 	}
-	for _, dep := range ctx.GetDirectDepsWithTag(sdkLibTag) {
+	for _, dep := range ctx.GetDirectDepsProxyWithTag(sdkLibTag) {
 		handleLibDeps(dep)
 	}
 	// handle the runtimeOnly tag for strict_mode
-	for _, dep := range ctx.GetDirectDepsWithTag(roboRuntimeOnlyTag) {
+	for _, dep := range ctx.GetDirectDepsProxyWithTag(roboRuntimeOnlyDepTag) {
 		handleLibDeps(dep)
 	}
 
-	if instrumentedApp != nil {
-		extraCombinedJars = append(extraCombinedJars, instrumentedApp.implementationAndResourcesJar)
+	if appInfo != nil {
+		extraCombinedJars = append(extraCombinedJars, instrumentedApp.ImplementationAndResourcesJars...)
 	}
 
 	r.stem = proptools.StringDefault(r.overridableProperties.Stem, ctx.ModuleName())
 	r.classLoaderContexts = r.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
 	r.dexpreopter.disableDexpreopt()
-	r.compile(ctx, nil, nil, nil, extraCombinedJars)
+	javaInfo := r.compile(ctx, nil, nil, nil, extraCombinedJars)
 
 	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
 	var installDeps android.InstallPaths
@@ -227,8 +247,8 @@
 		installDeps = append(installDeps, installedResourceApk)
 	}
 
-	runtimes := ctx.GetDirectDepWithTag("robolectric-android-all-prebuilts", roboRuntimesTag)
-	for _, runtime := range runtimes.(*robolectricRuntimes).runtimes {
+	runtimes := ctx.GetDirectDepProxyWithTag("robolectric-android-all-prebuilts", roboRuntimesTag)
+	for _, runtime := range android.OtherModuleProviderOrDefault(ctx, runtimes, RobolectricRuntimesInfoProvider).Runtimes {
 		installDeps = append(installDeps, runtime)
 	}
 
@@ -242,6 +262,33 @@
 	}
 
 	r.installFile = ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.outputFile, installDeps...)
+
+	if javaInfo != nil {
+		setExtraJavaInfo(ctx, r, javaInfo)
+		android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+	}
+
+	moduleInfoJSON := r.javaLibraryModuleInfoJSON(ctx)
+	if _, ok := r.testConfig.(android.WritablePath); ok {
+		moduleInfoJSON.AutoTestConfig = []string{"true"}
+	}
+	if r.testConfig != nil {
+		moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, r.testConfig.String())
+	}
+	if len(r.testProperties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, r.testProperties.Test_suites...)
+	} else {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
+	}
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: r.TestSuites(),
+	})
+
+	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
+		TestOnly:       Bool(r.sourceProperties.Test_only),
+		TopLevelTarget: r.sourceProperties.Top_level_test_target,
+	})
 }
 
 func generateSameDirRoboTestConfigJar(ctx android.ModuleContext, outputFile android.ModuleOutPath) {
@@ -292,8 +339,9 @@
 		&module.testProperties)
 
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
-
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
+	module.Module.sourceProperties.Test_only = proptools.BoolPtr(true)
+	module.Module.sourceProperties.Top_level_test_target = true
 	module.testProperties.Test_suites = []string{"robolectric-tests"}
 
 	InitJavaModule(module, android.DeviceSupported)
@@ -357,7 +405,7 @@
 	}
 
 	if !ctx.Config().AlwaysUsePrebuiltSdks() && r.props.Lib != nil {
-		runtimeFromSourceModule := ctx.GetDirectDepWithTag(String(r.props.Lib), libTag)
+		runtimeFromSourceModule := ctx.GetDirectDepProxyWithTag(String(r.props.Lib), libTag)
 		if runtimeFromSourceModule == nil {
 			if ctx.Config().AllowMissingDependencies() {
 				ctx.AddMissingDependencies([]string{String(r.props.Lib)})
@@ -375,6 +423,14 @@
 		installedRuntime := ctx.InstallFile(androidAllDir, runtimeName, runtimeFromSourceJar)
 		r.runtimes = append(r.runtimes, installedRuntime)
 	}
+
+	android.SetProvider(ctx, RobolectricRuntimesInfoProvider, RobolectricRuntimesInfo{
+		Runtimes: r.runtimes,
+	})
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: r.TestSuites(),
+	})
 }
 
 func (r *robolectricRuntimes) InstallInTestcases() bool { return true }
diff --git a/java/robolectric_test.go b/java/robolectric_test.go
index 4775bac..cc16c6a 100644
--- a/java/robolectric_test.go
+++ b/java/robolectric_test.go
@@ -32,6 +32,11 @@
 	}
 
 	java_library {
+		name: "Robolectric_all-target",
+		srcs: ["Robo.java"]
+	}
+
+	java_library {
 		name: "mockito-robolectric-prebuilt",
 		srcs: ["Mockito.java"]
 	}
@@ -44,6 +49,12 @@
 	java_library {
 		name: "junitxml",
 		srcs: ["JUnitXml.java"]
+
+	}
+
+	java_library {
+		name: "ClearcutJunitListenerAar",
+		srcs: ["Runtime.java"]
 	}
 
 	java_library_host {
@@ -60,6 +71,7 @@
 )
 
 func TestRobolectricJniTest(t *testing.T) {
+	t.Parallel()
 	if runtime.GOOS != "linux" {
 		t.Skip("requires linux")
 	}
@@ -93,6 +105,17 @@
 	CheckModuleHasDependency(t, ctx.TestContext, "robo-test", "android_common", "jni-lib1")
 
 	// Check that the .so files make it into the output.
-	module := ctx.ModuleForTests("robo-test", "android_common")
+	module := ctx.ModuleForTests(t, "robo-test", "android_common")
 	module.Output(installPathPrefix + "/robo-test/lib64/jni-lib1.so")
+
+	// Ensure they are listed as "test" modules for code coverage
+	expectedTestOnlyModules := []string{
+		"robo-test",
+	}
+
+	expectedTopLevelTests := []string{
+		"robo-test",
+	}
+	assertTestOnlyAndTopLevel(t, ctx, expectedTestOnlyModules, expectedTopLevelTests)
+
 }
diff --git a/java/rro.go b/java/rro.go
index ab4fafa..4ae8d7f 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -34,6 +34,15 @@
 	ctx.RegisterModuleType("override_runtime_resource_overlay", OverrideRuntimeResourceOverlayModuleFactory)
 }
 
+type RuntimeResourceOverlayInfo struct {
+	OutputFile                    android.Path
+	Certificate                   Certificate
+	Theme                         string
+	OverriddenManifestPackageName string
+}
+
+var RuntimeResourceOverlayInfoProvider = blueprint.NewProvider[RuntimeResourceOverlayInfo]()
+
 type RuntimeResourceOverlay struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
@@ -90,15 +99,6 @@
 	Overrides []string
 }
 
-// RuntimeResourceOverlayModule interface is used by the apex package to gather information from
-// a RuntimeResourceOverlay module.
-type RuntimeResourceOverlayModule interface {
-	android.Module
-	OutputFile() android.Path
-	Certificate() Certificate
-	Theme() string
-}
-
 // RRO's partition logic is different from the partition logic of other modules defined in soong/android/paths.go
 // The default partition for RRO is "/product" and not "/system"
 func rroPartition(ctx android.ModuleContext) string {
@@ -139,6 +139,25 @@
 	r.aapt.hasNoCode = true
 	// Do not remove resources without default values nor dedupe resource configurations with the same value
 	aaptLinkFlags := []string{"--no-resource-deduping", "--no-resource-removal"}
+
+	// Add TARGET_AAPT_CHARACTERISTICS values to AAPT link flags if they exist and --product flags were not provided.
+	hasProduct := android.PrefixInList(r.aaptProperties.Aaptflags, "--product")
+	if !hasProduct && len(ctx.Config().ProductAAPTCharacteristics()) > 0 {
+		aaptLinkFlags = append(aaptLinkFlags, "--product", ctx.Config().ProductAAPTCharacteristics())
+	}
+
+	if !Bool(r.aaptProperties.Aapt_include_all_resources) {
+		// Product AAPT config
+		for _, aaptConfig := range ctx.Config().ProductAAPTConfig() {
+			aaptLinkFlags = append(aaptLinkFlags, "-c", aaptConfig)
+		}
+
+		// Product AAPT preferred config
+		if len(ctx.Config().ProductAAPTPreferredConfig()) > 0 {
+			aaptLinkFlags = append(aaptLinkFlags, "--preferred-density", ctx.Config().ProductAAPTPreferredConfig())
+		}
+	}
+
 	// Allow the override of "package name" and "overlay target package name"
 	manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
 	if overridden || r.overridableProperties.Package_name != nil {
@@ -187,6 +206,16 @@
 	android.SetProvider(ctx, FlagsPackagesProvider, FlagsPackages{
 		AconfigTextFiles: aconfigTextFilePaths,
 	})
+
+	android.SetProvider(ctx, RuntimeResourceOverlayInfoProvider, RuntimeResourceOverlayInfo{
+		OutputFile:  r.outputFile,
+		Certificate: r.Certificate(),
+		Theme:       r.Theme(),
+	})
+
+	ctx.SetOutputFiles([]android.Path{r.outputFile}, "")
+
+	buildComplianceMetadata(ctx)
 }
 
 func (r *RuntimeResourceOverlay) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
@@ -216,10 +245,6 @@
 	return r.certificate
 }
 
-func (r *RuntimeResourceOverlay) OutputFile() android.Path {
-	return r.outputFile
-}
-
 func (r *RuntimeResourceOverlay) Theme() string {
 	return String(r.properties.Theme)
 }
@@ -370,10 +395,11 @@
 
 	a.aapt.buildActions(ctx,
 		aaptBuildActionOptions{
-			sdkContext:      a,
-			extraLinkFlags:  aaptLinkFlags,
-			rroDirs:         &rroDirs,
-			manifestForAapt: genManifest,
+			sdkContext:       a,
+			extraLinkFlags:   aaptLinkFlags,
+			rroDirs:          &rroDirs,
+			manifestForAapt:  genManifest,
+			aconfigTextFiles: getAconfigFilePaths(ctx),
 		},
 	)
 
@@ -390,6 +416,11 @@
 	// Install the signed apk
 	installDir := android.PathForModuleInstall(ctx, "overlay")
 	ctx.InstallFile(installDir, signed.Base(), signed)
+
+	android.SetProvider(ctx, RuntimeResourceOverlayInfoProvider, RuntimeResourceOverlayInfo{
+		OutputFile:  signed,
+		Certificate: a.certificate,
+	})
 }
 
 func (a *AutogenRuntimeResourceOverlay) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
diff --git a/java/rro_test.go b/java/rro_test.go
index 4d58bb4..3e4fed5 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -24,6 +24,7 @@
 )
 
 func TestRuntimeResourceOverlay(t *testing.T) {
+	t.Parallel()
 	fs := android.MockFS{
 		"baz/res/res/values/strings.xml": nil,
 		"bar/res/res/values/strings.xml": nil,
@@ -66,7 +67,7 @@
 		fs.AddToFixture(),
 	).RunTestWithBp(t, bp)
 
-	m := result.ModuleForTests("foo", "android_common")
+	m := result.ModuleForTests(t, "foo", "android_common")
 
 	// Check AAPT2 link flags.
 	aapt2Flags := m.Output("package-res.apk").Args["flags"]
@@ -115,7 +116,7 @@
 	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path)
 
 	// A themed module has a different device location
-	m = result.ModuleForTests("foo_themed", "android_common")
+	m = result.ModuleForTests(t, "foo_themed", "android_common")
 	androidMkEntries = android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0]
 	path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
 	expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay/faza")}
@@ -129,6 +130,7 @@
 }
 
 func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.FixtureModifyConfig(android.SetKatiEnabledForTests),
@@ -153,7 +155,7 @@
 	//
 	// RRO module with defaults
 	//
-	m := result.ModuleForTests("foo_with_defaults", "android_common")
+	m := result.ModuleForTests(t, "foo_with_defaults", "android_common")
 
 	// Check AAPT2 link flags.
 	aapt2Flags := strings.Split(m.Output("package-res.apk").Args["flags"], " ")
@@ -171,7 +173,7 @@
 	//
 	// RRO module without defaults
 	//
-	m = result.ModuleForTests("foo_barebones", "android_common")
+	m = result.ModuleForTests(t, "foo_barebones", "android_common")
 
 	// Check AAPT2 link flags.
 	aapt2Flags = strings.Split(m.Output("package-res.apk").Args["flags"], " ")
@@ -216,7 +218,7 @@
 	}{
 		{
 			variantName:       "android_common",
-			apkPath:           "out/soong/target/product/test_device/product/overlay/foo_overlay.apk",
+			apkPath:           "out/target/product/test_device/product/overlay/foo_overlay.apk",
 			overrides:         nil,
 			targetVariant:     "android_common",
 			packageFlag:       "",
@@ -224,7 +226,7 @@
 		},
 		{
 			variantName:       "android_common_bar_overlay",
-			apkPath:           "out/soong/target/product/test_device/product/overlay/bar_overlay.apk",
+			apkPath:           "out/target/product/test_device/product/overlay/bar_overlay.apk",
 			overrides:         []string{"foo_overlay"},
 			targetVariant:     "android_common_bar",
 			packageFlag:       "com.android.bar.overlay",
@@ -233,7 +235,7 @@
 		},
 	}
 	for _, expected := range expectedVariants {
-		variant := ctx.ModuleForTests("foo_overlay", expected.variantName)
+		variant := ctx.ModuleForTests(t, "foo_overlay", expected.variantName)
 
 		// Check the final apk name
 		variant.Output(expected.apkPath)
@@ -283,28 +285,28 @@
 	}{
 		{
 			name:         "device_specific",
-			expectedPath: "out/soong/target/product/test_device/odm/overlay",
+			expectedPath: "out/target/product/test_device/odm/overlay",
 		},
 		{
 			name:         "soc_specific",
-			expectedPath: "out/soong/target/product/test_device/vendor/overlay",
+			expectedPath: "out/target/product/test_device/vendor/overlay",
 		},
 		{
 			name:         "system_ext_specific",
-			expectedPath: "out/soong/target/product/test_device/system_ext/overlay",
+			expectedPath: "out/target/product/test_device/system_ext/overlay",
 		},
 		{
 			name:         "product_specific",
-			expectedPath: "out/soong/target/product/test_device/product/overlay",
+			expectedPath: "out/target/product/test_device/product/overlay",
 		},
 		{
 			name:         "default",
-			expectedPath: "out/soong/target/product/test_device/product/overlay",
+			expectedPath: "out/target/product/test_device/product/overlay",
 		},
 	}
 	for _, testCase := range testCases {
 		ctx, _ := testJava(t, bp)
-		mod := ctx.ModuleForTests(testCase.name, "android_common").Module().(*RuntimeResourceOverlay)
+		mod := ctx.ModuleForTests(t, testCase.name, "android_common").Module().(*RuntimeResourceOverlay)
 		android.AssertPathRelativeToTopEquals(t, "Install dir is not correct for "+testCase.name, testCase.expectedPath, mod.installDir)
 	}
 }
@@ -339,7 +341,7 @@
 		}
 	`)
 
-	foo := result.ModuleForTests("foo", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
 
 	// runtime_resource_overlay module depends on aconfig_declarations listed in flags_packages
 	android.AssertBoolEquals(t, "foo expected to depend on bar", true,
@@ -356,3 +358,21 @@
 		"--feature-flags @out/soong/.intermediates/bar/intermediate.txt --feature-flags @out/soong/.intermediates/baz/intermediate.txt",
 	)
 }
+
+func TestCanBeDataOfTest(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+	).RunTestWithBp(t, `
+		runtime_resource_overlay {
+			name: "foo",
+			sdk_version: "current",
+		}
+		android_test {
+			name: "bar",
+			data: [
+				":foo",
+			],
+		}
+	`)
+	// Just test that this doesn't get errors
+}
diff --git a/java/sdk.go b/java/sdk.go
index 407fdcf..1dbea3b 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -276,7 +276,7 @@
 func createFrameworkAidl(stubsModules []string, path android.WritablePath, ctx android.SingletonContext) *android.RuleBuilder {
 	stubsJars := make([]android.Paths, len(stubsModules))
 
-	ctx.VisitAllModules(func(module android.Module) {
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
 		// Collect dex jar paths for the modules listed above.
 		if j, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 			name := ctx.ModuleName(module)
@@ -360,7 +360,7 @@
 			"api_fingerprint",
 		}
 		count := 0
-		ctx.VisitAllModules(func(module android.Module) {
+		ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
 			name := ctx.ModuleName(module)
 			if android.InList(name, apiTxtFileModules) {
 				cmd.Inputs(android.OutputFilesForModule(ctx, module, ""))
@@ -383,6 +383,10 @@
 	}
 
 	rule.Build("api_fingerprint", "generate api_fingerprint.txt")
+
+	if ctx.Config().BuildOS.Linux() {
+		ctx.DistForGoals([]string{"sdk", "droidcore"}, out)
+	}
 }
 
 func sdkMakeVars(ctx android.MakeVarsContext) {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 7891776..00ba8b2 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -316,6 +316,15 @@
 	return name
 }
 
+func (scopes apiScopes) matchingScopeFromSdkKind(kind android.SdkKind) *apiScope {
+	for _, scope := range scopes {
+		if scope.kind == kind {
+			return scope
+		}
+	}
+	return nil
+}
+
 var (
 	scopeByName    = make(map[string]*apiScope)
 	allScopeNames  []string
@@ -480,6 +489,9 @@
 
 	// Extra libs used when compiling stubs for this scope.
 	Libs []string
+
+	// Name to override the api_surface that is passed down to droidstubs.
+	Api_surface *string
 }
 
 type sdkLibraryProperties struct {
@@ -690,9 +702,9 @@
 		paths.stubsHeaderPath = lib.HeaderJars
 		paths.stubsImplPath = lib.ImplementationJars
 
-		libDep := dep.(UsesLibraryDependency)
-		paths.stubsDexJarPath = libDep.DexJarBuildPath(ctx)
-		paths.exportableStubsDexJarPath = libDep.DexJarBuildPath(ctx)
+		libDep := android.OtherModuleProviderOrDefault(ctx, dep, JavaInfoProvider)
+		paths.stubsDexJarPath = libDep.DexJarBuildPath
+		paths.exportableStubsDexJarPath = libDep.DexJarBuildPath
 		return nil
 	} else {
 		return fmt.Errorf("expected module that has JavaInfoProvider, e.g. java_library")
@@ -706,8 +718,8 @@
 			paths.stubsImplPath = lib.ImplementationJars
 		}
 
-		libDep := dep.(UsesLibraryDependency)
-		paths.stubsDexJarPath = libDep.DexJarBuildPath(ctx)
+		libDep := android.OtherModuleProviderOrDefault(ctx, dep, JavaInfoProvider)
+		paths.stubsDexJarPath = libDep.DexJarBuildPath
 		return nil
 	} else {
 		return fmt.Errorf("expected module that has JavaInfoProvider, e.g. java_library")
@@ -720,58 +732,67 @@
 			paths.stubsImplPath = lib.ImplementationJars
 		}
 
-		libDep := dep.(UsesLibraryDependency)
-		paths.exportableStubsDexJarPath = libDep.DexJarBuildPath(ctx)
+		libDep := android.OtherModuleProviderOrDefault(ctx, dep, JavaInfoProvider)
+		paths.exportableStubsDexJarPath = libDep.DexJarBuildPath
 		return nil
 	} else {
 		return fmt.Errorf("expected module that has JavaInfoProvider, e.g. java_library")
 	}
 }
 
-func (paths *scopePaths) treatDepAsApiStubsProvider(dep android.Module, action func(provider ApiStubsProvider) error) error {
-	if apiStubsProvider, ok := dep.(ApiStubsProvider); ok {
-		err := action(apiStubsProvider)
+func (paths *scopePaths) treatDepAsApiStubsProvider(ctx android.ModuleContext, dep android.Module,
+	action func(*DroidStubsInfo, *StubsSrcInfo) error) error {
+	apiStubsProvider, ok := android.OtherModuleProvider(ctx, dep, DroidStubsInfoProvider)
+	if !ok {
+		return fmt.Errorf("expected module that provides DroidStubsInfo, e.g. droidstubs")
+	}
+
+	apiStubsSrcProvider, ok := android.OtherModuleProvider(ctx, dep, StubsSrcInfoProvider)
+	if !ok {
+		return fmt.Errorf("expected module that provides StubsSrcInfo, e.g. droidstubs")
+	}
+	return action(&apiStubsProvider, &apiStubsSrcProvider)
+}
+
+func (paths *scopePaths) treatDepAsApiStubsSrcProvider(
+	ctx android.ModuleContext, dep android.Module, action func(provider *StubsSrcInfo) error) error {
+	if apiStubsProvider, ok := android.OtherModuleProvider(ctx, dep, StubsSrcInfoProvider); ok {
+		err := action(&apiStubsProvider)
 		if err != nil {
 			return err
 		}
 		return nil
 	} else {
-		return fmt.Errorf("expected module that implements ExportableApiStubsSrcProvider, e.g. droidstubs")
+		return fmt.Errorf("expected module that provides DroidStubsInfo, e.g. droidstubs")
 	}
 }
 
-func (paths *scopePaths) treatDepAsApiStubsSrcProvider(dep android.Module, action func(provider ApiStubsSrcProvider) error) error {
-	if apiStubsProvider, ok := dep.(ApiStubsSrcProvider); ok {
-		err := action(apiStubsProvider)
-		if err != nil {
-			return err
-		}
-		return nil
-	} else {
-		return fmt.Errorf("expected module that implements ApiStubsSrcProvider, e.g. droidstubs")
+func (paths *scopePaths) extractApiInfoFromApiStubsProvider(provider *DroidStubsInfo, stubsType StubsType) error {
+	var currentApiFilePathErr, removedApiFilePathErr error
+	info, err := getStubsInfoForType(provider, stubsType)
+	if err != nil {
+		return err
 	}
-}
-
-func (paths *scopePaths) extractApiInfoFromApiStubsProvider(provider ApiStubsProvider, stubsType StubsType) error {
-	var annotationsZip, currentApiFilePath, removedApiFilePath android.Path
-	annotationsZip, annotationsZipErr := provider.AnnotationsZip(stubsType)
-	currentApiFilePath, currentApiFilePathErr := provider.ApiFilePath(stubsType)
-	removedApiFilePath, removedApiFilePathErr := provider.RemovedApiFilePath(stubsType)
-
-	combinedError := errors.Join(annotationsZipErr, currentApiFilePathErr, removedApiFilePathErr)
+	if info.ApiFile == nil {
+		currentApiFilePathErr = fmt.Errorf("expected module that provides ApiFile")
+	}
+	if info.RemovedApiFile == nil {
+		removedApiFilePathErr = fmt.Errorf("expected module that provides RemovedApiFile")
+	}
+	combinedError := errors.Join(currentApiFilePathErr, removedApiFilePathErr)
 
 	if combinedError == nil {
-		paths.annotationsZip = android.OptionalPathForPath(annotationsZip)
-		paths.currentApiFilePath = android.OptionalPathForPath(currentApiFilePath)
-		paths.removedApiFilePath = android.OptionalPathForPath(removedApiFilePath)
+		paths.annotationsZip = android.OptionalPathForPath(info.AnnotationsZip)
+		paths.currentApiFilePath = android.OptionalPathForPath(info.ApiFile)
+		paths.removedApiFilePath = android.OptionalPathForPath(info.RemovedApiFile)
 	}
 	return combinedError
 }
 
-func (paths *scopePaths) extractStubsSourceInfoFromApiStubsProviders(provider ApiStubsSrcProvider, stubsType StubsType) error {
-	stubsSrcJar, err := provider.StubsSrcJar(stubsType)
+func (paths *scopePaths) extractStubsSourceInfoFromApiStubsProviders(provider *StubsSrcInfo, stubsType StubsType) error {
+	path, err := getStubsSrcInfoForType(provider, stubsType)
 	if err == nil {
-		paths.stubsSrcJar = android.OptionalPathForPath(stubsSrcJar)
+		paths.stubsSrcJar = android.OptionalPathForPath(path)
 	}
 	return err
 }
@@ -781,7 +802,7 @@
 	if ctx.Config().ReleaseHiddenApiExportableStubs() {
 		stubsType = Exportable
 	}
-	return paths.treatDepAsApiStubsSrcProvider(dep, func(provider ApiStubsSrcProvider) error {
+	return paths.treatDepAsApiStubsSrcProvider(ctx, dep, func(provider *StubsSrcInfo) error {
 		return paths.extractStubsSourceInfoFromApiStubsProviders(provider, stubsType)
 	})
 }
@@ -791,17 +812,17 @@
 	if ctx.Config().ReleaseHiddenApiExportableStubs() {
 		stubsType = Exportable
 	}
-	return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) error {
-		extractApiInfoErr := paths.extractApiInfoFromApiStubsProvider(provider, stubsType)
-		extractStubsSourceInfoErr := paths.extractStubsSourceInfoFromApiStubsProviders(provider, stubsType)
+	return paths.treatDepAsApiStubsProvider(ctx, dep, func(apiStubsProvider *DroidStubsInfo, apiStubsSrcProvider *StubsSrcInfo) error {
+		extractApiInfoErr := paths.extractApiInfoFromApiStubsProvider(apiStubsProvider, stubsType)
+		extractStubsSourceInfoErr := paths.extractStubsSourceInfoFromApiStubsProviders(apiStubsSrcProvider, stubsType)
 		return errors.Join(extractApiInfoErr, extractStubsSourceInfoErr)
 	})
 }
 
-func extractOutputPaths(dep android.Module) (android.Paths, error) {
+func extractOutputPaths(ctx android.ModuleContext, dep android.Module) (android.Paths, error) {
 	var paths android.Paths
-	if sourceFileProducer, ok := dep.(android.SourceFileProducer); ok {
-		paths = sourceFileProducer.Srcs()
+	if sourceFileProducer, ok := android.OtherModuleProvider(ctx, dep, android.SourceFilesInfoProvider); ok {
+		paths = sourceFileProducer.Srcs
 		return paths, nil
 	} else {
 		return nil, fmt.Errorf("module %q does not produce source files", dep)
@@ -809,17 +830,47 @@
 }
 
 func (paths *scopePaths) extractLatestApiPath(ctx android.ModuleContext, dep android.Module) error {
-	outputPaths, err := extractOutputPaths(dep)
+	outputPaths, err := extractOutputPaths(ctx, dep)
 	paths.latestApiPaths = outputPaths
 	return err
 }
 
 func (paths *scopePaths) extractLatestRemovedApiPath(ctx android.ModuleContext, dep android.Module) error {
-	outputPaths, err := extractOutputPaths(dep)
+	outputPaths, err := extractOutputPaths(ctx, dep)
 	paths.latestRemovedApiPaths = outputPaths
 	return err
 }
 
+func getStubsInfoForType(info *DroidStubsInfo, stubsType StubsType) (ret *StubsInfo, err error) {
+	switch stubsType {
+	case Everything:
+		ret, err = &info.EverythingStubsInfo, nil
+	case Exportable:
+		ret, err = &info.ExportableStubsInfo, nil
+	default:
+		ret, err = nil, fmt.Errorf("stubs info not supported for the stub type %s", stubsType.String())
+	}
+	if ret == nil && err == nil {
+		err = fmt.Errorf("stubs info is null for the stub type %s", stubsType.String())
+	}
+	return ret, err
+}
+
+func getStubsSrcInfoForType(info *StubsSrcInfo, stubsType StubsType) (ret android.Path, err error) {
+	switch stubsType {
+	case Everything:
+		ret, err = info.EverythingStubsSrcJar, nil
+	case Exportable:
+		ret, err = info.ExportableStubsSrcJar, nil
+	default:
+		ret, err = nil, fmt.Errorf("stubs src info not supported for the stub type %s", stubsType.String())
+	}
+	if ret == nil && err == nil {
+		err = fmt.Errorf("stubs src info is null for the stub type %s", stubsType.String())
+	}
+	return ret, err
+}
+
 type commonToSdkLibraryAndImportProperties struct {
 	// Specifies whether this module can be used as an Android shared library; defaults
 	// to true.
@@ -880,6 +931,8 @@
 	RootLibraryName() string
 }
 
+var _ android.ApexModule = (*SdkLibrary)(nil)
+
 func (m *SdkLibrary) RootLibraryName() string {
 	return m.BaseModuleName()
 }
@@ -908,9 +961,9 @@
 	// This is non-empty only when api_only is false.
 	implLibraryHeaderJars android.Paths
 
-	// The reference to the implementation library created by the source module.
-	// Is nil if the source module does not exist.
-	implLibraryModule *Library
+	// The reference to the JavaInfo provided by implementation library created by
+	// the source module. Is nil if the source module does not exist.
+	implLibraryInfo *JavaInfo
 }
 
 func (c *commonToSdkLibraryAndImport) initCommon(module commonSdkLibraryAndImportModule) {
@@ -1170,6 +1223,8 @@
 
 	// Whether if this can be used as a shared library.
 	SharedLibrary bool
+
+	Prebuilt bool
 }
 
 var SdkLibraryInfoProvider = blueprint.NewProvider[SdkLibraryInfo]()
@@ -1200,7 +1255,8 @@
 
 	commonToSdkLibraryAndImport
 
-	builtInstalledForApex []dexpreopterInstall
+	apexSystemServerDexpreoptInstalls []DexpreopterInstall
+	apexSystemServerDexJars           android.Paths
 }
 
 func (module *SdkLibrary) generateTestAndSystemScopesByDefault() bool {
@@ -1211,16 +1267,16 @@
 
 // To satisfy the UsesLibraryDependency interface
 func (module *SdkLibrary) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
-	if module.implLibraryModule != nil {
-		return module.implLibraryModule.DexJarBuildPath(ctx)
+	if module.implLibraryInfo != nil {
+		return module.implLibraryInfo.DexJarFile
 	}
 	return makeUnsetDexJarPath()
 }
 
 // To satisfy the UsesLibraryDependency interface
 func (module *SdkLibrary) DexJarInstallPath() android.Path {
-	if module.implLibraryModule != nil {
-		return module.implLibraryModule.DexJarInstallPath()
+	if module.implLibraryInfo != nil {
+		return module.implLibraryInfo.InstallFile
 	}
 	return nil
 }
@@ -1281,10 +1337,10 @@
 
 func CheckMinSdkVersion(ctx android.ModuleContext, module *Library) {
 	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 {
-				if !do(ctx, parent, am, isExternal) {
+		ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
+			isExternal := !android.IsDepInSameApex(ctx, module, child)
+			if am, ok := android.OtherModuleProvider(ctx, child, android.CommonModuleInfoProvider); ok && am.IsApexModule {
+				if !do(ctx, parent, child, isExternal) {
 					return false
 				}
 			}
@@ -1407,11 +1463,11 @@
 	// Collate the components exported by this module. All scope specific modules are exported but
 	// the impl and xml component modules are not.
 	exportedComponents := map[string]struct{}{}
-
+	var implLib android.ModuleProxy
 	// Record the paths to the header jars of the library (stubs and impl).
 	// When this java_sdk_library is depended upon from others via "libs" property,
 	// the recorded paths will be returned depending on the link type of the caller.
-	ctx.VisitDirectDeps(func(to android.Module) {
+	ctx.VisitDirectDepsProxy(func(to android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(to)
 
 		// Extract information from any of the scope specific dependencies.
@@ -1431,7 +1487,8 @@
 		if tag == implLibraryTag {
 			if dep, ok := android.OtherModuleProvider(ctx, to, JavaInfoProvider); ok {
 				module.implLibraryHeaderJars = append(module.implLibraryHeaderJars, dep.HeaderJars...)
-				module.implLibraryModule = to.(*Library)
+				module.implLibraryInfo = dep
+				implLib = to
 			}
 		}
 	})
@@ -1442,44 +1499,44 @@
 		module.hideApexVariantFromMake = true
 	}
 
-	if module.implLibraryModule != nil {
+	if module.implLibraryInfo != nil {
 		if ctx.Device() {
-			module.classesJarPaths = android.Paths{module.implLibraryModule.implementationJarFile}
-			module.bootDexJarPath = module.implLibraryModule.bootDexJarPath
-			module.uncompressDexState = module.implLibraryModule.uncompressDexState
-			module.active = module.implLibraryModule.active
+			module.classesJarPaths = module.implLibraryInfo.ImplementationJars
+			module.bootDexJarPath = module.implLibraryInfo.BootDexJarPath
+			module.uncompressDexState = module.implLibraryInfo.UncompressDexState
+			module.active = module.implLibraryInfo.Active
 		}
 
-		module.outputFile = module.implLibraryModule.outputFile
-		module.dexJarFile = makeDexJarPathFromPath(module.implLibraryModule.dexJarFile.Path())
-		module.headerJarFile = module.implLibraryModule.headerJarFile
-		module.implementationAndResourcesJar = module.implLibraryModule.implementationAndResourcesJar
-		module.builtInstalledForApex = module.implLibraryModule.builtInstalledForApex
-		module.dexpreopter.configPath = module.implLibraryModule.dexpreopter.configPath
-		module.dexpreopter.outputProfilePathOnHost = module.implLibraryModule.dexpreopter.outputProfilePathOnHost
+		module.outputFile = module.implLibraryInfo.OutputFile
+		module.dexJarFile = makeDexJarPathFromPath(module.implLibraryInfo.DexJarFile.Path())
+		module.headerJarFile = module.implLibraryInfo.HeaderJars[0]
+		module.implementationAndResourcesJar = module.implLibraryInfo.ImplementationAndResourcesJars[0]
+		module.apexSystemServerDexpreoptInstalls = module.implLibraryInfo.DexpreopterInfo.ApexSystemServerDexpreoptInstalls
+		module.apexSystemServerDexJars = module.implLibraryInfo.DexpreopterInfo.ApexSystemServerDexJars
+		module.dexpreopter.configPath = module.implLibraryInfo.ConfigPath
+		module.dexpreopter.outputProfilePathOnHost = module.implLibraryInfo.DexpreopterInfo.OutputProfilePathOnHost
 
 		// Properties required for Library.AndroidMkEntries
-		module.logtagsSrcs = module.implLibraryModule.logtagsSrcs
-		module.dexpreopter.builtInstalled = module.implLibraryModule.dexpreopter.builtInstalled
-		module.jacocoReportClassesFile = module.implLibraryModule.jacocoReportClassesFile
-		module.dexer.proguardDictionary = module.implLibraryModule.dexer.proguardDictionary
-		module.dexer.proguardUsageZip = module.implLibraryModule.dexer.proguardUsageZip
-		module.linter.reports = module.implLibraryModule.linter.reports
+		module.logtagsSrcs = module.implLibraryInfo.LogtagsSrcs
+		module.dexpreopter.builtInstalled = module.implLibraryInfo.BuiltInstalled
+		module.jacocoReportClassesFile = module.implLibraryInfo.JacocoReportClassesFile
+		module.dexer.proguardDictionary = module.implLibraryInfo.ProguardDictionary
+		module.dexer.proguardUsageZip = module.implLibraryInfo.ProguardUsageZip
+		module.linter.reports = module.implLibraryInfo.LinterReports
 
-		if lintInfo, ok := android.OtherModuleProvider(ctx, module.implLibraryModule, LintProvider); ok {
+		if lintInfo, ok := android.OtherModuleProvider(ctx, implLib, LintProvider); ok {
 			android.SetProvider(ctx, LintProvider, lintInfo)
 		}
 
 		if !module.Host() {
-			module.hostdexInstallFile = module.implLibraryModule.hostdexInstallFile
+			module.hostdexInstallFile = module.implLibraryInfo.HostdexInstallFile
 		}
 
-		if installFilesInfo, ok := android.OtherModuleProvider(ctx, module.implLibraryModule, android.InstallFilesProvider); ok {
+		if installFilesInfo, ok := android.OtherModuleProvider(ctx, implLib, android.InstallFilesProvider); ok {
 			if installFilesInfo.CheckbuildTarget != nil {
 				ctx.CheckbuildFile(installFilesInfo.CheckbuildTarget)
 			}
 		}
-		android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: module.implLibraryModule.uniqueSrcFiles.Strings()})
 	}
 
 	// Make the set of components exported by this module available for use elsewhere.
@@ -1518,17 +1575,39 @@
 		}
 	}
 
-	if module.requiresRuntimeImplementationLibrary() && module.implLibraryModule != nil {
+	if module.requiresRuntimeImplementationLibrary() && module.implLibraryInfo != nil {
 		generatingLibs = append(generatingLibs, module.implLibraryModuleName())
-		setOutputFiles(ctx, module.implLibraryModule.Module)
+		setOutputFilesFromJavaInfo(ctx, module.implLibraryInfo)
 	}
 
+	javaInfo := &JavaInfo{
+		JacocoReportClassesFile: module.jacocoReportClassesFile,
+	}
+	setExtraJavaInfo(ctx, ctx.Module(), javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+
 	sdkLibInfo.GeneratingLibs = generatingLibs
+	sdkLibInfo.Prebuilt = false
 	android.SetProvider(ctx, SdkLibraryInfoProvider, sdkLibInfo)
 }
 
-func (module *SdkLibrary) BuiltInstalledForApex() []dexpreopterInstall {
-	return module.builtInstalledForApex
+func setOutputFilesFromJavaInfo(ctx android.ModuleContext, info *JavaInfo) {
+	ctx.SetOutputFiles(append(android.PathsIfNonNil(info.OutputFile), info.ExtraOutputFiles...), "")
+	ctx.SetOutputFiles(android.PathsIfNonNil(info.OutputFile), android.DefaultDistTag)
+	ctx.SetOutputFiles(info.ImplementationAndResourcesJars, ".jar")
+	ctx.SetOutputFiles(info.HeaderJars, ".hjar")
+	if info.ProguardDictionary.Valid() {
+		ctx.SetOutputFiles(android.Paths{info.ProguardDictionary.Path()}, ".proguard_map")
+	}
+	ctx.SetOutputFiles(info.GeneratedSrcjars, ".generated_srcjars")
+}
+
+func (module *SdkLibrary) ApexSystemServerDexpreoptInstalls() []DexpreopterInstall {
+	return module.apexSystemServerDexpreoptInstalls
+}
+
+func (module *SdkLibrary) ApexSystemServerDexJars() android.Paths {
+	return module.apexSystemServerDexJars
 }
 
 func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {
@@ -1636,15 +1715,22 @@
 }
 
 // Implements android.ApexModule
-func (module *SdkLibrary) DepIsInSameApex(mctx android.BaseModuleContext, dep android.Module) bool {
-	depTag := mctx.OtherModuleDependencyTag(dep)
-	if depTag == xmlPermissionsFileTag {
+func (m *SdkLibrary) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return SdkLibraryDepInSameApexChecker{}
+}
+
+type SdkLibraryDepInSameApexChecker struct {
+	android.BaseDepInSameApexChecker
+}
+
+func (m SdkLibraryDepInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool {
+	if tag == xmlPermissionsFileTag {
 		return true
 	}
-	if dep.Name() == module.implLibraryModuleName() {
+	if tag == implLibraryTag {
 		return true
 	}
-	return module.Library.DepIsInSameApex(mctx, dep)
+	return depIsInSameApex(tag)
 }
 
 // Implements android.ApexModule
@@ -1901,10 +1987,6 @@
 
 	commonToSdkLibraryAndImport
 
-	// The reference to the xml permissions module created by the source module.
-	// Is nil if the source module does not exist.
-	xmlPermissionsFileModule *sdkLibraryXml
-
 	// Build path to the dex implementation jar obtained from the prebuilt_apex, if any.
 	dexJarFile    OptionalDexJarPath
 	dexJarFileErr error
@@ -2059,9 +2141,16 @@
 var _ android.ApexModule = (*SdkLibraryImport)(nil)
 
 // Implements android.ApexModule
-func (module *SdkLibraryImport) DepIsInSameApex(mctx android.BaseModuleContext, dep android.Module) bool {
-	depTag := mctx.OtherModuleDependencyTag(dep)
-	if depTag == xmlPermissionsFileTag {
+func (m *SdkLibraryImport) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return SdkLibraryImportDepIsInSameApexChecker{}
+}
+
+type SdkLibraryImportDepIsInSameApexChecker struct {
+	android.BaseDepInSameApexChecker
+}
+
+func (m SdkLibraryImportDepIsInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool {
+	if tag == xmlPermissionsFileTag {
 		return true
 	}
 
@@ -2071,13 +2160,10 @@
 }
 
 // Implements android.ApexModule
-func (module *SdkLibraryImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
-	sdkVersion android.ApiLevel) error {
-	// we don't check prebuilt modules for sdk_version
-	return nil
+func (m *SdkLibraryImport) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
+	return android.MinApiLevel
 }
 
-// Implements android.ApexModule
 func (module *SdkLibraryImport) UniqueApexVariations() bool {
 	return module.uniqueApexVariations()
 }
@@ -2094,7 +2180,7 @@
 	module.installFile = android.PathForModuleInstall(ctx, "framework", module.Stem()+".jar")
 
 	// Record the paths to the prebuilt stubs library and stubs source.
-	ctx.VisitDirectDeps(func(to android.Module) {
+	ctx.VisitDirectDepsProxy(func(to android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(to)
 
 		// Extract information from any of the scope specific dependencies.
@@ -2106,17 +2192,11 @@
 			// is determined by the nature of the dependency which is determined by the tag.
 			scopeTag.extractDepInfo(ctx, to, scopePaths)
 		} else if tag == implLibraryTag {
-			if implLibrary, ok := to.(*Library); ok {
-				module.implLibraryModule = implLibrary
+			if implInfo, ok := android.OtherModuleProvider(ctx, to, JavaInfoProvider); ok {
+				module.implLibraryInfo = implInfo
 			} else {
 				ctx.ModuleErrorf("implementation library must be of type *java.Library but was %T", to)
 			}
-		} else if tag == xmlPermissionsFileTag {
-			if xmlPermissionsFileModule, ok := to.(*sdkLibraryXml); ok {
-				module.xmlPermissionsFileModule = xmlPermissionsFileModule
-			} else {
-				ctx.ModuleErrorf("xml permissions file module must be of type *sdkLibraryXml but was %T", to)
-			}
 		}
 	})
 	sdkLibInfo := module.generateCommonBuildActions(ctx)
@@ -2156,12 +2236,21 @@
 	}
 
 	module.setOutputFiles(ctx)
-	if module.implLibraryModule != nil {
+	if module.implLibraryInfo != nil {
 		generatingLibs = append(generatingLibs, module.implLibraryModuleName())
-		setOutputFiles(ctx, module.implLibraryModule.Module)
+		setOutputFilesFromJavaInfo(ctx, module.implLibraryInfo)
 	}
 
+	javaInfo := &JavaInfo{}
+	if module.implLibraryInfo != nil {
+		javaInfo.JacocoReportClassesFile = module.implLibraryInfo.JacocoReportClassesFile
+	}
+
+	setExtraJavaInfo(ctx, ctx.Module(), javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+
 	sdkLibInfo.GeneratingLibs = generatingLibs
+	sdkLibInfo.Prebuilt = true
 	android.SetProvider(ctx, SdkLibraryInfoProvider, sdkLibInfo)
 }
 
@@ -2177,10 +2266,10 @@
 	if module.dexJarFile.IsSet() {
 		return module.dexJarFile
 	}
-	if module.implLibraryModule == nil {
+	if module.implLibraryInfo == nil {
 		return makeUnsetDexJarPath()
 	} else {
-		return module.implLibraryModule.DexJarBuildPath(ctx)
+		return module.implLibraryInfo.DexJarFile
 	}
 }
 
@@ -2196,10 +2285,10 @@
 
 // to satisfy apex.javaDependency interface
 func (module *SdkLibraryImport) JacocoReportClassesFile() android.Path {
-	if module.implLibraryModule == nil {
+	if module.implLibraryInfo == nil {
 		return nil
 	} else {
-		return module.implLibraryModule.JacocoReportClassesFile()
+		return module.implLibraryInfo.JacocoReportClassesFile
 	}
 }
 
@@ -2212,19 +2301,19 @@
 
 // to satisfy java.ApexDependency interface
 func (module *SdkLibraryImport) HeaderJars() android.Paths {
-	if module.implLibraryModule == nil {
+	if module.implLibraryInfo == nil {
 		return nil
 	} else {
-		return module.implLibraryModule.HeaderJars()
+		return module.implLibraryInfo.HeaderJars
 	}
 }
 
 // to satisfy java.ApexDependency interface
 func (module *SdkLibraryImport) ImplementationAndResourcesJars() android.Paths {
-	if module.implLibraryModule == nil {
+	if module.implLibraryInfo == nil {
 		return nil
 	} else {
-		return module.implLibraryModule.ImplementationAndResourcesJars()
+		return module.implLibraryInfo.ImplementationAndResourcesJars
 	}
 }
 
@@ -2389,8 +2478,7 @@
 	s.Min_device_sdk = sdk.commonSdkLibraryProperties.Min_device_sdk
 	s.Max_device_sdk = sdk.commonSdkLibraryProperties.Max_device_sdk
 
-	implLibrary := sdk.implLibraryModule
-	if implLibrary != nil && implLibrary.dexpreopter.dexpreoptProperties.Dex_preopt_result.Profile_guided {
+	if sdk.implLibraryInfo != nil && sdk.implLibraryInfo.ProfileGuided {
 		s.DexPreoptProfileGuided = proptools.BoolPtr(true)
 	}
 }
diff --git a/java/sdk_library_internal.go b/java/sdk_library_internal.go
index 768e57a..f5feabe 100644
--- a/java/sdk_library_internal.go
+++ b/java/sdk_library_internal.go
@@ -15,12 +15,13 @@
 package java
 
 import (
-	"android/soong/android"
-	"android/soong/etc"
 	"fmt"
 	"path"
 	"strings"
 
+	"android/soong/android"
+	"android/soong/etc"
+
 	"github.com/google/blueprint/proptools"
 )
 
@@ -173,6 +174,20 @@
 	mctx.CreateModule(LibraryFactory, properties...)
 }
 
+// getApiSurfaceForScope returns the api surface name to use for the apiScope. If one is specified
+// in the corresponding ApiScopeProperties.Api_surface property that is used, otherwise the name of
+// the apiScope is used.
+func (module *SdkLibrary) getApiSurfaceForScope(apiScope *apiScope) *string {
+	scopeProperties := module.scopeToProperties[apiScope]
+
+	apiSurface := scopeProperties.Api_surface
+	if apiSurface == nil {
+		apiSurface = &apiScope.name
+	}
+
+	return apiSurface
+}
+
 // 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).
@@ -226,7 +241,7 @@
 	props.Srcs = append(props.Srcs, module.properties.Srcs...)
 	props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...)
 	props.Sdk_version = module.deviceProperties.Sdk_version
-	props.Api_surface = &apiScope.name
+	props.Api_surface = module.getApiSurfaceForScope(apiScope)
 	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
@@ -778,7 +793,11 @@
 
 // from android.ApexModule
 func (module *sdkLibraryXml) AvailableFor(what string) bool {
-	return true
+	return android.CheckAvailableForApex(what, module.ApexAvailableFor())
+}
+
+func (module *sdkLibraryXml) ApexAvailableFor() []string {
+	return []string{android.AvailableToPlatform, android.AvailableToAnyApex}
 }
 
 func (module *sdkLibraryXml) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -788,10 +807,8 @@
 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
+func (m *sdkLibraryXml) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
+	return android.MinApiLevel
 }
 
 // File path to the runtime implementation library
@@ -919,6 +936,8 @@
 	ctx.PackageFile(module.installDirPath, libName+".xml", module.outputFilePath)
 
 	ctx.SetOutputFiles(android.OutputPaths{module.outputFilePath}.Paths(), "")
+
+	etc.SetCommonPrebuiltEtcInfo(ctx, module)
 }
 
 func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries {
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 6031d72..6d27e54 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -25,6 +25,7 @@
 )
 
 func TestJavaSdkLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -114,19 +115,19 @@
 	`)
 
 	// check the existence of the internal modules
-	foo := result.ModuleForTests("foo", "android_common")
-	result.ModuleForTests(apiScopePublic.stubsLibraryModuleName("foo"), "android_common")
-	result.ModuleForTests(apiScopeSystem.stubsLibraryModuleName("foo"), "android_common")
-	result.ModuleForTests(apiScopeTest.stubsLibraryModuleName("foo"), "android_common")
-	result.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo"), "android_common")
-	result.ModuleForTests(apiScopeSystem.stubsSourceModuleName("foo"), "android_common")
-	result.ModuleForTests(apiScopeTest.stubsSourceModuleName("foo"), "android_common")
-	result.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo")+".api.contribution", "")
-	result.ModuleForTests(apiScopePublic.apiLibraryModuleName("foo"), "android_common")
-	result.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common")
-	result.ModuleForTests("foo.api.public.28", "")
-	result.ModuleForTests("foo.api.system.28", "")
-	result.ModuleForTests("foo.api.test.28", "")
+	foo := result.ModuleForTests(t, "foo", "android_common")
+	result.ModuleForTests(t, apiScopePublic.stubsLibraryModuleName("foo"), "android_common")
+	result.ModuleForTests(t, apiScopeSystem.stubsLibraryModuleName("foo"), "android_common")
+	result.ModuleForTests(t, apiScopeTest.stubsLibraryModuleName("foo"), "android_common")
+	result.ModuleForTests(t, apiScopePublic.stubsSourceModuleName("foo"), "android_common")
+	result.ModuleForTests(t, apiScopeSystem.stubsSourceModuleName("foo"), "android_common")
+	result.ModuleForTests(t, apiScopeTest.stubsSourceModuleName("foo"), "android_common")
+	result.ModuleForTests(t, apiScopePublic.stubsSourceModuleName("foo")+".api.contribution", "")
+	result.ModuleForTests(t, apiScopePublic.apiLibraryModuleName("foo"), "android_common")
+	result.ModuleForTests(t, "foo"+sdkXmlFileSuffix, "android_common")
+	result.ModuleForTests(t, "foo.api.public.28", "")
+	result.ModuleForTests(t, "foo.api.system.28", "")
+	result.ModuleForTests(t, "foo.api.test.28", "")
 
 	exportedComponentsInfo, _ := android.OtherModuleProvider(result, foo.Module(), android.ExportedComponentsInfoProvider)
 	expectedFooExportedComponents := []string{
@@ -146,28 +147,28 @@
 	}
 	android.AssertArrayString(t, "foo exported components", expectedFooExportedComponents, exportedComponentsInfo.Components)
 
-	bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac")
+	bazJavac := result.ModuleForTests(t, "baz", "android_common").Rule("javac")
 	// tests if baz is actually linked to the stubs lib
-	android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.system.jar")
+	android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.system.from-text.jar")
 	// ... and not to the impl lib
 	android.AssertStringDoesNotContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.jar")
 	// test if baz is not linked to the system variant of foo
 	android.AssertStringDoesNotContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.jar")
 
-	bazTestJavac := result.ModuleForTests("baz-test", "android_common").Rule("javac")
+	bazTestJavac := result.ModuleForTests(t, "baz-test", "android_common").Rule("javac")
 	// tests if baz-test is actually linked to the test stubs lib
-	android.AssertStringDoesContain(t, "baz-test javac classpath", bazTestJavac.Args["classpath"], "foo.stubs.test.jar")
+	android.AssertStringDoesContain(t, "baz-test javac classpath", bazTestJavac.Args["classpath"], "foo.stubs.test.from-text.jar")
 
-	baz29Javac := result.ModuleForTests("baz-29", "android_common").Rule("javac")
+	baz29Javac := result.ModuleForTests(t, "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/sdk_system_29_foo/android_common/combined/sdk_system_29_foo.jar")
+	android.AssertStringDoesContain(t, "baz-29 javac classpath", baz29Javac.Args["classpath"], "prebuilts/sdk/sdk_system_29_foo/android_common/local-combined/sdk_system_29_foo.jar")
 
-	bazModule30Javac := result.ModuleForTests("baz-module-30", "android_common").Rule("javac")
+	bazModule30Javac := result.ModuleForTests(t, "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/sdk_module-lib_30_foo/android_common/combined/sdk_module-lib_30_foo.jar")
+	android.AssertStringDoesContain(t, "baz-module-30 javac classpath", bazModule30Javac.Args["classpath"], "prebuilts/sdk/sdk_module-lib_30_foo/android_common/local-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")
+	qux := result.ModuleForTests(t, "qux", "android_common")
 	if quxLib, ok := qux.Module().(*Library); ok {
 		requiredSdkLibs, optionalSdkLibs := quxLib.ClassLoaderContexts().UsesLibs()
 		android.AssertDeepEquals(t, "qux exports (required)", []string{"fred", "quuz", "foo", "bar"}, requiredSdkLibs)
@@ -175,18 +176,19 @@
 	}
 
 	// test if quuz have created the api_contribution module
-	result.ModuleForTests(apiScopePublic.stubsSourceModuleName("quuz")+".api.contribution", "")
+	result.ModuleForTests(t, apiScopePublic.stubsSourceModuleName("quuz")+".api.contribution", "")
 
-	fooImplDexJar := result.ModuleForTests("foo.impl", "android_common").Rule("d8")
+	fooImplDexJar := result.ModuleForTests(t, "foo.impl", "android_common").Rule("d8")
 	// tests if kotlinc generated files are NOT excluded from output of foo.impl.
 	android.AssertStringDoesNotContain(t, "foo.impl dex", fooImplDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module")
 
-	barImplDexJar := result.ModuleForTests("bar.impl", "android_common").Rule("d8")
+	barImplDexJar := result.ModuleForTests(t, "bar.impl", "android_common").Rule("d8")
 	// tests if kotlinc generated files are excluded from output of bar.impl.
 	android.AssertStringDoesContain(t, "bar.impl dex", barImplDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module")
 }
 
 func TestJavaSdkLibrary_UpdatableLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -218,7 +220,7 @@
 `)
 
 	// test that updatability attributes are passed on correctly
-	fooUpdatable := result.ModuleForTests("fooUpdatable.xml", "android_common").Output("fooUpdatable.xml")
+	fooUpdatable := result.ModuleForTests(t, "fooUpdatable.xml", "android_common").Output("fooUpdatable.xml")
 	fooUpdatableContents := android.ContentFromFileRuleForTests(t, result.TestContext, fooUpdatable)
 	android.AssertStringDoesContain(t, "fooUpdatable.xml contents", fooUpdatableContents, `on-bootclasspath-since="U"`)
 	android.AssertStringDoesContain(t, "fooUpdatable.xml contents", fooUpdatableContents, `on-bootclasspath-before="V"`)
@@ -227,7 +229,7 @@
 
 	// double check that updatability attributes are not written if they don't exist in the bp file
 	// the permissions file for the foo library defined above
-	fooPermissions := result.ModuleForTests("foo.xml", "android_common").Output("foo.xml")
+	fooPermissions := result.ModuleForTests(t, "foo.xml", "android_common").Output("foo.xml")
 	fooPermissionsContents := android.ContentFromFileRuleForTests(t, result.TestContext, fooPermissions)
 	android.AssertStringDoesNotContain(t, "foo.xml contents", fooPermissionsContents, `on-bootclasspath-since`)
 	android.AssertStringDoesNotContain(t, "foo.xml contents", fooPermissionsContents, `on-bootclasspath-before`)
@@ -236,6 +238,7 @@
 }
 
 func TestJavaSdkLibrary_UpdatableLibrary_Validation_ValidVersion(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -263,6 +266,7 @@
 }
 
 func TestJavaSdkLibrary_UpdatableLibrary_Validation_AtLeastTAttributes(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -292,6 +296,7 @@
 }
 
 func TestJavaSdkLibrary_UpdatableLibrary_Validation_MinAndMaxDeviceSdk(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -319,6 +324,7 @@
 }
 
 func TestJavaSdkLibrary_UpdatableLibrary_Validation_MinAndMaxDeviceSdkAndModuleMinSdk(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -347,6 +353,7 @@
 }
 
 func TestJavaSdkLibrary_UpdatableLibrary_usesNewTag(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -363,13 +370,14 @@
 		}
 `)
 	// test that updatability attributes are passed on correctly
-	fooUpdatable := result.ModuleForTests("foo.xml", "android_common").Output("foo.xml")
+	fooUpdatable := result.ModuleForTests(t, "foo.xml", "android_common").Output("foo.xml")
 	fooUpdatableContents := android.ContentFromFileRuleForTests(t, result.TestContext, fooUpdatable)
 	android.AssertStringDoesContain(t, "foo.xml contents", fooUpdatableContents, `<apex-library`)
 	android.AssertStringDoesNotContain(t, "foo.xml contents", fooUpdatableContents, `<library`)
 }
 
 func TestJavaSdkLibrary_StubOrImplOnlyLibs(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -409,12 +417,12 @@
 		{lib: "stub-only-static-lib", in_stub_combined: true},
 	}
 	verify := func(sdklib, dep string, cp, combined bool) {
-		sdklibCp := result.ModuleForTests(sdklib, "android_common").Rule("javac").Args["classpath"]
+		sdklibCp := result.ModuleForTests(t, sdklib, "android_common").Rule("javac").Args["classpath"]
 		expected := cp || combined // Every combined jar is also on the classpath.
 		android.AssertStringContainsEquals(t, "bad classpath for "+sdklib, sdklibCp, "/"+dep+".jar", expected)
 
-		combineJarInputs := result.ModuleForTests(sdklib, "android_common").Rule("combineJar").Inputs.Strings()
-		depPath := filepath.Join("out", "soong", ".intermediates", dep, "android_common", "turbine-combined", dep+".jar")
+		combineJarInputs := result.ModuleForTests(t, sdklib, "android_common").Rule("combineJar").Inputs.Strings()
+		depPath := filepath.Join("out", "soong", ".intermediates", dep, "android_common", "turbine", dep+".jar")
 		android.AssertStringListContainsEquals(t, "bad combined inputs for "+sdklib, combineJarInputs, depPath, combined)
 	}
 	for _, expectation := range expectations {
@@ -426,6 +434,7 @@
 }
 
 func TestJavaSdkLibrary_DoNotAccessImplWhenItIsNotBuilt(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -448,13 +457,14 @@
 		`)
 
 	// The bar library should depend on the stubs jar.
-	barLibrary := result.ModuleForTests("bar", "android_common").Rule("javac")
-	if expected, actual := `^-classpath .*:out/soong/[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	barLibrary := result.ModuleForTests(t, "bar", "android_common").Rule("javac")
+	if expected, actual := `^-classpath .*:out/soong/[^:]*/foo\.stubs\.from-text/foo\.stubs\.from-text\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
 
 func TestJavaSdkLibrary_AccessOutputFiles(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -478,12 +488,13 @@
 }
 
 func TestJavaSdkLibrary_AccessOutputFiles_NoAnnotations(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
 	).
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": unsupported module reference tag ".public.annotations.zip"`)).
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": unsupported output tag ".public.annotations.zip"`)).
 		RunTestWithBp(t, `
 		java_sdk_library {
 			name: "foo",
@@ -503,12 +514,13 @@
 }
 
 func TestJavaSdkLibrary_AccessOutputFiles_MissingScope(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
 	).
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": unsupported module reference tag ".system.stubs.source"`)).
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": unsupported output tag ".system.stubs.source"`)).
 		RunTestWithBp(t, `
 		java_sdk_library {
 			name: "foo",
@@ -527,6 +539,7 @@
 }
 
 func TestJavaSdkLibrary_Deps(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -557,6 +570,7 @@
 }
 
 func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) {
+	t.Parallel()
 	prepareForJavaTest.RunTestWithBp(t, `
 		java_sdk_library_import {
 			name: "foo",
@@ -582,6 +596,7 @@
 }
 
 func TestJavaSdkLibraryImport_AccessOutputFiles_Invalid(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_sdk_library_import {
 			name: "foo",
@@ -592,6 +607,7 @@
 		`
 
 	t.Run("stubs.source", func(t *testing.T) {
+		t.Parallel()
 		prepareForJavaTest.
 			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo" is not a SourceFileProducer or having valid output file for tag ".public.stubs.source"`)).
 			RunTestWithBp(t, bp+`
@@ -607,6 +623,7 @@
 	})
 
 	t.Run("api.txt", func(t *testing.T) {
+		t.Parallel()
 		prepareForJavaTest.
 			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo" is not a SourceFileProducer or having valid output file for tag ".public.api.txt"`)).
 			RunTestWithBp(t, bp+`
@@ -621,6 +638,7 @@
 	})
 
 	t.Run("removed-api.txt", func(t *testing.T) {
+		t.Parallel()
 		prepareForJavaTest.
 			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo" is not a SourceFileProducer or having valid output file for tag ".public.removed-api.txt"`)).
 			RunTestWithBp(t, bp+`
@@ -636,6 +654,7 @@
 }
 
 func TestJavaSdkLibrary_InvalidScopes(t *testing.T) {
+	t.Parallel()
 	prepareForJavaTest.
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo": enabled api scope "system" depends on disabled scope "public"`)).
 		RunTestWithBp(t, `
@@ -656,6 +675,7 @@
 }
 
 func TestJavaSdkLibrary_SdkVersion_ForScope(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -674,6 +694,7 @@
 }
 
 func TestJavaSdkLibrary_ModuleLib(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -694,6 +715,7 @@
 }
 
 func TestJavaSdkLibrary_SystemServer(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -714,6 +736,7 @@
 }
 
 func TestJavaSdkLibrary_SystemServer_AccessToStubScopeLibs(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -768,13 +791,13 @@
 
 	stubsPath := func(name string, scope *apiScope) string {
 		name = scope.stubsLibraryModuleName(name)
-		return fmt.Sprintf("out/soong/.intermediates/%[1]s/android_common/turbine-combined/%[1]s.jar", name)
+		return fmt.Sprintf("out/soong/.intermediates/%[1]s.from-text/android_common/%[1]s.from-text/%[1]s.from-text.jar", name)
 	}
 
 	// The bar library should depend on the highest (where system server is highest and public is
 	// lowest) API scopes provided by each of the foo-* modules. The highest API scope provided by the
 	// foo-<x> module is <x>.
-	barLibrary := result.ModuleForTests("bar", "android_common").Rule("javac")
+	barLibrary := result.ModuleForTests(t, "bar", "android_common").Rule("javac")
 	stubLibraries := []string{
 		stubsPath("foo-public", apiScopePublic),
 		stubsPath("foo-system", apiScopeSystem),
@@ -788,6 +811,7 @@
 }
 
 func TestJavaSdkLibraryImport(t *testing.T) {
+	t.Parallel()
 	result := prepareForJavaTest.RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -826,10 +850,10 @@
 		`)
 
 	for _, scope := range []string{"", ".system", ".test"} {
-		fooModule := result.ModuleForTests("foo"+scope, "android_common")
+		fooModule := result.ModuleForTests(t, "foo"+scope, "android_common")
 		javac := fooModule.Rule("javac")
 
-		sdklibStubsJar := result.ModuleForTests("sdklib.stubs"+scope, "android_common").Output("combined/sdklib.stubs" + scope + ".jar").Output
+		sdklibStubsJar := result.ModuleForTests(t, "sdklib.stubs"+scope, "android_common").Output("local-combined/sdklib.stubs" + scope + ".jar").Output
 		android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], sdklibStubsJar.String())
 	}
 
@@ -844,6 +868,7 @@
 }
 
 func TestJavaSdkLibraryImport_WithSource(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -968,25 +993,25 @@
 	CheckModuleDependencies(t, result.TestContext, "combined", "android_common", []string{
 		// Each use of :sdklib{...} adds a dependency onto prebuilt_sdklib.
 		`prebuilt_sdklib`,
-		`prebuilt_sdklib`,
-		`prebuilt_sdklib`,
 		`prebuilt_sdklib.stubs`,
 		`prebuilt_sdklib.stubs.source`,
 	})
 
 	// Make sure that dependencies on sdklib that resolve to one of the child libraries use the
 	// prebuilt library.
-	public := result.ModuleForTests("public", "android_common")
+	public := result.ModuleForTests(t, "public", "android_common")
 	rule := public.Output("javac/public.jar")
 	inputs := rule.Implicits.Strings()
-	expected := "out/soong/.intermediates/prebuilt_sdklib.stubs/android_common/combined/sdklib.stubs.jar"
+	expected := "out/soong/.intermediates/prebuilt_sdklib.stubs/android_common/local-combined/sdklib.stubs.jar"
 	if !android.InList(expected, inputs) {
 		t.Errorf("expected %q to contain %q", inputs, expected)
 	}
 }
 
 func TestJavaSdkLibraryImport_Preferred(t *testing.T) {
+	t.Parallel()
 	t.Run("prefer", func(t *testing.T) {
+		t.Parallel()
 		testJavaSdkLibraryImport_Preferred(t, "prefer: true,", android.NullFixturePreparer)
 	})
 }
@@ -994,6 +1019,7 @@
 // If a module is listed in `mainline_module_contributions, it should be used
 // It will supersede any other source vs prebuilt selection mechanism like `prefer` attribute
 func TestSdkLibraryImport_MetadataModuleSupersedesPreferred(t *testing.T) {
+	t.Parallel()
 	bp := `
 		apex_contributions {
 			name: "my_mainline_module_contributions",
@@ -1093,17 +1119,17 @@
 	).RunTestWithBp(t, bp)
 
 	// Make sure that rdeps get the correct source vs prebuilt based on mainline_module_contributions
-	public := result.ModuleForTests("public", "android_common")
+	public := result.ModuleForTests(t, "public", "android_common")
 	rule := public.Output("javac/public.jar")
 	inputs := rule.Implicits.Strings()
 	expectedInputs := []string{
 		// source
-		"out/soong/.intermediates/sdklib.prebuilt_preferred_using_legacy_flags.stubs/android_common/turbine-combined/sdklib.prebuilt_preferred_using_legacy_flags.stubs.jar",
-		"out/soong/.intermediates/sdklib.prebuilt_preferred_using_legacy_flags.stubs.system/android_common/turbine-combined/sdklib.prebuilt_preferred_using_legacy_flags.stubs.system.jar",
+		"out/soong/.intermediates/sdklib.prebuilt_preferred_using_legacy_flags.stubs.from-text/android_common/sdklib.prebuilt_preferred_using_legacy_flags.stubs.from-text/sdklib.prebuilt_preferred_using_legacy_flags.stubs.from-text.jar",
+		"out/soong/.intermediates/sdklib.prebuilt_preferred_using_legacy_flags.stubs.system.from-text/android_common/sdklib.prebuilt_preferred_using_legacy_flags.stubs.system.from-text/sdklib.prebuilt_preferred_using_legacy_flags.stubs.system.from-text.jar",
 
 		// prebuilt
-		"out/soong/.intermediates/prebuilt_sdklib.source_preferred_using_legacy_flags.stubs/android_common/combined/sdklib.source_preferred_using_legacy_flags.stubs.jar",
-		"out/soong/.intermediates/prebuilt_sdklib.source_preferred_using_legacy_flags.stubs.system/android_common/combined/sdklib.source_preferred_using_legacy_flags.stubs.system.jar",
+		"out/soong/.intermediates/prebuilt_sdklib.source_preferred_using_legacy_flags.stubs/android_common/local-combined/sdklib.source_preferred_using_legacy_flags.stubs.jar",
+		"out/soong/.intermediates/prebuilt_sdklib.source_preferred_using_legacy_flags.stubs.system/android_common/local-combined/sdklib.source_preferred_using_legacy_flags.stubs.system.jar",
 	}
 	for _, expected := range expectedInputs {
 		if !android.InList(expected, inputs) {
@@ -1113,6 +1139,7 @@
 }
 
 func TestJavaSdkLibraryDist(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaBuildComponents,
 		PrepareForTestWithJavaDefaultModules,
@@ -1179,7 +1206,8 @@
 
 	for _, tt := range testCases {
 		t.Run(tt.module, func(t *testing.T) {
-			m := result.ModuleForTests(apiScopePublic.exportableStubsLibraryModuleName(tt.module), "android_common").Module().(*Library)
+			t.Parallel()
+			m := result.ModuleForTests(t, apiScopePublic.exportableStubsLibraryModuleName(tt.module), "android_common").Module().(*Library)
 			dists := m.Dists()
 			if len(dists) != 1 {
 				t.Fatalf("expected exactly 1 dist entry, got %d", len(dists))
@@ -1195,6 +1223,7 @@
 }
 
 func TestSdkLibrary_CheckMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	preparer := android.GroupFixturePreparers(
 		PrepareForTestWithJavaBuildComponents,
 		PrepareForTestWithJavaDefaultModules,
@@ -1279,6 +1308,7 @@
 }
 
 func TestJavaSdkLibrary_StubOnlyLibs_PassedToDroidstubs(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1300,12 +1330,13 @@
 		`)
 
 	// The foo.stubs.source should depend on bar-lib
-	fooStubsSources := result.ModuleForTests("foo.stubs.source", "android_common").Module().(*Droidstubs)
+	fooStubsSources := result.ModuleForTests(t, "foo.stubs.source", "android_common").Module().(*Droidstubs)
 	eval := fooStubsSources.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
 	android.AssertStringListContains(t, "foo stubs should depend on bar-lib", fooStubsSources.Javadoc.properties.Libs.GetOrDefault(eval, nil), "bar-lib")
 }
 
 func TestJavaSdkLibrary_Scope_Libs_PassedToDroidstubs(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1327,12 +1358,13 @@
 		`)
 
 	// The foo.stubs.source should depend on bar-lib
-	fooStubsSources := result.ModuleForTests("foo.stubs.source", "android_common").Module().(*Droidstubs)
+	fooStubsSources := result.ModuleForTests(t, "foo.stubs.source", "android_common").Module().(*Droidstubs)
 	eval := fooStubsSources.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
 	android.AssertStringListContains(t, "foo stubs should depend on bar-lib", fooStubsSources.Javadoc.properties.Libs.GetOrDefault(eval, nil), "bar-lib")
 }
 
 func TestJavaSdkLibrary_ApiLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1377,12 +1409,13 @@
 	}
 
 	for _, c := range testCases {
-		m := result.ModuleForTests(c.scope.apiLibraryModuleName("foo"), "android_common").Module().(*ApiLibrary)
+		m := result.ModuleForTests(t, c.scope.apiLibraryModuleName("foo"), "android_common").Module().(*ApiLibrary)
 		android.AssertArrayString(t, "Module expected to contain api contributions", c.apiContributions, m.properties.Api_contributions)
 	}
 }
 
 func TestStaticDepStubLibrariesVisibility(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1412,6 +1445,7 @@
 }
 
 func TestSdkLibraryDependency(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1438,12 +1472,13 @@
 		}
 `)
 
-	barPermissions := result.ModuleForTests("bar.xml", "android_common").Output("bar.xml")
+	barPermissions := result.ModuleForTests(t, "bar.xml", "android_common").Output("bar.xml")
 	barContents := android.ContentFromFileRuleForTests(t, result.TestContext, barPermissions)
 	android.AssertStringDoesContain(t, "bar.xml java_sdk_xml command", barContents, `dependency="foo"`)
 }
 
 func TestSdkLibraryExportableStubsLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1480,8 +1515,8 @@
 	exportableSourceStubsLibraryModuleName := apiScopePublic.exportableSourceStubsLibraryModuleName("foo")
 
 	// Check modules generation
-	result.ModuleForTests(exportableStubsLibraryModuleName, "android_common")
-	result.ModuleForTests(exportableSourceStubsLibraryModuleName, "android_common")
+	result.ModuleForTests(t, exportableStubsLibraryModuleName, "android_common")
+	result.ModuleForTests(t, exportableSourceStubsLibraryModuleName, "android_common")
 
 	// Check static lib dependency
 	android.AssertBoolEquals(t, "exportable top level stubs library module depends on the"+
@@ -1494,6 +1529,7 @@
 // For java libraries depending on java_sdk_library(_import) via libs, assert that
 // rdep gets stubs of source if source is listed in apex_contributions and prebuilt has prefer (legacy mechanism)
 func TestStubResolutionOfJavaSdkLibraryInLibs(t *testing.T) {
+	t.Parallel()
 	bp := `
 		apex_contributions {
 			name: "my_mainline_module_contributions",
@@ -1539,14 +1575,16 @@
 
 	result := fixture.RunTestWithBp(t, bp)
 	// Make sure that rdeps get the correct source vs prebuilt based on mainline_module_contributions
-	public := result.ModuleForTests("mymodule", "android_common")
+	public := result.ModuleForTests(t, "mymodule", "android_common")
 	rule := public.Output("javac/mymodule.jar")
 	inputs := rule.Implicits.Strings()
-	android.AssertStringListContains(t, "Could not find the expected stub on classpath", inputs, "out/soong/.intermediates/sdklib.stubs/android_common/turbine-combined/sdklib.stubs.jar")
+	android.AssertStringListContains(t, "Could not find the expected stub on classpath", inputs,
+		"out/soong/.intermediates/sdklib.stubs.from-text/android_common/sdklib.stubs.from-text/sdklib.stubs.from-text.jar")
 }
 
 // test that rdep gets resolved to the correct version of a java_sdk_library (source or a specific prebuilt)
 func TestMultipleSdkLibraryPrebuilts(t *testing.T) {
+	t.Parallel()
 	bp := `
 		apex_contributions {
 			name: "my_mainline_module_contributions",
@@ -1599,17 +1637,17 @@
 		{
 			desc:                   "Source library is selected using apex_contributions",
 			selectedDependencyName: "sdklib",
-			expectedStubPath:       "out/soong/.intermediates/sdklib.stubs/android_common/turbine-combined/sdklib.stubs.jar",
+			expectedStubPath:       "out/soong/.intermediates/sdklib.stubs.from-text/android_common/sdklib.stubs.from-text/sdklib.stubs.from-text.jar",
 		},
 		{
 			desc:                   "Prebuilt library v1 is selected using apex_contributions",
 			selectedDependencyName: "prebuilt_sdklib.v1",
-			expectedStubPath:       "out/soong/.intermediates/prebuilt_sdklib.v1.stubs/android_common/combined/sdklib.stubs.jar",
+			expectedStubPath:       "out/soong/.intermediates/prebuilt_sdklib.v1.stubs/android_common/local-combined/sdklib.stubs.jar",
 		},
 		{
 			desc:                   "Prebuilt library v2 is selected using apex_contributions",
 			selectedDependencyName: "prebuilt_sdklib.v2",
-			expectedStubPath:       "out/soong/.intermediates/prebuilt_sdklib.v2.stubs/android_common/combined/sdklib.stubs.jar",
+			expectedStubPath:       "out/soong/.intermediates/prebuilt_sdklib.v2.stubs/android_common/local-combined/sdklib.stubs.jar",
 		},
 	}
 
@@ -1624,7 +1662,7 @@
 		result := fixture.RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName))
 
 		// Make sure that rdeps get the correct source vs prebuilt based on mainline_module_contributions
-		public := result.ModuleForTests("mymodule", "android_common")
+		public := result.ModuleForTests(t, "mymodule", "android_common")
 		rule := public.Output("javac/mymodule.jar")
 		inputs := rule.Implicits.Strings()
 		android.AssertStringListContains(t, "Could not find the expected stub on classpath", inputs, tc.expectedStubPath)
@@ -1632,6 +1670,7 @@
 }
 
 func TestStubLinkType(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1668,6 +1707,7 @@
 }
 
 func TestSdkLibDirectDependency(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1732,6 +1772,7 @@
 }
 
 func TestSdkLibDirectDependencyWithPrebuiltSdk(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 9bfe6a2..6386a00 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -53,6 +53,7 @@
 }
 
 func TestClasspath(t *testing.T) {
+	t.Parallel()
 	const frameworkAidl = "-I" + defaultJavaDir + "/framework/aidl"
 	var classpathTestcases = []classpathTestCase{
 		{
@@ -388,22 +389,18 @@
 		},
 	}
 
-	t.Parallel()
 	t.Run("basic", func(t *testing.T) {
 		t.Parallel()
-		testClasspathTestCases(t, classpathTestcases, false, false)
+		testClasspathTestCases(t, classpathTestcases, false)
 	})
 
 	t.Run("Always_use_prebuilt_sdks=true", func(t *testing.T) {
-		testClasspathTestCases(t, classpathTestcases, true, false)
-	})
-
-	t.Run("UseTransitiveJarsInClasspath", func(t *testing.T) {
-		testClasspathTestCases(t, classpathTestcases, false, true)
+		t.Parallel()
+		testClasspathTestCases(t, classpathTestcases, true)
 	})
 }
 
-func testClasspathTestCases(t *testing.T, classpathTestcases []classpathTestCase, alwaysUsePrebuiltSdks, useTransitiveJarsInClasspath bool) {
+func testClasspathTestCases(t *testing.T, classpathTestcases []classpathTestCase, alwaysUsePrebuiltSdks bool) {
 	for _, testcase := range classpathTestcases {
 		if testcase.forAlwaysUsePrebuiltSdks != nil && *testcase.forAlwaysUsePrebuiltSdks != alwaysUsePrebuiltSdks {
 			continue
@@ -444,10 +441,8 @@
 					switch {
 					case e == `""`, strings.HasSuffix(e, ".jar"):
 						ret[i] = e
-					case useTransitiveJarsInClasspath:
-						ret[i] = filepath.Join("out", "soong", ".intermediates", defaultJavaDir, e, "android_common", "turbine", e+".jar")
 					default:
-						ret[i] = filepath.Join("out", "soong", ".intermediates", defaultJavaDir, e, "android_common", "turbine-combined", e+".jar")
+						ret[i] = filepath.Join("out", "soong", ".intermediates", defaultJavaDir, e, "android_common", "turbine", e+".jar")
 					}
 				}
 				return ret
@@ -499,7 +494,7 @@
 			}
 
 			checkClasspath := func(t *testing.T, result *android.TestResult, isJava8 bool) {
-				foo := result.ModuleForTests("foo", variant(result))
+				foo := result.ModuleForTests(t, "foo", variant(result))
 				javac := foo.Rule("javac")
 				var deps []string
 
@@ -542,9 +537,6 @@
 					variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
 				})
 			}
-			if useTransitiveJarsInClasspath {
-				preparer = PrepareForTestWithTransitiveClasspathEnabled
-			}
 
 			fixtureFactory := android.GroupFixturePreparers(
 				prepareForJavaTest,
@@ -571,12 +563,13 @@
 
 			// Test with legacy javac -source 1.8 -target 1.8
 			t.Run("Java language level 8", func(t *testing.T) {
+				t.Parallel()
 				result := fixtureFactory.RunTestWithBp(t, bpJava8)
 
 				checkClasspath(t, result, true /* isJava8 */)
 
 				if testcase.host != android.Host {
-					aidl := result.ModuleForTests("foo", variant(result)).Rule("aidl")
+					aidl := result.ModuleForTests(t, "foo", variant(result)).Rule("aidl")
 
 					android.AssertStringDoesContain(t, "aidl command", aidl.RuleParams.Command, testcase.aidl+" -I.")
 				}
@@ -584,12 +577,13 @@
 
 			// Test with default javac -source 9 -target 9
 			t.Run("Java language level 9", func(t *testing.T) {
+				t.Parallel()
 				result := fixtureFactory.RunTestWithBp(t, bp)
 
 				checkClasspath(t, result, false /* isJava8 */)
 
 				if testcase.host != android.Host {
-					aidl := result.ModuleForTests("foo", variant(result)).Rule("aidl")
+					aidl := result.ModuleForTests(t, "foo", variant(result)).Rule("aidl")
 
 					android.AssertStringDoesContain(t, "aidl command", aidl.RuleParams.Command, testcase.aidl+" -I.")
 				}
@@ -602,6 +596,7 @@
 
 			// Test again with PLATFORM_VERSION_CODENAME=REL, javac -source 8 -target 8
 			t.Run("REL + Java language level 8", func(t *testing.T) {
+				t.Parallel()
 				result := android.GroupFixturePreparers(
 					fixtureFactory, prepareWithPlatformVersionRel).RunTestWithBp(t, bpJava8)
 
@@ -610,6 +605,7 @@
 
 			// Test again with PLATFORM_VERSION_CODENAME=REL, javac -source 9 -target 9
 			t.Run("REL + Java language level 9", func(t *testing.T) {
+				t.Parallel()
 				result := android.GroupFixturePreparers(
 					fixtureFactory, prepareWithPlatformVersionRel).RunTestWithBp(t, bp)
 
diff --git a/java/sdk_version_test.go b/java/sdk_version_test.go
index 88351d2..03d55f7 100644
--- a/java/sdk_version_test.go
+++ b/java/sdk_version_test.go
@@ -25,6 +25,7 @@
 }
 
 func TestSystemSdkFromVendor(t *testing.T) {
+	t.Parallel()
 	fixtures := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -57,7 +58,7 @@
 			vendor: true,
 			sdk_version: "system_current",
 		}`)
-	fooModule := result.ModuleForTests("foo", "android_common")
+	fooModule := result.ModuleForTests(t, "foo", "android_common")
 	fooClasspath := fooModule.Rule("javac").Args["classpath"]
 
 	android.AssertStringDoesContain(t, "foo classpath", fooClasspath, "prebuilts/sdk/34/system/android.jar")
diff --git a/java/support_libraries.go b/java/support_libraries.go
index c483fc1..f76eb11 100644
--- a/java/support_libraries.go
+++ b/java/support_libraries.go
@@ -28,7 +28,7 @@
 func supportLibrariesMakeVarsProvider(ctx android.MakeVarsContext) {
 	var supportAars, supportJars []string
 
-	ctx.VisitAllModules(func(module android.Module) {
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
 		dir := ctx.ModuleDir(module)
 		switch {
 		case strings.HasPrefix(dir, "prebuilts/sdk/current/extras"),
@@ -47,11 +47,16 @@
 			return
 		}
 
-		switch module.(type) {
-		case *AndroidLibrary, *AARImport:
+		_, isAndroidLibrary := android.OtherModuleProvider(ctx, module, AndroidLibraryInfoProvider)
+		_, isAARImport := android.OtherModuleProvider(ctx, module, AARImportInfoProvider)
+		if isAndroidLibrary || isAARImport {
 			supportAars = append(supportAars, name)
-		case *Library, *Import:
-			supportJars = append(supportJars, name)
+		} else {
+			_, isJavaLibrary := android.OtherModuleProvider(ctx, module, JavaLibraryInfoProvider)
+			_, isJavaPlugin := android.OtherModuleProvider(ctx, module, JavaPluginInfoProvider)
+			if isJavaLibrary && !isJavaPlugin {
+				supportJars = append(supportJars, name)
+			}
 		}
 	})
 
diff --git a/java/system_modules_test.go b/java/system_modules_test.go
index b05b0e4..99301bc 100644
--- a/java/system_modules_test.go
+++ b/java/system_modules_test.go
@@ -51,10 +51,11 @@
 `)
 
 func TestJavaSystemModules(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForJavaTest, addSourceSystemModules).RunTest(t)
 
 	// check the existence of the source module
-	sourceSystemModules := result.ModuleForTests("system-modules", "android_common")
+	sourceSystemModules := result.ModuleForTests(t, "system-modules", "android_common")
 	sourceInputs := sourceSystemModules.Rule("jarsTosystemModules").Inputs
 
 	// The expected paths are the header jars from the source input modules.
@@ -78,10 +79,11 @@
 `)
 
 func TestJavaSystemModulesImport(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForJavaTest, addPrebuiltSystemModules).RunTest(t)
 
 	// check the existence of the renamed prebuilt module
-	prebuiltSystemModules := result.ModuleForTests("system-modules", "android_common")
+	prebuiltSystemModules := result.ModuleForTests(t, "system-modules", "android_common")
 	prebuiltInputs := prebuiltSystemModules.Rule("jarsTosystemModules").Inputs
 
 	// The expected paths are the header jars from the renamed prebuilt input modules.
@@ -90,6 +92,7 @@
 }
 
 func TestJavaSystemModulesMixSourceAndPrebuilt(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		addSourceSystemModules,
@@ -97,7 +100,7 @@
 	).RunTest(t)
 
 	// check the existence of the source module
-	sourceSystemModules := result.ModuleForTests("system-modules", "android_common")
+	sourceSystemModules := result.ModuleForTests(t, "system-modules", "android_common")
 	sourceInputs := sourceSystemModules.Rule("jarsTosystemModules").Inputs
 
 	// The expected paths are the header jars from the source input modules.
@@ -105,7 +108,7 @@
 	android.AssertArrayString(t, "source system modules inputs", expectedSourcePaths, sourceInputs.RelativeToTop().Strings())
 
 	// check the existence of the renamed prebuilt module
-	prebuiltSystemModules := result.ModuleForTests("prebuilt_system-modules", "android_common")
+	prebuiltSystemModules := result.ModuleForTests(t, "prebuilt_system-modules", "android_common")
 	prebuiltInputs := prebuiltSystemModules.Rule("jarsTosystemModules").Inputs
 
 	// The expected paths are the header jars from the renamed prebuilt input modules.
@@ -114,6 +117,7 @@
 }
 
 func TestMultipleSystemModulesPrebuilts(t *testing.T) {
+	t.Parallel()
 	bp := `
 		// an rdep
 		java_library {
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index 3176ad9..a60f6b8 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -58,6 +58,10 @@
 	return m
 }
 
+func (m *platformSystemServerClasspathModule) UniqueApexVariations() bool {
+	return true
+}
+
 func (p *platformSystemServerClasspathModule) AndroidMkEntries() (entries []android.AndroidMkEntries) {
 	return p.classpathFragmentBase().androidMkEntries()
 }
@@ -91,8 +95,10 @@
 	properties systemServerClasspathFragmentProperties
 }
 
-func (s *SystemServerClasspathModule) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
-	return nil
+var _ android.ApexModule = (*SystemServerClasspathModule)(nil)
+
+func (m *SystemServerClasspathModule) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
+	return android.MinApiLevel
 }
 
 type systemServerClasspathFragmentProperties struct {
@@ -116,6 +122,10 @@
 	return m
 }
 
+func (m *SystemServerClasspathModule) UniqueApexVariations() bool {
+	return true
+}
+
 func (s *SystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if len(s.properties.Contents.GetOrDefault(ctx, nil)) == 0 && len(s.properties.Standalone_contents.GetOrDefault(ctx, nil)) == 0 {
 		ctx.PropertyErrorf("contents", "Either contents or standalone_contents needs to be non-empty")
diff --git a/java/systemserver_classpath_fragment_test.go b/java/systemserver_classpath_fragment_test.go
index ba328e7..704f5a4 100644
--- a/java/systemserver_classpath_fragment_test.go
+++ b/java/systemserver_classpath_fragment_test.go
@@ -25,6 +25,7 @@
 )
 
 func TestPlatformSystemServerClasspathVariant(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithSystemServerClasspath,
 		android.FixtureWithRootAndroidBp(`
@@ -39,6 +40,7 @@
 }
 
 func TestPlatformSystemServerClasspath_ClasspathFragmentPaths(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithSystemServerClasspath,
 		android.FixtureWithRootAndroidBp(`
@@ -50,10 +52,11 @@
 
 	p := result.Module("platform-systemserverclasspath", "android_common").(*platformSystemServerClasspathModule)
 	android.AssertStringEquals(t, "output filepath", "systemserverclasspath.pb", p.ClasspathFragmentBase.outputFilepath.Base())
-	android.AssertPathRelativeToTopEquals(t, "install filepath", "out/soong/target/product/test_device/system/etc/classpaths", p.ClasspathFragmentBase.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install filepath", "out/target/product/test_device/system/etc/classpaths", p.ClasspathFragmentBase.installDirPath)
 }
 
 func TestPlatformSystemServerClasspathModule_AndroidMkEntries(t *testing.T) {
+	t.Parallel()
 	preparer := android.GroupFixturePreparers(
 		prepareForTestWithSystemServerClasspath,
 		android.FixtureWithRootAndroidBp(`
@@ -97,6 +100,7 @@
 }
 
 func TestSystemServerClasspathFragmentWithoutContents(t *testing.T) {
+	t.Parallel()
 	prepareForTestWithSystemServerClasspath.
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
 			`\QEither contents or standalone_contents needs to be non-empty\E`)).
diff --git a/java/testing.go b/java/testing.go
index ba33d90..a6e72cf 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -18,7 +18,6 @@
 	"fmt"
 	"reflect"
 	"regexp"
-	"sort"
 	"strings"
 	"testing"
 
@@ -237,29 +236,6 @@
 	)
 }
 
-func FixtureWithPrebuiltIncrementalApis(apiLevel2Modules map[string][]string) android.FixturePreparer {
-	mockFS := android.MockFS{}
-	path := "prebuilts/sdk/Android.bp"
-
-	bp := fmt.Sprintf(`
-			prebuilt_apis {
-				name: "sdk",
-				api_dirs: ["%s"],
-				allow_incremental_platform_api: true,
-				imports_sdk_version: "none",
-				imports_compile_dex: true,
-			}
-		`, strings.Join(android.SortedKeys(apiLevel2Modules), `", "`))
-
-	for release, modules := range apiLevel2Modules {
-		mockFS.Merge(prebuiltApisFilesForModules([]string{release}, modules))
-	}
-	return android.GroupFixturePreparers(
-		android.FixtureAddTextFile(path, bp),
-		android.FixtureMergeMockFs(mockFS),
-	)
-}
-
 func prebuiltApisFilesForModules(apiLevels []string, modules []string) map[string][]byte {
 	libs := append([]string{"android"}, modules...)
 
@@ -378,7 +354,6 @@
 	RegisterAppBuildComponents(ctx)
 	RegisterAppImportBuildComponents(ctx)
 	RegisterAppSetBuildComponents(ctx)
-	registerBootclasspathBuildComponents(ctx)
 	registerBootclasspathFragmentBuildComponents(ctx)
 	RegisterDexpreoptBootJarsComponents(ctx)
 	RegisterDocsBuildComponents(ctx)
@@ -611,19 +586,18 @@
 
 func getModuleDependencies(t *testing.T, ctx *android.TestContext, name, variant string) []string {
 	t.Helper()
-	module := ctx.ModuleForTests(name, variant).Module()
+	module := ctx.ModuleForTests(t, name, variant).Module()
 	deps := []string{}
 	ctx.VisitDirectDeps(module, func(m blueprint.Module) {
 		deps = append(deps, m.Name())
 	})
-	sort.Strings(deps)
-
-	return deps
+	return android.SortedUniqueStrings(deps)
 }
 
 // CheckModuleDependencies checks if the expected dependencies of the module are
 // identical to the actual dependencies.
 func CheckModuleDependencies(t *testing.T, ctx *android.TestContext, name, variant string, expected []string) {
+	t.Helper()
 	deps := getModuleDependencies(t, ctx, name, variant)
 
 	if actual := deps; !reflect.DeepEqual(expected, actual) {
@@ -643,7 +617,7 @@
 
 // CheckModuleHasDependency returns true if the module depends on the expected dependency.
 func CheckModuleHasDependencyWithTag(t *testing.T, ctx *android.TestContext, name, variant string, desiredTag blueprint.DependencyTag, expected string) bool {
-	module := ctx.ModuleForTests(name, variant).Module()
+	module := ctx.ModuleForTests(t, name, variant).Module()
 	found := false
 	ctx.VisitDirectDepsWithTags(module, func(m blueprint.Module, tag blueprint.DependencyTag) {
 		if tag == desiredTag && m.Name() == expected {
@@ -658,7 +632,7 @@
 func CheckPlatformBootclasspathModules(t *testing.T, result *android.TestResult, name string, expected []string) {
 	t.Helper()
 	platformBootclasspath := result.Module(name, "android_common").(*platformBootclasspathModule)
-	pairs := ApexNamePairsFromModules(result.TestContext, platformBootclasspath.configuredModules)
+	pairs := apexNamePairsFromModules(result.TestContext, platformBootclasspath.configuredModules, platformBootclasspath.libraryToApex)
 	android.AssertDeepEquals(t, fmt.Sprintf("%s modules", "platform-bootclasspath"), expected, pairs)
 }
 
@@ -673,23 +647,54 @@
 	android.AssertPathRelativeToTopEquals(t, "install filepath", installDir, info.ClasspathFragmentProtoInstallDir)
 }
 
-// ApexNamePairsFromModules returns the apex:module pair for the supplied modules.
-func ApexNamePairsFromModules(ctx *android.TestContext, modules []android.Module) []string {
+// CheckPlatformBootclasspathDependencies checks the dependencies of the selected module against the expected list.
+//
+// The expected list must be a list of strings of the form "<apex>:<module>", where <apex> is the
+// name of the apex, or platform is it is not part of an apex and <module> is the module name.
+func CheckPlatformBootclasspathDependencies(t *testing.T, ctx *android.TestContext, name, variant string, expected []string) {
+	t.Helper()
+	platformBootclasspath := ctx.ModuleForTests(t, name, variant).Module().(*platformBootclasspathModule)
+	modules := []android.Module{}
+	ctx.VisitDirectDeps(platformBootclasspath, func(m blueprint.Module) {
+		modules = append(modules, m.(android.Module))
+	})
+
+	pairs := apexNamePairsFromModules(ctx, modules, platformBootclasspath.libraryToApex)
+	android.AssertDeepEquals(t, "module dependencies", expected, pairs)
+}
+
+// apexNamePairsFromModules returns the apex:module pair for the supplied modules.
+func apexNamePairsFromModules(ctx *android.TestContext, modules []android.Module, modulesToApex map[android.Module]string) []string {
 	pairs := []string{}
 	for _, module := range modules {
-		pairs = append(pairs, apexNamePairFromModule(ctx, module))
+		pairs = append(pairs, apexNamePairFromModule(ctx, module, modulesToApex))
 	}
 	return pairs
 }
 
-func apexNamePairFromModule(ctx *android.TestContext, module android.Module) string {
+// ApexFragmentPairsFromModules returns the apex:fragment pair for the supplied fragments.
+func ApexFragmentPairsFromModules(ctx *android.TestContext, fragments []android.Module, apexNameToFragment map[string]android.Module) []string {
+	pairs := []string{}
+	for _, fragment := range fragments {
+		found := false
+		for apex, apexFragment := range apexNameToFragment {
+			if apexFragment == fragment {
+				pairs = append(pairs, apex+":"+ctx.ModuleName(fragment))
+				found = true
+			}
+		}
+		if !found {
+			pairs = append(pairs, "platform:"+ctx.ModuleName(fragment))
+		}
+	}
+	return pairs
+}
+
+func apexNamePairFromModule(ctx *android.TestContext, module android.Module, modulesToApex map[android.Module]string) string {
 	name := module.Name()
-	var apex string
-	apexInfo, _ := android.OtherModuleProvider(ctx, module, android.ApexInfoProvider)
-	if apexInfo.IsForPlatform() {
+	apex := modulesToApex[module]
+	if apex == "" {
 		apex = "platform"
-	} else {
-		apex = apexInfo.InApexVariants[0]
 	}
 
 	return fmt.Sprintf("%s:%s", apex, name)
@@ -700,7 +705,7 @@
 func CheckPlatformBootclasspathFragments(t *testing.T, result *android.TestResult, name string, expected []string) {
 	t.Helper()
 	platformBootclasspath := result.Module(name, "android_common").(*platformBootclasspathModule)
-	pairs := ApexNamePairsFromModules(result.TestContext, platformBootclasspath.fragments)
+	pairs := ApexFragmentPairsFromModules(result.TestContext, platformBootclasspath.fragments, platformBootclasspath.apexNameToFragment)
 	android.AssertDeepEquals(t, fmt.Sprintf("%s fragments", "platform-bootclasspath"), expected, pairs)
 }
 
@@ -723,7 +728,7 @@
 
 // Check that the merged file create by platform_compat_config_singleton has the correct inputs.
 func CheckMergedCompatConfigInputs(t *testing.T, result *android.TestResult, message string, expectedPaths ...string) {
-	sourceGlobalCompatConfig := result.SingletonForTests("platform_compat_config_singleton")
+	sourceGlobalCompatConfig := result.SingletonForTests(t, "platform_compat_config_singleton")
 	allOutputs := sourceGlobalCompatConfig.AllOutputs()
 	android.AssertIntEquals(t, message+": output len", 1, len(allOutputs))
 	output := sourceGlobalCompatConfig.Output(allOutputs[0])
@@ -801,5 +806,3 @@
 		config.installDir = installDir
 	})
 }
-
-var PrepareForTestWithTransitiveClasspathEnabled = android.PrepareForTestWithBuildFlag("RELEASE_USE_TRANSITIVE_JARS_IN_CLASSPATH", "true")
diff --git a/java/tracereferences.go b/java/tracereferences.go
new file mode 100644
index 0000000..342b6a6
--- /dev/null
+++ b/java/tracereferences.go
@@ -0,0 +1,54 @@
+// Copyright 2025 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"
+
+	"github.com/google/blueprint"
+)
+
+var traceReferences = pctx.AndroidStaticRule("traceReferences",
+	blueprint.RuleParams{
+		Command: `${config.TraceReferencesCmd} ` +
+			// Note that we suppress missing def errors, as we're only interested
+			// in the direct deps between the sources and target.
+			`--map-diagnostics:MissingDefinitionsDiagnostic error none ` +
+			`--keep-rules ` +
+			`--output ${out} ` +
+			`--target ${in} ` +
+			// `--source` and `--lib` are already prepended to each
+			// jar reference in the sources and libs joined string args.
+			`${sources} ` +
+			`${libs}`,
+		CommandDeps: []string{"${config.TraceReferencesCmd}"},
+	}, "sources", "libs")
+
+// Generates keep rules in output corresponding to any references from sources
+// (a list of jars) onto target (the referenced jar) that are not included in
+// libs (a list of external jars).
+func TraceReferences(ctx android.ModuleContext, sources android.Paths, target android.Path, libs android.Paths,
+	output android.WritablePath) {
+	ctx.Build(pctx, android.BuildParams{
+		Rule:      traceReferences,
+		Input:     target,
+		Output:    output,
+		Implicits: append(sources, libs...),
+		Args: map[string]string{
+			"sources": android.JoinWithPrefix(sources.Strings(), "--source "),
+			"libs":    android.JoinWithPrefix(libs.Strings(), "--lib "),
+		},
+	})
+}
diff --git a/kernel/prebuilt_kernel_modules.go b/kernel/prebuilt_kernel_modules.go
index 001a1e7..1225da0 100644
--- a/kernel/prebuilt_kernel_modules.go
+++ b/kernel/prebuilt_kernel_modules.go
@@ -67,6 +67,10 @@
 
 	// Whether this module is directly installable to one of the partitions. Default is true
 	Installable *bool
+
+	// Whether debug symbols should be stripped from the *.ko files.
+	// Defaults to true.
+	Strip_debug_symbols *bool
 }
 
 // prebuilt_kernel_modules installs a set of prebuilt kernel module files to the correct directory.
@@ -100,7 +104,9 @@
 	systemModules := android.PathsForModuleSrc(ctx, pkm.properties.System_deps)
 
 	depmodOut := pkm.runDepmod(ctx, modules, systemModules)
-	strippedModules := stripDebugSymbols(ctx, modules)
+	if proptools.BoolDefault(pkm.properties.Strip_debug_symbols, true) {
+		modules = stripDebugSymbols(ctx, modules)
+	}
 
 	installDir := android.PathForModuleInstall(ctx, "lib", "modules")
 	// Kernel module is installed to vendor_ramdisk/lib/modules regardless of product
@@ -114,7 +120,7 @@
 		installDir = installDir.Join(ctx, pkm.KernelVersion())
 	}
 
-	for _, m := range strippedModules {
+	for _, m := range modules {
 		ctx.InstallFile(installDir, filepath.Base(m.String()), m)
 	}
 	ctx.InstallFile(installDir, "modules.load", depmodOut.modulesLoad)
@@ -165,9 +171,9 @@
 		}, "stripCmd")
 )
 
-func stripDebugSymbols(ctx android.ModuleContext, modules android.Paths) android.OutputPaths {
+func stripDebugSymbols(ctx android.ModuleContext, modules android.Paths) android.Paths {
 	dir := android.PathForModuleOut(ctx, "stripped").OutputPath
-	var outputs android.OutputPaths
+	var outputs android.Paths
 
 	for _, m := range modules {
 		stripped := dir.Join(ctx, filepath.Base(m.String()))
@@ -241,6 +247,8 @@
 		return modulesDir.Join(ctx, "vendor", "lib", "modules")
 	} else if ctx.InstallInOdmDlkm() {
 		return modulesDir.Join(ctx, "odm", "lib", "modules")
+	} else if ctx.InstallInVendorRamdisk() {
+		return modulesDir.Join(ctx, "lib", "modules")
 	} else {
 		// not an android dlkm module.
 		return modulesDir
@@ -303,9 +311,9 @@
 	builder.Build("depmod", fmt.Sprintf("depmod %s", ctx.ModuleName()))
 
 	finalModulesDep := modulesDep
-	// Add a leading slash to paths in modules.dep of android dlkm
-	if ctx.InstallInSystemDlkm() || ctx.InstallInVendorDlkm() || ctx.InstallInOdmDlkm() {
-		finalModulesDep := modulesDep.ReplaceExtension(ctx, "intermediates")
+	// Add a leading slash to paths in modules.dep of android dlkm and vendor ramdisk
+	if ctx.InstallInSystemDlkm() || ctx.InstallInVendorDlkm() || ctx.InstallInOdmDlkm() || ctx.InstallInVendorRamdisk() {
+		finalModulesDep = modulesDep.ReplaceExtension(ctx, "intermediates")
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   addLeadingSlashToPaths,
 			Input:  modulesDep,
diff --git a/kernel/prebuilt_kernel_modules_test.go b/kernel/prebuilt_kernel_modules_test.go
index 7b81869..0fc2720 100644
--- a/kernel/prebuilt_kernel_modules_test.go
+++ b/kernel/prebuilt_kernel_modules_test.go
@@ -50,7 +50,7 @@
 
 	var actual []string
 	for _, ps := range android.OtherModuleProviderOrDefault(
-		ctx, ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module(), android.InstallFilesProvider).PackagingSpecs {
+		ctx, ctx.ModuleForTests(t, "foo", "android_arm64_armv8-a").Module(), android.InstallFilesProvider).PackagingSpecs {
 		actual = append(actual, ps.RelPathInPackage())
 	}
 	actual = android.SortedUniqueStrings(actual)
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index d422871..925d7b1 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -76,9 +76,7 @@
 	input := android.PathForModuleSrc(ctx, android.String(l.properties.Src))
 	output := android.PathForModuleOut(ctx, "linker.config.pb").OutputPath
 
-	builder := android.NewRuleBuilder(pctx, ctx)
-	BuildLinkerConfig(ctx, builder, android.Paths{input}, nil, nil, output)
-	builder.Build("conv_linker_config", "Generate linker config protobuf "+output.String())
+	BuildLinkerConfig(ctx, android.Paths{input}, nil, nil, output)
 
 	l.outputFilePath = output
 	l.installDirPath = android.PathForModuleInstall(ctx, "etc")
@@ -88,12 +86,19 @@
 	ctx.InstallFile(l.installDirPath, l.outputFilePath.Base(), l.outputFilePath)
 
 	ctx.SetOutputFiles(android.Paths{l.outputFilePath}, "")
+
+	etc.SetCommonPrebuiltEtcInfo(ctx, l)
 }
 
-func BuildLinkerConfig(ctx android.ModuleContext, builder *android.RuleBuilder,
-	inputs android.Paths, provideModules []android.Module, requireModules []android.Module, output android.OutputPath) {
-
+func BuildLinkerConfig(
+	ctx android.ModuleContext,
+	inputs android.Paths,
+	provideModules []android.ModuleProxy,
+	requireModules []android.ModuleProxy,
+	output android.WritablePath,
+) {
 	// First, convert the input json to protobuf format
+	builder := android.NewRuleBuilder(pctx, ctx)
 	interimOutput := android.PathForModuleOut(ctx, "temp.pb")
 	cmd := builder.Command().
 		BuiltTool("conv_linker_config").
@@ -107,9 +112,10 @@
 	// Secondly, if there's provideLibs gathered from provideModules, append them
 	var provideLibs []string
 	for _, m := range provideModules {
-		if c, ok := m.(*cc.Module); ok && (cc.IsStubTarget(c) || c.HasLlndkStubs()) {
+		ccInfo, ok := android.OtherModuleProvider(ctx, m, cc.CcInfoProvider)
+		if ok && (cc.IsStubTarget(android.OtherModuleProviderOrDefault(ctx, m, cc.LinkableInfoProvider)) || ccInfo.HasLlndkStubs) {
 			for _, ps := range android.OtherModuleProviderOrDefault(
-				ctx, c, android.InstallFilesProvider).PackagingSpecs {
+				ctx, m, android.InstallFilesProvider).PackagingSpecs {
 				provideLibs = append(provideLibs, ps.FileName())
 			}
 		}
@@ -119,8 +125,15 @@
 
 	var requireLibs []string
 	for _, m := range requireModules {
-		if c, ok := m.(*cc.Module); ok && c.HasStubsVariants() && !c.Host() {
-			requireLibs = append(requireLibs, c.ImplementationModuleName(ctx)+".so")
+		if _, ok := android.OtherModuleProvider(ctx, m, cc.CcInfoProvider); ok {
+			if android.OtherModuleProviderOrDefault(ctx, m, cc.LinkableInfoProvider).HasStubsVariants &&
+				!android.OtherModulePointerProviderOrDefault(ctx, m, android.CommonModuleInfoProvider).Host {
+				name := ctx.OtherModuleName(m)
+				if ccInfo, ok := android.OtherModuleProvider(ctx, m, cc.CcInfoProvider); ok && ccInfo.LinkerInfo != nil && ccInfo.LinkerInfo.ImplementationModuleName != nil {
+					name = *ccInfo.LinkerInfo.ImplementationModuleName
+				}
+				requireLibs = append(requireLibs, name+".so")
+			}
 		}
 	}
 
@@ -157,6 +170,7 @@
 
 	builder.Temporary(interimOutput)
 	builder.DeleteTemporaryFiles()
+	builder.Build("conv_linker_config_"+output.String(), "Generate linker config protobuf "+output.String())
 }
 
 // linker_config generates protobuf file from json file. This protobuf file will be used from
diff --git a/linkerconfig/linkerconfig_test.go b/linkerconfig/linkerconfig_test.go
index 939e4bb..9e08b19 100644
--- a/linkerconfig/linkerconfig_test.go
+++ b/linkerconfig/linkerconfig_test.go
@@ -46,7 +46,7 @@
 		"LOCAL_INSTALLED_MODULE_STEM": {"linker.config.pb"},
 	}
 
-	p := result.ModuleForTests("linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig)
+	p := result.ModuleForTests(t, "linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig)
 
 	if p.outputFilePath.Base() != "linker.config.pb" {
 		t.Errorf("expected linker.config.pb, got %q", p.outputFilePath.Base())
@@ -79,7 +79,7 @@
 
 	expected := []string{"true"}
 
-	p := result.ModuleForTests("linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig)
+	p := result.ModuleForTests(t, "linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig)
 	entries := android.AndroidMkEntriesForTest(t, result.TestContext, p)[0]
 	if value, ok := entries.EntryMap["LOCAL_UNINSTALLABLE_MODULE"]; ok {
 		if !reflect.DeepEqual(value, expected) {
diff --git a/phony/phony.go b/phony/phony.go
index 807b95b..e75f4c8 100644
--- a/phony/phony.go
+++ b/phony/phony.go
@@ -38,9 +38,11 @@
 
 type phony struct {
 	android.ModuleBase
+
 	requiredModuleNames       []string
 	hostRequiredModuleNames   []string
 	targetRequiredModuleNames []string
+	outputDeps                android.Paths
 }
 
 func PhonyFactory() android.Module {
@@ -54,6 +56,14 @@
 	p.requiredModuleNames = ctx.RequiredModuleNames(ctx)
 	p.hostRequiredModuleNames = ctx.HostRequiredModuleNames()
 	p.targetRequiredModuleNames = ctx.TargetRequiredModuleNames()
+
+	ctx.VisitDirectDepsWithTag(android.RequiredDepTag, func(dep android.Module) {
+		if o, ok := android.OtherModuleProvider(ctx, dep, android.OutputFilesProvider); ok {
+			p.outputDeps = append(p.outputDeps, o.DefaultOutputFiles...)
+		}
+	})
+
+	ctx.Phony(p.Name(), p.outputDeps...)
 }
 
 func (p *phony) AndroidMk() android.AndroidMkData {
@@ -77,6 +87,10 @@
 				fmt.Fprintln(w, "LOCAL_TARGET_REQUIRED_MODULES :=",
 					strings.Join(p.targetRequiredModuleNames, " "))
 			}
+			if len(p.outputDeps) > 0 {
+				fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES :=",
+					strings.Join(p.outputDeps.Strings(), " "))
+			}
 			// AconfigUpdateAndroidMkData may have added elements to Extra.  Process them here.
 			for _, extra := range data.Extra {
 				extra(w, nil)
@@ -90,8 +104,7 @@
 	android.ModuleBase
 	android.DefaultableModuleBase
 
-	phonyDepsModuleNames []string
-	properties           PhonyProperties
+	properties PhonyProperties
 }
 
 type PhonyProperties struct {
@@ -112,18 +125,8 @@
 }
 
 func (p *PhonyRule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	p.phonyDepsModuleNames = p.properties.Phony_deps.GetOrDefault(ctx, nil)
-}
-
-func (p *PhonyRule) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
-		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-			if len(p.phonyDepsModuleNames) > 0 {
-				depModulesStr := strings.Join(p.phonyDepsModuleNames, " ")
-				fmt.Fprintln(w, ".PHONY:", name)
-				fmt.Fprintln(w, name, ":", depModulesStr)
-			}
-		},
+	for _, dep := range p.properties.Phony_deps.GetOrDefault(ctx, nil) {
+		ctx.Phony(ctx.ModuleName(), android.PathForPhony(ctx, dep))
 	}
 }
 
diff --git a/provenance/provenance_metadata_proto/Android.bp b/provenance/provenance_metadata_proto/Android.bp
index 7fc47a9..b4176a5 100644
--- a/provenance/provenance_metadata_proto/Android.bp
+++ b/provenance/provenance_metadata_proto/Android.bp
@@ -20,11 +20,6 @@
 
 python_library_host {
     name: "provenance_metadata_proto",
-    version: {
-        py3: {
-            enabled: true,
-        },
-    },
     srcs: [
         "provenance_metadata.proto",
     ],
diff --git a/provenance/provenance_singleton.go b/provenance/provenance_singleton.go
index c372db2..c1bc1c7 100644
--- a/provenance/provenance_singleton.go
+++ b/provenance/provenance_singleton.go
@@ -99,6 +99,7 @@
 	})
 
 	context.Phony("droidcore", android.PathForPhony(context, "provenance_metadata"))
+	context.DistForGoal("droidcore", p.mergedMetaDataFile)
 }
 
 func GenerateArtifactProvenanceMetaData(ctx android.ModuleContext, artifactPath android.Path, installedFile android.InstallPath) android.Path {
@@ -116,9 +117,3 @@
 
 	return artifactMetaDataFile
 }
-
-func (p *provenanceInfoSingleton) MakeVars(ctx android.MakeVarsContext) {
-	ctx.DistForGoal("droidcore", p.mergedMetaDataFile)
-}
-
-var _ android.SingletonMakeVarsProvider = (*provenanceInfoSingleton)(nil)
diff --git a/provenance/provenance_singleton_test.go b/provenance/provenance_singleton_test.go
index 0f1eae2..05f3474 100644
--- a/provenance/provenance_singleton_test.go
+++ b/provenance/provenance_singleton_test.go
@@ -28,9 +28,9 @@
 		PrepareForTestWithProvenanceSingleton,
 		android.PrepareForTestWithAndroidMk).RunTestWithBp(t, "")
 
-	outputs := result.SingletonForTests("provenance_metadata_singleton").AllOutputs()
+	outputs := result.SingletonForTests(t, "provenance_metadata_singleton").AllOutputs()
 	for _, output := range outputs {
-		testingBuildParam := result.SingletonForTests("provenance_metadata_singleton").Output(output)
+		testingBuildParam := result.SingletonForTests(t, "provenance_metadata_singleton").Output(output)
 		switch {
 		case strings.Contains(output, "soong/provenance_metadata.textproto"):
 			android.AssertStringEquals(t, "Invalid build rule", "android/soong/provenance.mergeProvenanceMetaData", testingBuildParam.Rule.String())
diff --git a/python/binary.go b/python/binary.go
index 5f60761..f894299 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -22,8 +22,15 @@
 	"strings"
 
 	"android/soong/android"
+	"android/soong/cc"
+
+	"github.com/google/blueprint"
 )
 
+type PythonBinaryInfo struct{}
+
+var PythonBinaryInfoProvider = blueprint.NewProvider[PythonBinaryInfo]()
+
 func init() {
 	registerPythonBinaryComponents(android.InitRegistrationContext)
 }
@@ -103,12 +110,22 @@
 	p.buildBinary(ctx)
 	p.installedDest = ctx.InstallFile(installDir(ctx, "bin", "", ""),
 		p.installSource.Base(), p.installSource)
+
+	android.SetProvider(ctx, PythonBinaryInfoProvider, PythonBinaryInfo{})
+
 	ctx.SetOutputFiles(android.Paths{p.installSource}, "")
+
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Class = []string{"EXECUTABLES"}
+	moduleInfoJSON.Dependencies = append(moduleInfoJSON.Dependencies, p.androidMkSharedLibs...)
+	moduleInfoJSON.SharedLibs = append(moduleInfoJSON.SharedLibs, p.androidMkSharedLibs...)
+	moduleInfoJSON.SystemSharedLibs = []string{"none"}
 }
 
 func (p *PythonBinaryModule) buildBinary(ctx android.ModuleContext) {
 	embeddedLauncher := p.isEmbeddedLauncherEnabled()
 	depsSrcsZips := p.collectPathsFromTransitiveDeps(ctx, embeddedLauncher)
+	bundleSharedLibs := p.collectSharedLibDeps(ctx)
 	main := ""
 	if p.autorun() {
 		main = p.getPyMainFile(ctx, p.srcsPathMappings)
@@ -116,13 +133,13 @@
 
 	var launcherPath android.OptionalPath
 	if embeddedLauncher {
-		ctx.VisitDirectDepsWithTag(launcherTag, func(m android.Module) {
-			if provider, ok := m.(IntermPathProvider); ok {
+		ctx.VisitDirectDepsProxyWithTag(launcherTag, func(m android.ModuleProxy) {
+			if provider, ok := android.OtherModuleProvider(ctx, m, cc.LinkableInfoProvider); ok {
 				if launcherPath.Valid() {
 					panic(fmt.Errorf("launcher path was found before: %q",
 						launcherPath))
 				}
-				launcherPath = provider.IntermPathForModuleOut()
+				launcherPath = provider.OutputFile
 			}
 		})
 	}
@@ -133,17 +150,25 @@
 		srcsZips = append(srcsZips, p.srcsZip)
 	}
 	srcsZips = append(srcsZips, depsSrcsZips...)
+	if ctx.Host() && len(bundleSharedLibs) > 0 {
+		// only bundle shared libs for host binaries
+		sharedLibZip := p.zipSharedLibs(ctx, bundleSharedLibs)
+		srcsZips = append(srcsZips, sharedLibZip)
+	}
 	p.installSource = registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
-		p.getHostInterpreterName(ctx, p.properties.Actual_version),
-		main, p.getStem(ctx), srcsZips)
+		"python3", main, p.getStem(ctx), srcsZips)
 
 	var sharedLibs []string
 	// if embedded launcher is enabled, we need to collect the shared library dependencies of the
 	// launcher
-	for _, dep := range ctx.GetDirectDepsWithTag(launcherSharedLibTag) {
+	for _, dep := range ctx.GetDirectDepsProxyWithTag(launcherSharedLibTag) {
 		sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep))
 	}
 	p.androidMkSharedLibs = sharedLibs
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: p.binaryProperties.Test_suites,
+	})
 }
 
 func (p *PythonBinaryModule) AndroidMkEntries() []android.AndroidMkEntries {
@@ -196,23 +221,6 @@
 	return BoolDefault(b.binaryProperties.Autorun, true)
 }
 
-// get host interpreter name.
-func (p *PythonBinaryModule) getHostInterpreterName(ctx android.ModuleContext,
-	actualVersion string) string {
-	var interp string
-	switch actualVersion {
-	case pyVersion2:
-		interp = "python2.7"
-	case pyVersion3:
-		interp = "python3"
-	default:
-		panic(fmt.Errorf("unknown Python actualVersion: %q for module: %q.",
-			actualVersion, ctx.ModuleName()))
-	}
-
-	return interp
-}
-
 // find main program path within runfiles tree.
 func (p *PythonBinaryModule) getPyMainFile(ctx android.ModuleContext,
 	srcsPathMappings []pathMapping) string {
diff --git a/python/defaults.go b/python/defaults.go
index 3dc5bc4..b5ee2bc 100644
--- a/python/defaults.go
+++ b/python/defaults.go
@@ -18,10 +18,6 @@
 	"android/soong/android"
 )
 
-func init() {
-	android.RegisterModuleType("python_defaults", DefaultsFactory)
-}
-
 type Defaults struct {
 	android.ModuleBase
 	android.DefaultsModuleBase
diff --git a/python/library.go b/python/library.go
index 7cdb80b..c197028 100644
--- a/python/library.go
+++ b/python/library.go
@@ -27,6 +27,7 @@
 func registerPythonLibraryComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("python_library_host", PythonLibraryHostFactory)
 	ctx.RegisterModuleType("python_library", PythonLibraryFactory)
+	ctx.RegisterModuleType("python_defaults", DefaultsFactory)
 }
 
 func PythonLibraryHostFactory() android.Module {
diff --git a/python/python.go b/python/python.go
index d3e5743..de21e39 100644
--- a/python/python.go
+++ b/python/python.go
@@ -20,26 +20,28 @@
 	"fmt"
 	"path/filepath"
 	"regexp"
+	"sort"
 	"strings"
 
+	"android/soong/cc"
+
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/depset"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
 )
 
-func init() {
-	registerPythonMutators(android.InitRegistrationContext)
+type PythonLibraryInfo struct {
+	SrcsPathMappings   []pathMapping
+	DataPathMappings   []pathMapping
+	SrcsZip            android.Path
+	PrecompiledSrcsZip android.Path
+	PkgPath            string
+	BundleSharedLibs   android.Paths
 }
 
-func registerPythonMutators(ctx android.RegistrationContext) {
-	ctx.PreDepsMutators(RegisterPythonPreDepsMutators)
-}
-
-// Exported to support other packages using Python modules in tests.
-func RegisterPythonPreDepsMutators(ctx android.RegisterMutatorsContext) {
-	ctx.Transition("python_version", &versionSplitTransitionMutator{})
-}
+var PythonLibraryInfoProvider = blueprint.NewProvider[PythonLibraryInfo]()
 
 // the version-specific properties that apply to python modules.
 type VersionProperties struct {
@@ -95,12 +97,21 @@
 	// device.
 	Device_common_data []string `android:"path_device_common"`
 
+	// Same as data, but will add dependencies on modules via a device os variation and the
+	// device's first supported arch's variation. Useful for a host test that wants to embed a
+	// module built for device.
+	Device_first_data []string `android:"path_device_first"`
+
 	// list of java modules that provide data that should be installed alongside the test.
 	Java_data []string
 
 	// list of the Python libraries compatible both with Python2 and Python3.
 	Libs []string `android:"arch_variant"`
 
+	// TODO: b/403060602 - add unit tests for this property and related code
+	// list of shared libraries that should be packaged with the python code for this module.
+	Shared_libs []string `android:"arch_variant"`
+
 	Version struct {
 		// Python2-specific properties, including whether Python2 is supported for this module
 		// and version-specific sources, exclusions and dependencies.
@@ -111,18 +122,14 @@
 		Py3 VersionProperties `android:"arch_variant"`
 	} `android:"arch_variant"`
 
-	// the actual version each module uses after variations created.
-	// this property name is hidden from users' perspectives, and soong will populate it during
-	// runtime.
-	Actual_version string `blueprint:"mutated"`
-
-	// whether the module is required to be built with actual_version.
-	// this is set by the python version mutator based on version-specific properties
+	// This enabled property is to accept the collapsed enabled property from the VersionProperties.
+	// It is unused now, as all builds should be python3.
 	Enabled *bool `blueprint:"mutated"`
 
-	// whether the binary is required to be built with embedded launcher for this actual_version.
-	// this is set by the python version mutator based on version-specific properties
-	Embedded_launcher *bool `blueprint:"mutated"`
+	// whether the binary is required to be built with an embedded python interpreter, defaults to
+	// true. This allows taking the resulting binary outside of the build and running it on machines
+	// that don't have python installed or may have an older version of python.
+	Embedded_launcher *bool
 }
 
 // Used to store files of current module after expanding dependencies
@@ -158,6 +165,10 @@
 	precompiledSrcsZip android.Path
 
 	sourceProperties android.SourceProperties
+
+	// The shared libraries that should be bundled with the python code for
+	// any standalone python binaries that depend on this module.
+	bundleSharedLibs android.Paths
 }
 
 // newModule generates new Python base module
@@ -168,16 +179,6 @@
 	}
 }
 
-// interface implemented by Python modules to provide source and data mappings and zip to python
-// modules that depend on it
-type pythonDependency interface {
-	getSrcsPathMappings() []pathMapping
-	getDataPathMappings() []pathMapping
-	getSrcsZip() android.Path
-	getPrecompiledSrcsZip() android.Path
-	getPkgPath() string
-}
-
 // getSrcsPathMappings gets this module's path mapping of src source path : runfiles destination
 func (p *PythonLibraryModule) getSrcsPathMappings() []pathMapping {
 	return p.srcsPathMappings
@@ -207,7 +208,9 @@
 	return &p.properties
 }
 
-var _ pythonDependency = (*PythonLibraryModule)(nil)
+func (p *PythonLibraryModule) getBundleSharedLibs() android.Paths {
+	return p.bundleSharedLibs
+}
 
 func (p *PythonLibraryModule) init() android.Module {
 	p.AddProperties(&p.properties, &p.protoProperties, &p.sourceProperties)
@@ -236,6 +239,7 @@
 var (
 	pythonLibTag = dependencyTag{name: "pythonLib"}
 	javaDataTag  = dependencyTag{name: "javaData"}
+	sharedLibTag = dependencyTag{name: "sharedLib"}
 	// The python interpreter, with soong module name "py3-launcher" or "py3-launcher-autorun".
 	launcherTag          = dependencyTag{name: "launcher"}
 	launcherSharedLibTag = installDependencyTag{name: "launcherSharedLib"}
@@ -248,8 +252,6 @@
 	pathComponentRegexp      = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
 	pyExt                    = ".py"
 	protoExt                 = ".proto"
-	pyVersion2               = "PY2"
-	pyVersion3               = "PY3"
 	internalPath             = "internal"
 )
 
@@ -257,71 +259,6 @@
 	getBaseProperties() *BaseProperties
 }
 
-type versionSplitTransitionMutator struct{}
-
-func (versionSplitTransitionMutator) Split(ctx android.BaseModuleContext) []string {
-	if base, ok := ctx.Module().(basePropertiesProvider); ok {
-		props := base.getBaseProperties()
-		var variants []string
-		// PY3 is first so that we alias the PY3 variant rather than PY2 if both
-		// are available
-		if proptools.BoolDefault(props.Version.Py3.Enabled, true) {
-			variants = append(variants, pyVersion3)
-		}
-		if proptools.BoolDefault(props.Version.Py2.Enabled, false) {
-			if ctx.ModuleName() != "py2-cmd" &&
-				ctx.ModuleName() != "py2-stdlib" {
-				ctx.PropertyErrorf("version.py2.enabled", "Python 2 is no longer supported, please convert to python 3.")
-			}
-			variants = append(variants, pyVersion2)
-		}
-		return variants
-	}
-	return []string{""}
-}
-
-func (versionSplitTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
-	return ""
-}
-
-func (versionSplitTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
-	if incomingVariation != "" {
-		return incomingVariation
-	}
-	if base, ok := ctx.Module().(basePropertiesProvider); ok {
-		props := base.getBaseProperties()
-		if proptools.BoolDefault(props.Version.Py3.Enabled, true) {
-			return pyVersion3
-		} else {
-			return pyVersion2
-		}
-	}
-
-	return ""
-}
-
-func (versionSplitTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
-	if variation == "" {
-		return
-	}
-	if base, ok := ctx.Module().(basePropertiesProvider); ok {
-		props := base.getBaseProperties()
-		props.Actual_version = variation
-
-		var versionProps *VersionProperties
-		if variation == pyVersion3 {
-			versionProps = &props.Version.Py3
-		} else if variation == pyVersion2 {
-			versionProps = &props.Version.Py2
-		}
-
-		err := proptools.AppendMatchingProperties([]interface{}{props}, versionProps, nil)
-		if err != nil {
-			panic(err)
-		}
-	}
-}
-
 func anyHasExt(paths []string, ext string) bool {
 	for _, p := range paths {
 		if filepath.Ext(p) == ext {
@@ -341,25 +278,38 @@
 //   - if required, specifies launcher and adds launcher dependencies,
 //   - applies python version mutations to Python dependencies
 func (p *PythonLibraryModule) DepsMutator(ctx android.BottomUpMutatorContext) {
-	android.ProtoDeps(ctx, &p.protoProperties)
+	// Flatten the version.py3 props down into the main property struct. Leftover from when
+	// there was both python2 and 3 in the build, and properties could be different between them.
+	if base, ok := ctx.Module().(basePropertiesProvider); ok {
+		props := base.getBaseProperties()
 
-	versionVariation := []blueprint.Variation{
-		{"python_version", p.properties.Actual_version},
+		err := proptools.AppendMatchingProperties([]interface{}{props}, &props.Version.Py3, nil)
+		if err != nil {
+			panic(err)
+		}
 	}
 
+	android.ProtoDeps(ctx, &p.protoProperties)
+
 	// If sources contain a proto file, add dependency on libprotobuf-python
 	if p.anySrcHasExt(ctx, protoExt) && p.Name() != "libprotobuf-python" {
-		ctx.AddVariationDependencies(versionVariation, pythonLibTag, "libprotobuf-python")
+		ctx.AddDependency(ctx.Module(), pythonLibTag, "libprotobuf-python")
 	}
 
 	// Add python library dependencies for this python version variation
-	ctx.AddVariationDependencies(versionVariation, pythonLibTag, android.LastUniqueStrings(p.properties.Libs)...)
+	ctx.AddDependency(ctx.Module(), pythonLibTag, android.LastUniqueStrings(p.properties.Libs)...)
 
 	// Emulate the data property for java_data but with the arch variation overridden to "common"
 	// so that it can point to java modules.
 	javaDataVariation := []blueprint.Variation{{"arch", android.Common.String()}}
 	ctx.AddVariationDependencies(javaDataVariation, javaDataTag, p.properties.Java_data...)
 
+	if ctx.Host() {
+		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), sharedLibTag, p.properties.Shared_libs...)
+	} else if len(p.properties.Shared_libs) > 0 {
+		ctx.PropertyErrorf("shared_libs", "shared_libs is not supported for device builds")
+	}
+
 	p.AddDepsOnPythonLauncherAndStdlib(ctx, hostStdLibTag, hostLauncherTag, hostlauncherSharedLibTag, false, ctx.Config().BuildOSTarget)
 }
 
@@ -390,55 +340,38 @@
 		launcherSharedLibDeps = append(launcherSharedLibDeps, "libc_musl")
 	}
 
-	switch p.properties.Actual_version {
-	case pyVersion2:
-		stdLib = "py2-stdlib"
-
-		launcherModule = "py2-launcher"
-		if autorun {
-			launcherModule = "py2-launcher-autorun"
-		}
-
-		launcherSharedLibDeps = append(launcherSharedLibDeps, "libc++")
-	case pyVersion3:
-		var prebuiltStdLib bool
-		if targetForDeps.Os.Bionic() {
-			prebuiltStdLib = false
-		} else if ctx.Config().VendorConfig("cpython3").Bool("force_build_host") {
-			prebuiltStdLib = false
-		} else {
-			prebuiltStdLib = true
-		}
-
-		if prebuiltStdLib {
-			stdLib = "py3-stdlib-prebuilt"
-		} else {
-			stdLib = "py3-stdlib"
-		}
-
-		launcherModule = "py3-launcher"
-		if autorun {
-			launcherModule = "py3-launcher-autorun"
-		}
-		if ctx.Config().HostStaticBinaries() && targetForDeps.Os == android.LinuxMusl {
-			launcherModule += "-static"
-		}
-		if ctx.Device() {
-			launcherSharedLibDeps = append(launcherSharedLibDeps, "liblog")
-		}
-	default:
-		panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
-			p.properties.Actual_version, ctx.ModuleName()))
+	var prebuiltStdLib bool
+	if targetForDeps.Os.Bionic() {
+		prebuiltStdLib = false
+	} else if ctx.Config().VendorConfig("cpython3").Bool("force_build_host") {
+		prebuiltStdLib = false
+	} else {
+		prebuiltStdLib = true
 	}
+
+	if prebuiltStdLib {
+		stdLib = "py3-stdlib-prebuilt"
+	} else {
+		stdLib = "py3-stdlib"
+	}
+
+	launcherModule = "py3-launcher"
+	if autorun {
+		launcherModule = "py3-launcher-autorun"
+	}
+	if ctx.Config().HostStaticBinaries() && targetForDeps.Os == android.LinuxMusl {
+		launcherModule += "-static"
+	}
+	if ctx.Device() {
+		launcherSharedLibDeps = append(launcherSharedLibDeps, "liblog")
+	}
+
 	targetVariations := targetForDeps.Variations()
 	if ctx.ModuleName() != stdLib {
-		stdLibVariations := make([]blueprint.Variation, 0, len(targetVariations)+1)
-		stdLibVariations = append(stdLibVariations, blueprint.Variation{Mutator: "python_version", Variation: p.properties.Actual_version})
-		stdLibVariations = append(stdLibVariations, targetVariations...)
 		// Using AddFarVariationDependencies for all of these because they can be for a different
 		// platform, like if the python module itself was being compiled for device, we may want
 		// the python interpreter built for host so that we can precompile python sources.
-		ctx.AddFarVariationDependencies(stdLibVariations, stdLibTag, stdLib)
+		ctx.AddFarVariationDependencies(targetVariations, stdLibTag, stdLib)
 	}
 	ctx.AddFarVariationDependencies(targetVariations, launcherTag, launcherModule)
 	ctx.AddFarVariationDependencies(targetVariations, launcherSharedLibTag, launcherSharedLibDeps...)
@@ -446,8 +379,10 @@
 
 // GenerateAndroidBuildActions performs build actions common to all Python modules
 func (p *PythonLibraryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if proptools.BoolDefault(p.properties.Version.Py2.Enabled, false) {
+		ctx.PropertyErrorf("version.py2.enabled", "Python 2 is no longer supported, please convert to python 3.")
+	}
 	expandedSrcs := android.PathsForModuleSrcExcludes(ctx, p.properties.Srcs, p.properties.Exclude_srcs)
-	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: expandedSrcs.Strings()})
 	// Keep before any early returns.
 	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
 		TestOnly:       Bool(p.sourceProperties.Test_only),
@@ -457,12 +392,32 @@
 	// expand data files from "data" property.
 	expandedData := android.PathsForModuleSrc(ctx, p.properties.Data)
 	expandedData = append(expandedData, android.PathsForModuleSrc(ctx, p.properties.Device_common_data)...)
+	expandedData = append(expandedData, android.PathsForModuleSrc(ctx, p.properties.Device_first_data)...)
 
 	// Emulate the data property for java_data dependencies.
-	for _, javaData := range ctx.GetDirectDepsWithTag(javaDataTag) {
+	for _, javaData := range ctx.GetDirectDepsProxyWithTag(javaDataTag) {
 		expandedData = append(expandedData, android.OutputFilesForModule(ctx, javaData, "")...)
 	}
 
+	var directImplementationDeps android.Paths
+	var transitiveImplementationDeps []depset.DepSet[android.Path]
+	ctx.VisitDirectDepsProxyWithTag(sharedLibTag, func(dep android.ModuleProxy) {
+		sharedLibInfo, _ := android.OtherModuleProvider(ctx, dep, cc.SharedLibraryInfoProvider)
+		if sharedLibInfo.SharedLibrary != nil {
+			expandedData = append(expandedData, android.OutputFilesForModule(ctx, dep, "")...)
+			directImplementationDeps = append(directImplementationDeps, android.OutputFilesForModule(ctx, dep, "")...)
+			if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
+				transitiveImplementationDeps = append(transitiveImplementationDeps, info.ImplementationDeps)
+				p.bundleSharedLibs = append(p.bundleSharedLibs, info.ImplementationDeps.ToList()...)
+			}
+		} else {
+			ctx.PropertyErrorf("shared_libs", "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep))
+		}
+	})
+	android.SetProvider(ctx, cc.ImplementationDepInfoProvider, &cc.ImplementationDepInfo{
+		ImplementationDeps: depset.New(depset.PREORDER, directImplementationDeps, transitiveImplementationDeps),
+	})
+
 	// Validate pkg_path property
 	pkgPath := String(p.properties.Pkg_path)
 	if pkgPath != "" {
@@ -487,6 +442,15 @@
 	// generate the zipfile of all source and data files
 	p.srcsZip = p.createSrcsZip(ctx, pkgPath)
 	p.precompiledSrcsZip = p.precompileSrcs(ctx)
+
+	android.SetProvider(ctx, PythonLibraryInfoProvider, PythonLibraryInfo{
+		SrcsPathMappings:   p.getSrcsPathMappings(),
+		DataPathMappings:   p.getDataPathMappings(),
+		SrcsZip:            p.getSrcsZip(),
+		PkgPath:            p.getPkgPath(),
+		PrecompiledSrcsZip: p.getPrecompiledSrcsZip(),
+		BundleSharedLibs:   p.getBundleSharedLibs(),
+	})
 }
 
 func isValidPythonPath(path string) error {
@@ -652,16 +616,16 @@
 		stdLib = p.srcsZip
 		stdLibPkg = p.getPkgPath()
 	} else {
-		ctx.VisitDirectDepsWithTag(hostStdLibTag, func(module android.Module) {
-			if dep, ok := module.(pythonDependency); ok {
-				stdLib = dep.getPrecompiledSrcsZip()
-				stdLibPkg = dep.getPkgPath()
+		ctx.VisitDirectDepsProxyWithTag(hostStdLibTag, func(module android.ModuleProxy) {
+			if dep, ok := android.OtherModuleProvider(ctx, module, PythonLibraryInfoProvider); ok {
+				stdLib = dep.PrecompiledSrcsZip
+				stdLibPkg = dep.PkgPath
 			}
 		})
 	}
-	ctx.VisitDirectDepsWithTag(hostLauncherTag, func(module android.Module) {
-		if dep, ok := module.(IntermPathProvider); ok {
-			optionalLauncher := dep.IntermPathForModuleOut()
+	ctx.VisitDirectDepsProxyWithTag(hostLauncherTag, func(module android.ModuleProxy) {
+		if dep, ok := android.OtherModuleProvider(ctx, module, cc.LinkableInfoProvider); ok {
+			optionalLauncher := dep.OutputFile
 			if optionalLauncher.Valid() {
 				launcher = optionalLauncher.Path()
 			}
@@ -669,9 +633,9 @@
 	})
 	var launcherSharedLibs android.Paths
 	var ldLibraryPath []string
-	ctx.VisitDirectDepsWithTag(hostlauncherSharedLibTag, func(module android.Module) {
-		if dep, ok := module.(IntermPathProvider); ok {
-			optionalPath := dep.IntermPathForModuleOut()
+	ctx.VisitDirectDepsProxyWithTag(hostlauncherSharedLibTag, func(module android.ModuleProxy) {
+		if dep, ok := android.OtherModuleProvider(ctx, module, cc.LinkableInfoProvider); ok {
+			optionalPath := dep.OutputFile
 			if optionalPath.Valid() {
 				launcherSharedLibs = append(launcherSharedLibs, optionalPath.Path())
 				ldLibraryPath = append(ldLibraryPath, filepath.Dir(optionalPath.Path().String()))
@@ -702,16 +666,6 @@
 	return out
 }
 
-// isPythonLibModule returns whether the given module is a Python library PythonLibraryModule or not
-func isPythonLibModule(module blueprint.Module) bool {
-	if _, ok := module.(*PythonLibraryModule); ok {
-		if _, ok := module.(*PythonBinaryModule); !ok {
-			return true
-		}
-	}
-	return false
-}
-
 // collectPathsFromTransitiveDeps checks for source/data files for duplicate paths
 // for module and its transitive dependencies and collects list of data/source file
 // zips for transitive dependencies.
@@ -732,7 +686,7 @@
 	var result android.Paths
 
 	// visit all its dependencies in depth first.
-	ctx.WalkDeps(func(child, parent android.Module) bool {
+	ctx.WalkDepsProxy(func(child, _ android.ModuleProxy) bool {
 		// we only collect dependencies tagged as python library deps
 		if ctx.OtherModuleDependencyTag(child) != pythonLibTag {
 			return false
@@ -742,27 +696,29 @@
 		}
 		seen[child] = true
 		// Python modules only can depend on Python libraries.
-		if !isPythonLibModule(child) {
+		dep, isLibrary := android.OtherModuleProvider(ctx, child, PythonLibraryInfoProvider)
+		_, isBinary := android.OtherModuleProvider(ctx, child, PythonBinaryInfoProvider)
+		if !isLibrary || isBinary {
 			ctx.PropertyErrorf("libs",
 				"the dependency %q of module %q is not Python library!",
 				ctx.OtherModuleName(child), ctx.ModuleName())
 		}
 		// collect source and data paths, checking that there are no duplicate output file conflicts
-		if dep, ok := child.(pythonDependency); ok {
-			srcs := dep.getSrcsPathMappings()
+		if isLibrary {
+			srcs := dep.SrcsPathMappings
 			for _, path := range srcs {
 				checkForDuplicateOutputPath(ctx, destToPySrcs,
 					path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child))
 			}
-			data := dep.getDataPathMappings()
+			data := dep.DataPathMappings
 			for _, path := range data {
 				checkForDuplicateOutputPath(ctx, destToPyData,
 					path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child))
 			}
 			if precompiled {
-				result = append(result, dep.getPrecompiledSrcsZip())
+				result = append(result, dep.PrecompiledSrcsZip)
 			} else {
-				result = append(result, dep.getSrcsZip())
+				result = append(result, dep.SrcsZip)
 			}
 		}
 		return true
@@ -770,6 +726,56 @@
 	return result
 }
 
+func (p *PythonLibraryModule) collectSharedLibDeps(ctx android.ModuleContext) android.Paths {
+	seen := make(map[android.Module]bool)
+
+	var result android.Paths
+
+	ctx.WalkDepsProxy(func(child, _ android.ModuleProxy) bool {
+		// we only collect dependencies tagged as python library deps
+		if ctx.OtherModuleDependencyTag(child) != pythonLibTag {
+			return false
+		}
+		if seen[child] {
+			return false
+		}
+		seen[child] = true
+		dep, isLibrary := android.OtherModuleProvider(ctx, child, PythonLibraryInfoProvider)
+		if isLibrary {
+			result = append(result, dep.BundleSharedLibs...)
+		}
+		return true
+	})
+	return result
+}
+
+func (p *PythonLibraryModule) zipSharedLibs(ctx android.ModuleContext, bundleSharedLibs android.Paths) android.Path {
+	// sort the paths to keep the output deterministic
+	sort.Slice(bundleSharedLibs, func(i, j int) bool {
+		return bundleSharedLibs[i].String() < bundleSharedLibs[j].String()
+	})
+
+	parArgs := []string{"-symlinks=false", "-P lib64"}
+	paths := android.Paths{}
+	for _, path := range bundleSharedLibs {
+		// specify relative root of file in following -f arguments
+		parArgs = append(parArgs, `-C `+filepath.Dir(path.String()))
+		parArgs = append(parArgs, `-f `+path.String())
+		paths = append(paths, path)
+	}
+	srcsZip := android.PathForModuleOut(ctx, ctx.ModuleName()+".sharedlibs.srcszip")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        zip,
+		Description: "bundle shared libraries for python binary",
+		Output:      srcsZip,
+		Implicits:   paths,
+		Args: map[string]string{
+			"args": strings.Join(parArgs, " "),
+		},
+	})
+	return srcsZip
+}
+
 // chckForDuplicateOutputPath checks whether outputPath has already been included in map m, which
 // would result in two files being placed in the same location.
 // If there is a duplicate path, an error is thrown and true is returned
diff --git a/python/python_test.go b/python/python_test.go
index 6a6bd1d..5f971cd 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -36,10 +36,8 @@
 }
 
 var (
-	buildNamePrefix = "soong_python_test"
-	// We allow maching almost anything before the actual variant so that the os/arch variant
-	// is matched.
-	moduleVariantErrTemplate = `%s: module %q variant "[a-zA-Z0-9_]*%s": `
+	buildNamePrefix          = "soong_python_test"
+	moduleVariantErrTemplate = `%s: module %q variant "[a-zA-Z0-9_]*": `
 	pkgPathErrTemplate       = moduleVariantErrTemplate +
 		"pkg_path: %q must be a relative path contained in par file."
 	badIdentifierErrTemplate = moduleVariantErrTemplate +
@@ -48,9 +46,8 @@
 		"found two files to be placed at the same location within zip %q." +
 		" First file: in module %s at path %q." +
 		" Second file: in module %s at path %q."
-	noSrcFileErr      = moduleVariantErrTemplate + "doesn't have any source files!"
-	badSrcFileExtErr  = moduleVariantErrTemplate + "srcs: found non (.py|.proto) file: %q!"
-	badDataFileExtErr = moduleVariantErrTemplate + "data: found (.py) file: %q!"
+	badSrcFileExtErr  = moduleVariantErrTemplate + `srcs: found non \(.py\|.proto\) file: %q!`
+	badDataFileExtErr = moduleVariantErrTemplate + `data: found \(.py\) file: %q!`
 	bpFile            = "Android.bp"
 
 	data = []struct {
@@ -61,20 +58,6 @@
 		expectedBinaries []pyModule
 	}{
 		{
-			desc: "module without any src files",
-			mockFiles: map[string][]byte{
-				filepath.Join("dir", bpFile): []byte(
-					`python_library_host {
-						name: "lib1",
-					}`,
-				),
-			},
-			errors: []string{
-				fmt.Sprintf(noSrcFileErr,
-					"dir/Android.bp:1:1", "lib1", "PY3"),
-			},
-		},
-		{
 			desc: "module with bad src file ext",
 			mockFiles: map[string][]byte{
 				filepath.Join("dir", bpFile): []byte(
@@ -89,7 +72,7 @@
 			},
 			errors: []string{
 				fmt.Sprintf(badSrcFileExtErr,
-					"dir/Android.bp:3:11", "lib1", "PY3", "dir/file1.exe"),
+					"dir/Android.bp:3:11", "lib1", "dir/file1.exe"),
 			},
 		},
 		{
@@ -111,7 +94,7 @@
 			},
 			errors: []string{
 				fmt.Sprintf(badDataFileExtErr,
-					"dir/Android.bp:6:11", "lib1", "PY3", "dir/file2.py"),
+					"dir/Android.bp:6:11", "lib1", "dir/file2.py"),
 			},
 		},
 		{
@@ -146,9 +129,9 @@
 			},
 			errors: []string{
 				fmt.Sprintf(pkgPathErrTemplate,
-					"dir/Android.bp:11:15", "lib2", "PY3", "a/c/../../../"),
+					"dir/Android.bp:11:15", "lib2", "a/c/../../../"),
 				fmt.Sprintf(pkgPathErrTemplate,
-					"dir/Android.bp:19:15", "lib3", "PY3", "/a/c/../../"),
+					"dir/Android.bp:19:15", "lib3", "/a/c/../../"),
 			},
 		},
 		{
@@ -171,11 +154,11 @@
 			},
 			errors: []string{
 				fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11",
-					"lib1", "PY3", "a/b/c/-e/f/file1.py", "-e"),
+					"lib1", "a/b/c/-e/f/file1.py", "-e"),
 				fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11",
-					"lib1", "PY3", "a/b/c/.file1.py", ".file1"),
+					"lib1", "a/b/c/.file1.py", ".file1"),
 				fmt.Sprintf(badIdentifierErrTemplate, "dir/Android.bp:4:11",
-					"lib1", "PY3", "a/b/c/123/file1.py", "123"),
+					"lib1", "a/b/c/123/file1.py", "123"),
 			},
 		},
 		{
@@ -219,115 +202,15 @@
 			},
 			errors: []string{
 				fmt.Sprintf(dupRunfileErrTemplate, "dir/Android.bp:20:6",
-					"bin", "PY3", "a/b/c/file1.py", "bin", "dir/file1.py",
+					"bin", "a/b/c/file1.py", "bin", "dir/file1.py",
 					"lib1", "dir/c/file1.py"),
 			},
 		},
-		{
-			desc: "module for testing dependencies",
-			mockFiles: map[string][]byte{
-				filepath.Join("dir", bpFile): []byte(
-					`python_defaults {
-						name: "default_lib",
-						srcs: [
-							"default.py",
-						],
-						version: {
-							py2: {
-								enabled: true,
-								srcs: [
-									"default_py2.py",
-								],
-							},
-							py3: {
-								enabled: false,
-								srcs: [
-									"default_py3.py",
-								],
-							},
-						},
-					}
-
-					python_library_host {
-						name: "lib5",
-						pkg_path: "a/b/",
-						srcs: [
-							"file1.py",
-						],
-						version: {
-							py2: {
-								enabled: true,
-							},
-							py3: {
-								enabled: true,
-							},
-						},
-					}
-
-					python_library_host {
-						name: "lib6",
-						pkg_path: "c/d/",
-						srcs: [
-							"file2.py",
-						],
-						libs: [
-							"lib5",
-						],
-					}
-
-					python_binary_host {
-						name: "bin",
-						defaults: ["default_lib"],
-						pkg_path: "e/",
-						srcs: [
-							"bin.py",
-						],
-						libs: [
-							"lib5",
-						],
-						version: {
-							py3: {
-								enabled: true,
-								srcs: [
-									"file4.py",
-								],
-								libs: [
-									"lib6",
-								],
-							},
-						},
-					}`,
-				),
-				filepath.Join("dir", "default.py"):     nil,
-				filepath.Join("dir", "default_py2.py"): nil,
-				filepath.Join("dir", "default_py3.py"): nil,
-				filepath.Join("dir", "file1.py"):       nil,
-				filepath.Join("dir", "file2.py"):       nil,
-				filepath.Join("dir", "bin.py"):         nil,
-				filepath.Join("dir", "file4.py"):       nil,
-			},
-			expectedBinaries: []pyModule{
-				{
-					name:          "bin",
-					actualVersion: "PY3",
-					pyRunfiles: []string{
-						"e/default.py",
-						"e/bin.py",
-						"e/default_py3.py",
-						"e/file4.py",
-					},
-					srcsZip: "out/soong/.intermediates/dir/bin/PY3/bin.py.srcszip",
-				},
-			},
-		},
 	}
 )
 
 func TestPythonModule(t *testing.T) {
 	for _, d := range data {
-		if d.desc != "module with duplicate runfile path" {
-			continue
-		}
 		d.mockFiles[filepath.Join("common", bpFile)] = []byte(`
 python_library {
   name: "py3-stdlib",
@@ -416,10 +299,6 @@
 	for i, bp := range testCases {
 		ctx := android.GroupFixturePreparers(
 			PrepareForTestWithPythonBuildComponents,
-			android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
-
-				ctx.RegisterModuleType("python_defaults", DefaultsFactory)
-			}),
 			android.PrepareForTestWithAllowMissingDependencies).
 			ExtendWithErrorHandler(android.FixtureIgnoreErrors).
 			RunTestWithBp(t, bp)
@@ -434,7 +313,7 @@
 }
 
 func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles []string) {
-	module := ctx.ModuleForTests(name, variant)
+	module := ctx.ModuleForTests(t, name, variant)
 
 	base, baseOk := module.Module().(*PythonLibraryModule)
 	if !baseOk {
diff --git a/python/scripts/main.py b/python/scripts/main.py
index 225dbe4..35cdfc4 100644
--- a/python/scripts/main.py
+++ b/python/scripts/main.py
@@ -1,5 +1,13 @@
+
+import os
 import runpy
+import shutil
 import sys
+import tempfile
+import zipfile
+
+from pathlib import PurePath
+
 
 sys.argv[0] = __loader__.archive
 
@@ -9,4 +17,32 @@
 # when people try to use it.
 sys.executable = None
 
-runpy._run_module_as_main("ENTRY_POINT", alter_argv=False)
+# Extract the shared libraries from the zip file into a temporary directory.
+# This works around the limitations of dynamic linker.  Some Python libraries
+# reference the .so files relatively and so extracting only the .so files
+# does not work, so we extract the entire parent directory of the .so files to a
+# tempdir and then add that to sys.path.
+tempdir = None
+with zipfile.ZipFile(__loader__.archive) as z:
+  # any root so files or root directories that contain so files will be
+  # extracted to the tempdir so the linker load them, this minimizes the
+  # number of files that need to be extracted to a tempdir
+  extract_paths = {}
+  for member in z.infolist():
+    if member.filename.endswith('.so'):
+      extract_paths[PurePath(member.filename).parts[0]] = member.filename
+  if extract_paths:
+    tempdir = tempfile.mkdtemp()
+    for member in z.infolist():
+      if not PurePath(member.filename).parts[0] in extract_paths.keys():
+        continue
+      if member.is_dir():
+        os.makedirs(os.path.join(tempdir, member.filename))
+      else:
+        z.extract(member, tempdir)
+    sys.path.insert(0, tempdir)
+try:
+  runpy._run_module_as_main("ENTRY_POINT", alter_argv=False)
+finally:
+  if tempdir is not None:
+    shutil.rmtree(tempdir)
diff --git a/python/test.go b/python/test.go
index 9f57bea..df62ab7 100644
--- a/python/test.go
+++ b/python/test.go
@@ -68,6 +68,11 @@
 	// device.
 	Device_common_data []string `android:"path_device_common"`
 
+	// Same as data, but will add dependencies on modules via a device os variation and the
+	// device's first supported arch's variation. Useful for a host test that wants to embed a
+	// module built for device.
+	Device_first_data []string `android:"path_device_first"`
+
 	// list of java modules that provide data that should be installed alongside the test.
 	Java_data []string
 
@@ -189,15 +194,18 @@
 	for _, dataSrcPath := range android.PathsForModuleSrc(ctx, p.testProperties.Device_common_data) {
 		p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath})
 	}
+	for _, dataSrcPath := range android.PathsForModuleSrc(ctx, p.testProperties.Device_first_data) {
+		p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath})
+	}
 
 	if p.isTestHost() && len(p.testProperties.Data_device_bins_both) > 0 {
-		ctx.VisitDirectDepsWithTag(dataDeviceBinsTag, func(dep android.Module) {
+		ctx.VisitDirectDepsProxyWithTag(dataDeviceBinsTag, func(dep android.ModuleProxy) {
 			p.data = append(p.data, android.DataPath{SrcPath: android.OutputFileForModule(ctx, dep, "")})
 		})
 	}
 
 	// Emulate the data property for java_data dependencies.
-	for _, javaData := range ctx.GetDirectDepsWithTag(javaDataTag) {
+	for _, javaData := range ctx.GetDirectDepsProxyWithTag(javaDataTag) {
 		for _, javaDataSrcPath := range android.OutputFilesForModule(ctx, javaData, "") {
 			p.data = append(p.data, android.DataPath{SrcPath: javaDataSrcPath})
 		}
@@ -206,6 +214,50 @@
 	installDir := installDir(ctx, "nativetest", "nativetest64", ctx.ModuleName())
 	installedData := ctx.InstallTestData(installDir, p.data)
 	p.installedDest = ctx.InstallFile(installDir, p.installSource.Base(), p.installSource, installedData...)
+
+	// TODO: Remove the special case for kati
+	if !ctx.Config().KatiEnabled() {
+		// Install the test config in testcases/ directory for atest.
+		// Install configs in the root of $PRODUCT_OUT/testcases/$module
+		testCases := android.PathForModuleInPartitionInstall(ctx, "testcases", ctx.ModuleName())
+		if ctx.PrimaryArch() {
+			if p.testConfig != nil {
+				ctx.InstallFile(testCases, ctx.ModuleName()+".config", p.testConfig)
+			}
+			dynamicConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "DynamicConfig.xml")
+			if dynamicConfig.Valid() {
+				ctx.InstallFile(testCases, ctx.ModuleName()+".dynamic", dynamicConfig.Path())
+			}
+		}
+		// Install tests and data in arch specific subdir $PRODUCT_OUT/testcases/$module/$arch
+		testCases = testCases.Join(ctx, ctx.Target().Arch.ArchType.String())
+		installedData := ctx.InstallTestData(testCases, p.data)
+		ctx.InstallFile(testCases, p.installSource.Base(), p.installSource, installedData...)
+	}
+
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Class = []string{"NATIVE_TESTS"}
+	if len(p.binaryProperties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, p.binaryProperties.Test_suites...)
+	} else {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
+	}
+	if p.testConfig != nil {
+		moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, p.testConfig.String())
+	}
+	if _, ok := p.testConfig.(android.WritablePath); ok {
+		moduleInfoJSON.AutoTestConfig = []string{"true"}
+	}
+	moduleInfoJSON.TestOptionsTags = append(moduleInfoJSON.TestOptionsTags, p.testProperties.Test_options.Tags...)
+	moduleInfoJSON.Dependencies = append(moduleInfoJSON.Dependencies, p.androidMkSharedLibs...)
+	moduleInfoJSON.SharedLibs = append(moduleInfoJSON.Dependencies, p.androidMkSharedLibs...)
+	moduleInfoJSON.SystemSharedLibs = []string{"none"}
+	if proptools.Bool(p.testProperties.Test_options.Unit_test) {
+		moduleInfoJSON.IsUnitTest = "true"
+		if p.isTestHost() {
+			moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "host-unit-tests")
+		}
+	}
 }
 
 func (p *PythonTestModule) AndroidMkEntries() []android.AndroidMkEntries {
diff --git a/python/testing.go b/python/testing.go
index ce1a5ab..fe53ee5 100644
--- a/python/testing.go
+++ b/python/testing.go
@@ -20,5 +20,4 @@
 	android.FixtureRegisterWithContext(registerPythonBinaryComponents),
 	android.FixtureRegisterWithContext(registerPythonLibraryComponents),
 	android.FixtureRegisterWithContext(registerPythonTestComponents),
-	android.FixtureRegisterWithContext(registerPythonMutators),
 )
diff --git a/python/tests/runtest.sh b/python/tests/runtest.sh
index c44ec58..9167561 100755
--- a/python/tests/runtest.sh
+++ b/python/tests/runtest.sh
@@ -25,15 +25,9 @@
 
 if [[ ( ! -f $ANDROID_HOST_OUT/nativetest64/par_test/par_test ) ||
       ( ! -f $ANDROID_HOST_OUT/bin/py3-cmd )]]; then
-  echo "Run 'm par_test py2-cmd py3-cmd' first"
+  echo "Run 'm par_test py3-cmd' first"
   exit 1
 fi
-if [ $(uname -s) = Linux ]; then
-  if [[ ! -f $ANDROID_HOST_OUT/bin/py2-cmd ]]; then
-    echo "Run 'm par_test py2-cmd py3-cmd' first"
-    exit 1
-  fi
-fi
 
 export LD_LIBRARY_PATH=$ANDROID_HOST_OUT/lib64
 
@@ -47,16 +41,8 @@
 
 cd $(dirname ${BASH_SOURCE[0]})
 
-if [ $(uname -s) = Linux ]; then
-  PYTHONPATH=/extra $ANDROID_HOST_OUT/bin/py2-cmd py-cmd_test.py
-fi
 PYTHONPATH=/extra $ANDROID_HOST_OUT/bin/py3-cmd py-cmd_test.py
 
-if [ $(uname -s) = Linux ]; then
-  ARGTEST=true PYTHONPATH=/extra $ANDROID_HOST_OUT/bin/py2-cmd py-cmd_test.py arg1 arg2
-  ARGTEST2=true PYTHONPATH=/extra $ANDROID_HOST_OUT/bin/py2-cmd py-cmd_test.py --arg1 arg2
-fi
-
 ARGTEST=true PYTHONPATH=/extra $ANDROID_HOST_OUT/bin/py3-cmd py-cmd_test.py arg1 arg2
 ARGTEST2=true PYTHONPATH=/extra $ANDROID_HOST_OUT/bin/py3-cmd py-cmd_test.py --arg1 arg2
 
diff --git a/root.bp b/root.bp
index ee208a0..7e0c1ed 100644
--- a/root.bp
+++ b/root.bp
@@ -2,12 +2,3 @@
 // subdirs= and optional_subdirs= are obsolete and this file no longer
 // needs a list of the top level directories that may contain Android.bp
 // files.
-
-// TODO(b/253827323) Remove this. A module in internal builds needs to disable a new check,
-// IdentifierName, when errorprone is updated. In order to avoid having the update errorprone
-// in internal first, and then aosp, create this variable that we can fill out in internal in the
-// same topic as the errorprone update, then move the flag out of the variable after the update,
-// then remove the variable.
-disable_identifiername_for_errorprone_update = [
-    "-Xep:IdentifierName:OFF",
-]
diff --git a/rust/afdo.go b/rust/afdo.go
index 6bd4bae..1bec709 100644
--- a/rust/afdo.go
+++ b/rust/afdo.go
@@ -66,7 +66,7 @@
 		return flags, deps
 	}
 
-	ctx.VisitDirectDepsWithTag(cc.FdoProfileTag, func(m android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(cc.FdoProfileTag, func(m android.ModuleProxy) {
 		if info, ok := android.OtherModuleProvider(ctx, m, cc.FdoProfileProvider); ok {
 			path := info.Path
 			profileUseFlag := fmt.Sprintf(afdoFlagFormat, path.String())
diff --git a/rust/afdo_test.go b/rust/afdo_test.go
index 0cdf704..69aa97e 100644
--- a/rust/afdo_test.go
+++ b/rust/afdo_test.go
@@ -50,7 +50,7 @@
 		rustMockedFiles.AddToFixture(),
 	).RunTestWithBp(t, bp)
 
-	foo := result.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustc")
+	foo := result.ModuleForTests(t, "foo", "android_arm64_armv8-a").Rule("rustc")
 
 	expectedCFlag := fmt.Sprintf(afdoFlagFormat, "afdo_profiles_package/foo.afdo")
 
@@ -96,8 +96,8 @@
 		rustMockedFiles.AddToFixture(),
 	).RunTestWithBp(t, bp)
 
-	fooArm := result.ModuleForTests("foo", "android_arm_armv7-a-neon").Rule("rustc")
-	fooArm64 := result.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustc")
+	fooArm := result.ModuleForTests(t, "foo", "android_arm_armv7-a-neon").Rule("rustc")
+	fooArm64 := result.ModuleForTests(t, "foo", "android_arm64_armv8-a").Rule("rustc")
 
 	expectedCFlagArm := fmt.Sprintf(afdoFlagFormat, "afdo_profiles_package/foo_arm.afdo")
 	expectedCFlagArm64 := fmt.Sprintf(afdoFlagFormat, "afdo_profiles_package/foo_arm64.afdo")
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 8de6b60..9894684 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -92,9 +92,6 @@
 func (binary *binaryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
 	ctx.SubAndroidMk(ret, binary.baseCompiler)
 
-	if binary.distFile.Valid() {
-		ret.DistFiles = android.MakeDefaultDistFiles(binary.distFile.Path())
-	}
 	ret.Class = "EXECUTABLES"
 }
 
@@ -143,9 +140,6 @@
 	} else if library.shared() {
 		ret.Class = "SHARED_LIBRARIES"
 	}
-	if library.distFile.Valid() {
-		ret.DistFiles = android.MakeDefaultDistFiles(library.distFile.Path())
-	}
 	ret.ExtraEntries = append(ret.ExtraEntries,
 		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 			if library.tocFile.Valid() {
@@ -158,10 +152,6 @@
 	ctx.SubAndroidMk(ret, procMacro.baseCompiler)
 
 	ret.Class = "PROC_MACRO_LIBRARIES"
-	if procMacro.distFile.Valid() {
-		ret.DistFiles = android.MakeDefaultDistFiles(procMacro.distFile.Path())
-	}
-
 }
 
 func (sourceProvider *BaseSourceProvider) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
diff --git a/rust/benchmark.go b/rust/benchmark.go
index 8c3e515..3aa2f17 100644
--- a/rust/benchmark.go
+++ b/rust/benchmark.go
@@ -89,7 +89,7 @@
 	return rlibAutoDep
 }
 
-func (benchmark *benchmarkDecorator) stdLinkage(ctx *depsContext) RustLinkage {
+func (benchmark *benchmarkDecorator) stdLinkage(device bool) RustLinkage {
 	return RlibLinkage
 }
 
@@ -130,3 +130,24 @@
 
 	benchmark.binaryDecorator.install(ctx)
 }
+
+func (benchmark *benchmarkDecorator) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
+	benchmark.binaryDecorator.moduleInfoJSON(ctx, moduleInfoJSON)
+	moduleInfoJSON.Class = []string{"NATIVE_TESTS"}
+	if benchmark.testConfig != nil {
+		if _, ok := benchmark.testConfig.(android.WritablePath); ok {
+			moduleInfoJSON.AutoTestConfig = []string{"true"}
+		}
+		moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, benchmark.testConfig.String())
+	}
+
+	if len(benchmark.Properties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, benchmark.Properties.Test_suites...)
+	} else {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
+	}
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: benchmark.Properties.Test_suites,
+	})
+}
diff --git a/rust/benchmark_test.go b/rust/benchmark_test.go
index 734dda7..c239a09 100644
--- a/rust/benchmark_test.go
+++ b/rust/benchmark_test.go
@@ -28,7 +28,7 @@
 			srcs: ["foo.rs"],
 		}`)
 
-	testingModule := ctx.ModuleForTests("my_bench", "linux_glibc_x86_64")
+	testingModule := ctx.ModuleForTests(t, "my_bench", "linux_glibc_x86_64")
 	expectedOut := "my_bench/linux_glibc_x86_64/my_bench"
 	outPath := testingModule.Output("my_bench").Output.String()
 	if !strings.Contains(outPath, expectedOut) {
@@ -43,7 +43,7 @@
 			srcs: ["foo.rs"],
 		}`)
 
-	testingModule := ctx.ModuleForTests("my_bench", "android_arm64_armv8-a").Module().(*Module)
+	testingModule := ctx.ModuleForTests(t, "my_bench", "android_arm64_armv8-a").Module().(*Module)
 
 	if !android.InList("libcriterion.rlib-std", testingModule.Properties.AndroidMkRlibs) {
 		t.Errorf("rlib-std variant for libcriterion not detected as a rustlib-defined rlib dependency for device rust_benchmark module")
diff --git a/rust/binary.go b/rust/binary.go
index cba29a0..5a03d91 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -139,7 +139,10 @@
 
 	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
 	flags.LinkFlags = append(flags.LinkFlags, deps.depLinkFlags...)
-	flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects...)
+	flags.LinkFlags = append(flags.LinkFlags, deps.rustLibObjects...)
+	flags.LinkFlags = append(flags.LinkFlags, deps.sharedLibObjects...)
+	flags.LinkFlags = append(flags.LinkFlags, deps.staticLibObjects...)
+	flags.LinkFlags = append(flags.LinkFlags, deps.wholeStaticLibObjects...)
 
 	if binary.stripper.NeedsStrip(ctx) {
 		strippedOutputFile := outputFile
@@ -165,11 +168,11 @@
 	}
 }
 
-func (binary *binaryDecorator) stdLinkage(ctx *depsContext) RustLinkage {
+func (binary *binaryDecorator) stdLinkage(device bool) RustLinkage {
 	if binary.preferRlib() {
 		return RlibLinkage
 	}
-	return binary.baseCompiler.stdLinkage(ctx)
+	return binary.baseCompiler.stdLinkage(device)
 }
 
 func (binary *binaryDecorator) binary() bool {
@@ -183,3 +186,8 @@
 func (binary *binaryDecorator) testBinary() bool {
 	return false
 }
+
+func (binary *binaryDecorator) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
+	binary.baseCompiler.moduleInfoJSON(ctx, moduleInfoJSON)
+	moduleInfoJSON.Class = []string{"EXECUTABLES"}
+}
diff --git a/rust/binary_test.go b/rust/binary_test.go
index ef93037..33710f9 100644
--- a/rust/binary_test.go
+++ b/rust/binary_test.go
@@ -36,7 +36,7 @@
 			host_supported: true,
 		}
 	`)
-	fizzBuzz := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
+	fizzBuzz := ctx.ModuleForTests(t, "fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
 	if !android.InList("libfoo.rlib-std", fizzBuzz.Properties.AndroidMkRlibs) {
 		t.Errorf("rustlibs dependency libfoo should be an rlib dep for host binaries")
 	}
@@ -65,8 +65,8 @@
 			host_supported: true,
 		}`)
 
-	fizzBuzzHost := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
-	fizzBuzzDevice := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Module().(*Module)
+	fizzBuzzHost := ctx.ModuleForTests(t, "fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
+	fizzBuzzDevice := ctx.ModuleForTests(t, "fizz-buzz", "android_arm64_armv8-a").Module().(*Module)
 
 	if !android.InList("libfoo.rlib-std", fizzBuzzHost.Properties.AndroidMkRlibs) {
 		t.Errorf("rustlibs dependency libfoo should be an rlib dep for host modules")
@@ -76,7 +76,7 @@
 		t.Errorf("rustlibs dependency libfoo should be an dylib dep for device modules")
 	}
 
-	rlibLinkDevice := ctx.ModuleForTests("rlib_linked", "android_arm64_armv8-a").Module().(*Module)
+	rlibLinkDevice := ctx.ModuleForTests(t, "rlib_linked", "android_arm64_armv8-a").Module().(*Module)
 
 	if !android.InList("libfoo.rlib-std", rlibLinkDevice.Properties.AndroidMkRlibs) {
 		t.Errorf("rustlibs dependency libfoo should be an rlib dep for device modules when prefer_rlib is set")
@@ -100,7 +100,7 @@
 			host_supported: true,
 		}`)
 
-	mod := ctx.ModuleForTests("rlib_linked", "android_arm64_armv8-a").Module().(*Module)
+	mod := ctx.ModuleForTests(t, "rlib_linked", "android_arm64_armv8-a").Module().(*Module)
 
 	if !android.InList("libfoo.rlib-std", mod.Properties.AndroidMkRlibs) {
 		t.Errorf("rustlibs dependency libfoo should be an rlib dep when prefer_rlib is defined")
@@ -119,7 +119,7 @@
 			srcs: ["foo.rs"],
 		}`)
 
-	path := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module).HostToolPath()
+	path := ctx.ModuleForTests(t, "fizz-buzz", "linux_glibc_x86_64").Module().(*Module).HostToolPath()
 	if g, w := path.String(), "/host/linux-x86/bin/fizz-buzz"; !strings.Contains(g, w) {
 		t.Errorf("wrong host tool path, expected %q got %q", w, g)
 	}
@@ -133,7 +133,7 @@
 			srcs: ["foo.rs"],
 		}`)
 
-	fizzBuzz := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Rule("rustc")
+	fizzBuzz := ctx.ModuleForTests(t, "fizz-buzz", "linux_glibc_x86_64").Rule("rustc")
 
 	flags := fizzBuzz.Args["rustcFlags"]
 	if strings.Contains(flags, "--test") {
@@ -150,7 +150,7 @@
 			bootstrap: true,
 		}`)
 
-	foo := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustc")
+	foo := ctx.ModuleForTests(t, "foo", "android_arm64_armv8-a").Rule("rustc")
 
 	flag := "-Wl,-dynamic-linker,/system/bin/bootstrap/linker64"
 	if !strings.Contains(foo.Args["linkFlags"], flag) {
@@ -166,8 +166,8 @@
 			static_executable: true,
 		}`)
 
-	fizzOut := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Rule("rustc")
-	fizzMod := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Module().(*Module)
+	fizzOut := ctx.ModuleForTests(t, "fizz", "android_arm64_armv8-a").Rule("rustc")
+	fizzMod := ctx.ModuleForTests(t, "fizz", "android_arm64_armv8-a").Module().(*Module)
 
 	flags := fizzOut.Args["rustcFlags"]
 	linkFlags := fizzOut.Args["linkFlags"]
@@ -200,7 +200,7 @@
 			name: "libfoo",
 		}`)
 
-	fizzBuzz := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Rule("rustc")
+	fizzBuzz := ctx.ModuleForTests(t, "fizz-buzz", "android_arm64_armv8-a").Rule("rustc")
 	linkFlags := fizzBuzz.Args["linkFlags"]
 	if !strings.Contains(linkFlags, "/libfoo.so") {
 		t.Errorf("missing shared dependency 'libfoo.so' in linkFlags: %#v", linkFlags)
@@ -223,7 +223,7 @@
 		}
 	`)
 
-	foo := ctx.ModuleForTests("foo", "android_arm64_armv8-a")
+	foo := ctx.ModuleForTests(t, "foo", "android_arm64_armv8-a")
 	foo.Output("unstripped/foo")
 	foo.Output("foo")
 
@@ -233,7 +233,7 @@
 		t.Errorf("installed binary not based on stripped version: %v", cp.Input)
 	}
 
-	fizzBar := ctx.ModuleForTests("bar", "android_arm64_armv8-a").MaybeOutput("unstripped/bar")
+	fizzBar := ctx.ModuleForTests(t, "bar", "android_arm64_armv8-a").MaybeOutput("unstripped/bar")
 	if fizzBar.Rule != nil {
 		t.Errorf("unstripped binary exists, so stripped binary has incorrectly been generated")
 	}
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 898e792..2f84168 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -309,7 +309,8 @@
 
 	var cmd, cmdDesc string
 	if b.Properties.Custom_bindgen != "" {
-		cmd = ctx.GetDirectDepWithTag(b.Properties.Custom_bindgen, customBindgenDepTag).(android.HostToolProvider).HostToolPath().String()
+		m := ctx.GetDirectDepProxyWithTag(b.Properties.Custom_bindgen, customBindgenDepTag)
+		cmd = android.OtherModuleProviderOrDefault(ctx, m, android.HostToolProviderInfoProvider).HostToolPath.String()
 		cmdDesc = b.Properties.Custom_bindgen
 	} else {
 		cmd = "$bindgenCmd"
@@ -397,7 +398,7 @@
 		//
 		// This is necessary to avoid a circular dependency between the source variant and the
 		// dependent cc module.
-		deps.StaticLibs = append(deps.StaticLibs, String(b.Properties.Static_inline_library))
+		deps.WholeStaticLibs = append(deps.WholeStaticLibs, String(b.Properties.Static_inline_library))
 	}
 
 	deps.SharedLibs = append(deps.SharedLibs, b.ClangProperties.Shared_libs.GetOrDefault(ctx, nil)...)
diff --git a/rust/bindgen_test.go b/rust/bindgen_test.go
index 2b7362f..267fb1c 100644
--- a/rust/bindgen_test.go
+++ b/rust/bindgen_test.go
@@ -67,10 +67,10 @@
 			cflags: ["--default-flag"],
 		}
 	`)
-	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a_source").Output("bindings.rs")
-	libbindgenStatic := ctx.ModuleForTests("libbindgen_staticlib", "android_arm64_armv8-a_source").Output("bindings.rs")
-	libbindgenHeader := ctx.ModuleForTests("libbindgen_headerlib", "android_arm64_armv8-a_source").Output("bindings.rs")
-	libbindgenHeaderModule := ctx.ModuleForTests("libbindgen_headerlib", "android_arm64_armv8-a_source").Module().(*Module)
+	libbindgen := ctx.ModuleForTests(t, "libbindgen", "android_arm64_armv8-a_source").Output("bindings.rs")
+	libbindgenStatic := ctx.ModuleForTests(t, "libbindgen_staticlib", "android_arm64_armv8-a_source").Output("bindings.rs")
+	libbindgenHeader := ctx.ModuleForTests(t, "libbindgen_headerlib", "android_arm64_armv8-a_source").Output("bindings.rs")
+	libbindgenHeaderModule := ctx.ModuleForTests(t, "libbindgen_headerlib", "android_arm64_armv8-a_source").Module().(*Module)
 	// Ensure that the flags are present and escaped
 	if !strings.Contains(libbindgen.Args["flags"], "'--bindgen-flag.*'") {
 		t.Errorf("missing bindgen flags in rust_bindgen rule: flags %#v", libbindgen.Args["flags"])
@@ -113,7 +113,7 @@
 		}
 	`)
 
-	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a_source").Output("bindings.rs")
+	libbindgen := ctx.ModuleForTests(t, "libbindgen", "android_arm64_armv8-a_source").Output("bindings.rs")
 
 	// The rule description should contain the custom binary name rather than bindgen, so checking the description
 	// should be sufficient.
@@ -155,8 +155,8 @@
 		}
 	`)
 
-	libbindgen_cstd := ctx.ModuleForTests("libbindgen_cstd", "android_arm64_armv8-a_source").Output("bindings.rs")
-	libbindgen_cppstd := ctx.ModuleForTests("libbindgen_cppstd", "android_arm64_armv8-a_source").Output("bindings.rs")
+	libbindgen_cstd := ctx.ModuleForTests(t, "libbindgen_cstd", "android_arm64_armv8-a_source").Output("bindings.rs")
+	libbindgen_cppstd := ctx.ModuleForTests(t, "libbindgen_cppstd", "android_arm64_armv8-a_source").Output("bindings.rs")
 
 	if !strings.Contains(libbindgen_cstd.Args["cflags"], "-std=foo") {
 		t.Errorf("c_std value not passed in to rust_bindgen as a clang flag")
@@ -216,7 +216,7 @@
 			],
 		}
 	`)
-	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a_source").Output("bindings.rs")
+	libbindgen := ctx.ModuleForTests(t, "libbindgen", "android_arm64_armv8-a_source").Output("bindings.rs")
 
 	if !strings.Contains(libbindgen.Args["flagfiles"], "/dev/null") {
 		t.Errorf("missing /dev/null in rust_bindgen rule: flags %#v", libbindgen.Args["flagfiles"])
@@ -246,7 +246,7 @@
 			include_dirs: ["src/"],
 		}
 	`)
-	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a_source").Output("bindings.rs")
+	libbindgen := ctx.ModuleForTests(t, "libbindgen", "android_arm64_armv8-a_source").Output("bindings.rs")
 	// Make sure the flag to support `static inline` functions is present
 	if !strings.Contains(libbindgen.Args["flags"], "--wrap-static-fns") {
 		t.Errorf("missing flag to handle static inlining in rust_bindgen rule: flags %#v", libbindgen.Args["flags"])
diff --git a/rust/builder.go b/rust/builder.go
index a1e17fc..1b6a6c1 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -30,11 +30,11 @@
 	rustc = pctx.AndroidStaticRule("rustc",
 		blueprint.RuleParams{
 			Command: "$envVars $rustcCmd " +
-				"-C linker=${config.RustLinker} " +
-				"-C link-args=\"${crtBegin} ${earlyLinkFlags} ${linkFlags} ${crtEnd}\" " +
+				"-C linker=${RustcLinkerCmd} " +
+				"-C link-args=\"--android-clang-bin=${config.ClangCmd} ${crtBegin} ${earlyLinkFlags} ${linkFlags} ${crtEnd}\" " +
 				"--emit link -o $out --emit dep-info=$out.d.raw $in ${libFlags} $rustcFlags" +
 				" && grep ^$out: $out.d.raw > $out.d",
-			CommandDeps: []string{"$rustcCmd"},
+			CommandDeps: []string{"$rustcCmd", "${RustcLinkerCmd}", "${config.ClangCmd}"},
 			// Rustc deps-info writes out make compatible dep files: https://github.com/rust-lang/rust/issues/7633
 			// Rustc emits unneeded dependency lines for the .d and input .rs files.
 			// Those extra lines cause ninja warning:
@@ -102,10 +102,10 @@
 				`KYTHE_CANONICALIZE_VNAME_PATHS=prefer-relative ` +
 				`$rustExtractor $envVars ` +
 				`$rustcCmd ` +
-				`-C linker=${config.RustLinker} ` +
-				`-C link-args="${crtBegin} ${linkFlags} ${crtEnd}" ` +
+				`-C linker=${RustcLinkerCmd} ` +
+				`-C link-args="--android-clang-bin=${config.ClangCmd} ${crtBegin} ${linkFlags} ${crtEnd}" ` +
 				`$in ${libFlags} $rustcFlags`,
-			CommandDeps:    []string{"$rustExtractor", "$kytheVnames"},
+			CommandDeps:    []string{"$rustExtractor", "$kytheVnames", "${RustcLinkerCmd}", "${config.ClangCmd}"},
 			Rspfile:        "${out}.rsp",
 			RspfileContent: "$in",
 		},
@@ -119,6 +119,7 @@
 
 func init() {
 	pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
+	pctx.HostBinToolVariable("RustcLinkerCmd", "rustc_linker")
 	cc.TransformRlibstoStaticlib = TransformRlibstoStaticlib
 }
 
@@ -170,7 +171,7 @@
 	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "rlib"))
 }
 
-// TransformRlibstoStaticlib is assumed to be called from the cc module, and
+// TransformRlibstoStaticlib is assumed to be callable from the cc module, and
 // thus needs to reconstruct the common set of flags which need to be passed
 // to the rustc compiler.
 func TransformRlibstoStaticlib(ctx android.ModuleContext, mainSrc android.Path, deps []cc.RustRlibDep,
@@ -184,7 +185,7 @@
 		rustPathDeps.linkDirs = append(rustPathDeps.linkDirs, rlibDep.LinkDirs...)
 	}
 
-	ccModule := ctx.(cc.ModuleContext).Module().(*cc.Module)
+	mod := ctx.Module().(cc.LinkableInterface)
 	toolchain := config.FindToolchain(ctx.Os(), ctx.Arch())
 	t := transformProperties{
 		// Crate name can be a predefined value as this is a staticlib and
@@ -194,10 +195,10 @@
 		crateName:       "generated_rust_staticlib",
 		is64Bit:         toolchain.Is64Bit(),
 		targetTriple:    toolchain.RustTriple(),
-		bootstrap:       ccModule.Bootstrap(),
-		inRecovery:      ccModule.InRecovery(),
-		inRamdisk:       ccModule.InRamdisk(),
-		inVendorRamdisk: ccModule.InVendorRamdisk(),
+		bootstrap:       mod.Bootstrap(),
+		inRecovery:      mod.InRecovery(),
+		inRamdisk:       mod.InRamdisk(),
+		inVendorRamdisk: mod.InVendorRamdisk(),
 
 		// crateType indicates what type of crate to build
 		crateType: "staticlib",
@@ -263,7 +264,7 @@
 		libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String())
 	}
 	for _, lib := range deps.DyLibs {
-		libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String())
+		libFlags = append(libFlags, "--extern force:"+lib.CrateName+"="+lib.Path.String())
 	}
 	for _, proc_macro := range deps.ProcMacros {
 		libFlags = append(libFlags, "--extern "+proc_macro.CrateName+"="+proc_macro.Path.String())
@@ -401,6 +402,11 @@
 		linkFlags = append(linkFlags, dynamicLinker)
 	}
 
+	if generatedLib := cc.GenerateRustStaticlib(ctx, deps.ccRlibDeps); generatedLib != nil {
+		deps.StaticLibs = append(deps.StaticLibs, generatedLib)
+		linkFlags = append(linkFlags, generatedLib.String())
+	}
+
 	libFlags := makeLibFlags(deps)
 
 	// Collect dependencies
@@ -411,6 +417,7 @@
 	implicits = append(implicits, deps.SharedLibDeps...)
 	implicits = append(implicits, deps.srcProviderFiles...)
 	implicits = append(implicits, deps.AfdoProfiles...)
+	implicits = append(implicits, deps.LinkerDeps...)
 
 	implicits = append(implicits, deps.CrtBegin...)
 	implicits = append(implicits, deps.CrtEnd...)
diff --git a/rust/builder_test.go b/rust/builder_test.go
index ae5ccde..7d6b56a 100644
--- a/rust/builder_test.go
+++ b/rust/builder_test.go
@@ -85,7 +85,7 @@
 				"out/soong/.intermediates/libfizz_buzz/android_arm64_armv8-a_dylib/libfizz_buzz.dylib.so",
 				"out/soong/.intermediates/libfizz_buzz/android_arm64_armv8-a_dylib/libfizz_buzz.dylib.so.clippy",
 				"out/soong/.intermediates/libfizz_buzz/android_arm64_armv8-a_dylib/unstripped/libfizz_buzz.dylib.so",
-				"out/soong/target/product/test_device/system/lib64/libfizz_buzz.dylib.so",
+				"out/target/product/test_device/system/lib64/libfizz_buzz.dylib.so",
 				"out/soong/.intermediates/libfizz_buzz/android_arm64_armv8-a_dylib/meta_lic",
 			},
 		},
@@ -118,7 +118,7 @@
 				"out/soong/.intermediates/fizz_buzz/android_arm64_armv8-a/fizz_buzz",
 				"out/soong/.intermediates/fizz_buzz/android_arm64_armv8-a/fizz_buzz.clippy",
 				"out/soong/.intermediates/fizz_buzz/android_arm64_armv8-a/unstripped/fizz_buzz",
-				"out/soong/target/product/test_device/system/bin/fizz_buzz",
+				"out/target/product/test_device/system/bin/fizz_buzz",
 				"out/soong/.intermediates/fizz_buzz/android_arm64_armv8-a/meta_lic",
 			},
 		},
@@ -154,13 +154,13 @@
 				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_shared/unstripped/librust_ffi.so.toc",
 				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_shared/meta_lic",
 				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_shared/rustdoc.timestamp",
-				"out/soong/target/product/test_device/system/lib64/librust_ffi.so",
+				"out/target/product/test_device/system/lib64/librust_ffi.so",
 			},
 		},
 	}
 	for _, tc := range testcases {
 		t.Run(tc.testName, func(t *testing.T) {
-			modOutputs := ctx.ModuleForTests(tc.moduleName, tc.variant).AllOutputs()
+			modOutputs := ctx.ModuleForTests(t, tc.moduleName, tc.variant).AllOutputs()
 			sort.Strings(tc.expectedFiles)
 			sort.Strings(modOutputs)
 			android.AssertStringPathsRelativeToTopEquals(
diff --git a/rust/clippy_test.go b/rust/clippy_test.go
index bd3bfb1..3563348 100644
--- a/rust/clippy_test.go
+++ b/rust/clippy_test.go
@@ -62,13 +62,13 @@
 				android.FixtureAddTextFile(tc.modulePath+"Android.bp", bp),
 			).RunTest(t)
 
-			r := result.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
+			r := result.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
 			android.AssertStringEquals(t, "libfoo flags", tc.fooFlags, r.Args["clippyFlags"])
 
-			r = result.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
+			r = result.ModuleForTests(t, "libbar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
 			android.AssertStringEquals(t, "libbar flags", "${config.ClippyDefaultLints}", r.Args["clippyFlags"])
 
-			r = result.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
+			r = result.ModuleForTests(t, "libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
 			if r.Rule != nil {
 				t.Errorf("libfoobar is setup to use clippy when explicitly disabled: clippyFlags=%q", r.Args["clippyFlags"])
 			}
diff --git a/rust/compiler.go b/rust/compiler.go
index fd86917..c3bc937 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -30,9 +30,8 @@
 type RustLinkage int
 
 const (
-	DefaultLinkage RustLinkage = iota
+	DylibLinkage RustLinkage = iota
 	RlibLinkage
-	DylibLinkage
 )
 
 type compiler interface {
@@ -40,6 +39,7 @@
 	compilerFlags(ctx ModuleContext, flags Flags) Flags
 	cfgFlags(ctx ModuleContext, flags Flags) Flags
 	featureFlags(ctx ModuleContext, module *Module, flags Flags) Flags
+	baseCompilerProps() BaseCompilerProperties
 	compilerProps() []interface{}
 	compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput
 	compilerDeps(ctx DepsContext, deps Deps) Deps
@@ -69,7 +69,7 @@
 	Disabled() bool
 	SetDisabled()
 
-	stdLinkage(ctx *depsContext) RustLinkage
+	stdLinkage(device bool) RustLinkage
 	noStdlibs() bool
 
 	unstrippedOutputFilePath() android.Path
@@ -78,6 +78,8 @@
 	checkedCrateRootPath() (android.Path, error)
 
 	Aliases() map[string]string
+
+	moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON)
 }
 
 func (compiler *baseCompiler) edition() string {
@@ -150,21 +152,21 @@
 	Aliases []string
 
 	// list of rust rlib crate dependencies
-	Rlibs []string `android:"arch_variant"`
+	Rlibs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// list of rust automatic crate dependencies.
 	// Rustlibs linkage is rlib for host targets and dylib for device targets.
 	Rustlibs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// list of rust proc_macro crate dependencies
-	Proc_macros []string `android:"arch_variant"`
+	Proc_macros proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// list of C shared library dependencies
-	Shared_libs []string `android:"arch_variant"`
+	Shared_libs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// list of C static library dependencies. These dependencies do not normally propagate to dependents
 	// and may need to be redeclared. See whole_static_libs for bundling static dependencies into a library.
-	Static_libs []string `android:"arch_variant"`
+	Static_libs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// Similar to static_libs, but will bundle the static library dependency into a library. This is helpful
 	// to avoid having to redeclare the dependency for dependents of this library, but in some cases may also
@@ -179,13 +181,13 @@
 	//
 	// For rust_library rlib variants, these libraries will be bundled into the resulting rlib library. This will
 	// include all of the static libraries symbols in any dylibs or binaries which use this rlib as well.
-	Whole_static_libs []string `android:"arch_variant"`
+	Whole_static_libs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// list of Rust system library dependencies.
 	//
 	// This is usually only needed when `no_stdlibs` is true, in which case it can be used to depend on system crates
 	// like `core` and `alloc`.
-	Stdlibs []string `android:"arch_variant"`
+	Stdlibs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// crate name, required for modules which produce Rust libraries: rust_library, rust_ffi and SourceProvider
 	// modules which create library variants (rust_bindgen). This must be the expected extern crate name used in
@@ -255,8 +257,6 @@
 	location installLocation
 	sanitize *sanitize
 
-	distFile android.OptionalPath
-
 	installDeps android.InstallPaths
 
 	// unstripped output file.
@@ -316,17 +316,42 @@
 	return aliases
 }
 
-func (compiler *baseCompiler) stdLinkage(ctx *depsContext) RustLinkage {
+func (compiler *baseCompiler) stdLinkage(device bool) RustLinkage {
 	// For devices, we always link stdlibs in as dylibs by default.
 	if compiler.preferRlib() {
 		return RlibLinkage
-	} else if ctx.Device() {
+	} else if device {
 		return DylibLinkage
 	} else {
 		return RlibLinkage
 	}
 }
 
+func (compiler *baseCompiler) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
+	moduleInfoJSON.Class = []string{"ETC"}
+
+	mod := ctx.Module().(*Module)
+
+	moduleInfoJSON.SharedLibs = mod.transitiveAndroidMkSharedLibs.ToList()
+	moduleInfoJSON.Dependencies = append(moduleInfoJSON.Dependencies, mod.transitiveAndroidMkSharedLibs.ToList()...)
+	moduleInfoJSON.Dependencies = append(moduleInfoJSON.Dependencies, mod.Properties.AndroidMkDylibs...)
+	moduleInfoJSON.Dependencies = append(moduleInfoJSON.Dependencies, mod.Properties.AndroidMkHeaderLibs...)
+	moduleInfoJSON.Dependencies = append(moduleInfoJSON.Dependencies, mod.Properties.AndroidMkProcMacroLibs...)
+	moduleInfoJSON.Dependencies = append(moduleInfoJSON.Dependencies, mod.Properties.AndroidMkRlibs...)
+	moduleInfoJSON.Dependencies = append(moduleInfoJSON.Dependencies, mod.Properties.AndroidMkStaticLibs...)
+	moduleInfoJSON.SystemSharedLibs = []string{"none"}
+	moduleInfoJSON.StaticLibs = mod.Properties.AndroidMkStaticLibs
+
+	if mod.sourceProvider != nil {
+		moduleInfoJSON.SubName += mod.sourceProvider.getSubName()
+	}
+	moduleInfoJSON.SubName += mod.AndroidMkSuffix()
+
+	if mod.Properties.IsSdkVariant {
+		moduleInfoJSON.Uninstallable = true
+	}
+}
+
 var _ compiler = (*baseCompiler)(nil)
 
 func (compiler *baseCompiler) inData() bool {
@@ -337,6 +362,10 @@
 	return []interface{}{&compiler.Properties}
 }
 
+func (compiler *baseCompiler) baseCompilerProps() BaseCompilerProperties {
+	return compiler.Properties
+}
+
 func cfgsToFlags(cfgs []string) []string {
 	flags := make([]string, 0, len(cfgs))
 	for _, cfg := range cfgs {
@@ -496,13 +525,13 @@
 }
 
 func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps {
-	deps.Rlibs = append(deps.Rlibs, compiler.Properties.Rlibs...)
+	deps.Rlibs = append(deps.Rlibs, compiler.Properties.Rlibs.GetOrDefault(ctx, nil)...)
 	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...)
-	deps.SharedLibs = append(deps.SharedLibs, compiler.Properties.Shared_libs...)
-	deps.Stdlibs = append(deps.Stdlibs, compiler.Properties.Stdlibs...)
+	deps.ProcMacros = append(deps.ProcMacros, compiler.Properties.Proc_macros.GetOrDefault(ctx, nil)...)
+	deps.StaticLibs = append(deps.StaticLibs, compiler.Properties.Static_libs.GetOrDefault(ctx, nil)...)
+	deps.WholeStaticLibs = append(deps.WholeStaticLibs, compiler.Properties.Whole_static_libs.GetOrDefault(ctx, nil)...)
+	deps.SharedLibs = append(deps.SharedLibs, compiler.Properties.Shared_libs.GetOrDefault(ctx, nil)...)
+	deps.Stdlibs = append(deps.Stdlibs, compiler.Properties.Stdlibs.GetOrDefault(ctx, nil)...)
 
 	if !Bool(compiler.Properties.No_stdlibs) {
 		for _, stdlib := range config.Stdlibs {
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index 4caa12b..8805d15 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -34,7 +34,7 @@
 			],
 		}`)
 
-	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
+	libfooDylib := ctx.ModuleForTests(t, "libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
 
 	if !strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'feature=\"fizz\"'") ||
 		!strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'feature=\"buzz\"'") {
@@ -55,7 +55,7 @@
 			],
 		}`)
 
-	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
+	libfooDylib := ctx.ModuleForTests(t, "libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
 
 	if !strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'std'") ||
 		!strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'cfg1=\"one\"'") {
@@ -81,8 +81,8 @@
 		}
 		`)
 
-	libfoo := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
-	libfooLto := ctx.ModuleForTests("libfoo_lto", "linux_glibc_x86_64_dylib").Rule("rustc")
+	libfoo := ctx.ModuleForTests(t, "libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
+	libfooLto := ctx.ModuleForTests(t, "libfoo_lto", "linux_glibc_x86_64_dylib").Rule("rustc")
 
 	if strings.Contains(libfoo.Args["rustcFlags"], "-C lto=thin") {
 		t.Fatalf("libfoo expected to disable lto -- rustcFlags: %#v", libfoo.Args["rustcFlags"])
@@ -174,7 +174,7 @@
 			cargo_pkg_version: "1.0.0"
 		}`)
 
-	fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Rule("rustc")
+	fizz := ctx.ModuleForTests(t, "fizz", "android_arm64_armv8-a").Rule("rustc")
 
 	if !strings.Contains(fizz.Args["envVars"], "CARGO_BIN_NAME=fizz") {
 		t.Fatalf("expected 'CARGO_BIN_NAME=fizz' in envVars, actual envVars: %#v", fizz.Args["envVars"])
@@ -199,11 +199,11 @@
 			srcs: ["foo.rs"],
 		}`)
 
-	install_path_lib64 := ctx.ModuleForTests("libfoo",
+	install_path_lib64 := ctx.ModuleForTests(t, "libfoo",
 		"android_arm64_armv8-a_dylib").Module().(*Module).compiler.(*libraryDecorator).path.String()
-	install_path_lib32 := ctx.ModuleForTests("libfoo",
+	install_path_lib32 := ctx.ModuleForTests(t, "libfoo",
 		"android_arm_armv7-a-neon_dylib").Module().(*Module).compiler.(*libraryDecorator).path.String()
-	install_path_bin := ctx.ModuleForTests("fizzbuzz",
+	install_path_bin := ctx.ModuleForTests(t, "fizzbuzz",
 		"android_arm64_armv8-a").Module().(*Module).compiler.(*binaryDecorator).path.String()
 
 	if !strings.HasSuffix(install_path_lib64, "system/lib64/libfoo.dylib.so") {
@@ -259,13 +259,13 @@
 				android.FixtureAddTextFile(tc.modulePath+"Android.bp", bp),
 			).RunTest(t)
 
-			r := result.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
+			r := result.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
 			android.AssertStringDoesContain(t, "libfoo flags", r.Args["rustcFlags"], tc.fooFlags)
 
-			r = result.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
+			r = result.ModuleForTests(t, "libbar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
 			android.AssertStringDoesContain(t, "libbar flags", r.Args["rustcFlags"], "${config.RustDefaultLints}")
 
-			r = result.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
+			r = result.ModuleForTests(t, "libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
 			android.AssertStringDoesContain(t, "libfoobar flags", r.Args["rustcFlags"], "${config.RustAllowAllLints}")
 		})
 	}
@@ -283,9 +283,9 @@
 			srcs: ["foo.rs"],
 			crate_name: "foo",
 		}`)
-	fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Module().(*Module)
-	fooRlib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std").Module().(*Module)
-	fooDylib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
+	fizz := ctx.ModuleForTests(t, "fizz", "android_arm64_armv8-a").Module().(*Module)
+	fooRlib := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_rlib_dylib-std").Module().(*Module)
+	fooDylib := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
 
 	if !android.InList("libstd", fizz.Properties.AndroidMkDylibs) {
 		t.Errorf("libstd is not linked dynamically for device binaries")
diff --git a/rust/config/OWNERS b/rust/config/OWNERS
index dfff873..e4bf5e6 100644
--- a/rust/config/OWNERS
+++ b/rust/config/OWNERS
@@ -1,2 +1,2 @@
-per-file global.go = srhines@google.com, chh@google.com, pirama@google.com, yikong@google.com
+per-file global.go = srhines@google.com, pirama@google.com, yikong@google.com
 
diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go
index 94a4457..efcd56a 100644
--- a/rust/config/arm64_device.go
+++ b/rust/config/arm64_device.go
@@ -45,6 +45,14 @@
 			"-Z branch-protection=bti,pac-ret",
 			"-Z stack-protector=none",
 		},
+		"armv9-3a": []string{
+			"-Z branch-protection=bti,pac-ret",
+			"-Z stack-protector=none",
+		},
+		"armv9-4a": []string{
+			"-Z branch-protection=bti,pac-ret",
+			"-Z stack-protector=none",
+		},
 	}
 )
 
diff --git a/rust/config/global.go b/rust/config/global.go
index 7b79fca..907316f 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -25,7 +25,7 @@
 var (
 	pctx = android.NewPackageContext("android/soong/rust/config")
 
-	RustDefaultVersion = "1.81.0"
+	RustDefaultVersion = "1.83.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
@@ -42,6 +42,8 @@
 	}
 
 	GlobalRustFlags = []string{
+		// Allow `--extern force:foo` for dylib support
+		"-Z unstable-options",
 		"-Z stack-protector=strong",
 		"-Z remap-cwd-prefix=.",
 		"-C debuginfo=2",
@@ -121,7 +123,7 @@
 	pctx.StaticVariable("RustBin", "${RustPath}/bin")
 
 	pctx.ImportAs("cc_config", "android/soong/cc/config")
-	pctx.StaticVariable("RustLinker", "${cc_config.ClangBin}/clang++")
+	pctx.StaticVariable("ClangCmd", "${cc_config.ClangBin}/clang++")
 
 	pctx.StaticVariable("DeviceGlobalLinkFlags", strings.Join(deviceGlobalLinkFlags, " "))
 
diff --git a/rust/config/lints.go b/rust/config/lints.go
index 735aa16..715e8aa 100644
--- a/rust/config/lints.go
+++ b/rust/config/lints.go
@@ -55,6 +55,7 @@
 	defaultClippyLints = []string{
 		// Let people hack in peace. ;)
 		"-A clippy::disallowed_names",
+		"-A clippy::empty_line_after_doc_comments",
 		"-A clippy::type-complexity",
 		"-A clippy::unnecessary_fallible_conversions",
 		"-A clippy::unnecessary-wraps",
diff --git a/rust/coverage.go b/rust/coverage.go
index 381fcf1..798b21d 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -15,12 +15,13 @@
 package rust
 
 import (
+	"android/soong/android"
+
 	"github.com/google/blueprint"
 
 	"android/soong/cc"
 )
 
-var CovLibraryName = "libprofile-clang-extras"
 var ProfilerBuiltins = "libprofiler_builtins.rust_sysroot"
 
 // Add '%c' to default specifier after we resolve http://b/210012154
@@ -37,12 +38,20 @@
 	return []interface{}{&cov.Properties}
 }
 
+func getClangProfileLibraryName(ctx ModuleContextIntf) string {
+	if ctx.RustModule().UseSdk() {
+		return "libprofile-clang-extras_ndk"
+	} else {
+		return "libprofile-clang-extras"
+	}
+}
+
 func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps {
 	if cov.Properties.NeedCoverageVariant {
 		if ctx.Device() {
 			ctx.AddVariationDependencies([]blueprint.Variation{
 				{Mutator: "link", Variation: "static"},
-			}, cc.CoverageDepTag, CovLibraryName)
+			}, cc.CoverageDepTag, getClangProfileLibraryName(ctx))
 		}
 
 		// no_std modules are missing libprofiler_builtins which provides coverage, so we need to add it as a dependency.
@@ -65,16 +74,18 @@
 		flags.RustFlags = append(flags.RustFlags,
 			"-C instrument-coverage", "-g")
 		if ctx.Device() {
-			coverage := ctx.GetDirectDepWithTag(CovLibraryName, cc.CoverageDepTag).(cc.LinkableInterface)
+			m := ctx.GetDirectDepProxyWithTag(getClangProfileLibraryName(ctx), cc.CoverageDepTag)
+			coverage := android.OtherModuleProviderOrDefault(ctx, m, cc.LinkableInfoProvider)
 			flags.LinkFlags = append(flags.LinkFlags,
-				profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open")
-			deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
+				profileInstrFlag, "-g", coverage.OutputFile.Path().String(), "-Wl,--wrap,open")
+			deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile.Path())
 		}
 
 		// no_std modules are missing libprofiler_builtins which provides coverage, so we need to add it as a dependency.
 		if rustModule, ok := ctx.Module().(*Module); ok && rustModule.compiler.noStdlibs() {
-			profiler_builtins := ctx.GetDirectDepWithTag(ProfilerBuiltins, rlibDepTag).(*Module)
-			deps.RLibs = append(deps.RLibs, RustLibrary{Path: profiler_builtins.OutputFile().Path(), CrateName: profiler_builtins.CrateName()})
+			m := ctx.GetDirectDepProxyWithTag(ProfilerBuiltins, rlibDepTag)
+			profiler_builtins := android.OtherModuleProviderOrDefault(ctx, m, cc.LinkableInfoProvider)
+			deps.RLibs = append(deps.RLibs, RustLibrary{Path: profiler_builtins.OutputFile.Path(), CrateName: profiler_builtins.CrateName})
 		}
 
 		if cc.EnableContinuousCoverage(ctx) {
diff --git a/rust/coverage_test.go b/rust/coverage_test.go
index 0f599d7..f9198f1 100644
--- a/rust/coverage_test.go
+++ b/rust/coverage_test.go
@@ -51,10 +51,10 @@
 	}
 
 	// Just test the dylib variants unless the library coverage logic changes to distinguish between the types.
-	libfooCov := ctx.ModuleForTests("libfoo_cov", "android_arm64_armv8-a_dylib_cov").Rule("rustc")
-	libbarNoCov := ctx.ModuleForTests("libbar_nocov", "android_arm64_armv8-a_dylib").Rule("rustc")
-	fizzCov := ctx.ModuleForTests("fizz_cov", "android_arm64_armv8-a_cov").Rule("rustc")
-	buzzNoCov := ctx.ModuleForTests("buzzNoCov", "android_arm64_armv8-a").Rule("rustc")
+	libfooCov := ctx.ModuleForTests(t, "libfoo_cov", "android_arm64_armv8-a_dylib_cov").Rule("rustc")
+	libbarNoCov := ctx.ModuleForTests(t, "libbar_nocov", "android_arm64_armv8-a_dylib").Rule("rustc")
+	fizzCov := ctx.ModuleForTests(t, "fizz_cov", "android_arm64_armv8-a_cov").Rule("rustc")
+	buzzNoCov := ctx.ModuleForTests(t, "buzzNoCov", "android_arm64_armv8-a").Rule("rustc")
 
 	rustcCoverageFlags := []string{"-C instrument-coverage", " -g "}
 	for _, flag := range rustcCoverageFlags {
@@ -103,7 +103,7 @@
 			srcs: ["foo.rs"],
 		}`)
 
-	fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a_cov").Rule("rustc")
+	fizz := ctx.ModuleForTests(t, "fizz", "android_arm64_armv8-a_cov").Rule("rustc")
 	if !strings.Contains(fizz.Args["linkFlags"], "libprofile-clang-extras.a") {
 		t.Fatalf("missing expected coverage 'libprofile-clang-extras' dependency in linkFlags: %#v", fizz.Args["linkFlags"])
 	}
diff --git a/rust/doc.go b/rust/doc.go
index fe20523..3616c8e 100644
--- a/rust/doc.go
+++ b/rust/doc.go
@@ -37,14 +37,14 @@
 		FlagWithArg("-C ", docDir.String()).
 		FlagWithArg("-D ", docDir.String())
 
-	ctx.VisitAllModules(func(module android.Module) {
-		if !module.Enabled(ctx) {
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+		if !android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider).Enabled {
 			return
 		}
 
-		if m, ok := module.(*Module); ok {
-			if m.docTimestampFile.Valid() {
-				zipCmd.Implicit(m.docTimestampFile.Path())
+		if m, ok := android.OtherModuleProvider(ctx, module, RustInfoProvider); ok {
+			if m.DocTimestampFile.Valid() {
+				zipCmd.Implicit(m.DocTimestampFile.Path())
 			}
 		}
 	})
diff --git a/rust/fuzz.go b/rust/fuzz.go
index 1770d2e..9e8efd7 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -121,7 +121,7 @@
 	return out
 }
 
-func (fuzzer *fuzzDecorator) stdLinkage(ctx *depsContext) RustLinkage {
+func (fuzzer *fuzzDecorator) stdLinkage(device bool) RustLinkage {
 	return RlibLinkage
 }
 
@@ -130,7 +130,7 @@
 }
 
 func (fuzz *fuzzDecorator) install(ctx ModuleContext) {
-	fuzz.fuzzPackagedModule = cc.PackageFuzzModule(ctx, fuzz.fuzzPackagedModule, pctx)
+	fuzz.fuzzPackagedModule = cc.PackageFuzzModule(ctx, fuzz.fuzzPackagedModule)
 
 	installBase := "fuzz"
 
diff --git a/rust/fuzz_test.go b/rust/fuzz_test.go
index 6cb8b93..f462795 100644
--- a/rust/fuzz_test.go
+++ b/rust/fuzz_test.go
@@ -41,7 +41,7 @@
 	`)
 
 	// Check that appropriate dependencies are added and that the rustlib linkage is correct.
-	fuzz_libtest_mod := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Module().(*Module)
+	fuzz_libtest_mod := ctx.ModuleForTests(t, "fuzz_libtest", "android_arm64_armv8-a_fuzzer").Module().(*Module)
 	if !android.InList("liblibfuzzer_sys.rlib-std", fuzz_libtest_mod.Properties.AndroidMkRlibs) {
 		t.Errorf("liblibfuzzer_sys rlib library dependency missing for rust_fuzz module. %#v", fuzz_libtest_mod.Properties.AndroidMkRlibs)
 	}
@@ -50,21 +50,21 @@
 	}
 
 	// Check that compiler flags are set appropriately .
-	fuzz_libtest := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Rule("rustc")
+	fuzz_libtest := ctx.ModuleForTests(t, "fuzz_libtest", "android_arm64_armv8-a_fuzzer").Rule("rustc")
 	if !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-C passes='sancov-module'") ||
 		!strings.Contains(fuzz_libtest.Args["rustcFlags"], "--cfg fuzzing") {
 		t.Errorf("rust_fuzz module does not contain the expected flags (sancov-module, cfg fuzzing).")
 	}
 
 	// Check that host modules support fuzzing.
-	host_fuzzer := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Rule("rustc")
+	host_fuzzer := ctx.ModuleForTests(t, "fuzz_libtest", "android_arm64_armv8-a_fuzzer").Rule("rustc")
 	if !strings.Contains(host_fuzzer.Args["rustcFlags"], "-C passes='sancov-module'") ||
 		!strings.Contains(host_fuzzer.Args["rustcFlags"], "--cfg fuzzing") {
 		t.Errorf("rust_fuzz_host module does not contain the expected flags (sancov-module, cfg fuzzing).")
 	}
 
 	// Check that dependencies have 'fuzzer' variants produced for them as well.
-	libtest_fuzzer := ctx.ModuleForTests("libtest_fuzzing", "android_arm64_armv8-a_rlib_rlib-std_fuzzer").Output("libtest_fuzzing.rlib")
+	libtest_fuzzer := ctx.ModuleForTests(t, "libtest_fuzzing", "android_arm64_armv8-a_rlib_rlib-std_fuzzer").Output("libtest_fuzzing.rlib")
 	if !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-C passes='sancov-module'") ||
 		!strings.Contains(libtest_fuzzer.Args["rustcFlags"], "--cfg fuzzing") {
 		t.Errorf("rust_fuzz dependent library does not contain the expected flags (sancov-module, cfg fuzzing).")
@@ -93,7 +93,7 @@
 			}
 	`)
 
-	fuzz_libtest := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Module().(*Module)
+	fuzz_libtest := ctx.ModuleForTests(t, "fuzz_libtest", "android_arm64_armv8-a_fuzzer").Module().(*Module)
 
 	if !strings.Contains(fuzz_libtest.FuzzSharedLibraries().String(), ":libcc_direct_dep.so") {
 		t.Errorf("rust_fuzz does not contain the expected bundled direct shared libs ('libcc_direct_dep'): %#v", fuzz_libtest.FuzzSharedLibraries().String())
@@ -134,17 +134,24 @@
 			}
 	`)
 
-	fuzz_shared_libtest := ctx.ModuleForTests("fuzz_shared_libtest", "android_arm64_armv8-a_fuzzer").Module().(cc.LinkableInterface)
-	fuzz_static_libtest := ctx.ModuleForTests("fuzz_static_libtest", "android_arm64_armv8-a_fuzzer").Module().(cc.LinkableInterface)
-	fuzz_staticffi_libtest := ctx.ModuleForTests("fuzz_staticffi_libtest", "android_arm64_armv8-a_fuzzer").Module().(cc.LinkableInterface)
+	fuzz_shared_libtest := ctx.ModuleForTests(t, "fuzz_shared_libtest", "android_arm64_armv8-a_fuzzer").Module()
+	fuzz_static_libtest := ctx.ModuleForTests(t, "fuzz_static_libtest", "android_arm64_armv8-a_fuzzer").Module()
+	fuzz_staticffi_libtest := ctx.ModuleForTests(t, "fuzz_staticffi_libtest", "android_arm64_armv8-a_fuzzer").Module()
 
-	if !strings.Contains(fuzz_shared_libtest.FuzzSharedLibraries().String(), ":libcc_transitive_dep.so") {
-		t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_shared ('libcc_transitive_dep'): %#v", fuzz_shared_libtest.FuzzSharedLibraries().String())
+	fuzzSharedLibraries := func(module android.Module) string {
+		if info, ok := android.OtherModuleProvider(ctx, module, cc.LinkableInfoProvider); ok {
+			return info.FuzzSharedLibraries.String()
+		}
+		return ""
 	}
-	if !strings.Contains(fuzz_static_libtest.FuzzSharedLibraries().String(), ":libcc_transitive_dep.so") {
-		t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_static ('libcc_transitive_dep'): %#v", fuzz_static_libtest.FuzzSharedLibraries().String())
+
+	if libs := fuzzSharedLibraries(fuzz_shared_libtest); !strings.Contains(libs, ":libcc_transitive_dep.so") {
+		t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_shared ('libcc_transitive_dep'): %#v", libs)
 	}
-	if !strings.Contains(fuzz_staticffi_libtest.FuzzSharedLibraries().String(), ":libcc_transitive_dep.so") {
-		t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_rlib ('libcc_transitive_dep'): %#v", fuzz_staticffi_libtest.FuzzSharedLibraries().String())
+	if libs := fuzzSharedLibraries(fuzz_static_libtest); !strings.Contains(libs, ":libcc_transitive_dep.so") {
+		t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_static ('libcc_transitive_dep'): %#v", libs)
+	}
+	if libs := fuzzSharedLibraries(fuzz_staticffi_libtest); !strings.Contains(libs, ":libcc_transitive_dep.so") {
+		t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_static ('libcc_transitive_dep'): %#v", libs)
 	}
 }
diff --git a/rust/image.go b/rust/image.go
index 51b8289..aa10a6d 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -137,7 +137,7 @@
 	// Additionally check if this module is inVendor() that means it is a "vendor" variant of a
 	// module. As well as SoC specific modules, vendor variants must be installed to /vendor
 	// unless they have "odm_available: true".
-	return mod.InVendor() && !mod.VendorVariantToOdm()
+	return mod.HasVendorVariant() && mod.InVendor() && !mod.VendorVariantToOdm()
 }
 
 func (mod *Module) InstallInOdm() bool {
diff --git a/rust/image_test.go b/rust/image_test.go
index d84eb10..e5ecd79 100644
--- a/rust/image_test.go
+++ b/rust/image_test.go
@@ -22,14 +22,13 @@
 	"android/soong/cc"
 )
 
-// Test that cc modules can depend on vendor_available rust_ffi_rlib/rust_ffi_static libraries.
+// Test that cc modules can depend on vendor_available rust_ffi_static libraries.
 func TestVendorLinkage(t *testing.T) {
 	ctx := testRust(t, `
 			cc_binary {
 				name: "fizz_vendor_available",
 				static_libs: [
 					"libfoo_vendor",
-					"libfoo_vendor_static"
 				],
 				vendor_available: true,
 			}
@@ -38,24 +37,18 @@
 				static_libs: ["libfoo_vendor"],
 				soc_specific: true,
 			}
-			rust_ffi_rlib {
-				name: "libfoo_vendor",
-				crate_name: "foo",
-				srcs: ["foo.rs"],
-				vendor_available: true,
-			}
 			rust_ffi_static {
-				name: "libfoo_vendor_static",
+				name: "libfoo_vendor",
 				crate_name: "foo",
 				srcs: ["foo.rs"],
 				vendor_available: true,
 			}
 		`)
 
-	vendorBinary := ctx.ModuleForTests("fizz_vendor_available", "android_vendor_arm64_armv8-a").Module().(*cc.Module)
+	vendorBinary := ctx.ModuleForTests(t, "fizz_vendor_available", "android_vendor_arm64_armv8-a").Module().(*cc.Module)
 
-	if android.InList("libfoo_vendor_static.vendor", vendorBinary.Properties.AndroidMkStaticLibs) {
-		t.Errorf("vendorBinary should not have a staticlib dependency on libfoo_vendor_static.vendor: %#v", vendorBinary.Properties.AndroidMkStaticLibs)
+	if android.InList("libfoo_vendor.vendor", vendorBinary.Properties.AndroidMkStaticLibs) {
+		t.Errorf("vendorBinary should not have a staticlib dependency on libfoo_vendor.vendor: %#v", vendorBinary.Properties.AndroidMkStaticLibs)
 	}
 }
 
@@ -71,7 +64,7 @@
 			}
 		`)
 
-	vendor := ctx.ModuleForTests("libfoo", "android_vendor_arm64_armv8-a_shared").Rule("rustc")
+	vendor := ctx.ModuleForTests(t, "libfoo", "android_vendor_arm64_armv8-a_shared").Rule("rustc")
 
 	if !strings.Contains(vendor.Args["rustcFlags"], "--cfg 'android_vndk'") {
 		t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"])
@@ -83,7 +76,7 @@
 		t.Errorf("unexpected \"--cfg 'android_product'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"])
 	}
 
-	product := ctx.ModuleForTests("libfoo", "android_product_arm64_armv8-a_shared").Rule("rustc")
+	product := ctx.ModuleForTests(t, "libfoo", "android_product_arm64_armv8-a_shared").Rule("rustc")
 	if !strings.Contains(product.Args["rustcFlags"], "--cfg 'android_vndk'") {
 		t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"])
 	}
@@ -94,7 +87,7 @@
 		t.Errorf("missing \"--cfg 'android_product'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"])
 	}
 
-	system := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("rustc")
+	system := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared").Rule("rustc")
 	if strings.Contains(system.Args["rustcFlags"], "--cfg 'android_vndk'") {
 		t.Errorf("unexpected \"--cfg 'android_vndk'\" for libfoo system variant, rustcFlags: %#v", system.Args["rustcFlags"])
 	}
@@ -107,36 +100,29 @@
 
 }
 
-// Test that cc modules can link against vendor_ramdisk_available rust_ffi_rlib and rust_ffi_static libraries.
+// Test that cc modules can link against vendor_ramdisk_available rust_ffi_static libraries.
 func TestVendorRamdiskLinkage(t *testing.T) {
 	ctx := testRust(t, `
 			cc_library_shared {
 				name: "libcc_vendor_ramdisk",
 				static_libs: [
 					"libfoo_vendor_ramdisk",
-					"libfoo_static_vendor_ramdisk"
 				],
 				system_shared_libs: [],
 				vendor_ramdisk_available: true,
 			}
-			rust_ffi_rlib {
-				name: "libfoo_vendor_ramdisk",
-				crate_name: "foo",
-				srcs: ["foo.rs"],
-				vendor_ramdisk_available: true,
-			}
 			rust_ffi_static {
-				name: "libfoo_static_vendor_ramdisk",
+				name: "libfoo_vendor_ramdisk",
 				crate_name: "foo",
 				srcs: ["foo.rs"],
 				vendor_ramdisk_available: true,
 			}
 		`)
 
-	vendorRamdiskLibrary := ctx.ModuleForTests("libcc_vendor_ramdisk", "android_vendor_ramdisk_arm64_armv8-a_shared").Module().(*cc.Module)
+	vendorRamdiskLibrary := ctx.ModuleForTests(t, "libcc_vendor_ramdisk", "android_vendor_ramdisk_arm64_armv8-a_shared").Module().(*cc.Module)
 
-	if android.InList("libfoo_static_vendor_ramdisk.vendor_ramdisk", vendorRamdiskLibrary.Properties.AndroidMkStaticLibs) {
-		t.Errorf("libcc_vendor_ramdisk should not have a dependency on the libfoo_static_vendor_ramdisk static library")
+	if android.InList("libfoo_vendor_ramdisk.vendor_ramdisk", vendorRamdiskLibrary.Properties.AndroidMkStaticLibs) {
+		t.Errorf("libcc_vendor_ramdisk should not have a dependency on the libfoo_vendor_ramdisk static library")
 	}
 }
 
@@ -158,7 +144,7 @@
 }
 
 func checkInstallPartition(t *testing.T, ctx *android.TestContext, name, variant, expected string) {
-	mod := ctx.ModuleForTests(name, variant).Module().(*Module)
+	mod := ctx.ModuleForTests(t, name, variant).Module().(*Module)
 	partitionDefined := false
 	checkPartition := func(specific bool, partition string) {
 		if specific {
diff --git a/rust/library.go b/rust/library.go
index bd3359b..415785a 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -25,6 +25,7 @@
 
 	"android/soong/android"
 	"android/soong/cc"
+	cc_config "android/soong/cc/config"
 )
 
 var (
@@ -40,15 +41,10 @@
 	android.RegisterModuleType("rust_library_host_rlib", RustLibraryRlibHostFactory)
 	android.RegisterModuleType("rust_ffi", RustFFIFactory)
 	android.RegisterModuleType("rust_ffi_shared", RustFFISharedFactory)
-	android.RegisterModuleType("rust_ffi_rlib", RustFFIRlibFactory)
 	android.RegisterModuleType("rust_ffi_host", RustFFIHostFactory)
 	android.RegisterModuleType("rust_ffi_host_shared", RustFFISharedHostFactory)
-	android.RegisterModuleType("rust_ffi_host_rlib", RustFFIRlibHostFactory)
-
-	// TODO: Remove when all instances of rust_ffi_static have been switched to rust_ffi_rlib
-	// Alias rust_ffi_static to the rust_ffi_rlib factory
-	android.RegisterModuleType("rust_ffi_static", RustFFIRlibFactory)
-	android.RegisterModuleType("rust_ffi_host_static", RustFFIRlibHostFactory)
+	android.RegisterModuleType("rust_ffi_static", RustLibraryRlibFactory)
+	android.RegisterModuleType("rust_ffi_host_static", RustLibraryRlibHostFactory)
 }
 
 type VariantLibraryProperties struct {
@@ -69,12 +65,28 @@
 	// path to include directories to export to cc_* modules, only relevant for static/shared variants.
 	Export_include_dirs []string `android:"path,arch_variant"`
 
+	// Version script to pass to the linker. By default this will replace the
+	// implicit rustc emitted version script to mirror expected behavior in CC.
+	// This is only relevant for rust_ffi_shared modules which are exposing a
+	// versioned C API.
+	Version_script *string `android:"path,arch_variant"`
+
+	// A version_script formatted text file with additional symbols to export
+	// for rust shared or dylibs which the rustc compiler does not automatically
+	// export, e.g. additional symbols from whole_static_libs. Unlike
+	// Version_script, this is not meant to imply a stable API.
+	Extra_exported_symbols *string `android:"path,arch_variant"`
+
 	// Whether this library is part of the Rust toolchain sysroot.
 	Sysroot *bool
 
-	// Exclude this rust_ffi target from being included in APEXes.
-	// TODO(b/362509506): remove this once stubs are properly supported by rust_ffi targets.
+	// Deprecated - exclude this rust_ffi target from being included in APEXes.
+	// TODO(b/362509506): remove this once all apex_exclude uses are switched to stubs.
 	Apex_exclude *bool
+
+	// Generate stubs to make this library accessible to APEXes.
+	// Can only be set for modules producing shared libraries.
+	Stubs cc.StubsProperties `android:"arch_variant"`
 }
 
 type LibraryMutatedProperties struct {
@@ -102,6 +114,15 @@
 
 	// Whether this library variant should be link libstd via rlibs
 	VariantIsStaticStd bool `blueprint:"mutated"`
+
+	// This variant is a stubs lib
+	BuildStubs bool `blueprint:"mutated"`
+	// This variant is the latest version
+	IsLatestVersion bool `blueprint:"mutated"`
+	// Version of the stubs lib
+	StubsVersion string `blueprint:"mutated"`
+	// List of all stubs versions associated with an implementation lib
+	AllStubsVersions []string `blueprint:"mutated"`
 }
 
 type libraryDecorator struct {
@@ -114,13 +135,30 @@
 	includeDirs       android.Paths
 	sourceProvider    SourceProvider
 
-	isFFI bool
-
 	// table-of-contents file for cdylib crates to optimize out relinking when possible
 	tocFile android.OptionalPath
+
+	// Path to the file containing the APIs exported by this library
+	stubsSymbolFilePath    android.Path
+	apiListCoverageXmlPath android.ModuleOutPath
+	versionScriptPath      android.OptionalPath
+}
+
+func (library *libraryDecorator) stubs() bool {
+	return library.MutatedProperties.BuildStubs
+}
+
+func (library *libraryDecorator) setAPIListCoverageXMLPath(xml android.ModuleOutPath) {
+	library.apiListCoverageXmlPath = xml
+}
+
+func (library *libraryDecorator) libraryProperties() LibraryCompilerProperties {
+	return library.Properties
 }
 
 type libraryInterface interface {
+	cc.VersionedInterface
+
 	rlib() bool
 	dylib() bool
 	static() bool
@@ -157,10 +195,16 @@
 
 	toc() android.OptionalPath
 
-	isFFILibrary() bool
+	IsStubsImplementationRequired() bool
+	setAPIListCoverageXMLPath(out android.ModuleOutPath)
+
+	libraryProperties() LibraryCompilerProperties
 }
 
 func (library *libraryDecorator) nativeCoverage() bool {
+	if library.BuildStubs() {
+		return false
+	}
 	return true
 }
 
@@ -262,18 +306,96 @@
 	}
 }
 
-func (library *libraryDecorator) stdLinkage(ctx *depsContext) RustLinkage {
-	if library.static() || library.MutatedProperties.VariantIsStaticStd || (library.rlib() && library.isFFILibrary()) {
+func (library *libraryDecorator) stdLinkage(device bool) RustLinkage {
+	if library.static() || library.MutatedProperties.VariantIsStaticStd {
 		return RlibLinkage
 	} else if library.baseCompiler.preferRlib() {
 		return RlibLinkage
 	}
-	return DefaultLinkage
+	return DylibLinkage
 }
 
 var _ compiler = (*libraryDecorator)(nil)
 var _ libraryInterface = (*libraryDecorator)(nil)
+var _ cc.VersionedInterface = (*libraryDecorator)(nil)
 var _ exportedFlagsProducer = (*libraryDecorator)(nil)
+var _ cc.VersionedInterface = (*libraryDecorator)(nil)
+
+func (library *libraryDecorator) HasLLNDKStubs() bool {
+	// Rust LLNDK is currently unsupported
+	return false
+}
+
+func (library *libraryDecorator) HasVendorPublicLibrary() bool {
+	// Rust does not support vendor_public_library yet.
+	return false
+}
+
+func (library *libraryDecorator) HasLLNDKHeaders() bool {
+	// Rust LLNDK is currently unsupported
+	return false
+}
+
+func (library *libraryDecorator) HasStubsVariants() bool {
+	// Just having stubs.symbol_file is enough to create a stub variant. In that case
+	// the stub for the future API level is created.
+	return library.Properties.Stubs.Symbol_file != nil ||
+		len(library.Properties.Stubs.Versions) > 0
+}
+
+func (library *libraryDecorator) IsStubsImplementationRequired() bool {
+	return BoolDefault(library.Properties.Stubs.Implementation_installable, true)
+}
+
+func (library *libraryDecorator) GetAPIListCoverageXMLPath() android.ModuleOutPath {
+	return library.apiListCoverageXmlPath
+}
+
+func (library *libraryDecorator) AllStubsVersions() []string {
+	return library.MutatedProperties.AllStubsVersions
+}
+
+func (library *libraryDecorator) SetAllStubsVersions(versions []string) {
+	library.MutatedProperties.AllStubsVersions = versions
+}
+
+func (library *libraryDecorator) SetStubsVersion(version string) {
+	library.MutatedProperties.StubsVersion = version
+}
+
+func (library *libraryDecorator) SetBuildStubs(isLatest bool) {
+	library.MutatedProperties.BuildStubs = true
+	library.MutatedProperties.IsLatestVersion = isLatest
+}
+
+func (library *libraryDecorator) BuildStubs() bool {
+	return library.MutatedProperties.BuildStubs
+}
+
+func (library *libraryDecorator) ImplementationModuleName(name string) string {
+	return name
+}
+
+func (library *libraryDecorator) IsLLNDKMovedToApex() bool {
+	// Rust does not support LLNDK.
+	return false
+}
+
+func (library *libraryDecorator) StubsVersion() string {
+	return library.MutatedProperties.StubsVersion
+}
+
+// stubsVersions implements cc.VersionedInterface.
+func (library *libraryDecorator) StubsVersions(ctx android.BaseModuleContext) []string {
+	if !library.HasStubsVariants() {
+		return nil
+	}
+
+	// Future API level is implicitly added if there isn't
+	versions := cc.AddCurrentVersionIfNotPresent(library.Properties.Stubs.Versions)
+	cc.NormalizeVersions(ctx, versions)
+	return versions
+}
 
 // rust_library produces all Rust variants (rust_library_dylib and
 // rust_library_rlib).
@@ -283,8 +405,7 @@
 	return module.Init()
 }
 
-// rust_ffi produces all FFI variants (rust_ffi_shared, rust_ffi_static, and
-// rust_ffi_rlib).
+// rust_ffi produces all FFI variants (rust_ffi_shared, rust_ffi_static).
 func RustFFIFactory() android.Module {
 	module, library := NewRustLibrary(android.HostAndDeviceSupported)
 	library.BuildOnlyFFI()
@@ -298,7 +419,7 @@
 	return module.Init()
 }
 
-// rust_library_rlib produces an rlib (Rust crate type "rlib").
+// rust_library_rlib and rust_ffi_static produces an rlib (Rust crate type "rlib").
 func RustLibraryRlibFactory() android.Module {
 	module, library := NewRustLibrary(android.HostAndDeviceSupported)
 	library.BuildOnlyRlib()
@@ -322,7 +443,7 @@
 }
 
 // rust_ffi_host produces all FFI variants for the host
-// (rust_ffi_rlib_host, rust_ffi_static_host, and rust_ffi_shared_host).
+// (rust_ffi_static_host and rust_ffi_shared_host).
 func RustFFIHostFactory() android.Module {
 	module, library := NewRustLibrary(android.HostSupported)
 	library.BuildOnlyFFI()
@@ -337,8 +458,8 @@
 	return module.Init()
 }
 
-// rust_library_rlib_host produces an rlib for the host (Rust crate
-// type "rlib").
+// rust_library_rlib_host and rust_ffi_static_host produces an rlib for the host
+// (Rust crate type "rlib").
 func RustLibraryRlibHostFactory() android.Module {
 	module, library := NewRustLibrary(android.HostSupported)
 	library.BuildOnlyRlib()
@@ -353,23 +474,16 @@
 	return module.Init()
 }
 
-// rust_ffi_rlib_host produces an rlib for the host (Rust crate
-// type "rlib").
-func RustFFIRlibHostFactory() android.Module {
-	module, library := NewRustLibrary(android.HostSupported)
-	library.BuildOnlyRlib()
+func CheckRustLibraryProperties(mctx android.DefaultableHookContext) {
+	lib := mctx.Module().(*Module).compiler.(libraryInterface)
+	if !lib.buildShared() {
+		if lib.libraryProperties().Stubs.Symbol_file != nil ||
+			lib.libraryProperties().Stubs.Implementation_installable != nil ||
+			len(lib.libraryProperties().Stubs.Versions) > 0 {
 
-	library.isFFI = true
-	return module.Init()
-}
-
-// rust_ffi_rlib produces an rlib (Rust crate type "rlib").
-func RustFFIRlibFactory() android.Module {
-	module, library := NewRustLibrary(android.HostAndDeviceSupported)
-	library.BuildOnlyRlib()
-
-	library.isFFI = true
-	return module.Init()
+			mctx.PropertyErrorf("stubs", "stubs properties can only be set for rust_ffi or rust_ffi_shared modules")
+		}
+	}
 }
 
 func (library *libraryDecorator) BuildOnlyFFI() {
@@ -378,8 +492,6 @@
 	library.MutatedProperties.BuildRlib = true
 	library.MutatedProperties.BuildShared = true
 	library.MutatedProperties.BuildStatic = false
-
-	library.isFFI = true
 }
 
 func (library *libraryDecorator) BuildOnlyRust() {
@@ -408,8 +520,6 @@
 	library.MutatedProperties.BuildDylib = false
 	library.MutatedProperties.BuildShared = false
 	library.MutatedProperties.BuildStatic = true
-
-	library.isFFI = true
 }
 
 func (library *libraryDecorator) BuildOnlyShared() {
@@ -417,12 +527,6 @@
 	library.MutatedProperties.BuildDylib = false
 	library.MutatedProperties.BuildStatic = false
 	library.MutatedProperties.BuildShared = true
-
-	library.isFFI = true
-}
-
-func (library *libraryDecorator) isFFILibrary() bool {
-	return library.isFFI
 }
 
 func NewRustLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
@@ -441,6 +545,7 @@
 
 	module.compiler = library
 
+	module.SetDefaultableHook(CheckRustLibraryProperties)
 	return module, library
 }
 
@@ -484,13 +589,6 @@
 
 	cfgs := library.baseCompiler.Properties.Cfgs.GetOrDefault(ctx, nil)
 
-	if library.dylib() {
-		// We need to add a dependency on std in order to link crates as dylibs.
-		// The hack to add this dependency is guarded by the following cfg so
-		// that we don't force a dependency when it isn't needed.
-		cfgs = append(cfgs, "android_dylib")
-	}
-
 	cfgFlags := cfgsToFlags(cfgs)
 
 	flags.RustFlags = append(flags.RustFlags, cfgFlags...)
@@ -511,7 +609,9 @@
 
 	flags = CommonLibraryCompilerFlags(ctx, flags)
 
-	if library.isFFI {
+	if library.rlib() || library.shared() {
+		// rlibs collect include dirs as well since they are used to
+		// produce staticlibs in the final C linkages
 		library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Include_dirs)...)
 		library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Export_include_dirs)...)
 	}
@@ -574,9 +674,36 @@
 
 	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
 	flags.LinkFlags = append(flags.LinkFlags, deps.depLinkFlags...)
-	flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects...)
+	flags.LinkFlags = append(flags.LinkFlags, deps.rustLibObjects...)
+	flags.LinkFlags = append(flags.LinkFlags, deps.sharedLibObjects...)
+	flags.LinkFlags = append(flags.LinkFlags, deps.staticLibObjects...)
+	flags.LinkFlags = append(flags.LinkFlags, deps.wholeStaticLibObjects...)
+
+	if String(library.Properties.Version_script) != "" {
+		if String(library.Properties.Extra_exported_symbols) != "" {
+			ctx.ModuleErrorf("version_script and extra_exported_symbols cannot both be set.")
+		}
+
+		if library.shared() {
+			// "-Wl,--android-version-script" signals to the rustcLinker script
+			// that the default version script should be removed.
+			flags.LinkFlags = append(flags.LinkFlags, "-Wl,--android-version-script="+android.PathForModuleSrc(ctx, String(library.Properties.Version_script)).String())
+			deps.LinkerDeps = append(deps.LinkerDeps, android.PathForModuleSrc(ctx, String(library.Properties.Version_script)))
+		} else if !library.static() && !library.rlib() {
+			// We include rlibs here because rust_ffi produces rlib variants
+			ctx.PropertyErrorf("version_script", "can only be set for rust_ffi modules")
+		}
+	}
+
+	if String(library.Properties.Extra_exported_symbols) != "" {
+		// Passing a second version script (rustc calculates and emits a
+		// default version script) will concatenate the first version script.
+		flags.LinkFlags = append(flags.LinkFlags, "-Wl,--version-script="+android.PathForModuleSrc(ctx, String(library.Properties.Extra_exported_symbols)).String())
+		deps.LinkerDeps = append(deps.LinkerDeps, android.PathForModuleSrc(ctx, String(library.Properties.Extra_exported_symbols)))
+	}
 
 	if library.dylib() {
+
 		// We need prefer-dynamic for now to avoid linking in the static stdlib. See:
 		// https://github.com/rust-lang/rust/issues/19680
 		// https://github.com/rust-lang/rust/issues/34909
@@ -584,7 +711,11 @@
 	}
 
 	// Call the appropriate builder for this library type
-	if library.rlib() {
+	if library.stubs() {
+		ccFlags := library.getApiStubsCcFlags(ctx)
+		stubObjs := library.compileModuleLibApiStubs(ctx, ccFlags)
+		cc.BuildRustStubs(ctx, outputFile, stubObjs, ccFlags)
+	} else if library.rlib() {
 		ret.kytheFile = TransformSrctoRlib(ctx, crateRootPath, deps, flags, outputFile).kytheFile
 	} else if library.dylib() {
 		ret.kytheFile = TransformSrctoDylib(ctx, crateRootPath, deps, flags, outputFile).kytheFile
@@ -594,19 +725,36 @@
 		ret.kytheFile = TransformSrctoShared(ctx, crateRootPath, deps, flags, outputFile).kytheFile
 	}
 
+	// rlibs and dylibs propagate their shared, whole static, and rustlib dependencies
 	if library.rlib() || library.dylib() {
 		library.flagExporter.exportLinkDirs(deps.linkDirs...)
-		library.flagExporter.exportLinkObjects(deps.linkObjects...)
+		library.flagExporter.exportRustLibs(deps.rustLibObjects...)
+		library.flagExporter.exportSharedLibs(deps.sharedLibObjects...)
+		library.flagExporter.exportWholeStaticLibs(deps.wholeStaticLibObjects...)
 	}
 
+	// rlibs also propagate their staticlibs dependencies
+	if library.rlib() {
+		library.flagExporter.exportStaticLibs(deps.staticLibObjects...)
+	}
 	// Since we have FFI rlibs, we need to collect their includes as well
-	if library.static() || library.shared() || library.rlib() {
-		android.SetProvider(ctx, cc.FlagExporterInfoProvider, cc.FlagExporterInfo{
+	if library.static() || library.shared() || library.rlib() || library.stubs() {
+		ccExporter := cc.FlagExporterInfo{
 			IncludeDirs: android.FirstUniquePaths(library.includeDirs),
-		})
+		}
+		if library.rlib() {
+			ccExporter.RustRlibDeps = append(ccExporter.RustRlibDeps, deps.reexportedCcRlibDeps...)
+			ccExporter.RustRlibDeps = append(ccExporter.RustRlibDeps, deps.reexportedWholeCcRlibDeps...)
+		}
+		android.SetProvider(ctx, cc.FlagExporterInfoProvider, ccExporter)
 	}
 
-	if library.shared() {
+	if library.dylib() {
+		// reexport whole-static'd dependencies for dylibs.
+		library.flagExporter.wholeRustRlibDeps = append(library.flagExporter.wholeRustRlibDeps, deps.reexportedWholeCcRlibDeps...)
+	}
+
+	if library.shared() || library.stubs() {
 		// Optimize out relinking against shared libraries whose interface hasn't changed by
 		// depending on a table of contents file instead of the library itself.
 		tocFile := outputFile.ReplaceExtension(ctx, flags.Toolchain.SharedLibSuffix()[1:]+".toc")
@@ -617,9 +765,7 @@
 			TableOfContents: android.OptionalPathForPath(tocFile),
 			SharedLibrary:   outputFile,
 			Target:          ctx.Target(),
-			// TODO: when rust supports stubs uses the stubs state rather than inferring it from
-			//  apex_exclude.
-			IsStubs: Bool(library.Properties.Apex_exclude),
+			IsStubs:         library.BuildStubs(),
 		})
 	}
 
@@ -631,8 +777,10 @@
 			TransitiveStaticLibrariesForOrdering: depSet,
 		})
 	}
+	cc.AddStubDependencyProviders(ctx)
 
-	library.flagExporter.setProvider(ctx)
+	// Set our flagexporter provider to export relevant Rust flags
+	library.flagExporter.setRustProvider(ctx)
 
 	return ret
 }
@@ -650,6 +798,53 @@
 	}
 }
 
+func (library *libraryDecorator) getApiStubsCcFlags(ctx ModuleContext) cc.Flags {
+	ccFlags := cc.Flags{}
+	toolchain := cc_config.FindToolchain(ctx.Os(), ctx.Arch())
+
+	platformSdkVersion := ""
+	if ctx.Device() {
+		platformSdkVersion = ctx.Config().PlatformSdkVersion().String()
+	}
+	minSdkVersion := cc.MinSdkVersion(ctx.RustModule(), cc.CtxIsForPlatform(ctx), ctx.Device(), platformSdkVersion)
+
+	// Collect common CC compilation flags
+	ccFlags = cc.CommonLinkerFlags(ctx, ccFlags, true, toolchain, false)
+	ccFlags = cc.CommonLibraryLinkerFlags(ctx, ccFlags, toolchain, library.getStem(ctx))
+	ccFlags = cc.AddStubLibraryCompilerFlags(ccFlags)
+	ccFlags = cc.AddTargetFlags(ctx, ccFlags, toolchain, minSdkVersion, false)
+
+	return ccFlags
+}
+
+func (library *libraryDecorator) compileModuleLibApiStubs(ctx ModuleContext, ccFlags cc.Flags) cc.Objects {
+	mod := ctx.RustModule()
+
+	symbolFile := String(library.Properties.Stubs.Symbol_file)
+	library.stubsSymbolFilePath = android.PathForModuleSrc(ctx, symbolFile)
+
+	apiParams := cc.ApiStubsParams{
+		NotInPlatform:  mod.NotInPlatform(),
+		IsNdk:          mod.IsNdk(ctx.Config()),
+		BaseModuleName: mod.BaseModuleName(),
+		ModuleName:     ctx.ModuleName(),
+	}
+	flag := cc.GetApiStubsFlags(apiParams)
+
+	nativeAbiResult := cc.ParseNativeAbiDefinition(ctx, symbolFile,
+		android.ApiLevelOrPanic(ctx, library.MutatedProperties.StubsVersion), flag)
+	objs := cc.CompileStubLibrary(ctx, ccFlags, nativeAbiResult.StubSrc, mod.getSharedFlags())
+
+	library.versionScriptPath = android.OptionalPathForPath(nativeAbiResult.VersionScript)
+
+	// Parse symbol file to get API list for coverage
+	if library.StubsVersion() == "current" && ctx.PrimaryArch() && !mod.InRecovery() && !mod.InProduct() && !mod.InVendor() {
+		library.apiListCoverageXmlPath = cc.ParseSymbolFileForAPICoverage(ctx, symbolFile)
+	}
+
+	return objs
+}
+
 func (library *libraryDecorator) rustdoc(ctx ModuleContext, flags Flags,
 	deps PathDeps) android.OptionalPath {
 	// rustdoc has builtin support for documenting config specific information
@@ -687,6 +882,20 @@
 	library.MutatedProperties.VariantIsDisabled = true
 }
 
+func (library *libraryDecorator) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
+	library.baseCompiler.moduleInfoJSON(ctx, moduleInfoJSON)
+
+	if library.rlib() {
+		moduleInfoJSON.Class = []string{"RLIB_LIBRARIES"}
+	} else if library.dylib() {
+		moduleInfoJSON.Class = []string{"DYLIB_LIBRARIES"}
+	} else if library.static() {
+		moduleInfoJSON.Class = []string{"STATIC_LIBRARIES"}
+	} else if library.shared() {
+		moduleInfoJSON.Class = []string{"SHARED_LIBRARIES"}
+	}
+}
+
 var validCrateName = regexp.MustCompile("[^a-zA-Z0-9_]+")
 
 func validateLibraryStem(ctx BaseModuleContext, filename string, crate_name string) {
@@ -745,6 +954,9 @@
 }
 
 func (libraryTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	if ctx.DepTag() == android.PrebuiltDepTag {
+		return sourceVariation
+	}
 	return ""
 }
 
@@ -812,6 +1024,12 @@
 			},
 			sourceDepTag, ctx.ModuleName())
 	}
+
+	if prebuilt, ok := m.compiler.(*prebuiltLibraryDecorator); ok {
+		if Bool(prebuilt.Properties.Force_use_prebuilt) && len(prebuilt.prebuiltSrcs()) > 0 {
+			m.Prebuilt().SetUsePrebuilt(true)
+		}
+	}
 }
 
 type libstdTransitionMutator struct{}
@@ -821,11 +1039,7 @@
 		// Only create a variant if a library is actually being built.
 		if library, ok := m.compiler.(libraryInterface); ok {
 			if library.rlib() && !library.sysroot() {
-				if library.isFFILibrary() {
-					return []string{"rlib-std"}
-				} else {
-					return []string{"rlib-std", "dylib-std"}
-				}
+				return []string{"rlib-std", "dylib-std"}
 			}
 		}
 	}
@@ -833,6 +1047,9 @@
 }
 
 func (libstdTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	if ctx.DepTag() == android.PrebuiltDepTag {
+		return sourceVariation
+	}
 	return ""
 }
 
diff --git a/rust/library_test.go b/rust/library_test.go
index 35a420c..6cc4f25 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -42,10 +42,10 @@
 		}`)
 
 	// Test all variants are being built.
-	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
-	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
-	libfooFFIRlib := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
-	libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared").Rule("rustc")
+	libfooRlib := ctx.ModuleForTests(t, "libfoo", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
+	libfooDylib := ctx.ModuleForTests(t, "libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
+	libfooFFIRlib := ctx.ModuleForTests(t, "libfoo.ffi", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
+	libfooShared := ctx.ModuleForTests(t, "libfoo.ffi", "linux_glibc_x86_64_shared").Rule("rustc")
 
 	rlibCrateType := "rlib"
 	dylibCrateType := "dylib"
@@ -82,29 +82,13 @@
 			crate_name: "foo",
 		}`)
 
-	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
+	libfooDylib := ctx.ModuleForTests(t, "libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
 
 	if !strings.Contains(libfooDylib.Args["rustcFlags"], "prefer-dynamic") {
 		t.Errorf("missing prefer-dynamic flag for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"])
 	}
 }
 
-// Check that we are passing the android_dylib config flag
-func TestAndroidDylib(t *testing.T) {
-	ctx := testRust(t, `
-		rust_library_host_dylib {
-			name: "libfoo",
-			srcs: ["foo.rs"],
-			crate_name: "foo",
-		}`)
-
-	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
-
-	if !strings.Contains(libfooDylib.Args["rustcFlags"], "--cfg 'android_dylib'") {
-		t.Errorf("missing android_dylib cfg flag for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"])
-	}
-}
-
 func TestValidateLibraryStem(t *testing.T) {
 	testRustError(t, "crate_name must be defined.", `
 			rust_library_host {
@@ -150,7 +134,7 @@
 			crate_name: "foo",
 		}`)
 
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared")
+	libfoo := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_shared")
 
 	libfooOutput := libfoo.Rule("rustc")
 	if !strings.Contains(libfooOutput.Args["linkFlags"], "-Wl,-soname=libfoo.so") {
@@ -176,7 +160,7 @@
 			shared_libs: ["libfoo"],
 		}`)
 
-	fizzbuzz := ctx.ModuleForTests("fizzbuzz", "android_arm64_armv8-a").Rule("ld")
+	fizzbuzz := ctx.ModuleForTests(t, "fizzbuzz", "android_arm64_armv8-a").Rule("ld")
 
 	if !android.SuffixInList(fizzbuzz.Implicits.Strings(), "libfoo.so.toc") {
 		t.Errorf("missing expected libfoo.so.toc implicit dependency, instead found: %#v",
@@ -192,7 +176,7 @@
 			crate_name: "foo",
 		}`)
 
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_rlib-std")
+	libfoo := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_rlib_rlib-std")
 
 	if !android.InList("libstd", libfoo.Module().(*Module).Properties.AndroidMkRlibs) {
 		t.Errorf("Static libstd rlib expected to be a dependency of Rust rlib libraries. Rlib deps are: %#v",
@@ -202,9 +186,9 @@
 
 func TestNativeDependencyOfRlib(t *testing.T) {
 	ctx := testRust(t, `
-		rust_ffi_rlib {
-			name: "libffi_rlib",
-			crate_name: "ffi_rlib",
+		rust_ffi_static {
+			name: "libffi_static",
+			crate_name: "ffi_static",
 			rlibs: ["librust_rlib"],
 			srcs: ["foo.rs"],
 		}
@@ -225,9 +209,9 @@
 		}
 		`)
 
-	rustRlibRlibStd := ctx.ModuleForTests("librust_rlib", "android_arm64_armv8-a_rlib_rlib-std")
-	rustRlibDylibStd := ctx.ModuleForTests("librust_rlib", "android_arm64_armv8-a_rlib_dylib-std")
-	ffiRlib := ctx.ModuleForTests("libffi_rlib", "android_arm64_armv8-a_rlib_rlib-std")
+	rustRlibRlibStd := ctx.ModuleForTests(t, "librust_rlib", "android_arm64_armv8-a_rlib_rlib-std")
+	rustRlibDylibStd := ctx.ModuleForTests(t, "librust_rlib", "android_arm64_armv8-a_rlib_dylib-std")
+	ffiRlib := ctx.ModuleForTests(t, "libffi_static", "android_arm64_armv8-a_rlib_rlib-std")
 
 	modules := []android.TestingModule{
 		rustRlibRlibStd,
@@ -301,10 +285,10 @@
 			],
 		}`)
 
-	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std")
-	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib")
-	libfooFFIRlib := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_rlib_rlib-std")
-	libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared")
+	libfooRlib := ctx.ModuleForTests(t, "libfoo", "linux_glibc_x86_64_rlib_rlib-std")
+	libfooDylib := ctx.ModuleForTests(t, "libfoo", "linux_glibc_x86_64_dylib")
+	libfooFFIRlib := ctx.ModuleForTests(t, "libfoo.ffi", "linux_glibc_x86_64_rlib_rlib-std")
+	libfooShared := ctx.ModuleForTests(t, "libfoo.ffi", "linux_glibc_x86_64_shared")
 
 	for _, static := range []android.TestingModule{libfooRlib, libfooFFIRlib} {
 		if !android.InList("libbar.rlib-std", static.Module().(*Module).Properties.AndroidMkRlibs) {
@@ -346,7 +330,7 @@
 		}
 	`)
 
-	foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib")
+	foo := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_dylib")
 	foo.Output("libfoo.dylib.so")
 	foo.Output("unstripped/libfoo.dylib.so")
 	// Check that the `cp` rule is using the stripped version as input.
@@ -355,7 +339,7 @@
 		t.Errorf("installed library not based on stripped version: %v", cp.Input)
 	}
 
-	fizzBar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeOutput("unstripped/libbar.dylib.so")
+	fizzBar := ctx.ModuleForTests(t, "libbar", "android_arm64_armv8-a_dylib").MaybeOutput("unstripped/libbar.dylib.so")
 	if fizzBar.Rule != nil {
 		t.Errorf("unstripped library exists, so stripped library has incorrectly been generated")
 	}
@@ -388,15 +372,15 @@
 			prefer_rlib: true,
 		}`)
 
-	libfooDylib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
-	libfooRlibStatic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_rlib-std").Module().(*Module)
-	libfooRlibDynamic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std").Module().(*Module)
+	libfooDylib := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
+	libfooRlibStatic := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_rlib_rlib-std").Module().(*Module)
+	libfooRlibDynamic := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_rlib_dylib-std").Module().(*Module)
 
-	libbarShared := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module().(*Module)
-	libbarFFIRlib := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_rlib_rlib-std").Module().(*Module)
+	libbarShared := ctx.ModuleForTests(t, "libbar", "android_arm64_armv8-a_shared").Module().(*Module)
+	libbarFFIRlib := ctx.ModuleForTests(t, "libbar", "android_arm64_armv8-a_rlib_rlib-std").Module().(*Module)
 
 	// prefer_rlib works the same for both rust_library and rust_ffi, so a single check is sufficient here.
-	libbarRlibStd := ctx.ModuleForTests("libbar.prefer_rlib", "android_arm64_armv8-a_shared").Module().(*Module)
+	libbarRlibStd := ctx.ModuleForTests(t, "libbar.prefer_rlib", "android_arm64_armv8-a_shared").Module().(*Module)
 
 	if !android.InList("libstd", libfooRlibStatic.Properties.AndroidMkRlibs) {
 		t.Errorf("rlib-std variant for device rust_library_rlib does not link libstd as an rlib")
@@ -412,10 +396,10 @@
 		t.Errorf("Device rust_ffi_shared does not link libstd as an dylib")
 	}
 	if !android.InList("libstd", libbarFFIRlib.Properties.AndroidMkRlibs) {
-		t.Errorf("Device rust_ffi_rlib does not link libstd as an rlib")
+		t.Errorf("Device rust_ffi_static does not link libstd as an rlib")
 	}
 	if !android.InList("libfoo.rlib-std", libbarFFIRlib.Properties.AndroidMkRlibs) {
-		t.Errorf("Device rust_ffi_rlib does not link dependent rustlib rlib-std variant")
+		t.Errorf("Device rust_ffi_static does not link dependent rustlib rlib-std variant")
 	}
 	if !android.InList("libstd", libbarRlibStd.Properties.AndroidMkRlibs) {
 		t.Errorf("rust_ffi with prefer_rlib does not link libstd as an rlib")
@@ -438,6 +422,388 @@
 			shared_libs: ["libbar"],
 			host_supported: true,
 		}`)
-	libfooStatic := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_static").Rule("cc")
+	libfooStatic := ctx.ModuleForTests(t, "libfoo", "linux_glibc_x86_64_static").Rule("cc")
 	android.AssertStringDoesContain(t, "cFlags for lib module", libfooStatic.Args["cFlags"], " -Irust_includes ")
 }
+
+// Make sure cc_rustlibs_for_make has the expected behavior, and that
+// cc_library_static does as well.
+// This is here instead of cc/library_test.go because the test needs to
+// define a rust_ffi module which can't be done in soong-cc to avoid the
+// circular dependency.
+func TestCCRustlibsForMake(t *testing.T) {
+	t.Parallel()
+	result := testRust(t, `
+		rust_ffi_static {
+			name: "libbar",
+			srcs: ["foo.rs"],
+			crate_name: "bar",
+			export_include_dirs: ["rust_includes"],
+			host_supported: true,
+		}
+
+		cc_rustlibs_for_make {
+			name: "libmakerustlibs",
+			whole_static_libs: ["libbar"],
+		}
+
+		cc_library_static {
+			name: "libccstatic",
+			whole_static_libs: ["libbar"],
+		}
+	`)
+
+	libmakerustlibs := result.ModuleForTests(t, "libmakerustlibs", "android_arm64_armv8-a_static").MaybeRule("rustc")
+	libccstatic := result.ModuleForTests(t, "libccstatic", "android_arm64_armv8-a_static").MaybeRule("rustc")
+
+	if libmakerustlibs.Output == nil {
+		t.Errorf("cc_rustlibs_for_make is not generating a  Rust staticlib when it should")
+	}
+
+	if libccstatic.Output != nil {
+		t.Errorf("cc_library_static is generating a Rust staticlib when it should not")
+	}
+}
+
+func TestRustVersionScript(t *testing.T) {
+	ctx := testRust(t, `
+	rust_library {
+		name: "librs",
+		srcs: ["bar.rs"],
+		crate_name: "rs",
+		extra_exported_symbols: "librs.map.txt",
+	}
+	rust_ffi {
+		name: "libffi",
+		srcs: ["foo.rs"],
+		crate_name: "ffi",
+		version_script: "libffi.map.txt",
+	}
+	`)
+
+	//linkFlags
+	librs := ctx.ModuleForTests(t, "librs", "android_arm64_armv8-a_dylib").Rule("rustc")
+	libffi := ctx.ModuleForTests(t, "libffi", "android_arm64_armv8-a_shared").Rule("rustc")
+
+	if !strings.Contains(librs.Args["linkFlags"], "-Wl,--version-script=librs.map.txt") {
+		t.Errorf("missing expected -Wl,--version-script= linker flag for libextended shared lib, linkFlags: %#v",
+			librs.Args["linkFlags"])
+	}
+	if strings.Contains(librs.Args["linkFlags"], "-Wl,--android-version-script=librs.map.txt") {
+		t.Errorf("unexpected -Wl,--android-version-script= linker flag for libextended shared lib, linkFlags: %#v",
+			librs.Args["linkFlags"])
+	}
+
+	if !strings.Contains(libffi.Args["linkFlags"], "-Wl,--android-version-script=libffi.map.txt") {
+		t.Errorf("missing -Wl,--android-version-script= linker flag for libreplaced shared lib, linkFlags: %#v",
+			libffi.Args["linkFlags"])
+	}
+	if strings.Contains(libffi.Args["linkFlags"], "-Wl,--version-script=libffi.map.txt") {
+		t.Errorf("unexpected -Wl,--version-script= linker flag for libextended shared lib, linkFlags: %#v",
+			libffi.Args["linkFlags"])
+	}
+}
+
+func TestRustVersionScriptPropertyErrors(t *testing.T) {
+	testRustError(t, "version_script: can only be set for rust_ffi modules", `
+		rust_library {
+			name: "librs",
+			srcs: ["bar.rs"],
+			crate_name: "rs",
+			version_script: "libbar.map.txt",
+		}`)
+	testRustError(t, "version_script and extra_exported_symbols", `
+		rust_ffi {
+			name: "librs",
+			srcs: ["bar.rs"],
+			crate_name: "rs",
+			version_script: "libbar.map.txt",
+			extra_exported_symbols: "libbar.map.txt",
+		}`)
+}
+
+func TestStubsVersions(t *testing.T) {
+	t.Parallel()
+	bp := `
+		rust_ffi {
+			name: "libfoo",
+			crate_name: "foo",
+			srcs: ["foo.rs"],
+			stubs: {
+				versions: ["29", "R", "current"],
+			},
+		}
+	`
+	ctx := android.GroupFixturePreparers(
+		prepareForRustTest,
+		android.PrepareForTestWithVisibility,
+		rustMockedFiles.AddToFixture(),
+		android.FixtureModifyConfigAndContext(func(config android.Config, ctx *android.TestContext) {
+			config.TestProductVariables.Platform_version_active_codenames = []string{"R"}
+		})).RunTestWithBp(t, bp)
+
+	variants := ctx.ModuleVariantsForTests("libfoo")
+	for _, expectedVer := range []string{"29", "R", "current"} {
+		expectedVariant := "android_arm_armv7-a-neon_shared_" + expectedVer
+		if !android.InList(expectedVariant, variants) {
+			t.Errorf("missing expected variant: %q", expectedVariant)
+		}
+	}
+}
+
+func TestStubsVersions_NotSorted(t *testing.T) {
+	t.Parallel()
+	bp := `
+	rust_ffi_shared {
+		name: "libfoo",
+		crate_name: "foo",
+		srcs: ["foo.rs"],
+		stubs: {
+				versions: ["29", "current", "R"],
+			},
+		}
+	`
+	fixture := android.GroupFixturePreparers(
+		prepareForRustTest,
+		android.PrepareForTestWithVisibility,
+		rustMockedFiles.AddToFixture(),
+
+		android.FixtureModifyConfigAndContext(func(config android.Config, ctx *android.TestContext) {
+			config.TestProductVariables.Platform_version_active_codenames = []string{"R"}
+		}))
+
+	fixture.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(`"libfoo" .*: versions: not sorted`)).RunTestWithBp(t, bp)
+}
+
+func TestStubsVersions_ParseError(t *testing.T) {
+	t.Parallel()
+	bp := `
+	rust_ffi_shared {
+		name: "libfoo",
+		crate_name: "foo",
+		srcs: ["foo.rs"],
+			stubs: {
+				versions: ["29", "current", "X"],
+			},
+		}
+	`
+	fixture := android.GroupFixturePreparers(
+		prepareForRustTest,
+		android.PrepareForTestWithVisibility,
+		rustMockedFiles.AddToFixture(),
+
+		android.FixtureModifyConfigAndContext(func(config android.Config, ctx *android.TestContext) {
+			config.TestProductVariables.Platform_version_active_codenames = []string{"R"}
+		}))
+
+	fixture.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(`"libfoo" .*: versions: "X" could not be parsed as an integer and is not a recognized codename`)).RunTestWithBp(t, bp)
+}
+
+func TestVersionedStubs(t *testing.T) {
+	t.Parallel()
+	bp := `
+	rust_ffi_shared {
+		name: "libFoo",
+		crate_name: "Foo",
+		srcs: ["foo.rs"],
+			stubs: {
+				symbol_file: "foo.map.txt",
+				versions: ["1", "2", "3"],
+			},
+		}
+
+	cc_library_shared {
+		name: "libBar",
+		srcs: ["bar.c"],
+		shared_libs: ["libFoo#1"],
+	}
+
+	rust_library {
+		name: "libbar_rs",
+		crate_name: "bar_rs",
+		srcs: ["bar.rs"],
+		shared_libs: ["libFoo#1"],
+	}
+	rust_ffi {
+		name: "libbar_ffi_rs",
+		crate_name: "bar_ffi_rs",
+		srcs: ["bar.rs"],
+		shared_libs: ["libFoo#1"],
+	}
+	`
+
+	ctx := android.GroupFixturePreparers(
+		prepareForRustTest,
+		android.PrepareForTestWithVisibility,
+		rustMockedFiles.AddToFixture()).RunTestWithBp(t, bp)
+
+	variants := ctx.ModuleVariantsForTests("libFoo")
+	expectedVariants := []string{
+		"android_arm64_armv8-a_shared",
+		"android_arm64_armv8-a_shared_1",
+		"android_arm64_armv8-a_shared_2",
+		"android_arm64_armv8-a_shared_3",
+		"android_arm64_armv8-a_shared_current",
+		"android_arm_armv7-a-neon_shared",
+		"android_arm_armv7-a-neon_shared_1",
+		"android_arm_armv7-a-neon_shared_2",
+		"android_arm_armv7-a-neon_shared_3",
+		"android_arm_armv7-a-neon_shared_current",
+	}
+	variantsMismatch := false
+	if len(variants) != len(expectedVariants) {
+		variantsMismatch = true
+	} else {
+		for _, v := range expectedVariants {
+			if !android.InList(v, variants) {
+				variantsMismatch = false
+			}
+		}
+	}
+	if variantsMismatch {
+		t.Errorf("variants of libFoo expected:\n")
+		for _, v := range expectedVariants {
+			t.Errorf("%q\n", v)
+		}
+		t.Errorf(", but got:\n")
+		for _, v := range variants {
+			t.Errorf("%q\n", v)
+		}
+	}
+
+	libBarLinkRule := ctx.ModuleForTests(t, "libBar", "android_arm64_armv8-a_shared").Rule("ld")
+	libBarFlags := libBarLinkRule.Args["libFlags"]
+
+	libBarRsRustcRule := ctx.ModuleForTests(t, "libbar_rs", "android_arm64_armv8-a_dylib").Rule("rustc")
+	libBarRsFlags := libBarRsRustcRule.Args["linkFlags"]
+
+	libBarFfiRsRustcRule := ctx.ModuleForTests(t, "libbar_ffi_rs", "android_arm64_armv8-a_shared").Rule("rustc")
+	libBarFfiRsFlags := libBarFfiRsRustcRule.Args["linkFlags"]
+
+	libFoo1StubPath := "libFoo/android_arm64_armv8-a_shared_1/unstripped/libFoo.so"
+	if !strings.Contains(libBarFlags, libFoo1StubPath) {
+		t.Errorf("%q is not found in %q", libFoo1StubPath, libBarFlags)
+	}
+	if !strings.Contains(libBarRsFlags, libFoo1StubPath) {
+		t.Errorf("%q is not found in %q", libFoo1StubPath, libBarRsFlags)
+	}
+	if !strings.Contains(libBarFfiRsFlags, libFoo1StubPath) {
+		t.Errorf("%q is not found in %q", libFoo1StubPath, libBarFfiRsFlags)
+	}
+}
+
+func TestCheckConflictingExplicitVersions(t *testing.T) {
+	t.Parallel()
+	bp := `
+	cc_library_shared {
+		name: "libbar",
+		srcs: ["bar.c"],
+		shared_libs: ["libfoo", "libfoo#impl"],
+	}
+
+	rust_ffi_shared {
+		name: "libfoo",
+		crate_name: "foo",
+		srcs: ["foo.rs"],
+		stubs: {
+			versions: ["29", "current"],
+		},
+	}
+	`
+	fixture := android.GroupFixturePreparers(
+		prepareForRustTest,
+		android.PrepareForTestWithVisibility,
+		rustMockedFiles.AddToFixture())
+
+	fixture.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(`duplicate shared libraries with different explicit versions`)).RunTestWithBp(t, bp)
+}
+
+func TestAddnoOverride64GlobalCflags(t *testing.T) {
+	t.Parallel()
+	bp := `
+		cc_library_shared {
+			name: "libclient",
+			srcs: ["foo.c"],
+			shared_libs: ["libfoo#1"],
+		}
+
+		rust_ffi_shared {
+			name: "libfoo",
+			crate_name: "foo",
+			srcs: ["foo.c"],
+			shared_libs: ["libbar"],
+			stubs: {
+				symbol_file: "foo.map.txt",
+				versions: ["1", "2", "3"],
+			},
+		}
+
+		cc_library_shared {
+			name: "libbar",
+			export_include_dirs: ["include/libbar"],
+			srcs: ["foo.c"],
+		}`
+	ctx := android.GroupFixturePreparers(
+		prepareForRustTest,
+		android.PrepareForTestWithVisibility,
+		rustMockedFiles.AddToFixture()).RunTestWithBp(t, bp)
+
+	cFlags := ctx.ModuleForTests(t, "libclient", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+
+	if !strings.Contains(cFlags, "${config.NoOverride64GlobalCflags}") {
+		t.Errorf("expected %q in cflags, got %q", "${config.NoOverride64GlobalCflags}", cFlags)
+	}
+}
+
+// Make sure the stubs properties can only be used in modules producing shared libs
+func TestRustStubsFFIOnly(t *testing.T) {
+	testRustError(t, "stubs properties", `
+		rust_library {
+			name: "libfoo",
+			crate_name: "foo",
+			srcs: ["foo.c"],
+			shared_libs: ["libbar"],
+			stubs: {
+				symbol_file: "foo.map.txt",
+			},
+		}
+	`)
+
+	testRustError(t, "stubs properties", `
+		rust_library {
+			name: "libfoo",
+			crate_name: "foo",
+			srcs: ["foo.c"],
+			shared_libs: ["libbar"],
+			stubs: {
+				versions: ["1"],
+			},
+		}
+	`)
+
+	testRustError(t, "stubs properties", `
+		rust_ffi_static {
+			name: "libfoo",
+			crate_name: "foo",
+			srcs: ["foo.c"],
+			shared_libs: ["libbar"],
+			stubs: {
+				symbol_file: "foo.map.txt",
+			},
+		}
+	`)
+	testRustError(t, "stubs properties", `
+		rust_ffi_static {
+			name: "libfoo",
+			crate_name: "foo",
+			srcs: ["foo.c"],
+			shared_libs: ["libbar"],
+			stubs: {
+				versions: ["1"],
+			},
+		}
+	`)
+}
+
+// TODO: When rust_ffi libraries support export_*_lib_headers,
+// add a test similar to cc.TestStubsLibReexportsHeaders
diff --git a/rust/prebuilt.go b/rust/prebuilt.go
index e35e510..7c92dda 100644
--- a/rust/prebuilt.go
+++ b/rust/prebuilt.go
@@ -30,6 +30,8 @@
 	Srcs []string `android:"path,arch_variant"`
 	// directories containing associated rlib dependencies
 	Link_dirs []string `android:"path,arch_variant"`
+
+	Force_use_prebuilt *bool `android:"arch_variant"`
 }
 
 type prebuiltLibraryDecorator struct {
@@ -158,7 +160,7 @@
 
 func (prebuilt *prebuiltLibraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
 	prebuilt.flagExporter.exportLinkDirs(android.PathsForModuleSrc(ctx, prebuilt.Properties.Link_dirs).Strings()...)
-	prebuilt.flagExporter.setProvider(ctx)
+	prebuilt.flagExporter.setRustProvider(ctx)
 	srcPath := prebuiltPath(ctx, prebuilt)
 	prebuilt.baseCompiler.unstrippedOutputFile = srcPath
 	return buildOutput{outputFile: srcPath}
@@ -211,7 +213,7 @@
 
 func (prebuilt *prebuiltProcMacroDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
 	prebuilt.flagExporter.exportLinkDirs(android.PathsForModuleSrc(ctx, prebuilt.Properties.Link_dirs).Strings()...)
-	prebuilt.flagExporter.setProvider(ctx)
+	prebuilt.flagExporter.setRustProvider(ctx)
 	srcPath := prebuiltPath(ctx, prebuilt)
 	prebuilt.baseCompiler.unstrippedOutputFile = srcPath
 	return buildOutput{outputFile: srcPath}
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index 1ff6637..837e1a6 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -67,6 +67,7 @@
 func (procMacro *procMacroDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
 	flags = procMacro.baseCompiler.compilerFlags(ctx, flags)
 	flags.RustFlags = append(flags.RustFlags, "--extern proc_macro")
+	flags.RustFlags = append(flags.RustFlags, "-C metadata="+ctx.ModuleName())
 	return flags
 }
 
@@ -99,3 +100,9 @@
 	// Proc_macros are never installed
 	return false
 }
+
+func (library *procMacroDecorator) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
+	library.baseCompiler.moduleInfoJSON(ctx, moduleInfoJSON)
+
+	moduleInfoJSON.Class = []string{"PROC_MACRO_LIBRARIES"}
+}
diff --git a/rust/proc_macro_test.go b/rust/proc_macro_test.go
index cc81938..8a95eb4 100644
--- a/rust/proc_macro_test.go
+++ b/rust/proc_macro_test.go
@@ -28,7 +28,7 @@
 	  }
 	`)
 
-	libprocmacro := ctx.ModuleForTests("libprocmacro", "linux_glibc_x86_64").Rule("rustc")
+	libprocmacro := ctx.ModuleForTests(t, "libprocmacro", "linux_glibc_x86_64").Rule("rustc")
 
 	if !strings.Contains(libprocmacro.Args["rustcFlags"], "--extern proc_macro") {
 		t.Errorf("--extern proc_macro flag not being passed to rustc for proc macro %#v", libprocmacro.Args["rustcFlags"])
diff --git a/rust/protobuf_test.go b/rust/protobuf_test.go
index cae071b..531e034 100644
--- a/rust/protobuf_test.go
+++ b/rust/protobuf_test.go
@@ -41,13 +41,13 @@
 		}
 	`)
 	// Check that libprotobuf is added as a dependency.
-	librust_proto := ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_dylib").Module().(*Module)
+	librust_proto := ctx.ModuleForTests(t, "librust_proto", "android_arm64_armv8-a_dylib").Module().(*Module)
 	if !android.InList("libprotobuf", librust_proto.Properties.AndroidMkDylibs) {
 		t.Errorf("libprotobuf dependency missing for rust_protobuf (dependency missing from AndroidMkDylibs)")
 	}
 
 	// Make sure the correct plugin is being used.
-	librust_proto_out := ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_source").Output("buf.rs")
+	librust_proto_out := ctx.ModuleForTests(t, "librust_proto", "android_arm64_armv8-a_source").Output("buf.rs")
 	cmd := librust_proto_out.RuleParams.Command
 	if w := "protoc-gen-rust"; !strings.Contains(cmd, w) {
 		t.Errorf("expected %q in %q", w, cmd)
@@ -62,7 +62,7 @@
 	}
 
 	// Check proto.rs, the second protobuf, is listed as an output
-	librust_proto_outputs := ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_source").AllOutputs()
+	librust_proto_outputs := ctx.ModuleForTests(t, "librust_proto", "android_arm64_armv8-a_source").AllOutputs()
 	if android.InList("proto.rs", librust_proto_outputs) {
 		t.Errorf("rust_protobuf is not producing multiple outputs; expected 'proto.rs' in list, got: %#v ",
 			librust_proto_outputs)
@@ -92,7 +92,7 @@
 		}
 	`)
 	// Check that librust_exported_proto is added as additional crate to generate source.
-	librust_proto := ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_source").Module().(*Module).sourceProvider.(*protobufDecorator)
+	librust_proto := ctx.ModuleForTests(t, "librust_proto", "android_arm64_armv8-a_source").Module().(*Module).sourceProvider.(*protobufDecorator)
 	if !android.InList("rust_exported_proto", librust_proto.additionalCrates) {
 		t.Errorf("librust_proto should have librust_exported_proto included as an additional crate for generated source, instead got: %#v", librust_proto.additionalCrates)
 	}
@@ -111,7 +111,7 @@
 	}
 
 	// Check librust_proto args includes -Iproto
-	librust_proto_rule := ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_source").Output("proto.rs")
+	librust_proto_rule := ctx.ModuleForTests(t, "librust_proto", "android_arm64_armv8-a_source").Output("proto.rs")
 	cmd := librust_proto_rule.RuleParams.Command
 	if w := "-Iproto"; !strings.Contains(cmd, w) {
 		t.Errorf("expected %q in %q", w, cmd)
@@ -131,7 +131,7 @@
 	`)
 
 	// Check that libprotobuf is added as a dependency.
-	librust_grpcio_module := ctx.ModuleForTests("librust_grpcio", "android_arm64_armv8-a_dylib").Module().(*Module)
+	librust_grpcio_module := ctx.ModuleForTests(t, "librust_grpcio", "android_arm64_armv8-a_dylib").Module().(*Module)
 
 	// Check that libgrpcio is added as a dependency.
 	if !android.InList("libgrpcio", librust_grpcio_module.Properties.AndroidMkDylibs) {
@@ -144,7 +144,7 @@
 	}
 
 	// Make sure the correct plugin is being used.
-	librust_grpcio_out := ctx.ModuleForTests("librust_grpcio", "android_arm64_armv8-a_source").Output("foo_grpc.rs")
+	librust_grpcio_out := ctx.ModuleForTests(t, "librust_grpcio", "android_arm64_armv8-a_source").Output("foo_grpc.rs")
 	cmd := librust_grpcio_out.RuleParams.Command
 	if w := "protoc-gen-grpc"; !strings.Contains(cmd, w) {
 		t.Errorf("expected %q in %q", w, cmd)
@@ -156,7 +156,7 @@
 	}
 
 	// Check proto.rs, the second protobuf, is listed as an output
-	librust_grpcio_outputs := ctx.ModuleForTests("librust_grpcio", "android_arm64_armv8-a_source").AllOutputs()
+	librust_grpcio_outputs := ctx.ModuleForTests(t, "librust_grpcio", "android_arm64_armv8-a_source").AllOutputs()
 	if android.InList("proto_grpc.rs", librust_grpcio_outputs) {
 		t.Errorf("rust_protobuf is not producing multiple outputs; expected 'proto_grpc.rs' in list, got: %#v ",
 			librust_grpcio_outputs)
diff --git a/rust/rust.go b/rust/rust.go
index eeb228c..54b5d92 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -34,6 +34,38 @@
 
 var pctx = android.NewPackageContext("android/soong/rust")
 
+type LibraryInfo struct {
+	Rlib  bool
+	Dylib bool
+}
+
+type CompilerInfo struct {
+	StdLinkageForDevice    RustLinkage
+	StdLinkageForNonDevice RustLinkage
+	NoStdlibs              bool
+	LibraryInfo            *LibraryInfo
+}
+
+type ProtobufDecoratorInfo struct{}
+
+type SourceProviderInfo struct {
+	Srcs                  android.Paths
+	ProtobufDecoratorInfo *ProtobufDecoratorInfo
+}
+
+type RustInfo struct {
+	AndroidMkSuffix               string
+	RustSubName                   string
+	TransitiveAndroidMkSharedLibs depset.DepSet[string]
+	CompilerInfo                  *CompilerInfo
+	SnapshotInfo                  *cc.SnapshotInfo
+	SourceProviderInfo            *SourceProviderInfo
+	XrefRustFiles                 android.Paths
+	DocTimestampFile              android.OptionalPath
+}
+
+var RustInfoProvider = blueprint.NewProvider[*RustInfo]()
+
 func init() {
 	android.RegisterModuleType("rust_defaults", defaultsFactory)
 	android.PreDepsMutators(registerPreDepsMutators)
@@ -133,9 +165,36 @@
 	// Make this module available when building for recovery
 	Recovery_available *bool
 
-	// Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
+	// The API level that this module is built against. The APIs of this API level will be
+	// visible at build time, but use of any APIs newer than min_sdk_version will render the
+	// module unloadable on older devices.  In the future it will be possible to weakly-link new
+	// APIs, making the behavior match Java: such modules will load on older devices, but
+	// calling new APIs on devices that do not support them will result in a crash.
+	//
+	// This property has the same behavior as sdk_version does for Java modules. For those
+	// familiar with Android Gradle, the property behaves similarly to how compileSdkVersion
+	// does for Java code.
+	//
+	// In addition, setting this property causes two variants to be built, one for the platform
+	// and one for apps.
+	Sdk_version *string
+
+	// Minimum OS API level supported by this C or C++ module. This property becomes the value
+	// of the __ANDROID_API__ macro. When the C or C++ module is included in an APEX or an APK,
+	// this property is also used to ensure that the min_sdk_version of the containing module is
+	// not older (i.e. less) than this module's min_sdk_version. When not set, this property
+	// defaults to the value of sdk_version.  When this is set to "apex_inherit", this tracks
+	// min_sdk_version of the containing APEX. When the module
+	// is not built for an APEX, "apex_inherit" defaults to sdk_version.
 	Min_sdk_version *string
 
+	// Variant is an SDK variant created by sdkMutator
+	IsSdkVariant bool `blueprint:"mutated"`
+
+	// Set by factories of module types that can only be referenced from variants compiled against
+	// the SDK.
+	AlwaysSdk bool `blueprint:"mutated"`
+
 	HideFromMake   bool `blueprint:"mutated"`
 	PreventInstall bool `blueprint:"mutated"`
 
@@ -180,6 +239,9 @@
 	apexSdkVersion android.ApiLevel
 
 	transitiveAndroidMkSharedLibs depset.DepSet[string]
+
+	// Shared flags among stubs build rules of this module
+	sharedFlags cc.SharedFlags
 }
 
 func (mod *Module) Header() bool {
@@ -345,7 +407,8 @@
 }
 
 func (mod *Module) IsVendorPublicLibrary() bool {
-	return mod.VendorProperties.IsVendorPublicLibrary
+	// Rust modules do not currently support vendor_public_library
+	return false
 }
 
 func (mod *Module) SdkAndPlatformVariantVisibleToMake() bool {
@@ -354,10 +417,12 @@
 }
 
 func (c *Module) IsVndkPrivate() bool {
+	// Rust modules do not currently support VNDK variants
 	return false
 }
 
 func (c *Module) IsLlndk() bool {
+	// Rust modules do not currently support LLNDK variants
 	return false
 }
 
@@ -366,35 +431,34 @@
 }
 
 func (m *Module) NeedsLlndkVariants() bool {
+	// Rust modules do not currently support LLNDK variants
 	return false
 }
 
 func (m *Module) NeedsVendorPublicLibraryVariants() bool {
+	// Rust modules do not currently support vendor_public_library
 	return false
 }
 
 func (mod *Module) HasLlndkStubs() bool {
+	// Rust modules do not currently support LLNDK stubs
 	return false
 }
 
-func (mod *Module) StubsVersion() string {
-	panic(fmt.Errorf("StubsVersion called on non-versioned module: %q", mod.BaseModuleName()))
-}
-
 func (mod *Module) SdkVersion() string {
-	return ""
+	return String(mod.Properties.Sdk_version)
 }
 
 func (mod *Module) AlwaysSdk() bool {
-	return false
+	return mod.Properties.AlwaysSdk
 }
 
 func (mod *Module) IsSdkVariant() bool {
-	return false
+	return mod.Properties.IsSdkVariant
 }
 
 func (mod *Module) SplitPerApiLevel() bool {
-	return false
+	return cc.CanUseSdk(mod) && mod.IsCrt()
 }
 
 func (mod *Module) XrefRustFiles() android.Paths {
@@ -427,15 +491,24 @@
 	StaticLibs    android.Paths
 	ProcMacros    RustLibraries
 	AfdoProfiles  android.Paths
+	LinkerDeps    android.Paths
 
 	// depFlags and depLinkFlags are rustc and linker (clang) flags.
 	depFlags     []string
 	depLinkFlags []string
 
+	// track cc static-libs that have Rlib dependencies
+	reexportedCcRlibDeps      []cc.RustRlibDep
+	reexportedWholeCcRlibDeps []cc.RustRlibDep
+	ccRlibDeps                []cc.RustRlibDep
+
 	// linkDirs are link paths passed via -L to rustc. linkObjects are objects passed directly to the linker
 	// Both of these are exported and propagate to dependencies.
-	linkDirs    []string
-	linkObjects []string
+	linkDirs              []string
+	rustLibObjects        []string
+	staticLibObjects      []string
+	wholeStaticLibObjects []string
+	sharedLibObjects      []string
 
 	// exportedLinkDirs are exported linkDirs for direct rlib dependencies to
 	// cc_library_static dependants of rlibs.
@@ -468,7 +541,10 @@
 
 type exportedFlagsProducer interface {
 	exportLinkDirs(...string)
-	exportLinkObjects(...string)
+	exportRustLibs(...string)
+	exportStaticLibs(...string)
+	exportWholeStaticLibs(...string)
+	exportSharedLibs(...string)
 }
 
 type xref interface {
@@ -476,23 +552,43 @@
 }
 
 type flagExporter struct {
-	linkDirs    []string
-	ccLinkDirs  []string
-	linkObjects []string
+	linkDirs              []string
+	ccLinkDirs            []string
+	rustLibPaths          []string
+	staticLibObjects      []string
+	sharedLibObjects      []string
+	wholeStaticLibObjects []string
+	wholeRustRlibDeps     []cc.RustRlibDep
 }
 
 func (flagExporter *flagExporter) exportLinkDirs(dirs ...string) {
 	flagExporter.linkDirs = android.FirstUniqueStrings(append(flagExporter.linkDirs, dirs...))
 }
 
-func (flagExporter *flagExporter) exportLinkObjects(flags ...string) {
-	flagExporter.linkObjects = android.FirstUniqueStrings(append(flagExporter.linkObjects, flags...))
+func (flagExporter *flagExporter) exportRustLibs(flags ...string) {
+	flagExporter.rustLibPaths = android.FirstUniqueStrings(append(flagExporter.rustLibPaths, flags...))
 }
 
-func (flagExporter *flagExporter) setProvider(ctx ModuleContext) {
-	android.SetProvider(ctx, FlagExporterInfoProvider, FlagExporterInfo{
-		LinkDirs:    flagExporter.linkDirs,
-		LinkObjects: flagExporter.linkObjects,
+func (flagExporter *flagExporter) exportStaticLibs(flags ...string) {
+	flagExporter.staticLibObjects = android.FirstUniqueStrings(append(flagExporter.staticLibObjects, flags...))
+}
+
+func (flagExporter *flagExporter) exportSharedLibs(flags ...string) {
+	flagExporter.sharedLibObjects = android.FirstUniqueStrings(append(flagExporter.sharedLibObjects, flags...))
+}
+
+func (flagExporter *flagExporter) exportWholeStaticLibs(flags ...string) {
+	flagExporter.wholeStaticLibObjects = android.FirstUniqueStrings(append(flagExporter.wholeStaticLibObjects, flags...))
+}
+
+func (flagExporter *flagExporter) setRustProvider(ctx ModuleContext) {
+	android.SetProvider(ctx, RustFlagExporterInfoProvider, RustFlagExporterInfo{
+		LinkDirs:              flagExporter.linkDirs,
+		RustLibObjects:        flagExporter.rustLibPaths,
+		StaticLibObjects:      flagExporter.staticLibObjects,
+		WholeStaticLibObjects: flagExporter.wholeStaticLibObjects,
+		SharedLibPaths:        flagExporter.sharedLibObjects,
+		WholeRustRlibDeps:     flagExporter.wholeRustRlibDeps,
 	})
 }
 
@@ -502,13 +598,17 @@
 	return &flagExporter{}
 }
 
-type FlagExporterInfo struct {
-	Flags       []string
-	LinkDirs    []string // TODO: this should be android.Paths
-	LinkObjects []string // TODO: this should be android.Paths
+type RustFlagExporterInfo struct {
+	Flags                 []string
+	LinkDirs              []string
+	RustLibObjects        []string
+	StaticLibObjects      []string
+	WholeStaticLibObjects []string
+	SharedLibPaths        []string
+	WholeRustRlibDeps     []cc.RustRlibDep
 }
 
-var FlagExporterInfoProvider = blueprint.NewProvider[FlagExporterInfo]()
+var RustFlagExporterInfoProvider = blueprint.NewProvider[RustFlagExporterInfo]()
 
 func (mod *Module) isCoverageVariant() bool {
 	return mod.coverage.Properties.IsCoverageVariant
@@ -531,6 +631,9 @@
 func (mod *Module) PreventInstall() bool {
 	return mod.Properties.PreventInstall
 }
+func (c *Module) ForceDisableSanitizers() {
+	c.sanitize.Properties.ForceDisable = true
+}
 
 func (mod *Module) MarkAsCoverageVariant(coverage bool) {
 	mod.coverage.Properties.IsCoverageVariant = coverage
@@ -594,7 +697,7 @@
 	if mod.compiler != nil {
 		// use build{Static,Shared}() instead of {static,shared}() here because this might be called before
 		// VariantIs{Static,Shared} is set.
-		if lib, ok := mod.compiler.(libraryInterface); ok && (lib.buildShared() || lib.buildStatic()) {
+		if lib, ok := mod.compiler.(libraryInterface); ok && (lib.buildShared() || lib.buildStatic() || lib.buildRlib()) {
 			return true
 		}
 	}
@@ -680,15 +783,6 @@
 	panic(fmt.Errorf("BuildRlibVariant called on non-library module: %q", mod.BaseModuleName()))
 }
 
-func (mod *Module) IsRustFFI() bool {
-	if mod.compiler != nil {
-		if library, ok := mod.compiler.(libraryInterface); ok {
-			return library.isFFILibrary()
-		}
-	}
-	return false
-}
-
 func (mod *Module) BuildSharedVariant() bool {
 	if mod.compiler != nil {
 		if library, ok := mod.compiler.(libraryInterface); ok {
@@ -722,11 +816,51 @@
 	return false
 }
 
-func (mod *Module) HasStubsVariants() bool {
+func (mod *Module) IsStubs() bool {
+	if lib, ok := mod.compiler.(libraryInterface); ok {
+		return lib.BuildStubs()
+	}
 	return false
 }
 
-func (mod *Module) IsStubs() bool {
+func (mod *Module) HasStubsVariants() bool {
+	if lib, ok := mod.compiler.(libraryInterface); ok {
+		return lib.HasStubsVariants()
+	}
+	return false
+}
+
+func (mod *Module) ApexSdkVersion() android.ApiLevel {
+	return mod.apexSdkVersion
+}
+
+func (mod *Module) RustApexExclude() bool {
+	return mod.ApexExclude()
+}
+
+func (mod *Module) getSharedFlags() *cc.SharedFlags {
+	shared := &mod.sharedFlags
+	if shared.FlagsMap == nil {
+		shared.NumSharedFlags = 0
+		shared.FlagsMap = make(map[string]string)
+	}
+	return shared
+}
+
+func (mod *Module) ImplementationModuleNameForMake() string {
+	name := mod.BaseModuleName()
+	if versioned, ok := mod.compiler.(cc.VersionedInterface); ok {
+		name = versioned.ImplementationModuleName(name)
+	}
+	return name
+}
+
+func (mod *Module) Multilib() string {
+	return mod.Arch().ArchType.Multilib
+}
+
+func (mod *Module) IsCrt() bool {
+	// Rust does not currently provide any crt modules.
 	return false
 }
 
@@ -750,6 +884,7 @@
 }
 
 var _ cc.LinkableInterface = (*Module)(nil)
+var _ cc.VersionedLinkableInterface = (*Module)(nil)
 
 func (mod *Module) Init() android.Module {
 	mod.AddProperties(&mod.Properties)
@@ -860,6 +995,25 @@
 	return mod.compiler != nil && mod.compiler.nativeCoverage()
 }
 
+func (mod *Module) SetStl(s string) {
+	// STL is a CC concept; do nothing for Rust
+}
+
+func (mod *Module) SetSdkVersion(s string) {
+	mod.Properties.Sdk_version = StringPtr(s)
+}
+
+func (mod *Module) SetMinSdkVersion(s string) {
+	mod.Properties.Min_sdk_version = StringPtr(s)
+}
+
+func (mod *Module) VersionedInterface() cc.VersionedInterface {
+	if _, ok := mod.compiler.(cc.VersionedInterface); ok {
+		return mod.compiler.(cc.VersionedInterface)
+	}
+	return nil
+}
+
 func (mod *Module) EverInstallable() bool {
 	return mod.compiler != nil &&
 		// Check to see whether the module is actually ever installable.
@@ -949,12 +1103,11 @@
 			mod.sourceProvider.GenerateSource(ctx, deps)
 			mod.sourceProvider.setSubName(ctx.ModuleSubDir())
 		} else {
-			sourceMod := actx.GetDirectDepWithTag(mod.Name(), sourceDepTag)
-			sourceLib := sourceMod.(*Module).compiler.(*libraryDecorator)
-			mod.sourceProvider.setOutputFiles(sourceLib.sourceProvider.Srcs())
+			sourceMod := actx.GetDirectDepProxyWithTag(mod.Name(), sourceDepTag)
+			sourceLib := android.OtherModuleProviderOrDefault(ctx, sourceMod, RustInfoProvider).SourceProviderInfo
+			mod.sourceProvider.setOutputFiles(sourceLib.Srcs)
 		}
 		ctx.CheckbuildFile(mod.sourceProvider.Srcs()...)
-		android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: mod.sourceProvider.Srcs().Strings()})
 	}
 
 	if mod.compiler != nil && !mod.compiler.Disabled() {
@@ -1005,13 +1158,76 @@
 		ctx.Phony("rust", ctx.RustModule().OutputFile().Path())
 	}
 
-	android.SetProvider(ctx, cc.LinkableInfoKey, cc.LinkableInfo{
-		StaticExecutable: mod.StaticExecutable(),
-	})
+	linkableInfo := cc.CreateCommonLinkableInfo(ctx, mod)
+	linkableInfo.Static = mod.Static()
+	linkableInfo.Shared = mod.Shared()
+	linkableInfo.CrateName = mod.CrateName()
+	linkableInfo.ExportedCrateLinkDirs = mod.ExportedCrateLinkDirs()
+	if lib, ok := mod.compiler.(cc.VersionedInterface); ok {
+		linkableInfo.StubsVersion = lib.StubsVersion()
+	}
+
+	android.SetProvider(ctx, cc.LinkableInfoProvider, linkableInfo)
+
+	rustInfo := &RustInfo{
+		AndroidMkSuffix:               mod.AndroidMkSuffix(),
+		RustSubName:                   mod.Properties.RustSubName,
+		TransitiveAndroidMkSharedLibs: mod.transitiveAndroidMkSharedLibs,
+		XrefRustFiles:                 mod.XrefRustFiles(),
+		DocTimestampFile:              mod.docTimestampFile,
+	}
+	if mod.compiler != nil {
+		rustInfo.CompilerInfo = &CompilerInfo{
+			NoStdlibs:              mod.compiler.noStdlibs(),
+			StdLinkageForDevice:    mod.compiler.stdLinkage(true),
+			StdLinkageForNonDevice: mod.compiler.stdLinkage(false),
+		}
+		if lib, ok := mod.compiler.(libraryInterface); ok {
+			rustInfo.CompilerInfo.LibraryInfo = &LibraryInfo{
+				Dylib: lib.dylib(),
+				Rlib:  lib.rlib(),
+			}
+		}
+		if lib, ok := mod.compiler.(cc.SnapshotInterface); ok {
+			rustInfo.SnapshotInfo = &cc.SnapshotInfo{
+				SnapshotAndroidMkSuffix: lib.SnapshotAndroidMkSuffix(),
+			}
+		}
+	}
+	if mod.sourceProvider != nil {
+		rustInfo.SourceProviderInfo = &SourceProviderInfo{
+			Srcs: mod.sourceProvider.Srcs(),
+		}
+		if _, ok := mod.sourceProvider.(*protobufDecorator); ok {
+			rustInfo.SourceProviderInfo.ProtobufDecoratorInfo = &ProtobufDecoratorInfo{}
+		}
+	}
+	android.SetProvider(ctx, RustInfoProvider, rustInfo)
+
+	ccInfo := &cc.CcInfo{
+		IsPrebuilt: mod.IsPrebuilt(),
+	}
+
+	// Define the linker info if compiler != nil because Rust currently
+	// does compilation and linking in one step. If this changes in the future,
+	// move this as appropriate.
+	baseCompilerProps := mod.compiler.baseCompilerProps()
+	ccInfo.LinkerInfo = &cc.LinkerInfo{
+		WholeStaticLibs: baseCompilerProps.Whole_static_libs.GetOrDefault(ctx, nil),
+		StaticLibs:      baseCompilerProps.Static_libs.GetOrDefault(ctx, nil),
+		SharedLibs:      baseCompilerProps.Shared_libs.GetOrDefault(ctx, nil),
+	}
+
+	android.SetProvider(ctx, cc.CcInfoProvider, ccInfo)
 
 	mod.setOutputFiles(ctx)
 
 	buildComplianceMetadataInfo(ctx, mod, deps)
+
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	if mod.compiler != nil {
+		mod.compiler.moduleInfoJSON(ctx, moduleInfoJSON)
+	}
 }
 
 func (mod *Module) setOutputFiles(ctx ModuleContext) {
@@ -1034,12 +1250,12 @@
 	metadataInfo.SetStringValue(android.ComplianceMetadataProp.BUILT_FILES, mod.outputFile.String())
 
 	// Static libs
-	staticDeps := ctx.GetDirectDepsWithTag(rlibDepTag)
+	staticDeps := ctx.GetDirectDepsProxyWithTag(rlibDepTag)
 	staticDepNames := make([]string, 0, len(staticDeps))
 	for _, dep := range staticDeps {
 		staticDepNames = append(staticDepNames, dep.Name())
 	}
-	ccStaticDeps := ctx.GetDirectDepsWithTag(cc.StaticDepTag(false))
+	ccStaticDeps := ctx.GetDirectDepsProxyWithTag(cc.StaticDepTag(false))
 	for _, dep := range ccStaticDeps {
 		staticDepNames = append(staticDepNames, dep.Name())
 	}
@@ -1057,7 +1273,7 @@
 	metadataInfo.SetListValue(android.ComplianceMetadataProp.STATIC_DEP_FILES, android.FirstUniqueStrings(staticDepPaths))
 
 	// C Whole static libs
-	ccWholeStaticDeps := ctx.GetDirectDepsWithTag(cc.StaticDepTag(true))
+	ccWholeStaticDeps := ctx.GetDirectDepsProxyWithTag(cc.StaticDepTag(true))
 	wholeStaticDepNames := make([]string, 0, len(ccWholeStaticDeps))
 	for _, dep := range ccStaticDeps {
 		wholeStaticDepNames = append(wholeStaticDepNames, dep.Name())
@@ -1170,6 +1386,21 @@
 	if mod.sanitize != nil {
 		mod.sanitize.begin(ctx)
 	}
+
+	if mod.UseSdk() && mod.IsSdkVariant() {
+		sdkVersion := ""
+		if ctx.Device() {
+			sdkVersion = mod.SdkVersion()
+		}
+		version, err := cc.NativeApiLevelFromUser(ctx, sdkVersion)
+		if err != nil {
+			ctx.PropertyErrorf("sdk_version", err.Error())
+			mod.Properties.Sdk_version = nil
+		} else {
+			mod.Properties.Sdk_version = StringPtr(version.String())
+		}
+	}
+
 }
 
 func (mod *Module) Prebuilt() *android.Prebuilt {
@@ -1184,21 +1415,21 @@
 	return nil
 }
 
-func rustMakeLibName(ctx android.ModuleContext, c cc.LinkableInterface, dep cc.LinkableInterface, depName string) string {
-	if rustDep, ok := dep.(*Module); ok {
+func rustMakeLibName(rustInfo *RustInfo, linkableInfo *cc.LinkableInfo, commonInfo *android.CommonModuleInfo, depName string) string {
+	if rustInfo != nil {
 		// Use base module name for snapshots when exporting to Makefile.
-		if snapshotPrebuilt, ok := rustDep.compiler.(cc.SnapshotInterface); ok {
-			baseName := rustDep.BaseModuleName()
-			return baseName + snapshotPrebuilt.SnapshotAndroidMkSuffix() + rustDep.AndroidMkSuffix()
+		if rustInfo.SnapshotInfo != nil {
+			baseName := commonInfo.BaseModuleName
+			return baseName + rustInfo.SnapshotInfo.SnapshotAndroidMkSuffix + rustInfo.AndroidMkSuffix
 		}
 	}
-	return cc.MakeLibName(ctx, c, dep, depName)
+	return cc.MakeLibName(nil, linkableInfo, commonInfo, depName)
 }
 
-func collectIncludedProtos(mod *Module, dep *Module) {
+func collectIncludedProtos(mod *Module, rustInfo *RustInfo, linkableInfo *cc.LinkableInfo) {
 	if protoMod, ok := mod.sourceProvider.(*protobufDecorator); ok {
-		if _, ok := dep.sourceProvider.(*protobufDecorator); ok {
-			protoMod.additionalCrates = append(protoMod.additionalCrates, dep.CrateName())
+		if rustInfo.SourceProviderInfo.ProtobufDecoratorInfo != nil {
+			protoMod.additionalCrates = append(protoMod.additionalCrates, linkableInfo.CrateName)
 		}
 	}
 }
@@ -1206,13 +1437,13 @@
 func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 	var depPaths PathDeps
 
-	directRlibDeps := []*Module{}
-	directDylibDeps := []*Module{}
-	directProcMacroDeps := []*Module{}
+	directRlibDeps := []*cc.LinkableInfo{}
+	directDylibDeps := []*cc.LinkableInfo{}
+	directProcMacroDeps := []*cc.LinkableInfo{}
 	directSharedLibDeps := []cc.SharedLibraryInfo{}
-	directStaticLibDeps := [](cc.LinkableInterface){}
-	directSrcProvidersDeps := []*Module{}
-	directSrcDeps := [](android.SourceFileProducer){}
+	directStaticLibDeps := [](*cc.LinkableInfo){}
+	directSrcProvidersDeps := []*android.ModuleProxy{}
+	directSrcDeps := []android.SourceFilesInfo{}
 
 	// For the dependency from platform to apex, use the latest stubs
 	mod.apexSdkVersion = android.FutureApiLevel
@@ -1233,9 +1464,11 @@
 	var transitiveAndroidMkSharedLibs []depset.DepSet[string]
 	var directAndroidMkSharedLibs []string
 
-	ctx.VisitDirectDeps(func(dep android.Module) {
+	ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
 		depName := ctx.OtherModuleName(dep)
 		depTag := ctx.OtherModuleDependencyTag(dep)
+		modStdLinkage := mod.compiler.stdLinkage(ctx.Device())
+
 		if _, exists := skipModuleList[depName]; exists {
 			return
 		}
@@ -1244,18 +1477,22 @@
 			return
 		}
 
-		if rustDep, ok := dep.(*Module); ok && !rustDep.Static() && !rustDep.Shared() {
+		rustInfo, hasRustInfo := android.OtherModuleProvider(ctx, dep, RustInfoProvider)
+		ccInfo, _ := android.OtherModuleProvider(ctx, dep, cc.CcInfoProvider)
+		linkableInfo, hasLinkableInfo := android.OtherModuleProvider(ctx, dep, cc.LinkableInfoProvider)
+		commonInfo := android.OtherModulePointerProviderOrDefault(ctx, dep, android.CommonModuleInfoProvider)
+		if hasRustInfo && !linkableInfo.Static && !linkableInfo.Shared {
 			//Handle Rust Modules
-			makeLibName := rustMakeLibName(ctx, mod, rustDep, depName+rustDep.Properties.RustSubName)
+			makeLibName := rustMakeLibName(rustInfo, linkableInfo, commonInfo, depName+rustInfo.RustSubName)
 
 			switch {
 			case depTag == dylibDepTag:
-				dylib, ok := rustDep.compiler.(libraryInterface)
-				if !ok || !dylib.dylib() {
+				dylib := rustInfo.CompilerInfo.LibraryInfo
+				if dylib == nil || !dylib.Dylib {
 					ctx.ModuleErrorf("mod %q not an dylib library", depName)
 					return
 				}
-				directDylibDeps = append(directDylibDeps, rustDep)
+				directDylibDeps = append(directDylibDeps, linkableInfo)
 				mod.Properties.AndroidMkDylibs = append(mod.Properties.AndroidMkDylibs, makeLibName)
 				mod.Properties.SnapshotDylibs = append(mod.Properties.SnapshotDylibs, cc.BaseLibName(depName))
 
@@ -1264,35 +1501,64 @@
 					depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
 				}
 
+				if !rustInfo.CompilerInfo.NoStdlibs {
+					rustDepStdLinkage := rustInfo.CompilerInfo.StdLinkageForNonDevice
+					if ctx.Device() {
+						rustDepStdLinkage = rustInfo.CompilerInfo.StdLinkageForDevice
+					}
+					if rustDepStdLinkage != modStdLinkage {
+						ctx.ModuleErrorf("Rust dependency %q has the wrong StdLinkage; expected %#v, got %#v", depName, modStdLinkage, rustDepStdLinkage)
+						return
+					}
+				}
+
 			case depTag == rlibDepTag:
-				rlib, ok := rustDep.compiler.(libraryInterface)
-				if !ok || !rlib.rlib() {
+				rlib := rustInfo.CompilerInfo.LibraryInfo
+				if rlib == nil || !rlib.Rlib {
 					ctx.ModuleErrorf("mod %q not an rlib library", makeLibName)
 					return
 				}
-				directRlibDeps = append(directRlibDeps, rustDep)
+				directRlibDeps = append(directRlibDeps, linkableInfo)
 				mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, makeLibName)
 				mod.Properties.SnapshotRlibs = append(mod.Properties.SnapshotRlibs, cc.BaseLibName(depName))
 
 				// rust_ffi rlibs may export include dirs, so collect those here.
 				exportedInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider)
 				depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
-				depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path()))
+				depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(linkableInfo.OutputFile.Path()))
 
 				// rlibs are not installed, so don't add the output file to directImplementationDeps
 				if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
 					depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
 				}
 
+				if !rustInfo.CompilerInfo.NoStdlibs {
+					rustDepStdLinkage := rustInfo.CompilerInfo.StdLinkageForNonDevice
+					if ctx.Device() {
+						rustDepStdLinkage = rustInfo.CompilerInfo.StdLinkageForDevice
+					}
+					if rustDepStdLinkage != modStdLinkage {
+						ctx.ModuleErrorf("Rust dependency %q has the wrong StdLinkage; expected %#v, got %#v", depName, modStdLinkage, rustDepStdLinkage)
+						return
+					}
+				}
+
+				if !mod.Rlib() {
+					depPaths.ccRlibDeps = append(depPaths.ccRlibDeps, exportedInfo.RustRlibDeps...)
+				} else {
+					// rlibs need to reexport these
+					depPaths.reexportedCcRlibDeps = append(depPaths.reexportedCcRlibDeps, exportedInfo.RustRlibDeps...)
+				}
+
 			case depTag == procMacroDepTag:
-				directProcMacroDeps = append(directProcMacroDeps, rustDep)
+				directProcMacroDeps = append(directProcMacroDeps, linkableInfo)
 				mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName)
 				// proc_macro link dirs need to be exported, so collect those here.
-				depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path()))
+				depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(linkableInfo.OutputFile.Path()))
 
 			case depTag == sourceDepTag:
 				if _, ok := mod.sourceProvider.(*protobufDecorator); ok {
-					collectIncludedProtos(mod, rustDep)
+					collectIncludedProtos(mod, rustInfo, linkableInfo)
 				}
 			case cc.IsStaticDepTag(depTag):
 				// Rust FFI rlibs should not be declared in a Rust modules
@@ -1305,7 +1571,7 @@
 
 			}
 
-			transitiveAndroidMkSharedLibs = append(transitiveAndroidMkSharedLibs, rustDep.transitiveAndroidMkSharedLibs)
+			transitiveAndroidMkSharedLibs = append(transitiveAndroidMkSharedLibs, rustInfo.TransitiveAndroidMkSharedLibs)
 
 			if android.IsSourceDepTagWithOutputTag(depTag, "") {
 				// Since these deps are added in path_properties.go via AddDependencies, we need to ensure the correct
@@ -1317,26 +1583,35 @@
 					helper = "device module defined?"
 				}
 
-				if dep.Target().Os != ctx.Os() {
+				if commonInfo.Target.Os != ctx.Os() {
 					ctx.ModuleErrorf("OS mismatch on dependency %q (%s)", dep.Name(), helper)
 					return
-				} else if dep.Target().Arch.ArchType != ctx.Arch().ArchType {
+				} else if commonInfo.Target.Arch.ArchType != ctx.Arch().ArchType {
 					ctx.ModuleErrorf("Arch mismatch on dependency %q (%s)", dep.Name(), helper)
 					return
 				}
-				directSrcProvidersDeps = append(directSrcProvidersDeps, rustDep)
+				directSrcProvidersDeps = append(directSrcProvidersDeps, &dep)
 			}
 
-			exportedInfo, _ := android.OtherModuleProvider(ctx, dep, FlagExporterInfoProvider)
-			//Append the dependencies exportedDirs, except for proc-macros which target a different arch/OS
+			exportedRustInfo, _ := android.OtherModuleProvider(ctx, dep, RustFlagExporterInfoProvider)
+			exportedInfo, _ := android.OtherModuleProvider(ctx, dep, RustFlagExporterInfoProvider)
+			//Append the dependencies exported objects, except for proc-macros which target a different arch/OS
 			if depTag != procMacroDepTag {
 				depPaths.depFlags = append(depPaths.depFlags, exportedInfo.Flags...)
-				depPaths.linkObjects = append(depPaths.linkObjects, exportedInfo.LinkObjects...)
+				depPaths.rustLibObjects = append(depPaths.rustLibObjects, exportedInfo.RustLibObjects...)
+				depPaths.sharedLibObjects = append(depPaths.sharedLibObjects, exportedInfo.SharedLibPaths...)
+				depPaths.staticLibObjects = append(depPaths.staticLibObjects, exportedInfo.StaticLibObjects...)
+				depPaths.wholeStaticLibObjects = append(depPaths.wholeStaticLibObjects, exportedInfo.WholeStaticLibObjects...)
 				depPaths.linkDirs = append(depPaths.linkDirs, exportedInfo.LinkDirs...)
+
+				depPaths.reexportedWholeCcRlibDeps = append(depPaths.reexportedWholeCcRlibDeps, exportedRustInfo.WholeRustRlibDeps...)
+				if !mod.Rlib() {
+					depPaths.ccRlibDeps = append(depPaths.ccRlibDeps, exportedRustInfo.WholeRustRlibDeps...)
+				}
 			}
 
 			if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
-				linkFile := rustDep.UnstrippedOutputFile()
+				linkFile := linkableInfo.UnstrippedOutputFile
 				linkDir := linkPathFromFilePath(linkFile)
 				if lib, ok := mod.compiler.(exportedFlagsProducer); ok {
 					lib.exportLinkDirs(linkDir)
@@ -1345,27 +1620,27 @@
 
 			if depTag == sourceDepTag {
 				if _, ok := mod.sourceProvider.(*protobufDecorator); ok && mod.Source() {
-					if _, ok := rustDep.sourceProvider.(*protobufDecorator); ok {
+					if rustInfo.SourceProviderInfo.ProtobufDecoratorInfo != nil {
 						exportedInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider)
 						depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
 					}
 				}
 			}
-		} else if ccDep, ok := dep.(cc.LinkableInterface); ok {
+		} else if hasLinkableInfo {
 			//Handle C dependencies
-			makeLibName := cc.MakeLibName(ctx, mod, ccDep, depName)
-			if _, ok := ccDep.(*Module); !ok {
-				if ccDep.Module().Target().Os != ctx.Os() {
+			makeLibName := cc.MakeLibName(ccInfo, linkableInfo, commonInfo, depName)
+			if !hasRustInfo {
+				if commonInfo.Target.Os != ctx.Os() {
 					ctx.ModuleErrorf("OS mismatch between %q and %q", ctx.ModuleName(), depName)
 					return
 				}
-				if ccDep.Module().Target().Arch.ArchType != ctx.Arch().ArchType {
+				if commonInfo.Target.Arch.ArchType != ctx.Arch().ArchType {
 					ctx.ModuleErrorf("Arch mismatch between %q and %q", ctx.ModuleName(), depName)
 					return
 				}
 			}
-			linkObject := ccDep.OutputFile()
-			if !linkObject.Valid() {
+			ccLibPath := linkableInfo.OutputFile
+			if !ccLibPath.Valid() {
 				if !ctx.Config().AllowMissingDependencies() {
 					ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
 				} else {
@@ -1374,7 +1649,7 @@
 				return
 			}
 
-			linkPath := linkPathFromFilePath(linkObject.Path())
+			linkPath := linkPathFromFilePath(ccLibPath.Path())
 
 			exportDep := false
 			switch {
@@ -1383,28 +1658,48 @@
 					// rustc will bundle static libraries when they're passed with "-lstatic=<lib>". This will fail
 					// if the library is not prefixed by "lib".
 					if mod.Binary() {
-						// Binaries may sometimes need to link whole static libraries that don't start with 'lib'.
 						// Since binaries don't need to 'rebundle' these like libraries and only use these for the
 						// final linkage, pass the args directly to the linker to handle these cases.
-						depPaths.depLinkFlags = append(depPaths.depLinkFlags, []string{"-Wl,--whole-archive", linkObject.Path().String(), "-Wl,--no-whole-archive"}...)
-					} else if libName, ok := libNameFromFilePath(linkObject.Path()); ok {
-						depPaths.depFlags = append(depPaths.depFlags, "-lstatic="+libName)
+						depPaths.depLinkFlags = append(depPaths.depLinkFlags, []string{"-Wl,--whole-archive", ccLibPath.Path().String(), "-Wl,--no-whole-archive"}...)
+					} else if libName, ok := libNameFromFilePath(ccLibPath.Path()); ok {
+						depPaths.depFlags = append(depPaths.depFlags, "-lstatic:+whole-archive="+libName)
+						depPaths.depLinkFlags = append(depPaths.depLinkFlags, ccLibPath.Path().String())
 					} else {
 						ctx.ModuleErrorf("'%q' cannot be listed as a whole_static_library in Rust modules unless the output is prefixed by 'lib'", depName, ctx.ModuleName())
 					}
 				}
 
-				// Add this to linkObjects to pass the library directly to the linker as well. This propagates
-				// to dependencies to avoid having to redeclare static libraries for dependents of the dylib variant.
-				depPaths.linkObjects = append(depPaths.linkObjects, linkObject.String())
-				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
-
 				exportedInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider)
+				if cc.IsWholeStaticLib(depTag) {
+					// Add whole staticlibs to wholeStaticLibObjects to propagate to Rust all dependents.
+					depPaths.wholeStaticLibObjects = append(depPaths.wholeStaticLibObjects, ccLibPath.String())
+
+					// We also propagate forward whole-static'd cc staticlibs with rust_ffi_rlib dependencies
+					// We don't need to check a hypothetical exportedRustInfo.WholeRustRlibDeps because we
+					// wouldn't expect a rust_ffi_rlib to be listed in `static_libs` (Soong explicitly disallows this)
+					depPaths.reexportedWholeCcRlibDeps = append(depPaths.reexportedWholeCcRlibDeps, exportedInfo.RustRlibDeps...)
+				} else {
+					// If not whole_static, add to staticLibObjects, which only propagate through rlibs to their dependents.
+					depPaths.staticLibObjects = append(depPaths.staticLibObjects, ccLibPath.String())
+
+					if mod.Rlib() {
+						// rlibs propagate their inherited rust_ffi_rlibs forward.
+						depPaths.reexportedCcRlibDeps = append(depPaths.reexportedCcRlibDeps, exportedInfo.RustRlibDeps...)
+					}
+				}
+
+				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
 				depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
 				depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
 				depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
 				depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
-				directStaticLibDeps = append(directStaticLibDeps, ccDep)
+
+				if !mod.Rlib() {
+					// rlibs don't need to build the generated static library, so they don't need to track these.
+					depPaths.ccRlibDeps = append(depPaths.ccRlibDeps, exportedInfo.RustRlibDeps...)
+				}
+
+				directStaticLibDeps = append(directStaticLibDeps, linkableInfo)
 
 				// Record baseLibName for snapshots.
 				mod.Properties.SnapshotStaticLibs = append(mod.Properties.SnapshotStaticLibs, cc.BaseLibName(depName))
@@ -1417,16 +1712,19 @@
 				sharedLibraryInfo, exportedInfo := cc.ChooseStubOrImpl(ctx, dep)
 
 				if !sharedLibraryInfo.IsStubs {
-					depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, ""))
-					if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
-						depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
+					// TODO(b/362509506): remove this additional check once all apex_exclude uses are switched to stubs.
+					if !linkableInfo.RustApexExclude {
+						depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, ""))
+						if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
+							depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
+						}
 					}
 				}
 
 				// Re-get linkObject as ChooseStubOrImpl actually tells us which
 				// object (either from stub or non-stub) to use.
-				linkObject = android.OptionalPathForPath(sharedLibraryInfo.SharedLibrary)
-				if !linkObject.Valid() {
+				ccLibPath = android.OptionalPathForPath(sharedLibraryInfo.SharedLibrary)
+				if !ccLibPath.Valid() {
 					if !ctx.Config().AllowMissingDependencies() {
 						ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
 					} else {
@@ -1434,10 +1732,10 @@
 					}
 					return
 				}
-				linkPath = linkPathFromFilePath(linkObject.Path())
+				linkPath = linkPathFromFilePath(ccLibPath.Path())
 
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
-				depPaths.linkObjects = append(depPaths.linkObjects, linkObject.String())
+				depPaths.sharedLibObjects = append(depPaths.sharedLibObjects, ccLibPath.String())
 				depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
 				depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
 				depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
@@ -1456,15 +1754,15 @@
 				depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
 				mod.Properties.AndroidMkHeaderLibs = append(mod.Properties.AndroidMkHeaderLibs, makeLibName)
 			case depTag == cc.CrtBeginDepTag:
-				depPaths.CrtBegin = append(depPaths.CrtBegin, linkObject.Path())
+				depPaths.CrtBegin = append(depPaths.CrtBegin, ccLibPath.Path())
 			case depTag == cc.CrtEndDepTag:
-				depPaths.CrtEnd = append(depPaths.CrtEnd, linkObject.Path())
+				depPaths.CrtEnd = append(depPaths.CrtEnd, ccLibPath.Path())
 			}
 
-			// Make sure these dependencies are propagated
+			// Make sure shared dependencies are propagated
 			if lib, ok := mod.compiler.(exportedFlagsProducer); ok && exportDep {
 				lib.exportLinkDirs(linkPath)
-				lib.exportLinkObjects(linkObject.String())
+				lib.exportSharedLibs(ccLibPath.String())
 			}
 		} else {
 			switch {
@@ -1475,7 +1773,7 @@
 			}
 		}
 
-		if srcDep, ok := dep.(android.SourceFileProducer); ok {
+		if srcDep, ok := android.OtherModuleProvider(ctx, dep, android.SourceFilesInfoProvider); ok {
 			if android.IsSourceDepTagWithOutputTag(depTag, "") {
 				// These are usually genrules which don't have per-target variants.
 				directSrcDeps = append(directSrcDeps, srcDep)
@@ -1488,32 +1786,32 @@
 	var rlibDepFiles RustLibraries
 	aliases := mod.compiler.Aliases()
 	for _, dep := range directRlibDeps {
-		crateName := dep.CrateName()
+		crateName := dep.CrateName
 		if alias, aliased := aliases[crateName]; aliased {
 			crateName = alias
 		}
-		rlibDepFiles = append(rlibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: crateName})
+		rlibDepFiles = append(rlibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile, CrateName: crateName})
 	}
 	var dylibDepFiles RustLibraries
 	for _, dep := range directDylibDeps {
-		crateName := dep.CrateName()
+		crateName := dep.CrateName
 		if alias, aliased := aliases[crateName]; aliased {
 			crateName = alias
 		}
-		dylibDepFiles = append(dylibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: crateName})
+		dylibDepFiles = append(dylibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile, CrateName: crateName})
 	}
 	var procMacroDepFiles RustLibraries
 	for _, dep := range directProcMacroDeps {
-		crateName := dep.CrateName()
+		crateName := dep.CrateName
 		if alias, aliased := aliases[crateName]; aliased {
 			crateName = alias
 		}
-		procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: crateName})
+		procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile, CrateName: crateName})
 	}
 
 	var staticLibDepFiles android.Paths
 	for _, dep := range directStaticLibDeps {
-		staticLibDepFiles = append(staticLibDepFiles, dep.OutputFile().Path())
+		staticLibDepFiles = append(staticLibDepFiles, dep.OutputFile.Path())
 	}
 
 	var sharedLibFiles android.Paths
@@ -1529,11 +1827,11 @@
 
 	var srcProviderDepFiles android.Paths
 	for _, dep := range directSrcProvidersDeps {
-		srcs := android.OutputFilesForModule(ctx, dep, "")
+		srcs := android.OutputFilesForModule(ctx, *dep, "")
 		srcProviderDepFiles = append(srcProviderDepFiles, srcs...)
 	}
 	for _, dep := range directSrcDeps {
-		srcs := dep.Srcs()
+		srcs := dep.Srcs
 		srcProviderDepFiles = append(srcProviderDepFiles, srcs...)
 	}
 
@@ -1547,11 +1845,18 @@
 
 	// Dedup exported flags from dependencies
 	depPaths.linkDirs = android.FirstUniqueStrings(depPaths.linkDirs)
-	depPaths.linkObjects = android.FirstUniqueStrings(depPaths.linkObjects)
+	depPaths.rustLibObjects = android.FirstUniqueStrings(depPaths.rustLibObjects)
+	depPaths.staticLibObjects = android.FirstUniqueStrings(depPaths.staticLibObjects)
+	depPaths.wholeStaticLibObjects = android.FirstUniqueStrings(depPaths.wholeStaticLibObjects)
+	depPaths.sharedLibObjects = android.FirstUniqueStrings(depPaths.sharedLibObjects)
 	depPaths.depFlags = android.FirstUniqueStrings(depPaths.depFlags)
 	depPaths.depClangFlags = android.FirstUniqueStrings(depPaths.depClangFlags)
 	depPaths.depIncludePaths = android.FirstUniquePaths(depPaths.depIncludePaths)
 	depPaths.depSystemIncludePaths = android.FirstUniquePaths(depPaths.depSystemIncludePaths)
+	depPaths.depLinkFlags = android.FirstUniqueStrings(depPaths.depLinkFlags)
+	depPaths.reexportedCcRlibDeps = android.FirstUniqueFunc(depPaths.reexportedCcRlibDeps, cc.EqRustRlibDeps)
+	depPaths.reexportedWholeCcRlibDeps = android.FirstUniqueFunc(depPaths.reexportedWholeCcRlibDeps, cc.EqRustRlibDeps)
+	depPaths.ccRlibDeps = android.FirstUniqueFunc(depPaths.ccRlibDeps, cc.EqRustRlibDeps)
 
 	return depPaths
 }
@@ -1602,7 +1907,7 @@
 	}
 
 	stdLinkage := "dylib-std"
-	if mod.compiler.stdLinkage(ctx) == RlibLinkage {
+	if mod.compiler.stdLinkage(ctx.Device()) == RlibLinkage {
 		stdLinkage = "rlib-std"
 	}
 
@@ -1669,7 +1974,7 @@
 
 	// stdlibs
 	if deps.Stdlibs != nil {
-		if mod.compiler.stdLinkage(ctx) == RlibLinkage {
+		if mod.compiler.stdLinkage(ctx.Device()) == RlibLinkage {
 			for _, lib := range deps.Stdlibs {
 				actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}...),
 					rlibDepTag, lib)
@@ -1787,7 +2092,7 @@
 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.
+// TODO(b/362509506): remove this once all apex_exclude usages are removed.
 func (m *Module) CanHaveApexVariants() bool {
 	if m.ApexExclude() {
 		return false
@@ -1801,71 +2106,113 @@
 }
 
 // Implements android.ApexModule
-func (mod *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
+func (mod *Module) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
 	minSdkVersion := mod.MinSdkVersion()
 	if minSdkVersion == "apex_inherit" {
-		return nil
-	}
-	if minSdkVersion == "" {
-		return fmt.Errorf("min_sdk_version is not specificed")
+		return android.MinApiLevel
 	}
 
+	if minSdkVersion == "" {
+		return android.NoneApiLevel
+	}
 	// Not using nativeApiLevelFromUser because the context here is not
 	// necessarily a native context.
-	ver, err := android.ApiLevelFromUser(ctx, minSdkVersion)
+	ver, err := android.ApiLevelFromUserWithConfig(ctx.Config(), minSdkVersion)
 	if err != nil {
-		return err
+		return android.NoneApiLevel
 	}
 
-	if ver.GreaterThan(sdkVersion) {
-		return fmt.Errorf("newer SDK(%v)", ver)
-	}
-	return nil
+	return ver
 }
 
 // Implements android.ApexModule
-func (mod *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
-	depTag := ctx.OtherModuleDependencyTag(dep)
+func (mod *Module) AlwaysRequiresPlatformApexVariant() bool {
+	// stub libraries and native bridge libraries are always available to platform
+	// TODO(b/362509506): remove the ApexExclude() check once all apex_exclude uses are switched to stubs.
+	return mod.IsStubs() || mod.Target().NativeBridge == android.NativeBridgeEnabled || mod.ApexExclude()
+}
 
-	if ccm, ok := dep.(*cc.Module); ok {
-		if ccm.HasStubsVariants() {
-			if cc.IsSharedDepTag(depTag) {
-				// dynamic dep to a stubs lib crosses APEX boundary
-				return false
-			}
-			if cc.IsRuntimeDepTag(depTag) {
-				// runtime dep to a stubs lib also crosses APEX boundary
-				return false
-			}
+// Implements android.ApexModule
+type RustDepInSameApexChecker struct {
+	Static           bool
+	HasStubsVariants bool
+	ApexExclude      bool
+	Host             bool
+}
 
-			if cc.IsHeaderDepTag(depTag) {
-				return false
-			}
-		}
-		if mod.Static() && cc.IsSharedDepTag(depTag) {
-			// shared_lib dependency from a static lib is considered as crossing
-			// the APEX boundary because the dependency doesn't actually is
-			// linked; the dependency is used only during the compilation phase.
-			return false
-		}
+func (mod *Module) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return RustDepInSameApexChecker{
+		Static:           mod.Static(),
+		HasStubsVariants: mod.HasStubsVariants(),
+		ApexExclude:      mod.ApexExclude(),
+		Host:             mod.Host(),
 	}
+}
 
+func (r RustDepInSameApexChecker) OutgoingDepIsInSameApex(depTag blueprint.DependencyTag) bool {
 	if depTag == procMacroDepTag || depTag == customBindgenDepTag {
 		return false
 	}
 
-	if rustDep, ok := dep.(*Module); ok && rustDep.ApexExclude() {
+	if r.Static && cc.IsSharedDepTag(depTag) {
+		// shared_lib dependency from a static lib is considered as crossing
+		// the APEX boundary because the dependency doesn't actually is
+		// linked; the dependency is used only during the compilation phase.
+		return false
+	}
+
+	if depTag == cc.StubImplDepTag {
+		// We don't track from an implementation library to its stubs.
+		return false
+	}
+
+	if cc.ExcludeInApexDepTag(depTag) {
+		return false
+	}
+
+	// TODO(b/362509506): remove once all apex_exclude uses are switched to stubs.
+	if r.ApexExclude {
 		return false
 	}
 
 	return true
 }
 
+func (r RustDepInSameApexChecker) IncomingDepIsInSameApex(depTag blueprint.DependencyTag) bool {
+	if r.Host {
+		return false
+	}
+	// TODO(b/362509506): remove once all apex_exclude uses are switched to stubs.
+	if r.ApexExclude {
+		return false
+	}
+
+	if r.HasStubsVariants {
+		if cc.IsSharedDepTag(depTag) && !cc.IsExplicitImplSharedDepTag(depTag) {
+			// dynamic dep to a stubs lib crosses APEX boundary
+			return false
+		}
+		if cc.IsRuntimeDepTag(depTag) {
+			// runtime dep to a stubs lib also crosses APEX boundary
+			return false
+		}
+		if cc.IsHeaderDepTag(depTag) {
+			return false
+		}
+	}
+	return true
+}
+
 // Overrides ApexModule.IsInstallabeToApex()
 func (mod *Module) IsInstallableToApex() bool {
+	// TODO(b/362509506): remove once all apex_exclude uses are switched to stubs.
+	if mod.ApexExclude() {
+		return false
+	}
+
 	if mod.compiler != nil {
-		if lib, ok := mod.compiler.(libraryInterface); ok && (lib.shared() || lib.dylib()) {
-			return true
+		if lib, ok := mod.compiler.(libraryInterface); ok {
+			return (lib.shared() || lib.dylib()) && !lib.BuildStubs()
 		}
 		if _, ok := mod.compiler.(*binaryDecorator); ok {
 			return true
@@ -1893,9 +2240,9 @@
 
 func (k kytheExtractRustSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var xrefTargets android.Paths
-	ctx.VisitAllModules(func(module android.Module) {
-		if rustModule, ok := module.(xref); ok {
-			xrefTargets = append(xrefTargets, rustModule.XrefRustFiles()...)
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+		if rustModule, ok := android.OtherModuleProvider(ctx, module, RustInfoProvider); ok {
+			xrefTargets = append(xrefTargets, rustModule.XrefRustFiles...)
 		}
 	})
 	if len(xrefTargets) > 0 {
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 767508d..f634bb5 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -184,8 +184,8 @@
 			srcs: ["foo.rs"],
 		}
 	`)
-	module := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
-	rustc := ctx.ModuleForTests("librlib", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
+	module := ctx.ModuleForTests(t, "fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
+	rustc := ctx.ModuleForTests(t, "librlib", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
 
 	// Since dependencies are added to AndroidMk* properties, we can check these to see if they've been picked up.
 	if !android.InList("librlib.rlib-std", module.Properties.AndroidMkRlibs) {
@@ -204,7 +204,7 @@
 		t.Errorf("Static library dependency not detected (dependency missing from AndroidMkStaticLibs)")
 	}
 
-	if !strings.Contains(rustc.Args["rustcFlags"], "-lstatic=wholestatic") {
+	if !strings.Contains(rustc.Args["rustcFlags"], "-lstatic:+whole-archive=wholestatic") {
 		t.Errorf("-lstatic flag not being passed to rustc for static library %#v", rustc.Args["rustcFlags"])
 	}
 
@@ -274,7 +274,7 @@
 		}
 	`)
 
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std").Rule("rustc")
+	libfoo := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_rlib_dylib-std").Rule("rustc")
 	if !android.SuffixInList(libfoo.Implicits.Strings(), "/out/bindings.rs") {
 		t.Errorf("rust_bindgen generated source not included as implicit input for libfoo; Implicits %#v", libfoo.Implicits.Strings())
 	}
@@ -282,7 +282,7 @@
 		t.Errorf("genrule generated source not included as implicit input for libfoo; Implicits %#v", libfoo.Implicits.Strings())
 	}
 
-	fizzBuzz := ctx.ModuleForTests("fizz-buzz-dep", "android_arm64_armv8-a").Rule("rustc")
+	fizzBuzz := ctx.ModuleForTests(t, "fizz-buzz-dep", "android_arm64_armv8-a").Rule("rustc")
 	if !android.SuffixInList(fizzBuzz.Implicits.Strings(), "/out/bindings.rs") {
 		t.Errorf("rust_bindgen generated source not included as implicit input for fizz-buzz-dep; Implicits %#v", libfoo.Implicits.Strings())
 	}
@@ -290,7 +290,7 @@
 		t.Errorf("genrule generated source not included as implicit input for fizz-buzz-dep; Implicits %#v", libfoo.Implicits.Strings())
 	}
 
-	libprocmacro := ctx.ModuleForTests("libprocmacro", "linux_glibc_x86_64").Rule("rustc")
+	libprocmacro := ctx.ModuleForTests(t, "libprocmacro", "linux_glibc_x86_64").Rule("rustc")
 	if !android.SuffixInList(libprocmacro.Implicits.Strings(), "/out/bindings.rs") {
 		t.Errorf("rust_bindgen generated source not included as implicit input for libprocmacro; Implicits %#v", libfoo.Implicits.Strings())
 	}
@@ -299,15 +299,15 @@
 	}
 
 	// Check that our bindings are picked up as crate dependencies as well
-	libfooMod := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
+	libfooMod := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
 	if !android.InList("libbindings", libfooMod.Properties.AndroidMkRlibs) {
 		t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
 	}
-	fizzBuzzMod := ctx.ModuleForTests("fizz-buzz-dep", "android_arm64_armv8-a").Module().(*Module)
+	fizzBuzzMod := ctx.ModuleForTests(t, "fizz-buzz-dep", "android_arm64_armv8-a").Module().(*Module)
 	if !android.InList("libbindings", fizzBuzzMod.Properties.AndroidMkRlibs) {
 		t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
 	}
-	libprocmacroMod := ctx.ModuleForTests("libprocmacro", "linux_glibc_x86_64").Module().(*Module)
+	libprocmacroMod := ctx.ModuleForTests(t, "libprocmacro", "linux_glibc_x86_64").Module().(*Module)
 	if !android.InList("libbindings.rlib-std", libprocmacroMod.Properties.AndroidMkRlibs) {
 		t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
 	}
@@ -354,7 +354,7 @@
 			srcs: ["foo.rs"],
 		}
 	`)
-	rustc := ctx.ModuleForTests("libpm", "linux_glibc_x86_64").Rule("rustc")
+	rustc := ctx.ModuleForTests(t, "libpm", "linux_glibc_x86_64").Rule("rustc")
 
 	if !strings.Contains(rustc.Args["libFlags"], "libbar/linux_glibc_x86_64") {
 		t.Errorf("Proc_macro is not using host variant of dependent modules.")
@@ -369,7 +369,7 @@
 			srcs: ["foo.rs"],
 			no_stdlibs: true,
 		}`)
-	module := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Module().(*Module)
+	module := ctx.ModuleForTests(t, "fizz-buzz", "android_arm64_armv8-a").Module().(*Module)
 
 	if android.InList("libstd", module.Properties.AndroidMkDylibs) {
 		t.Errorf("no_stdlibs did not suppress dependency on libstd")
@@ -385,8 +385,8 @@
 			crate_name: "foo",
 		}`)
 
-	_ = ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std")
-	_ = ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_rlib_dylib-std")
+	_ = ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_rlib_dylib-std")
+	_ = ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_rlib_dylib-std")
 }
 
 // Test that library size measurements are generated.
@@ -398,7 +398,7 @@
 			crate_name: "waldo",
 		}`)
 
-	m := ctx.SingletonForTests("file_metrics")
+	m := ctx.SingletonForTests(t, "file_metrics")
 	m.Output("unstripped/libwaldo.dylib.so.bloaty.csv")
 	m.Output("libwaldo.dylib.so.bloaty.csv")
 }
@@ -423,38 +423,66 @@
 			aliases: ["bar:bar_renamed"],
 		}`)
 
-	fooRustc := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustc")
-	if !strings.Contains(fooRustc.Args["libFlags"], "--extern bar_renamed=out/soong/.intermediates/libbar/android_arm64_armv8-a_dylib/unstripped/libbar.dylib.so") {
-		t.Errorf("--extern bar_renamed=out/soong/.intermediates/libbar/android_arm64_armv8-a_dylib/unstripped/libbar.dylib.so flag not being passed to rustc for rust_binary with aliases. libFlags: %#v", fooRustc.Args["libFlags"])
+	fooRustc := ctx.ModuleForTests(t, "foo", "android_arm64_armv8-a").Rule("rustc")
+	if !strings.Contains(fooRustc.Args["libFlags"], "--extern force:bar_renamed=out/soong/.intermediates/libbar/android_arm64_armv8-a_dylib/unstripped/libbar.dylib.so") {
+		t.Errorf("--extern force:bar_renamed=out/soong/.intermediates/libbar/android_arm64_armv8-a_dylib/unstripped/libbar.dylib.so flag not being passed to rustc for rust_binary with aliases. libFlags: %#v", fooRustc.Args["libFlags"])
 	}
-	if !strings.Contains(fooRustc.Args["libFlags"], "--extern baz=out/soong/.intermediates/libbaz/android_arm64_armv8-a_dylib/unstripped/libbaz.dylib.so") {
-		t.Errorf("--extern baz=out/soong/.intermediates/libbaz/android_arm64_armv8-a_dylib/unstripped/libbaz.dylib.so flag not being passed to rustc for rust_binary with aliases. libFlags: %#v", fooRustc.Args["libFlags"])
+	if !strings.Contains(fooRustc.Args["libFlags"], "--extern force:baz=out/soong/.intermediates/libbaz/android_arm64_armv8-a_dylib/unstripped/libbaz.dylib.so") {
+		t.Errorf("--extern force:baz=out/soong/.intermediates/libbaz/android_arm64_armv8-a_dylib/unstripped/libbaz.dylib.so flag not being passed to rustc for rust_binary with aliases. libFlags: %#v", fooRustc.Args["libFlags"])
 	}
 }
 
-func TestRustRlibs(t *testing.T) {
+func TestRustFFIRlibs(t *testing.T) {
 	ctx := testRust(t, `
-		rust_ffi_rlib {
+		rust_ffi_static {
 			name: "libbar",
 			crate_name: "bar",
 			srcs: ["src/lib.rs"],
 			export_include_dirs: ["bar_includes"]
 		}
 
-		rust_ffi_rlib {
+		rust_ffi_static {
 			name: "libfoo",
 			crate_name: "foo",
 			srcs: ["src/lib.rs"],
 			export_include_dirs: ["foo_includes"]
 		}
 
-		rust_ffi_rlib {
+		rust_ffi_static {
+			name: "libfoo_from_rlib",
+			crate_name: "foo_from_rlib",
+			srcs: ["src/lib.rs"],
+			export_include_dirs: ["foo_includes"]
+		}
+
+		rust_ffi_static {
+			name: "libfoo_from_rlib_whole",
+			crate_name: "foo_from_rlib_whole",
+			srcs: ["src/lib.rs"],
+			export_include_dirs: ["foo_includes"]
+		}
+
+		rust_ffi_static {
 			name: "libbuzz",
 			crate_name: "buzz",
 			srcs: ["src/lib.rs"],
 			export_include_dirs: ["buzz_includes"]
 		}
 
+		rust_ffi_static {
+			name: "libbuzz_from_rlib",
+			crate_name: "buzz_from_rlib",
+			srcs: ["src/lib.rs"],
+			export_include_dirs: ["buzz_includes"]
+		}
+
+		rust_ffi_static {
+			name: "libbuzz_from_rlib_whole",
+			crate_name: "buzz_from_rlib_whole",
+			srcs: ["src/lib.rs"],
+			export_include_dirs: ["buzz_includes"]
+		}
+
 		cc_library_shared {
 			name: "libcc_shared",
 			srcs:["foo.c"],
@@ -468,20 +496,64 @@
 			whole_static_libs: ["libfoo"],
 		}
 
+		cc_library_static {
+			name: "libcc_static_from_rlib",
+			srcs:["foo.c"],
+			static_libs: ["libbuzz_from_rlib"],
+			whole_static_libs: ["libfoo_from_rlib"],
+		}
+
+		cc_library_static {
+			name: "libcc_whole_static_from_rlib",
+			srcs:["foo.c"],
+			static_libs: ["libbuzz_from_rlib_whole"],
+			whole_static_libs: ["libfoo_from_rlib_whole"],
+		}
+
 		cc_binary {
 			name: "ccBin",
 			srcs:["foo.c"],
 			static_libs: ["libcc_static", "libbar"],
 		}
-		`)
 
-	libbar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_rlib_rlib-std").Rule("rustc")
-	libcc_shared_rustc := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("rustc")
-	libcc_shared_ld := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("ld")
-	libcc_shared_cc := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("cc")
-	ccbin_rustc := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("rustc")
-	ccbin_ld := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("ld")
-	ccbin_cc := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("cc")
+		rust_library {
+			name: "librs",
+			srcs:["src/foo.rs"],
+			crate_name: "rs",
+			static_libs: ["libcc_static_from_rlib"],
+			whole_static_libs: ["libcc_whole_static_from_rlib"],
+		}
+
+		rust_library {
+			name: "librs2",
+			srcs:["src/foo.rs"],
+			crate_name: "rs",
+			rustlibs: ["librs"],
+		}
+
+		rust_binary {
+			name: "rsBin",
+			srcs:["src/foo.rs"],
+			crate_name: "rsBin",
+			rlibs: ["librs", "libbar"],
+			static_libs: ["libcc_static"],
+		}
+	`)
+
+	libbar := ctx.ModuleForTests(t, "libbar", "android_arm64_armv8-a_rlib_rlib-std").Rule("rustc")
+	libcc_shared_rustc := ctx.ModuleForTests(t, "libcc_shared", "android_arm64_armv8-a_shared").Rule("rustc")
+	libcc_shared_ld := ctx.ModuleForTests(t, "libcc_shared", "android_arm64_armv8-a_shared").Rule("ld")
+	libcc_shared_cc := ctx.ModuleForTests(t, "libcc_shared", "android_arm64_armv8-a_shared").Rule("cc")
+	ccbin_rustc := ctx.ModuleForTests(t, "ccBin", "android_arm64_armv8-a").Rule("rustc")
+	ccbin_ld := ctx.ModuleForTests(t, "ccBin", "android_arm64_armv8-a").Rule("ld")
+	ccbin_cc := ctx.ModuleForTests(t, "ccBin", "android_arm64_armv8-a").Rule("cc")
+	rustbin_genlib := ctx.ModuleForTests(t, "rsBin", "android_arm64_armv8-a").Output("generated_rust_staticlib/librustlibs.a")
+	rustbin := ctx.ModuleForTests(t, "rsBin", "android_arm64_armv8-a").Output("unstripped/rsBin")
+	librs_rlib := ctx.ModuleForTests(t, "librs", "android_arm64_armv8-a_rlib_dylib-std").MaybeOutput("generated_rust_staticlib/librustlibs.a")
+	librs2_rlib := ctx.ModuleForTests(t, "librs2", "android_arm64_armv8-a_rlib_dylib-std").MaybeOutput("generated_rust_staticlib/librustlibs.a")
+	librs_genlib := ctx.ModuleForTests(t, "librs", "android_arm64_armv8-a_dylib").Output("generated_rust_staticlib/librustlibs.a")
+	librs2_genlib := ctx.ModuleForTests(t, "librs2", "android_arm64_armv8-a_dylib").Output("generated_rust_staticlib/librustlibs.a")
+	librs2_dylib := ctx.ModuleForTests(t, "librs2", "android_arm64_armv8-a_dylib").Output("unstripped/librs2.dylib.so")
 
 	if !strings.Contains(libbar.Args["rustcFlags"], "crate-type=rlib") {
 		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "rlib", libbar.Args["rustcFlags"])
@@ -513,7 +585,7 @@
 	// Make sure the static lib is included in the cc command
 	if !strings.Contains(ccbin_ld.Args["libFlags"], "generated_rust_staticlib/librustlibs.a") {
 		t.Errorf("missing generated static library in linker step libFlags, expecting %#v, libFlags: %#v",
-			"ccBin.generated_rust_staticlib.a", ccbin_ld.Args["libFlags"])
+			"generated_rust_staticlib/librustlibs.a", ccbin_ld.Args["libFlags"])
 	}
 
 	// Make sure the static lib includes are in the ld command
@@ -534,11 +606,72 @@
 		t.Errorf("Missing direct dependency libbar when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"])
 	}
 
+	// Make sure the static lib is included in the rustc command
+	if !strings.Contains(rustbin.Args["linkFlags"], "generated_rust_staticlib/librustlibs.a") {
+		t.Errorf("missing generated static library in linker step libFlags in Rust module, expecting %#v, libFlags: %#v",
+			"generated_rust_staticlib/librustlibs.a", rustbin.Args["libFlags"])
+	}
+	if !strings.Contains(librs2_dylib.Args["linkFlags"], "generated_rust_staticlib/librustlibs.a") {
+		t.Errorf("missing generated static library in linker step libFlags in Rust module, expecting %#v, libFlags: %#v",
+			"generated_rust_staticlib/librustlibs.a", librs2_dylib.Args["libFlags"])
+	}
+
+	// Make sure that direct dependencies and indirect whole static dependencies are
+	// propagating correctly for the rlib -> cc_library_static -> rust_* generated library example.
+	if !strings.Contains(rustbin_genlib.Args["libFlags"], "--extern foo=") {
+		t.Errorf("Missing indirect whole_static_lib dependency libfoo from cc static_lib when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
+	}
+	if strings.Contains(rustbin_genlib.Args["libFlags"], "--extern buzz=") {
+		t.Errorf("Indirect rlib dependency libbuzz from cc static_lib found when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
+	}
+	if strings.Contains(rustbin_genlib.Args["libFlags"], "--extern bar=") {
+		t.Errorf("Direct rlib dependency libbar getting included in the generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
+	}
+	if !strings.Contains(rustbin_genlib.Args["libFlags"], "--extern foo_from_rlib=") {
+		t.Errorf("Missing indirect whole_static_lib dependency libfoo_from_rlib from cc static_lib when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
+	}
+	if strings.Contains(rustbin_genlib.Args["libFlags"], "--extern buzz_from_rlib=") {
+		// While static-libs propagate for rust modules, this is not the
+		// expected behavior for cc modules. Thus, libbuzz_from_rlib would
+		// be expected to have to be re-declared as a direct rlib dependency.
+		t.Errorf("Indirect rlib dependency libbuzz_from_rlib from cc static_lib found when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
+	}
+
 	// Test indirect includes propagation
 	if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ifoo_includes") {
 		t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
 			"-Ifoo_includes", ccbin_cc.Args)
 	}
+
+	// Make sure we're not generating superfluous mto staticlibs.
+	if librs_rlib.Rule != nil {
+		t.Error("rlibs should not be generating mto staticlibs", "rlib", libbar.Args["rustcFlags"])
+	}
+	if librs2_rlib.Rule != nil {
+		t.Error("rlibs should not be generating mto staticlibs", "rlib", libbar.Args["rustcFlags"])
+	}
+
+	// Make sure that direct whole static dependencies are propagating correctly downstream
+	// foo_from_rlib_whole --(ws)--> libcc_whole_static_from_rlib --(ws)--> librs
+	if !strings.Contains(librs_genlib.Args["libFlags"], "--extern foo_from_rlib_whole=") {
+		t.Errorf("Missing direct whole_static_lib dependency libfoo_from_rlib_whole from rust dylib when writing generated Rust staticlib: %#v", librs_genlib.Args["libFlags"])
+	}
+
+	// Make sure that indirect whole static dependencies are propagating correctly downstream
+	// foo_from_rlib_whole --(ws)--> libcc_whole_static_from_rlib --(ws)--> librs --> rust_*
+	if !strings.Contains(librs2_genlib.Args["libFlags"], "--extern foo_from_rlib_whole=") {
+		t.Errorf("Missing indirect whole_static_lib dependency libfoo_from_rlib_whole from rust dylib when writing generated Rust staticlib: %#v", librs2_genlib.Args["libFlags"])
+	}
+	if !strings.Contains(rustbin_genlib.Args["libFlags"], "--extern foo_from_rlib_whole=") {
+		t.Errorf("Missing indirect whole_static_lib dependency libfoo_from_rlib_whole from rust dylib in rust binary when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
+	}
+
+	// Make sure that normal static dependencies are not propagating through dylib dependencies
+	// buzz_from_rlib_whole --(s)--> libcc_whole_static_from_rlib --(ws)--> librs --> rust_*
+	if strings.Contains(librs2_genlib.Args["libFlags"], "--extern buzz_from_rlib_whole=") {
+		t.Errorf("dependency from indirect cc staticlib from direct dylib dep found in rust dylib when writing generated Rust staticlib: %#v", librs2_genlib.Args["libFlags"])
+	}
+
 }
 
 func assertString(t *testing.T, got, expected string) {
@@ -547,3 +680,204 @@
 		t.Errorf("expected %q got %q", expected, got)
 	}
 }
+
+func TestStdLinkMismatch(t *testing.T) {
+	// Test that we catch cases where the std linkage mismatches. This leads to
+	// a confusing rustc error where a crate is declared missing despite being
+	// passed in as a rustlib dependency / via the --extern flag. Thus, we want
+	// to make sure we detect it in Soong.
+
+	// libfoo depends on libbar as an rlib, but does not link libstd as an rlib.
+	// libbar only links libstd as an rlib (prefer_rlib).
+	testRustError(t, "wrong StdLinkage", `
+		rust_library {
+			name: "libfoo",
+			crate_name: "foo",
+			srcs: [
+				"foo.rs",
+			],
+			rlibs: ["libbar"],
+		}
+		rust_library {
+			name: "libbar",
+			crate_name: "bar",
+			srcs: [
+				"bar.rs",
+			],
+			prefer_rlib: true,
+		}
+	`)
+}
+
+func TestRustLinkPropagation(t *testing.T) {
+	// Test static and whole static propagation behavior
+	//
+	//  Whole static libs propagate through rlibs and through dylibs to
+	//  dependencies further down. rustc does not re-export whole-archived
+	//  static libs for dylibs, so this simulates re-exporting those symbols.
+	//
+	//  Static libs only propagate through rlibs to some final dylib. We propagate
+	//  normal static libs because we allow rustlib dependencies to represent
+	//  either rlibs or dylibs. Not propagating static libs through rlibs would
+	//  mean we'd need to always redeclare static libs throughout a dependency tree
+	//  We don't propagate past dylibs because they represent a final link.
+
+	ctx := testRust(t, `
+	rust_library_rlib {
+		name: "librlib1",
+		crate_name: "rlib1",
+		srcs: ["src/lib.rs"],
+		static_libs: ["libcc_static_rlib1"],
+		whole_static_libs: ["libcc_whole_static_rlib1"],
+	}
+
+	rust_library_dylib {
+		name: "libdylib1",
+		crate_name: "dylib1",
+		static_libs: ["libcc_static_dylib1"],
+		srcs: ["src/lib.rs"],
+		whole_static_libs: ["libcc_whole_static_dylib1"],
+	}
+
+	rust_library_rlib {
+		name: "librlib2",
+		crate_name: "rlib2",
+		srcs: ["src/lib.rs"],
+		rlibs: ["librlib1"],
+		static_libs: ["libcc_static_rlib2"],
+		whole_static_libs: ["libcc_whole_static_rlib2"],
+	}
+
+	rust_library_dylib {
+		name: "libdylib2",
+		crate_name: "dylib2",
+		srcs: ["src/lib.rs"],
+		rlibs: ["librlib1"],
+		rustlibs: ["libdylib1"],
+		static_libs: ["libcc_static_dylib2"],
+		whole_static_libs: ["libcc_whole_static_dylib2"],
+	}
+
+	cc_library_static {
+		name: "libcc_static_rlib1",
+		srcs:["foo.c"],
+	}
+
+	cc_library_static {
+		name: "libcc_static_rlib2",
+		srcs:["foo.c"],
+	}
+
+	cc_library_static {
+		name: "libcc_static_dylib1",
+		srcs:["foo.c"],
+	}
+
+	cc_library_static {
+		name: "libcc_static_dylib2",
+		srcs:["foo.c"],
+	}
+
+	cc_library_static {
+		name: "libcc_whole_static_rlib1",
+		srcs:["foo.c"],
+	}
+
+	cc_library_static {
+		name: "libcc_whole_static_rlib2",
+		srcs:["foo.c"],
+	}
+
+	cc_library_static {
+		name: "libcc_whole_static_dylib1",
+		srcs:["foo.c"],
+	}
+
+	cc_library_static {
+		name: "libcc_whole_static_dylib2",
+		srcs:["foo.c"],
+	}
+
+	rust_library_rlib {
+		name: "librlib3",
+		crate_name: "rlib3",
+		srcs: ["src/lib.rs"],
+		rlibs: ["librlib2"],
+	}
+
+	rust_library_dylib {
+		name: "libdylib3",
+		crate_name: "dylib3",
+		srcs: ["src/lib.rs"],
+		rlibs: ["librlib2"],
+		rustlibs: ["libdylib2"],
+	}
+	`)
+
+	librlib3 := ctx.ModuleForTests(t, "librlib3", "android_arm64_armv8-a_rlib_dylib-std").Rule("rustc")
+	libdylib3 := ctx.ModuleForTests(t, "libdylib3", "android_arm64_armv8-a_dylib").Rule("rustc")
+
+	// Test static lib propagation from:
+	// rlib -> rlib
+	if !strings.Contains(librlib3.Args["linkFlags"], "libcc_static_rlib2.a") {
+		t.Errorf("direct dependency static lib not propagating from rlib to rlib; linkFlags %#v",
+			librlib3.Args["linkFlags"])
+	}
+	// rlib -> rlib -> rlib
+	if !strings.Contains(librlib3.Args["linkFlags"], "libcc_static_rlib1.a") {
+		t.Errorf("indirect dependency static lib not propagating from rlib to rlib: linkFlags %#v",
+			librlib3.Args["linkFlags"])
+	}
+	// rlib -> rlib -> dylib
+	if !strings.Contains(libdylib3.Args["linkFlags"], "libcc_static_rlib1.a") {
+		t.Errorf("indirect dependency static lib not propagating from rlib to dylib: linkFlags %#v",
+			libdylib3.Args["linkFlags"])
+	}
+	// rlib -> dylib
+	if !strings.Contains(libdylib3.Args["linkFlags"], "libcc_static_rlib2.a") {
+		t.Errorf("direct dependency static lib not propagating from rlib to dylib: linkFlags: %#v",
+			libdylib3.Args["linkFlags"])
+	}
+	// dylib -> dylib (negative case, should not propagate)
+	if strings.Contains(libdylib3.Args["linkFlags"], "libcc_static_dylib2.a") {
+		t.Errorf("direct dependency static lib propagating from dylib to dylib: linkFlags: %#v",
+			libdylib3.Args["linkFlags"])
+	}
+	// dylib -> dylib -> dylib (negative case, should not propagate)
+	if strings.Contains(libdylib3.Args["linkFlags"], "libcc_static_dylib1.a") {
+		t.Errorf("indirect dependency static lib propagating from dylib to dylib: linkFlags: %#v",
+			libdylib3.Args["linkFlags"])
+	}
+
+	// Test whole static lib propagation from:
+	// rlib -> rlib
+	if !strings.Contains(librlib3.Args["linkFlags"], "libcc_whole_static_rlib2.a") {
+		t.Errorf("direct dependency whole static lib not propagating from rlib to rlib: linkFlags %#v",
+			librlib3.Args["linkFlags"])
+	}
+	// rlib -> rlib -> rlib
+	if !strings.Contains(librlib3.Args["linkFlags"], "libcc_whole_static_rlib1.a") {
+		t.Errorf("indirect dependency whole static lib not propagating from rlib to rlib: linkFlags %#v",
+			librlib3.Args["linkFlags"])
+	}
+	// rlib -> dylib
+	if !strings.Contains(libdylib3.Args["linkFlags"], "libcc_whole_static_rlib2.a") {
+		t.Errorf("direct dependency whole static lib not propagating from rlib to dylib: linkFlags %#v",
+			libdylib3.Args["linkFlags"])
+	}
+	// rlib -> rlib -> dylib
+	if !strings.Contains(libdylib3.Args["linkFlags"], "libcc_whole_static_rlib1.a") {
+		t.Errorf("indirect dependency whole static lib not propagating from rlib to dylib: linkFlags %#v",
+			libdylib3.Args["linkFlags"])
+	}
+	// dylib -> dylib
+	if !strings.Contains(libdylib3.Args["linkFlags"], "libcc_whole_static_dylib2.a") {
+		t.Errorf("direct dependency whole static lib not propagating from dylib to dylib: linkFlags %#v",
+			libdylib3.Args["linkFlags"])
+	}
+	// dylib -> dylib -> dylib
+	if !strings.Contains(libdylib3.Args["linkFlags"], "libcc_whole_static_dylib1.a") {
+		t.Errorf("indirect dependency whole static lib not propagating from dylib to dylib: linkFlags %#v",
+			libdylib3.Args["linkFlags"])
+	}
+}
diff --git a/rust/sanitize.go b/rust/sanitize.go
index b8f922f..50f55ce 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -53,6 +53,9 @@
 
 	// Used when we need to place libraries in their own directory, such as ASAN.
 	InSanitizerDir bool `blueprint:"mutated"`
+
+	// ForceDisable is set by the version mutator to disable sanitization of stubs variants
+	ForceDisable bool `blueprint:"mutated"`
 }
 
 var fuzzerFlags = []string{
@@ -103,6 +106,10 @@
 func (sanitize *sanitize) begin(ctx BaseModuleContext) {
 	s := &sanitize.Properties.Sanitize
 
+	if sanitize.Properties.ForceDisable {
+		return
+	}
+
 	// Disable sanitizers for musl x86 modules, rustc does not support any sanitizers.
 	if ctx.Os() == android.LinuxMusl && ctx.Arch().ArchType == android.X86 {
 		s.Never = proptools.BoolPtr(true)
@@ -221,6 +228,10 @@
 }
 
 func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
+	if sanitize.Properties.ForceDisable {
+		return flags, deps
+	}
+
 	if !sanitize.Properties.SanitizerEnabled {
 		return flags, deps
 	}
@@ -253,6 +264,9 @@
 		if !mod.Enabled(mctx) {
 			return
 		}
+		if mod.sanitize.Properties.ForceDisable {
+			return
+		}
 
 		if Bool(mod.sanitize.Properties.Sanitize.Memtag_heap) && mod.Binary() {
 			noteDep := "note_memtag_heap_async"
@@ -364,7 +378,7 @@
 // distinguish between the cases. It isn't needed though - both cases can be
 // treated identically.
 func (sanitize *sanitize) isSanitizerEnabled(t cc.SanitizerType) bool {
-	if sanitize == nil || !sanitize.Properties.SanitizerEnabled {
+	if sanitize == nil || !sanitize.Properties.SanitizerEnabled || sanitize.Properties.ForceDisable {
 		return false
 	}
 
@@ -453,7 +467,7 @@
 }
 
 func (mod *Module) SanitizeNever() bool {
-	return Bool(mod.sanitize.Properties.Sanitize.Never)
+	return Bool(mod.sanitize.Properties.Sanitize.Never) || mod.sanitize.Properties.ForceDisable
 }
 
 var _ cc.PlatformSanitizeable = (*Module)(nil)
diff --git a/rust/sanitize_test.go b/rust/sanitize_test.go
index d6a14b2..a3fe282 100644
--- a/rust/sanitize_test.go
+++ b/rust/sanitize_test.go
@@ -153,65 +153,65 @@
 	).RunTest(t)
 	ctx := result.TestContext
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_no_override", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_async", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_sync", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_sync", variant), None)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_no_override", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_async", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_sync", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_sync", variant), None)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_no_override", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_disable", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_no_override", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_disable", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_sync", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_sync", variant), Async)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_no_override", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_disable", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_sync", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_sync", variant), Async)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_no_override", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_no_override", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_sync", variant), Sync)
 }
 
 func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) {
@@ -226,67 +226,67 @@
 	).RunTest(t)
 	ctx := result.TestContext
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_no_override", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_async", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_sync", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_sync", variant), None)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_no_override", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_async", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_sync", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_sync", variant), None)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_no_override", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_disable", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_no_override", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_disable", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_sync", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_sync", variant), Async)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_no_override", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_disable", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_sync", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_sync", variant), Async)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_sync", variant), Sync)
 
 	// should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_async", variant), Sync)
 	// should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_no_override", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_sync", variant), Sync)
 }
 
 func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) {
@@ -302,64 +302,64 @@
 	).RunTest(t)
 	ctx := result.TestContext
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_no_override", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_async", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_sync", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_binary_override_default_sync", variant), None)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_no_override", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_async", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_sync", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "no_memtag_test_override_default_sync", variant), None)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_no_override", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_disable", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_sync", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_binary_override_default_sync", variant), Async)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_no_override", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_async", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_disable", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_sync", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_async_test_override_default_sync", variant), Async)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "set_memtag_set_sync_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_async", variant), Sync)
 	// should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_memtag_set_sync_test_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_disable", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_binary_override_default_sync", variant), Sync)
 
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_no_override", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_async", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests(t, "unset_test_override_default_sync", variant), Sync)
 }
diff --git a/rust/source_provider.go b/rust/source_provider.go
index 3236bce..27c62c2 100644
--- a/rust/source_provider.go
+++ b/rust/source_provider.go
@@ -43,6 +43,7 @@
 	SourceProviderProps() []interface{}
 	SourceProviderDeps(ctx DepsContext, deps Deps) Deps
 	setSubName(subName string)
+	getSubName() string
 	setOutputFiles(outputFiles android.Paths)
 }
 
@@ -100,6 +101,10 @@
 	sp.subName = subName
 }
 
+func (sp *BaseSourceProvider) getSubName() string {
+	return sp.subName
+}
+
 func (sp *BaseSourceProvider) setOutputFiles(outputFiles android.Paths) {
 	sp.OutputFiles = outputFiles
 }
diff --git a/rust/test.go b/rust/test.go
index 20ccfb3..cedced2 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -46,9 +46,16 @@
 	// the test
 	Data []string `android:"path,arch_variant"`
 
-	// Same as data, but will add dependencies on the device's
+	// Same as data, but adds dependencies on modules using the device's os variant, and common
+	// architecture's variant. Can be useful to add device-built apps to the data of a host
+	// test.
 	Device_common_data []string `android:"path_device_common"`
 
+	// Same as data, but will add dependencies on modules using the host's os variation and
+	// the common arch variation. Useful for a device test that wants to depend on a host
+	// module, for example to include a custom Tradefed test runner.
+	Host_common_data []string `android:"path_host_common"`
+
 	// list of shared library modules that should be installed alongside the test
 	Data_libs []string `android:"arch_variant"`
 
@@ -147,36 +154,38 @@
 
 	dataSrcPaths := android.PathsForModuleSrc(ctx, test.Properties.Data)
 	dataSrcPaths = append(dataSrcPaths, android.PathsForModuleSrc(ctx, test.Properties.Device_common_data)...)
+	dataSrcPaths = append(dataSrcPaths, android.PathsForModuleSrc(ctx, test.Properties.Host_common_data)...)
 
-	ctx.VisitDirectDepsWithTag(dataLibDepTag, func(dep android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(dataLibDepTag, func(dep android.ModuleProxy) {
 		depName := ctx.OtherModuleName(dep)
-		linkableDep, ok := dep.(cc.LinkableInterface)
+		linkableDep, ok := android.OtherModuleProvider(ctx, dep, cc.LinkableInfoProvider)
 		if !ok {
 			ctx.ModuleErrorf("data_lib %q is not a linkable module", depName)
 		}
-		if linkableDep.OutputFile().Valid() {
+		if linkableDep.OutputFile.Valid() {
 			// Copy the output in "lib[64]" so that it's compatible with
 			// the default rpath values.
+			commonInfo := android.OtherModulePointerProviderOrDefault(ctx, dep, android.CommonModuleInfoProvider)
 			libDir := "lib"
-			if linkableDep.Target().Arch.ArchType.Multilib == "lib64" {
+			if commonInfo.Target.Arch.ArchType.Multilib == "lib64" {
 				libDir = "lib64"
 			}
 			test.data = append(test.data,
-				android.DataPath{SrcPath: linkableDep.OutputFile().Path(),
-					RelativeInstallPath: filepath.Join(libDir, linkableDep.RelativeInstallPath())})
+				android.DataPath{SrcPath: linkableDep.OutputFile.Path(),
+					RelativeInstallPath: filepath.Join(libDir, linkableDep.RelativeInstallPath)})
 		}
 	})
 
-	ctx.VisitDirectDepsWithTag(dataBinDepTag, func(dep android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(dataBinDepTag, func(dep android.ModuleProxy) {
 		depName := ctx.OtherModuleName(dep)
-		linkableDep, ok := dep.(cc.LinkableInterface)
+		linkableDep, ok := android.OtherModuleProvider(ctx, dep, cc.LinkableInfoProvider)
 		if !ok {
 			ctx.ModuleErrorf("data_bin %q is not a linkable module", depName)
 		}
-		if linkableDep.OutputFile().Valid() {
+		if linkableDep.OutputFile.Valid() {
 			test.data = append(test.data,
-				android.DataPath{SrcPath: linkableDep.OutputFile().Path(),
-					RelativeInstallPath: linkableDep.RelativeInstallPath()})
+				android.DataPath{SrcPath: linkableDep.OutputFile.Path(),
+					RelativeInstallPath: linkableDep.RelativeInstallPath})
 		}
 	})
 
@@ -194,6 +203,31 @@
 	if ctx.Host() && test.Properties.Test_options.Unit_test == nil {
 		test.Properties.Test_options.Unit_test = proptools.BoolPtr(true)
 	}
+
+	if !ctx.Config().KatiEnabled() { // TODO(spandandas): Remove the special case for kati
+		// Install the test config in testcases/ directory for atest.
+		r, ok := ctx.Module().(*Module)
+		if !ok {
+			ctx.ModuleErrorf("Not a rust test module")
+		}
+		// Install configs in the root of $PRODUCT_OUT/testcases/$module
+		testCases := android.PathForModuleInPartitionInstall(ctx, "testcases", ctx.ModuleName()+r.SubName())
+		if ctx.PrimaryArch() {
+			if test.testConfig != nil {
+				ctx.InstallFile(testCases, ctx.ModuleName()+".config", test.testConfig)
+			}
+			dynamicConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "DynamicConfig.xml")
+			if dynamicConfig.Valid() {
+				ctx.InstallFile(testCases, ctx.ModuleName()+".dynamic", dynamicConfig.Path())
+			}
+		}
+		// Install tests and data in arch specific subdir $PRODUCT_OUT/testcases/$module/$arch
+		testCases = testCases.Join(ctx, ctx.Target().Arch.ArchType.String())
+		ctx.InstallTestData(testCases, test.data)
+		testPath := ctx.RustModule().OutputFile().Path()
+		ctx.InstallFile(testCases, testPath.Base(), testPath)
+	}
+
 	test.binaryDecorator.installTestData(ctx, test.data)
 	test.binaryDecorator.install(ctx)
 }
@@ -202,11 +236,16 @@
 	flags = test.binaryDecorator.compilerFlags(ctx, flags)
 	if test.testHarness() {
 		flags.RustFlags = append(flags.RustFlags, "--test")
+		flags.RustFlags = append(flags.RustFlags, "-A missing-docs")
 	}
 	if ctx.Device() {
 		flags.RustFlags = append(flags.RustFlags, "-Z panic_abort_tests")
 	}
 
+	// Add a default rpath to allow tests to dlopen libraries specified in data_libs.
+	flags.GlobalLinkFlags = append(flags.GlobalLinkFlags, `-Wl,-rpath,\$$ORIGIN/lib64`)
+	flags.GlobalLinkFlags = append(flags.GlobalLinkFlags, `-Wl,-rpath,\$$ORIGIN/lib`)
+
 	return flags
 }
 
@@ -238,7 +277,7 @@
 	return module.Init()
 }
 
-func (test *testDecorator) stdLinkage(ctx *depsContext) RustLinkage {
+func (test *testDecorator) stdLinkage(device bool) RustLinkage {
 	return RlibLinkage
 }
 
@@ -257,6 +296,36 @@
 	return true
 }
 
+func (test *testDecorator) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
+	test.binaryDecorator.moduleInfoJSON(ctx, moduleInfoJSON)
+	moduleInfoJSON.Class = []string{"NATIVE_TESTS"}
+	if Bool(test.Properties.Test_options.Unit_test) {
+		moduleInfoJSON.IsUnitTest = "true"
+		if ctx.Host() {
+			moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "host-unit-tests")
+		}
+	}
+	moduleInfoJSON.TestOptionsTags = append(moduleInfoJSON.TestOptionsTags, test.Properties.Test_options.Tags...)
+	if test.testConfig != nil {
+		if _, ok := test.testConfig.(android.WritablePath); ok {
+			moduleInfoJSON.AutoTestConfig = []string{"true"}
+		}
+		moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, test.testConfig.String())
+	}
+
+	moduleInfoJSON.DataDependencies = append(moduleInfoJSON.DataDependencies, test.Properties.Data_bins...)
+
+	if len(test.Properties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, test.Properties.Test_suites...)
+	} else {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
+	}
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: test.Properties.Test_suites,
+	})
+}
+
 func rustTestHostMultilib(ctx android.LoadHookContext) {
 	type props struct {
 		Target struct {
diff --git a/rust/test_test.go b/rust/test_test.go
index 1097da2..076a259 100644
--- a/rust/test_test.go
+++ b/rust/test_test.go
@@ -29,7 +29,7 @@
 			data: ["data.txt"],
 		}`)
 
-	testingModule := ctx.ModuleForTests("my_test", "linux_glibc_x86_64")
+	testingModule := ctx.ModuleForTests(t, "my_test", "linux_glibc_x86_64")
 	expectedOut := "my_test/linux_glibc_x86_64/my_test"
 	outPath := testingModule.Output("my_test").Output.String()
 	if !strings.Contains(outPath, expectedOut) {
@@ -62,7 +62,7 @@
 			crate_name: "bar",
 		}`)
 
-	testingModule := ctx.ModuleForTests("my_test", "android_arm64_armv8-a").Module().(*Module)
+	testingModule := ctx.ModuleForTests(t, "my_test", "android_arm64_armv8-a").Module().(*Module)
 
 	if !android.InList("libfoo.rlib-std", testingModule.Properties.AndroidMkRlibs) {
 		t.Errorf("rlib-std variant for libfoo not detected as a rustlib-defined rlib dependency for device rust_test module")
@@ -106,7 +106,7 @@
 
 	ctx := testRust(t, bp)
 
-	testingModule := ctx.ModuleForTests("main_test", "android_arm64_armv8-a")
+	testingModule := ctx.ModuleForTests(t, "main_test", "android_arm64_armv8-a")
 	testBinary := testingModule.Module().(*Module).compiler.(*testDecorator)
 	outputFiles := testingModule.OutputFiles(ctx, t, "")
 	if len(outputFiles) != 1 {
@@ -165,7 +165,7 @@
  `
 
 	ctx := testRust(t, bp)
-	testingModule := ctx.ModuleForTests("main_test", "android_arm64_armv8-a")
+	testingModule := ctx.ModuleForTests(t, "main_test", "android_arm64_armv8-a")
 	module := testingModule.Module()
 	testBinary := module.(*Module).compiler.(*testDecorator)
 	outputFiles := testingModule.OutputFiles(ctx, t, "")
diff --git a/rust/testing.go b/rust/testing.go
index 32cc823..2082b52 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -80,7 +80,6 @@
 			no_libcrt: true,
 			nocrt: true,
 			system_shared_libs: [],
-			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
 			min_sdk_version: "29",
 			vendor_available: true,
 			host_supported: true,
@@ -88,6 +87,13 @@
 			llndk: {
 				symbol_file: "liblog.map.txt",
 			},
+			stubs: {
+				symbol_file: "liblog.map.txt",
+				versions: [
+					"29",
+					"30",
+				],
+			},
 		}
 		cc_library {
 			name: "libprotobuf-cpp-full",
@@ -188,12 +194,10 @@
 	ctx.RegisterModuleType("rust_fuzz_host", RustFuzzHostFactory)
 	ctx.RegisterModuleType("rust_ffi", RustFFIFactory)
 	ctx.RegisterModuleType("rust_ffi_shared", RustFFISharedFactory)
-	ctx.RegisterModuleType("rust_ffi_rlib", RustFFIRlibFactory)
-	ctx.RegisterModuleType("rust_ffi_static", RustFFIRlibFactory)
+	ctx.RegisterModuleType("rust_ffi_static", RustLibraryRlibFactory)
 	ctx.RegisterModuleType("rust_ffi_host", RustFFIHostFactory)
 	ctx.RegisterModuleType("rust_ffi_host_shared", RustFFISharedHostFactory)
-	ctx.RegisterModuleType("rust_ffi_host_rlib", RustFFIRlibHostFactory)
-	ctx.RegisterModuleType("rust_ffi_host_static", RustFFIRlibHostFactory)
+	ctx.RegisterModuleType("rust_ffi_host_static", RustLibraryRlibHostFactory)
 	ctx.RegisterModuleType("rust_proc_macro", ProcMacroFactory)
 	ctx.RegisterModuleType("rust_protobuf", RustProtobufFactory)
 	ctx.RegisterModuleType("rust_protobuf_host", RustProtobufHostFactory)
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 00b3ca5..b6cac32 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -254,11 +254,6 @@
     ],
 }
 
-sh_binary_host {
-    name: "list_image",
-    src: "list_image.sh",
-}
-
 filegroup {
     name: "rustfmt.toml",
     srcs: ["rustfmt.toml"],
@@ -287,16 +282,12 @@
     ],
 }
 
-sh_binary_host {
-    name: "keep-flagged-apis",
-    src: "keep-flagged-apis.sh",
-}
-
 python_binary_host {
-    name: "merge_directories",
-    main: "merge_directories.py",
-    srcs: [
-        "merge_directories.py",
+    name: "aconfig-to-metalava-flags",
+    main: "aconfig-to-metalava-flags.py",
+    srcs: ["aconfig-to-metalava-flags.py"],
+    libs: [
+        "libaconfig_python_proto",
     ],
 }
 
@@ -319,3 +310,11 @@
     main: "extra_install_zips_file_list.py",
     srcs: ["extra_install_zips_file_list.py"],
 }
+
+python_binary_host {
+    name: "rustc_linker",
+    main: "rustc_linker.py",
+    srcs: [
+        "rustc_linker.py",
+    ],
+}
diff --git a/scripts/aconfig-to-metalava-flags.py b/scripts/aconfig-to-metalava-flags.py
new file mode 100644
index 0000000..efa85ec
--- /dev/null
+++ b/scripts/aconfig-to-metalava-flags.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python3
+
+# 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.
+
+"""Converts a set of aconfig protobuf flags to a Metalava config file."""
+
+# Formatted using `pyformat -i scripts/aconfig-to-metalava-flags.py`
+
+import argparse
+import sys
+import xml.etree.ElementTree as ET
+
+from protos import aconfig_pb2
+
+_READ_ONLY = aconfig_pb2.flag_permission.READ_ONLY
+_ENABLED = aconfig_pb2.flag_state.ENABLED
+_DISABLED = aconfig_pb2.flag_state.DISABLED
+
+# The namespace of the Metalava config file.
+CONFIG_NS = 'http://www.google.com/tools/metalava/config'
+
+
+def config_name(tag: str):
+  """Create a QName in the config namespace.
+
+  :param:tag the name of the entity in the config namespace.
+  """
+  return f'{{{CONFIG_NS}}}{tag}'
+
+
+def main():
+  """Program entry point."""
+  args_parser = argparse.ArgumentParser(
+      description='Generate Metalava flags config from aconfig protobuf',
+  )
+  args_parser.add_argument(
+      'input',
+      help='The path to the aconfig protobuf file',
+  )
+  args = args_parser.parse_args(sys.argv[1:])
+
+  # Read the parsed_flags from the protobuf file.
+  with open(args.input, 'rb') as f:
+    parsed_flags = aconfig_pb2.parsed_flags.FromString(f.read())
+
+  # Create the structure of the XML config file.
+  config = ET.Element(config_name('config'))
+  api_flags = ET.SubElement(config, config_name('api-flags'))
+  # Create an <api-flag> element for each parsed_flag.
+  for flag in parsed_flags.parsed_flag:
+    if flag.permission == _READ_ONLY:
+      # Ignore any read only disabled flags as Metalava assumes that as the
+      # default when an <api-flags/> element is provided so this reduces the
+      # size of the file.
+      if flag.state == _DISABLED:
+        continue
+      mutability = 'immutable'
+    else:
+      mutability = 'mutable'
+    if flag.state == _ENABLED:
+      status = 'enabled'
+    else:
+      status = 'disabled'
+    attributes = {
+        'package': flag.package,
+        'name': flag.name,
+        'mutability': mutability,
+        'status': status,
+    }
+    # Convert the attribute names into qualified names in, what will become, the
+    # default namespace for the XML file. This is needed to ensure that the
+    # attribute will be written in the XML file without a prefix, e.g.
+    # `name="flag_name"`. Without it, a namespace prefix, e.g. `ns1`, will be
+    # synthesized for the attribute when writing the XML file, i.e. it
+    # will be written as `ns1:name="flag_name"`. Strictly speaking, that is
+    # unnecessary as the "Namespaces in XML 1.0 (Third Edition)" specification
+    # says that unprefixed attribute names have no namespace.
+    qualified_attributes = {config_name(k): v for (k, v) in attributes.items()}
+    ET.SubElement(api_flags, config_name('api-flag'), qualified_attributes)
+
+  # Create a tree and add white space so it will pretty print when written out.
+  tree = ET.ElementTree(config)
+  ET.indent(tree)
+
+  # Write the tree using the config namespace as the default.
+  tree.write(sys.stdout, encoding='unicode', default_namespace=CONFIG_NS)
+  sys.stdout.close()
+
+
+if __name__ == '__main__':
+  main()
diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh
index ef0f44a..b600443 100755
--- a/scripts/build-ndk-prebuilts.sh
+++ b/scripts/build-ndk-prebuilts.sh
@@ -23,9 +23,18 @@
 # TODO: remove ALLOW_MISSING_DEPENDENCIES=true when all the riscv64
 # dependencies exist (currently blocked by http://b/273792258).
 # TODO: remove BUILD_BROKEN_DISABLE_BAZEL=1 when bazel supports riscv64 (http://b/262192655).
+#
+# LTO is disabled because the NDK compiler is not necessarily in-sync with the
+# compiler used to build the platform sysroot, and the sysroot includes static
+# libraries which would be incompatible with mismatched compilers when built
+# with LTO. Disabling LTO globally for the NDK sysroot is okay because the only
+# compiled code in the sysroot that will end up in apps is those static
+# libraries.
+# https://github.com/android/ndk/issues/1591
 TARGET_RELEASE=trunk_staging \
 ALLOW_MISSING_DEPENDENCIES=true \
 BUILD_BROKEN_DISABLE_BAZEL=1 \
+DISABLE_LTO=true \
     TARGET_PRODUCT=ndk build/soong/soong_ui.bash --make-mode --soong-only ${OUT_DIR}/soong/ndk.timestamp
 
 if [ -n "${DIST_DIR}" ]; then
diff --git a/scripts/check_boot_jars/package_allowed_list.txt b/scripts/check_boot_jars/package_allowed_list.txt
index 2f0907c..2c97ae4 100644
--- a/scripts/check_boot_jars/package_allowed_list.txt
+++ b/scripts/check_boot_jars/package_allowed_list.txt
@@ -265,3 +265,7 @@
 omnirom\.platform
 org\.omnirom\.omnilib
 org\.omnirom\.omnilib\..*
+###################################################
+# Packages used for wear-sdk in Wear OS
+com\.google\.wear
+com\.google\.wear\..*
diff --git a/scripts/gen_build_prop.py b/scripts/gen_build_prop.py
index 3e4eb48..2167741 100644
--- a/scripts/gen_build_prop.py
+++ b/scripts/gen_build_prop.py
@@ -134,7 +134,7 @@
 
 def generate_common_build_props(args):
   print("####################################")
-  print("# from generate_common_build_props")
+  print("# from generate-common-build-props")
   print("# These properties identify this partition image.")
   print("####################################")
 
@@ -191,7 +191,7 @@
   print(f"ro.{partition}.build.version.release={config['Platform_version_last_stable']}")
   print(f"ro.{partition}.build.version.release_or_codename={config['Platform_version_name']}")
   print(f"ro.{partition}.build.version.sdk={config['Platform_sdk_version']}")
-  print(f"ro.{partition}.build.version.sdk_minor={build_flags['RELEASE_PLATFORM_SDK_MINOR_VERSION']}")
+  print(f"ro.{partition}.build.version.sdk_full={config['Platform_sdk_version_full']}")
 
 def generate_build_info(args):
   print()
@@ -227,7 +227,7 @@
     print(f"ro.build.display.id?={config['BuildDesc']}")
   print(f"ro.build.version.incremental={config['BuildNumber']}")
   print(f"ro.build.version.sdk={config['Platform_sdk_version']}")
-  print(f"ro.build.version.sdk_minor={build_flags['RELEASE_PLATFORM_SDK_MINOR_VERSION']}")
+  print(f"ro.build.version.sdk_full={config['Platform_sdk_version_full']}")
   print(f"ro.build.version.preview_sdk={config['Platform_preview_sdk_version']}")
   print(f"ro.build.version.preview_sdk_fingerprint={config['PlatformPreviewSdkFingerprint']}")
   print(f"ro.build.version.codename={config['Platform_sdk_codename']}")
@@ -277,9 +277,15 @@
   print(f"# end build properties")
 
 def write_properties_from_file(file):
+  # Make and Soong use different intermediate files to build vendor/build.prop.
+  # Although the sysprop contents are same, the absolute paths of these
+  # intermediate files are different.
+  # Print the filename for the intermediate files (files in OUT_DIR).
+  # This helps with validating mk->soong migration of android partitions.
+  filename = os.path.basename(file.name) if file.name.startswith(os.environ.get("OUT_DIR")) else file.name
   print()
   print("####################################")
-  print(f"# from {file.name}")
+  print(f"# from {filename}")
   print("####################################")
   print(file.read(), end="")
 
@@ -336,7 +342,15 @@
     props.append(f"ro.sanitize.{sanitize_target}=true")
 
   # Sets the default value of ro.postinstall.fstab.prefix to /system.
-  # Device board config should override the value to /product when needed by:
+  #
+  # Device board configs can override this to /product to use a
+  # product-specific fstab.postinstall file (installed to
+  # /product/etc/fstab.postinstall). If not overridden, the
+  # system/extras/cppreopts/fstab.postinstall file (installed to
+  # /system/etc/fstab.postinstall) will be used.
+  # Note: The default fstab.postinstall is generic and may be slower
+  # because it tries different mount options line by line to ensure
+  # compatibility across various devices.
   #
   #     PRODUCT_PRODUCT_PROPERTIES += ro.postinstall.fstab.prefix=/product
   #
@@ -389,9 +403,6 @@
       props = list(filter(lambda x: not x.startswith("ro.setupwizard.mode="), props))
       props.append("ro.setupwizard.mode=OPTIONAL")
 
-    if not config["SdkBuild"]:
-      # To speedup startup of non-preopted builds, don't verify or compile the boot image.
-      props.append("dalvik.vm.image-dex2oat-filter=extract")
     # b/323566535
     props.append("init.svc_debug.no_fatal.zygote=true")
 
@@ -484,7 +495,7 @@
   props.append(f"ro.vendor.build.security_patch={config['VendorSecurityPatch']}")
   props.append(f"ro.product.board={config['BootloaderBoardName']}")
   props.append(f"ro.board.platform={config['BoardPlatform']}")
-  props.append(f"ro.hwui.use_vulkan={'true' if config['UsesVulkan'] else 'false'}")
+  props.append(f"ro.hwui.use_vulkan={config['UsesVulkan']}")
 
   if config["ScreenDensity"]:
     props.append(f"ro.sf.lcd_density={config['ScreenDensity']}")
diff --git a/scripts/keep-flagged-apis.sh b/scripts/keep-flagged-apis.sh
deleted file mode 100755
index 48efb7a..0000000
--- a/scripts/keep-flagged-apis.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/bash -e
-#
-# 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.
-
-# Convert a list of flags in the input file to a list of metalava options
-# that will keep the APIs for those flags will hiding all other flagged
-# APIs.
-
-FLAGS="$1"
-
-FLAGGED="android.annotation.FlaggedApi"
-
-# Convert the list of feature flags in the input file to Metalava options
-# of the form `--revert-annotation !android.annotation.FlaggedApi("<flag>")`
-# to prevent the annotated APIs from being hidden, i.e. include the annotated
-# APIs in the SDK snapshots.
-while read -r line; do
-  # Escape and quote the key for sed
-  escaped_line=$(echo "$line" | sed "s/'/\\\'/g; s/ /\\ /g")
-
-  echo "--revert-annotation '!$FLAGGED(\"$escaped_line\")'"
-done < "$FLAGS"
-
-# Revert all flagged APIs, unless listed above.
-echo "--revert-annotation $FLAGGED"
diff --git a/scripts/list_image.sh b/scripts/list_image.sh
deleted file mode 100755
index 0542fa6..0000000
--- a/scripts/list_image.sh
+++ /dev/null
@@ -1,51 +0,0 @@
-#! /bin/bash
-
-# Recursively list Android image directory.
-set -eu
-set -o pipefail
-
-function die() { format=$1; shift; printf "$format\n" "$@"; exit 1; }
-
-# Figure out the filer utility.
-declare filer=
-[[ -z "${ANDROID_HOST_OUT:-}" ]] || filer=${ANDROID_HOST_OUT}/bin/debugfs_static
-if [[ "${1:-}" =~ --debugfs_path=(.*) ]]; then
-  filer=${BASH_REMATCH[1]}
-  shift
-fi
-if [[ -z "${filer:-}" ]]; then
-  maybefiler="$(dirname $0)/debugfs_static"
-  [[ ! -x "$maybefiler" ]] || filer="$maybefiler"
-fi
-
-(( $# >0 )) || die "%s [--debugfs_path=<path>] IMAGE" "$0"
-
-[[ -n "${filer:-}" ]] || die "cannot locate 'debugfs' executable: \
---debugfs_path= is missing, ANDROID_HOST_OUT is not set, \
-and 'debugfs_static' is not colocated with this script"
-declare -r image="$1"
-
-function dolevel() {
-  printf "%s/\n" "$1"
-  # Each line of the file output consists of 6 fields separated with '/'.
-  # The second one contains the file's attributes, and the fifth its name.
-  $filer -R "ls -l -p $1" "$image" 2>/dev/null |\
-    sed -nr 's|^/.*/(.*)/.*/.*/(.+)/.*/$|\2 \1|p' | LANG=C sort | \
-  while read name attr; do
-    [[ "$name" != '.' && "$name" != '..' ]] || continue
-    path="$1/$name"
-    # If the second char of the attributes is '4', it is a directory.
-    if [[ $attr =~ ^.4 ]]; then
-      dolevel "$path"
-    else
-      printf "%s\n" "$path"
-    fi
-  done
-}
-
-# The filer always prints its version on stderr, so we are going
-# to redirect it to the bit bucket. On the other hand, the filer's
-# return code on error is still 0. Let's run it once to without
-# redirecting stderr to see that there is at least one entry.
-$filer -R "ls -l -p" "$image" | grep -q -m1 -P '^/.*/.*/.*/.*/.+/.*/$'
-dolevel .
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py
index 175451e..96dc5a6 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -132,7 +132,7 @@
 
     #pylint: disable=line-too-long
     errmsg = ''.join([
-        'mismatch in the <uses-library> tags between the build system and the '
+        'mismatch or misordering in the <uses-library> tags between the build system and the '
         'manifest:\n',
         '\t- required libraries in build system: %s[%s]%s\n' % (C_RED, ', '.join(required), C_OFF),
         '\t                 vs. in the manifest: %s[%s]%s\n' % (C_RED, ', '.join(manifest_required), C_OFF),
diff --git a/scripts/merge_directories.py b/scripts/merge_directories.py
deleted file mode 100755
index 3f8631b..0000000
--- a/scripts/merge_directories.py
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/usr/bin/env python3
-import argparse
-import os
-import shutil
-import sys
-
-def main():
-    parser = argparse.ArgumentParser(
-        description="Given a list of directories, this script will copy the contents of all of "
-        "them into the first directory, erroring out if any duplicate files are found."
-    )
-    parser.add_argument(
-        "--ignore-duplicates",
-        action="store_true",
-        help="Don't error out on duplicate files, just skip them. The file from the earliest "
-        "directory listed on the command line will be the winner."
-    )
-    parser.add_argument(
-        "--file-list",
-        help="Path to a text file containing paths relative to in_dir. Only these paths will be "
-        "copied out of in_dir."
-    )
-    parser.add_argument("out_dir")
-    parser.add_argument("in_dir")
-    args = parser.parse_args()
-
-    if not os.path.isdir(args.out_dir):
-        sys.exit(f"error: {args.out_dir} must be a directory")
-    if not os.path.isdir(args.in_dir):
-        sys.exit(f"error: {args.in_dir} must be a directory")
-
-    file_list = None
-    if args.file_list:
-        with open(file_list_file, "r") as f:
-            file_list = f.read().strip().splitlines()
-
-    in_dir = args.in_dir
-    for root, dirs, files in os.walk(in_dir):
-        rel_root = os.path.relpath(root, in_dir)
-        dst_root = os.path.join(args.out_dir, rel_root)
-        made_parent_dirs = False
-        for f in files:
-            src = os.path.join(root, f)
-            dst = os.path.join(dst_root, f)
-            p = os.path.normpath(os.path.join(rel_root, f))
-            if file_list is not None and p not in file_list:
-                continue
-            if os.path.lexists(dst):
-                if args.ignore_duplicates:
-                    continue
-                sys.exit(f"error: {p} exists in both {args.out_dir} and {in_dir}")
-
-            if not made_parent_dirs:
-                os.makedirs(dst_root, exist_ok=True)
-                made_parent_dirs = True
-
-            shutil.copy2(src, dst, follow_symlinks=False)
-
-if __name__ == "__main__":
-    main()
diff --git a/scripts/microfactory.bash b/scripts/microfactory.bash
index ce4a0e4..49988fa 100644
--- a/scripts/microfactory.bash
+++ b/scripts/microfactory.bash
@@ -23,7 +23,14 @@
 # Ensure GOROOT is set to the in-tree version.
 case $(uname) in
     Linux)
-        export GOROOT="${TOP}/prebuilts/go/linux-x86/"
+        case $(uname -m) in
+            x86_64)
+                export GOROOT="${TOP}/prebuilts/go/linux-x86/"
+                ;;
+            aarch64)
+                export GOROOT="${TOP}/prebuilts/go/linux-arm64/"
+                ;;
+        esac
         ;;
     Darwin)
         export GOROOT="${TOP}/prebuilts/go/darwin-x86/"
diff --git a/scripts/run-soong-tests-with-go-tools.sh b/scripts/run-soong-tests-with-go-tools.sh
index 1fbb1fc..82efaa0 100755
--- a/scripts/run-soong-tests-with-go-tools.sh
+++ b/scripts/run-soong-tests-with-go-tools.sh
@@ -38,6 +38,11 @@
     CLANG_VERSION=$(build/soong/scripts/get_clang_version.py)
     export CC="${TOP}/prebuilts/clang/host/${OS}-x86/${CLANG_VERSION}/bin/clang"
     export CXX="${TOP}/prebuilts/clang/host/${OS}-x86/${CLANG_VERSION}/bin/clang++"
+    glibc_dir="${TOP}/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8"
+    export CGO_CFLAGS="--sysroot ${glibc_dir}/sysroot/"
+    export CGO_CPPFLAGS="--sysroot ${glibc_dir}/sysroot/"
+    export CGO_CXXFLAGS="--sysroot ${glibc_dir}/sysroot/"
+    export CGO_LDFLAGS="--sysroot ${glibc_dir}/sysroot/ -B ${glibc_dir}/lib/gcc/x86_64-linux/4.8.3 -L ${glibc_dir}/lib/gcc/x86_64-linux/4.8.3 -L ${glibc_dir}/x86_64-linux/lib64"
 fi
 
 # androidmk_test.go gets confused if ANDROID_BUILD_TOP is set.
diff --git a/scripts/rustc_linker.py b/scripts/rustc_linker.py
new file mode 100755
index 0000000..3f60708
--- /dev/null
+++ b/scripts/rustc_linker.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+#
+# 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.
+#
+
+"""
+This script is used as a replacement for the Rust linker to allow fine-grained
+control over the what gets emitted to the linker.
+"""
+
+import os
+import shutil
+import subprocess
+import sys
+import argparse
+
+replacementVersionScript = None
+
+argparser = argparse.ArgumentParser()
+argparser.add_argument('--android-clang-bin', required=True)
+args = argparser.parse_known_args()
+clang_args = [args[0].android_clang_bin] + args[1]
+
+for i, arg in enumerate(clang_args):
+   if arg.startswith('-Wl,--android-version-script='):
+      replacementVersionScript = arg.split("=")[1]
+      del clang_args[i]
+      break
+
+if replacementVersionScript:
+   versionScriptFound = False
+   for i, arg in enumerate(clang_args):
+      if arg.startswith('-Wl,--version-script='):
+         clang_args[i] ='-Wl,--version-script=' + replacementVersionScript
+         versionScriptFound = True
+         break
+
+   if not versionScriptFound:
+       # If rustc did not emit a version script, just append the arg
+       clang_args.append('-Wl,--version-script=' + replacementVersionScript)
+try:
+   subprocess.run(clang_args, encoding='utf-8', check=True)
+except subprocess.CalledProcessError as e:
+   sys.exit(-1)
+
diff --git a/scripts/strip.sh b/scripts/strip.sh
index 8d69f0d..5320ef6 100755
--- a/scripts/strip.sh
+++ b/scripts/strip.sh
@@ -101,7 +101,12 @@
 do_strip_keep_mini_debug_info_linux() {
     rm -f "${outfile}.mini_debuginfo.xz"
     local fail=
-    "${CLANG_BIN}/llvm-strip" --strip-all --keep-section=.ARM.attributes --remove-section=.comment "${infile}" -o "${outfile}.tmp" || fail=true
+    if [ -z "${windows}" ]; then
+        "${CLANG_BIN}/llvm-strip" --strip-all --keep-section=.ARM.attributes --remove-section=.comment "${infile}" -o "${outfile}.tmp" || fail=true
+    else
+        # --keep-section not supported for Windows COFF.
+        fail=true
+    fi
 
     if [ -z $fail ]; then
         # create_minidebuginfo has issues with compressed debug sections. Just
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 34e11f0..80ced00 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -66,6 +66,7 @@
 				exported_bootclasspath_fragments: [
 					"%s",
 				],
+				prefer: false,
 			}
 		`, apex, apexFile, fragment)),
 		android.FixtureAddFile(filepath.Join(dir, apexFile), nil),
@@ -73,6 +74,7 @@
 }
 
 func TestSnapshotWithBootclasspathFragment_ImageName(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		java.PrepareForTestWithDexpreopt,
@@ -225,8 +227,8 @@
 			checkBootJarsPackageCheckRule(t, result,
 				append(
 					[]string{
-						"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/prebuilts/apex/com.android.art/android_common_prebuilt_com.android.art/deapexer/javalib/core1.jar",
+						"out/soong/.intermediates/prebuilts/apex/com.android.art/android_common_prebuilt_com.android.art/deapexer/javalib/core2.jar",
 						"out/soong/.intermediates/default/java/framework/android_common/aligned/framework.jar",
 					},
 					java.ApexBootJarDexJarPaths...,
@@ -270,7 +272,7 @@
 // package check rule.
 func checkBootJarsPackageCheckRule(t *testing.T, result *android.TestResult, expectedModules ...string) {
 	t.Helper()
-	platformBcp := result.ModuleForTests("platform-bootclasspath", "android_common")
+	platformBcp := result.ModuleForTests(t, "platform-bootclasspath", "android_common")
 	bootJarsCheckRule := platformBcp.Rule("boot_jars_package_check")
 	command := bootJarsCheckRule.RuleParams.Command
 	expectedCommandArgs := " build/soong/scripts/check_boot_jars/package_allowed_list.txt " + strings.Join(expectedModules, " ") + " &&"
@@ -479,7 +481,7 @@
 		checkAllCopyRules(copyRules),
 		snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
 		snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
-			module := result.ModuleForTests("platform-bootclasspath", "android_common")
+			module := result.ModuleForTests(t, "platform-bootclasspath", "android_common")
 			var rule android.TestingBuildParams
 			rule = module.Output("out/soong/hiddenapi/hiddenapi-flags.csv")
 			java.CheckHiddenAPIRuleInputs(t, "monolithic flags", `
@@ -505,7 +507,7 @@
 		}),
 		snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
 		snapshotTestChecker(checkSnapshotWithSourcePreferred, func(t *testing.T, result *android.TestResult) {
-			module := result.ModuleForTests("platform-bootclasspath", "android_common")
+			module := result.ModuleForTests(t, "platform-bootclasspath", "android_common")
 			rule := module.Output("out/soong/hiddenapi/hiddenapi-flags.csv.valid")
 			android.AssertStringDoesContain(t, "verify-overlaps", rule.RuleParams.Command, " out/soong/.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-flags.csv:out/soong/.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv ")
 		}),
@@ -514,7 +516,9 @@
 }
 
 func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) {
+	t.Parallel()
 	t.Run("added-directly", func(t *testing.T) {
+		t.Parallel()
 		testSnapshotWithBootClasspathFragment_Contents(t, `
 			sdk {
 				name: "mysdk",
@@ -566,6 +570,7 @@
 .intermediates/mycoreplatform.stubs.source/android_common/exportable/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
 `
 	t.Run("added-via-apex", func(t *testing.T) {
+		t.Parallel()
 		testSnapshotWithBootClasspathFragment_Contents(t, `
 			sdk {
 				name: "mysdk",
@@ -575,6 +580,7 @@
 	})
 
 	t.Run("added-directly-and-indirectly", func(t *testing.T) {
+		t.Parallel()
 		testSnapshotWithBootClasspathFragment_Contents(t, `
 			sdk {
 				name: "mysdk",
@@ -599,6 +605,7 @@
 // TestSnapshotWithBootClasspathFragment_Fragments makes sure that the fragments property of a
 // bootclasspath_fragment is correctly output to the sdk snapshot.
 func TestSnapshotWithBootClasspathFragment_Fragments(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		java.PrepareForTestWithJavaDefaultModules,
@@ -734,9 +741,11 @@
 
 // Test that bootclasspath_fragment works with sdk.
 func TestBasicSdkWithBootclasspathFragment(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForSdkTestWithApex,
 		prepareForSdkTestWithJava,
+		java.PrepareForTestWithJavaDefaultModules,
 		android.FixtureMergeMockFs(android.MockFS{
 			"java/mybootlib.jar":                nil,
 			"hiddenapi/annotation-flags.csv":    nil,
@@ -752,6 +761,13 @@
 			bootclasspath_fragments: ["mybootclasspathfragment"],
 		}
 
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			min_sdk_version: "1",
+			bootclasspath_fragments: ["mybootclasspathfragment"],
+		}
+
 		bootclasspath_fragment {
 			name: "mybootclasspathfragment",
 			image_name: "art",
@@ -794,7 +810,7 @@
 		java_import {
 			name: "mybootlib",
 			visibility: ["//visibility:public"],
-			apex_available: ["com.android.art"],
+			apex_available: ["myapex"],
 			jars: ["java/mybootlib.jar"],
 		}
 	`),
@@ -802,6 +818,7 @@
 }
 
 func TestSnapshotWithBootclasspathFragment_HiddenAPI(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		java.PrepareForTestWithJavaDefaultModules,
@@ -1116,7 +1133,7 @@
 		`),
 	).RunTest(t)
 
-	bcpf := result.ModuleForTests("mybootclasspathfragment", "android_common")
+	bcpf := result.ModuleForTests(t, "mybootclasspathfragment", "android_common")
 	rule := bcpf.Output("out/soong/.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi" + suffix + "/stub-flags.csv")
 	android.AssertPathsRelativeToTopEquals(t, "stub flags inputs", android.SortedUniqueStrings(expectedStubFlagsInputs), android.SortedUniquePaths(rule.Implicits))
 
@@ -1127,7 +1144,9 @@
 }
 
 func TestSnapshotWithBootClasspathFragment_MinSdkVersion(t *testing.T) {
+	t.Parallel()
 	t.Run("target S build", func(t *testing.T) {
+		t.Parallel()
 		expectedSnapshot := `
 // This is auto-generated. DO NOT EDIT.
 
@@ -1184,6 +1203,7 @@
 	})
 
 	t.Run("target-Tiramisu-build", func(t *testing.T) {
+		t.Parallel()
 		expectedSnapshot := `
 // This is auto-generated. DO NOT EDIT.
 
@@ -1268,6 +1288,7 @@
 }
 
 func TestSnapshotWithEmptyBootClasspathFragment(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		java.PrepareForTestWithJavaDefaultModules,
diff --git a/sdk/bp_test.go b/sdk/bp_test.go
index c620ac2..d3eaafe 100644
--- a/sdk/bp_test.go
+++ b/sdk/bp_test.go
@@ -73,6 +73,7 @@
 }
 
 func TestAddPropertySimple(t *testing.T) {
+	t.Parallel()
 	set := newPropertySet()
 	for name, val := range map[string]interface{}{
 		"x":   "taxi",
@@ -91,14 +92,17 @@
 }
 
 func TestAddPropertySubset(t *testing.T) {
+	t.Parallel()
 	getFixtureMap := map[string]func() interface{}{
 		"property set":    propertySetFixture,
 		"property struct": propertyStructFixture,
 	}
 
 	t.Run("add new subset", func(t *testing.T) {
+		t.Parallel()
 		for name, getFixture := range getFixtureMap {
 			t.Run(name, func(t *testing.T) {
+				t.Parallel()
 				set := propertySetFixture().(*bpPropertySet)
 				set.AddProperty("new", getFixture())
 				checkPropertySetFixture(t, set, true)
@@ -108,8 +112,10 @@
 	})
 
 	t.Run("merge existing subset", func(t *testing.T) {
+		t.Parallel()
 		for name, getFixture := range getFixtureMap {
 			t.Run(name, func(t *testing.T) {
+				t.Parallel()
 				set := newPropertySet()
 				subset := set.AddPropertySet("sub")
 				subset.AddProperty("flag", false)
@@ -123,12 +129,14 @@
 	})
 
 	t.Run("add conflicting subset", func(t *testing.T) {
+		t.Parallel()
 		set := propertySetFixture().(*bpPropertySet)
 		android.AssertPanicMessageContains(t, "adding x again should panic", `Property "x" already exists in property set`,
 			func() { set.AddProperty("x", propertySetFixture()) })
 	})
 
 	t.Run("add non-pointer struct", func(t *testing.T) {
+		t.Parallel()
 		set := propertySetFixture().(*bpPropertySet)
 		str := propertyStructFixture().(*propertyStruct)
 		android.AssertPanicMessageContains(t, "adding a non-pointer struct should panic", "Value is a struct, not a pointer to one:",
@@ -137,6 +145,7 @@
 }
 
 func TestAddPropertySetNew(t *testing.T) {
+	t.Parallel()
 	set := newPropertySet()
 	subset := set.AddPropertySet("sub")
 	subset.AddProperty("new", "d^^b")
@@ -144,6 +153,7 @@
 }
 
 func TestAddPropertySetExisting(t *testing.T) {
+	t.Parallel()
 	set := propertySetFixture().(*bpPropertySet)
 	subset := set.AddPropertySet("sub")
 	subset.AddProperty("new", "d^^b")
@@ -176,6 +186,7 @@
 }
 
 func TestTransformRemoveProperty(t *testing.T) {
+	t.Parallel()
 	set := newPropertySet()
 	set.AddProperty("name", "name")
 	set.AddProperty("fred", "12")
@@ -188,6 +199,7 @@
 }
 
 func TestTransformRemovePropertySet(t *testing.T) {
+	t.Parallel()
 	set := newPropertySet()
 	set.AddProperty("name", "name")
 	set.AddPropertySet("fred")
diff --git a/sdk/build_release.go b/sdk/build_release.go
index 6bb05a3..3656c98 100644
--- a/sdk/build_release.go
+++ b/sdk/build_release.go
@@ -101,6 +101,8 @@
 	buildReleaseS = initBuildRelease("S")
 	buildReleaseT = initBuildRelease("Tiramisu")
 	buildReleaseU = initBuildRelease("UpsideDownCake")
+	buildReleaseV = initBuildRelease("VanillaIceCream")
+	buildReleaseB = initBuildRelease("Baklava")
 
 	// Add the current build release which is always treated as being more recent than any other
 	// build release, including those added in tests.
diff --git a/sdk/build_release_test.go b/sdk/build_release_test.go
index 5bf57b5..07f99ee 100644
--- a/sdk/build_release_test.go
+++ b/sdk/build_release_test.go
@@ -42,7 +42,7 @@
 		android.AssertDeepEquals(t, "release", (*buildRelease)(nil), release)
 		// Uses a wildcard in the error message to allow for additional build releases to be added to
 		// the supported set without breaking this test.
-		android.FailIfNoMatchingErrors(t, `unknown release "A", expected one of \[S,Tiramisu,UpsideDownCake,F1,F2,current\]`, []error{err})
+		android.FailIfNoMatchingErrors(t, `unknown release "A", expected one of \[S,Tiramisu,UpsideDownCake,VanillaIceCream,Baklava,F1,F2,current\]`, []error{err})
 	})
 }
 
@@ -60,7 +60,7 @@
 	t.Run("closed range", func(t *testing.T) {
 		set, err := parseBuildReleaseSet("S-F1")
 		android.AssertDeepEquals(t, "errors", nil, err)
-		android.AssertStringEquals(t, "set", "[S,Tiramisu,UpsideDownCake,F1]", set.String())
+		android.AssertStringEquals(t, "set", "[S,Tiramisu,UpsideDownCake,VanillaIceCream,Baklava,F1]", set.String())
 	})
 	invalidAReleaseMessage := `unknown release "A", expected one of ` + allBuildReleaseSet.String()
 	t.Run("invalid release", func(t *testing.T) {
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 25839b8..bf4ac13 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -58,6 +58,7 @@
 // Contains tests for SDK members provided by the cc package.
 
 func TestSingleDeviceOsAssumption(t *testing.T) {
+	t.Parallel()
 	// Mock a module with DeviceSupported() == true.
 	s := &sdk{}
 	android.InitAndroidArchModule(s, android.DeviceSupported, android.MultilibCommon)
@@ -72,6 +73,7 @@
 }
 
 func TestSdkIsCompileMultilibBoth(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -102,6 +104,7 @@
 }
 
 func TestSdkCompileMultilibOverride(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -161,6 +164,7 @@
 
 // Make sure the sdk can use host specific cc libraries static/shared and both.
 func TestHostSdkWithCc(t *testing.T) {
+	t.Parallel()
 	testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -184,6 +188,7 @@
 
 // Make sure the sdk can use cc libraries static/shared and both.
 func TestSdkWithCc(t *testing.T) {
+	t.Parallel()
 	testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -214,6 +219,7 @@
 }
 
 func TestSnapshotWithObject(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -268,6 +274,7 @@
 }
 
 func TestSnapshotWithCcDuplicateHeaders(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -305,6 +312,7 @@
 }
 
 func TestSnapshotWithCcExportGeneratedHeaders(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -393,6 +401,7 @@
 // handling is tested with the sanitize clauses (but note there's a lot of
 // built-in logic in sanitize.go that can affect those flags).
 func TestSnapshotWithCcSharedLibraryCommonProperties(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -475,6 +484,7 @@
 }
 
 func TestSnapshotWithCcBinary(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		module_exports {
 			name: "mymodule_exports",
@@ -523,6 +533,7 @@
 }
 
 func TestMultipleHostOsTypesSnapshotWithCcBinary(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		module_exports {
 			name: "myexports",
@@ -604,6 +615,7 @@
 }
 
 func TestSnapshotWithSingleHostOsType(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTest,
 		ccTestFs.AddToFixture(),
@@ -721,6 +733,7 @@
 // Test that we support the necessary flags for the linker binary, which is
 // special in several ways.
 func TestSnapshotWithCcStaticNocrtBinary(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		module_exports {
 			name: "mymodule_exports",
@@ -785,19 +798,26 @@
 }
 
 func TestSnapshotWithCcSharedLibrary(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
 			native_shared_libs: ["mynativelib"],
 		}
 
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			min_sdk_version: "1",
+		}
+
 		cc_library_shared {
 			name: "mynativelib",
 			srcs: [
 				"Test.cpp",
 				"aidl/foo/bar/Test.aidl",
 			],
-			apex_available: ["apex1", "apex2"],
+			apex_available: ["myapex"],
 			export_include_dirs: ["myinclude"],
 			aidl: {
 				export_aidl_headers: true,
@@ -807,6 +827,18 @@
 	`)
 
 	CheckSnapshot(t, result, "mysdk", "",
+		snapshotTestPreparer(checkSnapshotWithoutSource,
+			android.FixtureMergeMockFs(android.MockFS{
+				"myapex/Android.bp": []byte(`
+				apex {
+					name: "myapex",
+					key: "myapex.key",
+					min_sdk_version: "1",
+				}
+				`),
+				"myapex/apex_manifest.json": nil,
+			}),
+		),
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -819,10 +851,7 @@
     name: "mynativelib",
     prefer: false,
     visibility: ["//visibility:public"],
-    apex_available: [
-        "apex1",
-        "apex2",
-    ],
+    apex_available: ["myapex"],
     stl: "none",
     compile_multilib: "both",
     export_include_dirs: ["include/myinclude"],
@@ -856,6 +885,7 @@
 }
 
 func TestSnapshotWithCcSharedLibrarySharedLibs(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -1005,6 +1035,7 @@
 }
 
 func TestHostSnapshotWithCcSharedLibrary(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -1085,6 +1116,7 @@
 }
 
 func TestMultipleHostOsTypesSnapshotWithCcSharedLibrary(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -1168,6 +1200,7 @@
 }
 
 func TestSnapshotWithCcStaticLibrary(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		module_exports {
 			name: "myexports",
@@ -1232,6 +1265,7 @@
 }
 
 func TestHostSnapshotWithCcStaticLibrary(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		module_exports {
 			name: "myexports",
@@ -1307,6 +1341,7 @@
 }
 
 func TestSnapshotWithCcLibrary(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		module_exports {
 			name: "myexports",
@@ -1370,12 +1405,11 @@
 .intermediates/mynativelib/android_arm_armv7-a-neon_static/mynativelib.a -> arm/lib/mynativelib.a
 .intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
 `),
-		// TODO(b/183315522): Remove this and fix the issue.
-		snapshotTestErrorHandler(checkSnapshotPreferredWithSource, android.FixtureExpectsAtLeastOneErrorMatchingPattern(`\Qunrecognized property "arch.arm.shared.export_include_dirs"\E`)),
 	)
 }
 
 func TestSnapshotSameLibraryWithNativeLibsAndNativeSharedLib(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		module_exports {
 			host_supported: true,
@@ -1477,6 +1511,7 @@
 }
 
 func TestSnapshotSameLibraryWithAndroidNativeLibsAndHostNativeSharedLib(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		module_exports {
 			host_supported: true,
@@ -1578,6 +1613,7 @@
 }
 
 func TestSnapshotSameLibraryWithNativeStaticLibsAndNativeSharedLib(t *testing.T) {
+	t.Parallel()
 	testSdkError(t, "Incompatible member types", `
 		module_exports {
 			host_supported: true,
@@ -1609,6 +1645,7 @@
 }
 
 func TestHostSnapshotWithMultiLib64(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		module_exports {
 			name: "myexports",
@@ -1682,6 +1719,7 @@
 }
 
 func TestSnapshotWithCcHeadersLibrary(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -1721,6 +1759,7 @@
 }
 
 func TestSnapshotWithCcHeadersLibraryAndNativeBridgeSupport(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		cc.PrepareForTestWithCcDefaultModules,
 		PrepareForTestWithSdkBuildComponents,
@@ -1778,6 +1817,7 @@
 // module that has different output files for a native bridge target requests the native bridge
 // variants are copied into the sdk snapshot that it reports an error.
 func TestSnapshotWithCcHeadersLibrary_DetectsNativeBridgeSpecificProperties(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		cc.PrepareForTestWithCcDefaultModules,
 		PrepareForTestWithSdkBuildComponents,
@@ -1814,6 +1854,7 @@
 }
 
 func TestSnapshotWithCcHeadersLibraryAndImageVariants(t *testing.T) {
+	t.Parallel()
 	testImageVariant := func(t *testing.T, property, trait string) {
 		result := android.GroupFixturePreparers(
 			cc.PrepareForTestWithCcDefaultModules,
@@ -1877,6 +1918,7 @@
 }
 
 func TestHostSnapshotWithCcHeadersLibrary(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -1933,6 +1975,7 @@
 }
 
 func TestDeviceAndHostSnapshotWithCcHeadersLibrary(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -2002,6 +2045,7 @@
 }
 
 func TestSystemSharedLibPropagation(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -2162,6 +2206,7 @@
 }
 
 func TestStubsLibrary(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -2223,6 +2268,7 @@
 }
 
 func TestDeviceAndHostSnapshotWithStubsLibrary(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -2299,6 +2345,7 @@
 }
 
 func TestUniqueHostSoname(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -2364,6 +2411,7 @@
 }
 
 func TestNoSanitizerMembers(t *testing.T) {
+	t.Parallel()
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
diff --git a/sdk/compat_config_sdk_test.go b/sdk/compat_config_sdk_test.go
index 75b5229..1737b3a 100644
--- a/sdk/compat_config_sdk_test.go
+++ b/sdk/compat_config_sdk_test.go
@@ -76,6 +76,7 @@
 }
 
 func TestSnapshotWithCompatConfig(t *testing.T) {
+	t.Parallel()
 	testSnapshotWithCompatConfig(t, `
 		sdk {
 			name: "mysdk",
@@ -85,6 +86,7 @@
 }
 
 func TestSnapshotWithCompatConfig_Apex(t *testing.T) {
+	t.Parallel()
 	testSnapshotWithCompatConfig(t, `
 		apex {
 			name: "myapex",
diff --git a/sdk/exports_test.go b/sdk/exports_test.go
index 9d0a242..5a7ce84 100644
--- a/sdk/exports_test.go
+++ b/sdk/exports_test.go
@@ -20,6 +20,7 @@
 
 // Ensure that module_exports generates a module_exports_snapshot module.
 func TestModuleExportsSnapshot(t *testing.T) {
+	t.Parallel()
 	packageBp := `
 		module_exports {
 			name: "myexports",
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 4db163c..1e545ce 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -51,6 +51,7 @@
 // Contains tests for SDK members provided by the java package.
 
 func TestSdkDependsOnSourceEvenWhenPrebuiltPreferred(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
@@ -77,6 +78,7 @@
 }
 
 func TestSnapshotWithJavaHeaderLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		android.FixtureAddFile("aidl/foo/bar/Test.aidl", nil),
@@ -126,6 +128,7 @@
 }
 
 func TestHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		android.FixtureAddFile("aidl/foo/bar/Test.aidl", nil),
@@ -178,6 +181,7 @@
 }
 
 func TestDeviceAndHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
@@ -228,6 +232,7 @@
 }
 
 func TestSnapshotWithJavaImplLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		android.FixtureAddFile("aidl/foo/bar/Test.aidl", nil),
@@ -277,6 +282,7 @@
 }
 
 func TestSnapshotWithJavaBootLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		android.FixtureAddFile("aidl", nil),
@@ -328,6 +334,7 @@
 }
 
 func TestSnapshotWithJavaBootLibrary_UpdatableMedia(t *testing.T) {
+	t.Parallel()
 	runTest := func(t *testing.T, targetBuildRelease, expectedJarPath, expectedCopyRule string) {
 		result := android.GroupFixturePreparers(
 			prepareForSdkTestWithJava,
@@ -385,6 +392,7 @@
 }
 
 func TestSnapshotWithJavaLibrary_MinSdkVersion(t *testing.T) {
+	t.Parallel()
 	runTest := func(t *testing.T, targetBuildRelease, minSdkVersion, expectedMinSdkVersion string) {
 		result := android.GroupFixturePreparers(
 			prepareForSdkTestWithJava,
@@ -457,6 +465,7 @@
 }
 
 func TestSnapshotWithJavaSystemserverLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		android.FixtureAddFile("aidl", nil),
@@ -509,6 +518,7 @@
 }
 
 func TestHostSnapshotWithJavaImplLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		android.FixtureAddFile("aidl/foo/bar/Test.aidl", nil),
@@ -561,6 +571,7 @@
 }
 
 func TestSnapshotWithJavaTest(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
 		module_exports {
 			name: "myexports",
@@ -603,6 +614,7 @@
 }
 
 func TestHostSnapshotWithJavaTest(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
 		module_exports {
 			name: "myexports",
@@ -650,6 +662,7 @@
 }
 
 func TestSnapshotWithJavaSystemModules(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		java.PrepareForTestWithJavaDefaultModules,
@@ -853,6 +866,7 @@
 }
 
 func TestHostSnapshotWithJavaSystemModules(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
@@ -911,6 +925,7 @@
 }
 
 func TestDeviceAndHostSnapshotWithOsSpecificMembers(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
 		module_exports {
 			name: "myexports",
@@ -1004,6 +1019,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
@@ -1081,6 +1097,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_DistStem(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
@@ -1136,6 +1153,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_UseSrcJar(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJavaSdkLibrary,
 		android.FixtureMergeEnv(map[string]string{
@@ -1192,6 +1210,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_AnnotationsZip(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
@@ -1246,6 +1265,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_AnnotationsZip_PreT(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJavaSdkLibrary,
 		android.FixtureMergeEnv(map[string]string{
@@ -1303,6 +1323,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_CompileDex(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJavaSdkLibrary,
 		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
@@ -1385,6 +1406,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_SdkVersion_None(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
@@ -1435,6 +1457,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_SdkVersion_ForScope(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
@@ -1488,6 +1511,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_ApiScopes(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
@@ -1555,6 +1579,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_ModuleLib(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
@@ -1636,6 +1661,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_SystemServer(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
@@ -1703,6 +1729,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_DoctagFiles(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJavaSdkLibrary,
 		android.FixtureAddFile("docs/known_doctags", nil),
diff --git a/sdk/license_sdk_test.go b/sdk/license_sdk_test.go
index 754f019..eb8112b 100644
--- a/sdk/license_sdk_test.go
+++ b/sdk/license_sdk_test.go
@@ -21,6 +21,7 @@
 )
 
 func TestSnapshotWithPackageDefaultLicense(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		android.PrepareForTestWithLicenses,
diff --git a/sdk/member_trait_test.go b/sdk/member_trait_test.go
index 673d6fb..9b41e9b 100644
--- a/sdk/member_trait_test.go
+++ b/sdk/member_trait_test.go
@@ -116,6 +116,7 @@
 }
 
 func TestBasicTrait_WithoutTrait(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		android.FixtureWithRootAndroidBp(`
@@ -154,6 +155,7 @@
 }
 
 func TestBasicTrait_MultipleTraits(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		android.FixtureWithRootAndroidBp(`
@@ -262,6 +264,7 @@
 }
 
 func TestTraitUnsupportedByMemberType(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		android.FixtureWithRootAndroidBp(`
diff --git a/sdk/sdk.go b/sdk/sdk.go
index aa82abb..ab50659 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -178,22 +178,24 @@
 		s.buildSnapshot(ctx, sdkVariants)
 	}
 
+	if s.snapshotFile.Valid() != s.infoFile.Valid() {
+		panic(fmt.Sprintf("Snapshot (%q) and info file (%q) should both be set or neither should be set.", s.snapshotFile, s.infoFile))
+	}
+
 	if s.snapshotFile.Valid() {
 		ctx.SetOutputFiles([]android.Path{s.snapshotFile.Path()}, "")
+		ctx.SetOutputFiles([]android.Path{s.snapshotFile.Path(), s.infoFile.Path()}, android.DefaultDistTag)
 	}
 }
 
 func (s *sdk) AndroidMkEntries() []android.AndroidMkEntries {
-	if !s.snapshotFile.Valid() != !s.infoFile.Valid() {
-		panic("Snapshot (%q) and info file (%q) should both be set or neither should be set.")
-	} else if !s.snapshotFile.Valid() {
+	if !s.snapshotFile.Valid() {
 		return []android.AndroidMkEntries{}
 	}
 
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "FAKE",
 		OutputFile: s.snapshotFile,
-		DistFiles:  android.MakeDefaultDistFiles(s.snapshotFile.Path(), s.infoFile.Path()),
 		Include:    "$(BUILD_PHONY_PACKAGE)",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index 2532a25..985641e 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -40,6 +40,7 @@
 // Ensure that prebuilt modules have the same effective visibility as the source
 // modules.
 func TestSnapshotVisibility(t *testing.T) {
+	t.Parallel()
 	packageBp := `
 		package {
 			default_visibility: ["//other/foo"],
@@ -160,6 +161,7 @@
 }
 
 func TestSdkInstall(t *testing.T) {
+	t.Parallel()
 	sdk := `
 		sdk {
 			name: "mysdk",
@@ -326,6 +328,7 @@
 
 // Ensure that sdk snapshot related environment variables work correctly.
 func TestSnapshot_EnvConfiguration(t *testing.T) {
+	t.Parallel()
 	bp := `
 		sdk {
 			name: "mysdk",
@@ -347,11 +350,12 @@
 	)
 
 	checkZipFile := func(t *testing.T, result *android.TestResult, expected string) {
-		zipRule := result.ModuleForTests("mysdk", "common_os").Rule("SnapshotZipFiles")
+		zipRule := result.ModuleForTests(t, "mysdk", "common_os").Rule("SnapshotZipFiles")
 		android.AssertStringEquals(t, "snapshot zip file", expected, zipRule.Output.String())
 	}
 
 	t.Run("no env variables", func(t *testing.T) {
+		t.Parallel()
 		result := preparer.RunTest(t)
 
 		checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip")
@@ -377,6 +381,7 @@
 	})
 
 	t.Run("SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE=S", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			prepareForSdkTestWithJava,
 			java.PrepareForTestWithJavaDefaultModules,
@@ -468,6 +473,7 @@
 	})
 
 	t.Run("test replacing exportable module", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			prepareForSdkTestWithJava,
 			java.PrepareForTestWithJavaDefaultModules,
diff --git a/sdk/systemserverclasspath_fragment_sdk_test.go b/sdk/systemserverclasspath_fragment_sdk_test.go
index fd6c4e7..7ebdcd4 100644
--- a/sdk/systemserverclasspath_fragment_sdk_test.go
+++ b/sdk/systemserverclasspath_fragment_sdk_test.go
@@ -87,10 +87,23 @@
 
 	CheckSnapshot(t, result, "mysdk", "",
 		checkAndroidBpContents(expectedSdkSnapshot),
+		snapshotTestPreparer(checkSnapshotWithoutSource,
+			android.FixtureMergeMockFs(android.MockFS{
+				"myapex/Android.bp": []byte(`
+				apex {
+					name: "myapex",
+					key: "myapex.key",
+					min_sdk_version: "1",
+				}
+				`),
+				"myapex/apex_manifest.json": nil,
+			}),
+		),
 	)
 }
 
 func TestSnapshotWithPartialSystemServerClasspathFragment(t *testing.T) {
+	t.Parallel()
 	commonSdk := `
 		apex {
 			name: "myapex",
@@ -185,6 +198,7 @@
 }
 
 func TestSnapshotWithEmptySystemServerClasspathFragment(t *testing.T) {
+	t.Parallel()
 	commonSdk := `
 		apex {
 			name: "myapex",
@@ -231,6 +245,7 @@
 }
 
 func TestSnapshotWithSystemServerClasspathFragment(t *testing.T) {
+	t.Parallel()
 
 	commonSdk := `
 sdk {
@@ -298,6 +313,7 @@
 `
 
 	t.Run("target-s", func(t *testing.T) {
+		t.Parallel()
 		testSnapshotWithSystemServerClasspathFragment(t, commonSdk, "S", `
 // This is auto-generated. DO NOT EDIT.
 
@@ -319,6 +335,7 @@
 	})
 
 	t.Run("target-t", func(t *testing.T) {
+		t.Parallel()
 		testSnapshotWithSystemServerClasspathFragment(t, commonSdk, "Tiramisu", `
 // This is auto-generated. DO NOT EDIT.
 
@@ -361,6 +378,7 @@
 	})
 
 	t.Run("target-u", func(t *testing.T) {
+		t.Parallel()
 		testSnapshotWithSystemServerClasspathFragment(t, commonSdk, "UpsideDownCake", `
 // This is auto-generated. DO NOT EDIT.
 
@@ -409,10 +427,12 @@
 	})
 
 	t.Run("added-directly", func(t *testing.T) {
+		t.Parallel()
 		testSnapshotWithSystemServerClasspathFragment(t, commonSdk, `latest`, expectedLatestSnapshot)
 	})
 
 	t.Run("added-via-apex", func(t *testing.T) {
+		t.Parallel()
 		testSnapshotWithSystemServerClasspathFragment(t, `
 			sdk {
 				name: "mysdk",
diff --git a/sdk/testing.go b/sdk/testing.go
index f4e2b03..cd7bbf5 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -128,12 +128,11 @@
 // generated, etc.
 func getSdkSnapshotBuildInfo(t *testing.T, result *android.TestResult, sdk *sdk) *snapshotBuildInfo {
 	info := &snapshotBuildInfo{
-		t:                          t,
-		r:                          result,
-		androidBpContents:          sdk.GetAndroidBpContentsForTests(),
-		infoContents:               sdk.GetInfoContentsForTests(),
-		snapshotTestCustomizations: map[snapshotTest]*snapshotTestCustomization{},
-		targetBuildRelease:         sdk.builderForTests.targetBuildRelease,
+		t:                  t,
+		r:                  result,
+		androidBpContents:  sdk.GetAndroidBpContentsForTests(),
+		infoContents:       sdk.GetInfoContentsForTests(),
+		targetBuildRelease: sdk.builderForTests.targetBuildRelease,
 	}
 
 	buildParams := sdk.BuildParamsForTests()
@@ -144,7 +143,7 @@
 	seenBuildNumberFile := false
 	for _, bp := range buildParams {
 		switch bp.Rule.String() {
-		case android.Cp.String():
+		case android.Cp.String(), android.CpWithBash.String():
 			output := bp.Output
 			// Get destination relative to the snapshot root
 			dest := output.Rel()
@@ -282,7 +281,7 @@
 
 		// Run the snapshot with the snapshot preparer and the extra preparer, which must come after as
 		// it may need to modify parts of the MockFS populated by the snapshot preparer.
-		result := android.GroupFixturePreparers(snapshotPreparer, extraPreparer, customizedPreparers).
+		result := android.GroupFixturePreparers(snapshotPreparer, customizedPreparers, extraPreparer).
 			ExtendWithErrorHandler(customization.errorHandler).
 			RunTest(t)
 
@@ -293,6 +292,7 @@
 	}
 
 	t.Run("snapshot without source", func(t *testing.T) {
+		t.Parallel()
 		// Remove the source Android.bp file to make sure it works without.
 		removeSourceAndroidBp := android.FixtureModifyMockFS(func(fs android.MockFS) {
 			delete(fs, "Android.bp")
@@ -302,16 +302,23 @@
 	})
 
 	t.Run("snapshot with source preferred", func(t *testing.T) {
+		t.Parallel()
 		runSnapshotTestWithCheckers(t, checkSnapshotWithSourcePreferred, android.NullFixturePreparer)
 	})
 
 	t.Run("snapshot preferred with source", func(t *testing.T) {
+		t.Parallel()
 		// Replace the snapshot/Android.bp file with one where "prefer: false," has been replaced with
 		// "prefer: true,"
 		preferPrebuilts := android.FixtureModifyMockFS(func(fs android.MockFS) {
 			snapshotBpFile := filepath.Join(snapshotSubDir, "Android.bp")
 			unpreferred := string(fs[snapshotBpFile])
 			fs[snapshotBpFile] = []byte(strings.ReplaceAll(unpreferred, "prefer: false,", "prefer: true,"))
+
+			prebuiltApexBpFile := "prebuilts/apex/Android.bp"
+			if prebuiltApexBp, ok := fs[prebuiltApexBpFile]; ok {
+				fs[prebuiltApexBpFile] = []byte(strings.ReplaceAll(string(prebuiltApexBp), "prefer: false,", "prefer: true,"))
+			}
 		})
 
 		runSnapshotTestWithCheckers(t, checkSnapshotPreferredWithSource, preferPrebuilts)
@@ -469,19 +476,40 @@
 	targetBuildRelease *buildRelease
 
 	// The test specific customizations for each snapshot test.
-	snapshotTestCustomizations map[snapshotTest]*snapshotTestCustomization
+	snapshotTestCustomizations snapshotTestCustomizationSet
+}
+
+type snapshotTestCustomizationSet struct {
+	snapshotWithoutSource       *snapshotTestCustomization
+	snapshotWithSourcePreferred *snapshotTestCustomization
+	snapshotPreferredWithSource *snapshotTestCustomization
+}
+
+func (s *snapshotTestCustomizationSet) customization(snapshotTest snapshotTest) **snapshotTestCustomization {
+	var customization **snapshotTestCustomization
+	switch snapshotTest {
+	case checkSnapshotWithoutSource:
+
+		customization = &s.snapshotWithoutSource
+	case checkSnapshotWithSourcePreferred:
+		customization = &s.snapshotWithSourcePreferred
+	case checkSnapshotPreferredWithSource:
+		customization = &s.snapshotPreferredWithSource
+	default:
+		panic(fmt.Errorf("unsupported snapshotTest %v", snapshotTest))
+	}
+	return customization
 }
 
 // snapshotTestCustomization gets the test specific customization for the specified snapshotTest.
 //
 // If no customization was created previously then it creates a default customization.
 func (i *snapshotBuildInfo) snapshotTestCustomization(snapshotTest snapshotTest) *snapshotTestCustomization {
-	customization := i.snapshotTestCustomizations[snapshotTest]
-	if customization == nil {
-		customization = &snapshotTestCustomization{
+	customization := i.snapshotTestCustomizations.customization(snapshotTest)
+	if *customization == nil {
+		*customization = &snapshotTestCustomization{
 			errorHandler: android.FixtureExpectsNoErrors,
 		}
-		i.snapshotTestCustomizations[snapshotTest] = customization
 	}
-	return customization
+	return *customization
 }
diff --git a/sdk/update.go b/sdk/update.go
index 5a899a2..00352cb 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -1145,7 +1145,7 @@
 
 	// The licenses are the same for all variants.
 	mctx := s.ctx
-	licenseInfo, _ := android.OtherModuleProvider(mctx, variant, android.LicenseInfoProvider)
+	licenseInfo, _ := android.OtherModuleProvider(mctx, variant, android.LicensesInfoProvider)
 	if len(licenseInfo.Licenses) > 0 {
 		m.AddPropertyWithTag("licenses", licenseInfo.Licenses, s.OptionalSdkMemberReferencePropertyTag())
 	}
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 7f5a426..57f5ad1 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -41,6 +41,14 @@
 	registerShBuildComponents(android.InitRegistrationContext)
 }
 
+type ShBinaryInfo struct {
+	SubDir     string
+	OutputFile android.Path
+	Symlinks   []string
+}
+
+var ShBinaryInfoProvider = blueprint.NewProvider[ShBinaryInfo]()
+
 func registerShBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("sh_binary", ShBinaryFactory)
 	ctx.RegisterModuleType("sh_binary_host", ShBinaryHostFactory)
@@ -130,6 +138,11 @@
 	// host test.
 	Device_first_data []string `android:"path_device_first"`
 
+	// Same as data, but will add dependencies on modules using the host's os variation and
+	// the common arch variation. Useful for a device test that wants to depend on a host
+	// module, for example to include a custom Tradefed test runner.
+	Host_common_data []string `android:"path_host_common"`
+
 	// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
 	// with root permission.
 	Require_root *bool
@@ -314,7 +327,11 @@
 
 	s.properties.SubName = s.GetSubname(ctx)
 
-	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: []string{s.sourceFilePath.String()}})
+	android.SetProvider(ctx, ShBinaryInfoProvider, ShBinaryInfo{
+		SubDir:     s.SubDir(),
+		OutputFile: s.OutputFile(),
+		Symlinks:   s.Symlinks(),
+	})
 
 	ctx.SetOutputFiles(android.Paths{s.outputFilePath}, "")
 }
@@ -339,6 +356,8 @@
 	for _, symlink := range s.Symlinks() {
 		ctx.InstallSymlink(installDir, symlink, s.installedFile)
 	}
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Class = []string{"EXECUTABLES"}
 }
 
 func (s *ShBinary) AndroidMkEntries() []android.AndroidMkEntries {
@@ -424,8 +443,9 @@
 	expandedData := android.PathsForModuleSrc(ctx, s.testProperties.Data)
 	expandedData = append(expandedData, android.PathsForModuleSrc(ctx, s.testProperties.Device_common_data)...)
 	expandedData = append(expandedData, android.PathsForModuleSrc(ctx, s.testProperties.Device_first_data)...)
+	expandedData = append(expandedData, android.PathsForModuleSrc(ctx, s.testProperties.Host_common_data)...)
 	// Emulate the data property for java_data dependencies.
-	for _, javaData := range ctx.GetDirectDepsWithTag(shTestJavaDataTag) {
+	for _, javaData := range ctx.GetDirectDepsProxyWithTag(shTestJavaDataTag) {
 		expandedData = append(expandedData, android.OutputFilesForModule(ctx, javaData, "")...)
 	}
 	for _, d := range expandedData {
@@ -478,21 +498,24 @@
 
 	s.extraTestConfigs = android.PathsForModuleSrc(ctx, s.testProperties.Extra_test_configs)
 	s.dataModules = make(map[string]android.Path)
-	ctx.VisitDirectDeps(func(dep android.Module) {
+	ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
 		depTag := ctx.OtherModuleDependencyTag(dep)
 		switch depTag {
 		case shTestDataBinsTag, shTestDataDeviceBinsTag:
 			path := android.OutputFileForModule(ctx, dep, "")
 			s.addToDataModules(ctx, path.Base(), path)
 		case shTestDataLibsTag, shTestDataDeviceLibsTag:
-			if cc, isCc := dep.(*cc.Module); isCc {
+			if _, isCc := android.OtherModuleProvider(ctx, dep, cc.CcInfoProvider); isCc {
 				// Copy to an intermediate output directory to append "lib[64]" to the path,
 				// so that it's compatible with the default rpath values.
 				var relPath string
-				if cc.Arch().ArchType.Multilib == "lib64" {
-					relPath = filepath.Join("lib64", cc.OutputFile().Path().Base())
+				linkableInfo := android.OtherModuleProviderOrDefault(ctx, dep, cc.LinkableInfoProvider)
+				commonInfo := android.OtherModulePointerProviderOrDefault(ctx, dep, android.CommonModuleInfoProvider)
+
+				if commonInfo.Target.Arch.ArchType.Multilib == "lib64" {
+					relPath = filepath.Join("lib64", linkableInfo.OutputFile.Path().Base())
 				} else {
-					relPath = filepath.Join("lib", cc.OutputFile().Path().Base())
+					relPath = filepath.Join("lib", linkableInfo.OutputFile.Path().Base())
 				}
 				if _, exist := s.dataModules[relPath]; exist {
 					return
@@ -500,7 +523,7 @@
 				relocatedLib := android.PathForModuleOut(ctx, "relocated").Join(ctx, relPath)
 				ctx.Build(pctx, android.BuildParams{
 					Rule:   android.Cp,
-					Input:  cc.OutputFile().Path(),
+					Input:  linkableInfo.OutputFile.Path(),
 					Output: relocatedLib,
 				})
 				s.addToDataModules(ctx, relPath, relocatedLib)
@@ -529,6 +552,32 @@
 		MkAppClass:           mkEntries.Class,
 		InstallDir:           s.installDir,
 	})
+
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Class = []string{"NATIVE_TESTS"}
+	if len(s.testProperties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, s.testProperties.Test_suites...)
+	} else {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
+	}
+	if proptools.Bool(s.testProperties.Test_options.Unit_test) {
+		moduleInfoJSON.IsUnitTest = "true"
+		if ctx.Host() {
+			moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "host-unit-tests")
+		}
+	}
+	moduleInfoJSON.DataDependencies = append(moduleInfoJSON.DataDependencies, s.testProperties.Data_bins...)
+	if s.testConfig != nil {
+		if _, ok := s.testConfig.(android.WritablePath); ok {
+			moduleInfoJSON.AutoTestConfig = []string{"true"}
+		}
+		moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, s.testConfig.String())
+	}
+	moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, s.extraTestConfigs.Strings()...)
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: s.testProperties.Test_suites,
+	})
 }
 
 func addArch(archType string, paths android.Paths) []string {
@@ -564,6 +613,8 @@
 					entries.AddStrings("LOCAL_EXTRA_FULL_TEST_CONFIGS", s.extraTestConfigs.Strings()...)
 				}
 
+				entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !proptools.BoolDefault(s.testProperties.Auto_gen_config, true))
+
 				s.testProperties.Test_options.SetAndroidMkEntries(entries)
 			},
 		},
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
index 28f997d..c2e2d2b 100644
--- a/sh/sh_binary_test.go
+++ b/sh/sh_binary_test.go
@@ -58,7 +58,7 @@
 		}
 	`)
 
-	mod := result.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
+	mod := result.ModuleForTests(t, "foo", "android_arm64_armv8-a").Module().(*ShTest)
 
 	entries := android.AndroidMkEntriesForTest(t, result.TestContext, mod)[0]
 
@@ -83,7 +83,7 @@
 		}
 	`)
 
-	mod := result.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
+	mod := result.ModuleForTests(t, "foo", "android_arm64_armv8-a").Module().(*ShTest)
 
 	entries := android.AndroidMkEntriesForTest(t, result.TestContext, mod)[0]
 
@@ -129,7 +129,7 @@
 	buildOS := config.BuildOS.String()
 	arches := []string{"android_arm64_armv8-a", buildOS + "_x86_64"}
 	for _, arch := range arches {
-		variant := ctx.ModuleForTests("foo", arch)
+		variant := ctx.ModuleForTests(t, "foo", arch)
 
 		libExt := ".so"
 		if arch == "darwin_x86_64" {
@@ -167,7 +167,7 @@
 	`)
 
 	buildOS := ctx.Config().BuildOS.String()
-	mod := ctx.ModuleForTests("foo", buildOS+"_x86_64").Module().(*ShTest)
+	mod := ctx.ModuleForTests(t, "foo", buildOS+"_x86_64").Module().(*ShTest)
 	if !mod.Host() {
 		t.Errorf("host bit is not set for a sh_test_host module.")
 	}
@@ -186,7 +186,7 @@
 		}
 	`)
 
-	mod := result.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
+	mod := result.ModuleForTests(t, "foo", "android_arm64_armv8-a").Module().(*ShTest)
 	entries := android.AndroidMkEntriesForTest(t, result, mod)[0]
 	actualData := entries.EntryMap["LOCAL_EXTRA_FULL_TEST_CONFIGS"]
 	android.AssertStringPathsRelativeToTopEquals(t, "extra_configs", result.Config(), []string{"config1.xml", "config2.xml"}, actualData)
@@ -221,7 +221,7 @@
 
 	buildOS := config.BuildOS.String()
 	variant := buildOS + "_x86_64"
-	foo := ctx.ModuleForTests("foo", variant)
+	foo := ctx.ModuleForTests(t, "foo", variant)
 
 	relocated := foo.Output(filepath.Join("out/soong/.intermediates/foo", variant, "relocated/lib64/libbar.so"))
 	expectedInput := "out/soong/.intermediates/libbar/android_arm64_armv8-a_shared/libbar.so"
@@ -266,7 +266,7 @@
 	`)
 
 	buildOS := config.BuildOS.String()
-	fooModule := ctx.ModuleForTests("foo", buildOS+"_x86_64")
+	fooModule := ctx.ModuleForTests(t, "foo", buildOS+"_x86_64")
 
 	expectedBinAutogenConfig := `<option name="push-file" key="bar" value="/data/local/tests/unrestricted/foo/bar" />`
 	autogen := fooModule.Rule("autogen")
@@ -296,7 +296,7 @@
 		}
 	`)
 	buildOS := ctx.Config().BuildOS.String()
-	mod := ctx.ModuleForTests("foo", buildOS+"_x86_64").Module().(*ShTest)
+	mod := ctx.ModuleForTests(t, "foo", buildOS+"_x86_64").Module().(*ShTest)
 	if !mod.Host() {
 		t.Errorf("host bit is not set for a sh_test_host module.")
 	}
@@ -345,7 +345,7 @@
 
 	`)
 	buildOS := ctx.Config().BuildOS.String()
-	mod := ctx.ModuleForTests("foo", buildOS+"_x86_64").Module().(*ShTest)
+	mod := ctx.ModuleForTests(t, "foo", buildOS+"_x86_64").Module().(*ShTest)
 	if !mod.Host() {
 		t.Errorf("host bit is not set for a sh_test_host module.")
 	}
@@ -365,7 +365,7 @@
 		":testdata/data1",
 		":testdata/sub/data2",
 	}
-	mod = ctx.ModuleForTests("sh-test", "android_arm64_armv8-a").Module().(*ShTest)
+	mod = ctx.ModuleForTests(t, "sh-test", "android_arm64_armv8-a").Module().(*ShTest)
 	entries = android.AndroidMkEntriesForTest(t, ctx, mod)[0]
 	actualData = entries.EntryMap["LOCAL_TEST_DATA"]
 	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_TEST_DATA", config, expectedData, actualData)
@@ -388,7 +388,7 @@
 		}
 	`)
 	buildOS := ctx.Config().BuildOS.String()
-	mod := ctx.ModuleForTests("the-host-binary", buildOS+"_x86_64").Module().(*ShBinary)
+	mod := ctx.ModuleForTests(t, "the-host-binary", buildOS+"_x86_64").Module().(*ShBinary)
 	if !mod.Host() {
 		t.Errorf("host bit is not set for a sh_binary_host module.")
 	}
@@ -396,6 +396,6 @@
 	expectedFilename := "test.sh"
 	android.AssertStringEquals(t, "Filename", expectedFilename, *mod.properties.Filename)
 
-	mod = ctx.ModuleForTests("the-binary", "android_arm64_armv8-a").Module().(*ShBinary)
+	mod = ctx.ModuleForTests(t, "the-binary", "android_arm64_armv8-a").Module().(*ShBinary)
 	android.AssertStringEquals(t, "Filename", expectedFilename, *mod.properties.Filename)
 }
diff --git a/snapshot/snapshot_base.go b/snapshot/snapshot_base.go
index 6bf3c87..510e9cf 100644
--- a/snapshot/snapshot_base.go
+++ b/snapshot/snapshot_base.go
@@ -45,14 +45,3 @@
 	LicenseKinds []string `json:",omitempty"`
 	LicenseTexts []string `json:",omitempty"`
 }
-
-func (prop *SnapshotJsonFlags) InitBaseSnapshotPropsWithName(m android.Module, name string) {
-	prop.ModuleName = name
-
-	prop.LicenseKinds = m.EffectiveLicenseKinds()
-	prop.LicenseTexts = m.EffectiveLicenseFiles().Strings()
-}
-
-func (prop *SnapshotJsonFlags) InitBaseSnapshotProps(m android.Module) {
-	prop.InitBaseSnapshotPropsWithName(m, m.Name())
-}
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 84f20c5..a398cbc 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -226,6 +226,9 @@
 	// Make this module available when building for ramdisk
 	Ramdisk_available *bool
 
+	// Make this module available when building for vendor ramdisk
+	Vendor_ramdisk_available *bool
+
 	// Make this module available when building for recovery
 	Recovery_available *bool
 
@@ -346,7 +349,6 @@
 			ctx.PropertyErrorf("srcs", "srcs contains non-sysprop file %q", syspropFile.String())
 		}
 	}
-	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()})
 
 	if ctx.Failed() {
 		return
@@ -460,9 +462,8 @@
 var _ android.ApexModule = (*syspropLibrary)(nil)
 
 // Implements android.ApexModule
-func (m *syspropLibrary) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
-	sdkVersion android.ApiLevel) error {
-	return fmt.Errorf("sysprop_library is not supposed to be part of apex modules")
+func (m *syspropLibrary) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
+	return android.MinApiLevel
 }
 
 // sysprop_library creates schematized APIs from sysprop description files (.sysprop).
@@ -502,17 +503,18 @@
 			Static_libs []string
 		}
 	}
-	Required           []string
-	Recovery           *bool
-	Recovery_available *bool
-	Vendor_available   *bool
-	Product_available  *bool
-	Ramdisk_available  *bool
-	Host_supported     *bool
-	Apex_available     []string
-	Min_sdk_version    *string
-	Cflags             []string
-	Ldflags            []string
+	Required                 []string
+	Recovery                 *bool
+	Recovery_available       *bool
+	Vendor_available         *bool
+	Product_available        *bool
+	Ramdisk_available        *bool
+	Vendor_ramdisk_available *bool
+	Host_supported           *bool
+	Apex_available           []string
+	Min_sdk_version          *string
+	Cflags                   []string
+	Ldflags                  []string
 }
 
 type javaLibraryProperties struct {
@@ -605,6 +607,7 @@
 	ccProps.Vendor_available = m.properties.Vendor_available
 	ccProps.Product_available = m.properties.Product_available
 	ccProps.Ramdisk_available = m.properties.Ramdisk_available
+	ccProps.Vendor_ramdisk_available = m.properties.Vendor_ramdisk_available
 	ccProps.Host_supported = m.properties.Host_supported
 	ccProps.Apex_available = m.ApexProperties.Apex_available
 	ccProps.Min_sdk_version = m.properties.Cpp.Min_sdk_version
@@ -680,7 +683,7 @@
 		Sysprop_srcs: m.properties.Srcs,
 		Scope:        scope,
 		Check_api:    proptools.StringPtr(ctx.ModuleName()),
-		Installable:  proptools.BoolPtr(false),
+		Installable:  m.properties.Installable,
 		Crate_name:   m.rustCrateName(),
 		Rustlibs: []string{
 			"liblog_rust",
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index 7d4e69d..b2cfaa7 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -261,9 +261,9 @@
 		"android_vendor_arm64_armv8-a_shared",
 		"android_vendor_arm64_armv8-a_static",
 	} {
-		result.ModuleForTests("libsysprop-platform", variant)
-		result.ModuleForTests("libsysprop-vendor", variant)
-		result.ModuleForTests("libsysprop-odm", variant)
+		result.ModuleForTests(t, "libsysprop-platform", variant)
+		result.ModuleForTests(t, "libsysprop-vendor", variant)
+		result.ModuleForTests(t, "libsysprop-odm", variant)
 	}
 
 	// product variant of vendor-owned sysprop_library
@@ -273,7 +273,7 @@
 		"android_product_arm64_armv8-a_shared",
 		"android_product_arm64_armv8-a_static",
 	} {
-		result.ModuleForTests("libsysprop-vendor-on-product", variant)
+		result.ModuleForTests(t, "libsysprop-vendor-on-product", variant)
 	}
 
 	for _, variant := range []string{
@@ -282,15 +282,15 @@
 		"android_arm64_armv8-a_shared",
 		"android_arm64_armv8-a_static",
 	} {
-		library := result.ModuleForTests("libsysprop-platform", variant).Module().(*cc.Module)
+		library := result.ModuleForTests(t, "libsysprop-platform", variant).Module().(*cc.Module)
 		expectedApexAvailableOnLibrary := []string{"//apex_available:platform"}
 		android.AssertDeepEquals(t, "apex available property on libsysprop-platform", expectedApexAvailableOnLibrary, library.ApexProperties.Apex_available)
 	}
 
-	result.ModuleForTests("sysprop-platform", "android_common")
-	result.ModuleForTests("sysprop-platform_public", "android_common")
-	result.ModuleForTests("sysprop-vendor", "android_common")
-	result.ModuleForTests("sysprop-vendor-on-product", "android_common")
+	result.ModuleForTests(t, "sysprop-platform", "android_common")
+	result.ModuleForTests(t, "sysprop-platform_public", "android_common")
+	result.ModuleForTests(t, "sysprop-vendor", "android_common")
+	result.ModuleForTests(t, "sysprop-vendor-on-product", "android_common")
 
 	// Check for exported includes
 	coreVariant := "android_arm64_armv8-a_static"
@@ -305,19 +305,19 @@
 	vendorInternalPath := "libsysprop-vendor/android_vendor_arm64_armv8-a_static/gen/sysprop/include"
 	vendorOnProductPath := "libsysprop-vendor-on-product/android_product_arm64_armv8-a_static/gen/sysprop/public/include"
 
-	platformClient := result.ModuleForTests("cc-client-platform", coreVariant)
+	platformClient := result.ModuleForTests(t, "cc-client-platform", coreVariant)
 	platformFlags := platformClient.Rule("cc").Args["cFlags"]
 
 	// platform should use platform's internal header
 	android.AssertStringDoesContain(t, "flags for platform", platformFlags, platformInternalPath)
 
-	platformStaticClient := result.ModuleForTests("cc-client-platform-static", coreVariant)
+	platformStaticClient := result.ModuleForTests(t, "cc-client-platform-static", coreVariant)
 	platformStaticFlags := platformStaticClient.Rule("cc").Args["cFlags"]
 
 	// platform-static should use platform's internal header
 	android.AssertStringDoesContain(t, "flags for platform-static", platformStaticFlags, platformInternalPath)
 
-	productClient := result.ModuleForTests("cc-client-product", productVariant)
+	productClient := result.ModuleForTests(t, "cc-client-product", productVariant)
 	productFlags := productClient.Rule("cc").Args["cFlags"]
 
 	// Product should use platform's and vendor's public headers
@@ -327,7 +327,7 @@
 			platformOnProductPath, vendorOnProductPath, productFlags)
 	}
 
-	vendorClient := result.ModuleForTests("cc-client-vendor", vendorVariant)
+	vendorClient := result.ModuleForTests(t, "cc-client-vendor", vendorVariant)
 	vendorFlags := vendorClient.Rule("cc").Args["cFlags"]
 
 	// Vendor should use platform's public header and vendor's internal header
@@ -338,8 +338,8 @@
 	}
 
 	// Java modules linking against system API should use public stub
-	javaSystemApiClient := result.ModuleForTests("java-platform", "android_common").Rule("javac")
-	syspropPlatformPublic := result.ModuleForTests("sysprop-platform_public", "android_common").Description("for turbine")
+	javaSystemApiClient := result.ModuleForTests(t, "java-platform", "android_common").Rule("javac")
+	syspropPlatformPublic := result.ModuleForTests(t, "sysprop-platform_public", "android_common").Description("turbine")
 	if g, w := javaSystemApiClient.Implicits.Strings(), syspropPlatformPublic.Output.String(); !android.InList(w, g) {
 		t.Errorf("system api client should use public stub %q, got %q", w, g)
 	}
@@ -358,15 +358,15 @@
 
 	expected := []string{"//apex_available:platform"}
 
-	ccModule := result.ModuleForTests("libsysprop-platform", "android_arm64_armv8-a_shared").Module().(*cc.Module)
+	ccModule := result.ModuleForTests(t, "libsysprop-platform", "android_arm64_armv8-a_shared").Module().(*cc.Module)
 	propFromCc := ccModule.ApexProperties.Apex_available
 	android.AssertDeepEquals(t, "apex_available forwarding to cc module", expected, propFromCc)
 
-	javaModule := result.ModuleForTests("sysprop-platform", "android_common").Module().(*java.Library)
+	javaModule := result.ModuleForTests(t, "sysprop-platform", "android_common").Module().(*java.Library)
 	propFromJava := javaModule.ApexProperties.Apex_available
 	android.AssertDeepEquals(t, "apex_available forwarding to java module", expected, propFromJava)
 
-	rustModule := result.ModuleForTests("libsysprop_platform_rust", "android_arm64_armv8-a_rlib_rlib-std").Module().(*rust.Module)
+	rustModule := result.ModuleForTests(t, "libsysprop_platform_rust", "android_arm64_armv8-a_rlib_rlib-std").Module().(*rust.Module)
 	propFromRust := rustModule.ApexProperties.Apex_available
 	android.AssertDeepEquals(t, "apex_available forwarding to rust module", expected, propFromRust)
 }
@@ -390,15 +390,15 @@
 		}
 	`)
 
-	ccModule := result.ModuleForTests("libsysprop-platform", "android_arm64_armv8-a_shared").Module().(*cc.Module)
+	ccModule := result.ModuleForTests(t, "libsysprop-platform", "android_arm64_armv8-a_shared").Module().(*cc.Module)
 	propFromCc := proptools.String(ccModule.Properties.Min_sdk_version)
 	android.AssertStringEquals(t, "min_sdk_version forwarding to cc module", "29", propFromCc)
 
-	javaModule := result.ModuleForTests("sysprop-platform", "android_common").Module().(*java.Library)
+	javaModule := result.ModuleForTests(t, "sysprop-platform", "android_common").Module().(*java.Library)
 	propFromJava := javaModule.MinSdkVersionString()
 	android.AssertStringEquals(t, "min_sdk_version forwarding to java module", "30", propFromJava)
 
-	rustModule := result.ModuleForTests("libsysprop_platform_rust", "android_arm64_armv8-a_rlib_rlib-std").Module().(*rust.Module)
+	rustModule := result.ModuleForTests(t, "libsysprop_platform_rust", "android_arm64_armv8-a_rlib_rlib-std").Module().(*rust.Module)
 	propFromRust := proptools.String(rustModule.Properties.Min_sdk_version)
 	android.AssertStringEquals(t, "min_sdk_version forwarding to rust module", "29", propFromRust)
 }
diff --git a/systemfeatures/system_features.go b/systemfeatures/system_features.go
index 0c1a566..b8dacfb 100644
--- a/systemfeatures/system_features.go
+++ b/systemfeatures/system_features.go
@@ -20,6 +20,8 @@
 
 	"android/soong/android"
 	"android/soong/genrule"
+
+	"github.com/google/blueprint/proptools"
 )
 
 var (
@@ -39,6 +41,11 @@
 	properties struct {
 		// The fully qualified class name for the generated code, e.g., com.android.Foo
 		Full_class_name string
+		// Whether to generate only a simple metadata class with details about the full API surface.
+		// This is useful for tools that rely on the mapping from feature names to their generated
+		// method names, but don't want the fully generated API class (e.g., for linting).
+
+		Metadata_only *bool
 	}
 	outputFiles android.WritablePaths
 }
@@ -72,6 +79,7 @@
 		Flag(m.properties.Full_class_name).
 		FlagForEachArg("--feature=", features).
 		FlagWithArg("--readonly=", fmt.Sprint(ctx.Config().ReleaseUseSystemFeatureBuildFlags())).
+		FlagWithArg("--metadata-only=", fmt.Sprint(proptools.Bool(m.properties.Metadata_only))).
 		FlagWithOutput(" > ", outputFile)
 	rule.Build(ctx.ModuleName(), "Generating systemfeatures srcs filegroup")
 
@@ -97,6 +105,7 @@
 func JavaSystemFeaturesSrcsFactory() android.Module {
 	module := &javaSystemFeaturesSrcs{}
 	module.AddProperties(&module.properties)
+	module.properties.Metadata_only = proptools.BoolPtr(false)
 	android.InitAndroidModule(module)
 	return module
 }
diff --git a/systemfeatures/system_features_test.go b/systemfeatures/system_features_test.go
index 558bb95..58e6a06 100644
--- a/systemfeatures/system_features_test.go
+++ b/systemfeatures/system_features_test.go
@@ -36,7 +36,7 @@
 		android.PrepareForTestWithBuildFlag("RELEASE_NOT_SYSTEM_FEATURE_FOO", "BAR"),
 	).RunTestWithBp(t, bp)
 
-	module := res.ModuleForTests("system-features-srcs", "")
+	module := res.ModuleForTests(t, "system-features-srcs", "")
 	cmd := module.Rule("system-features-srcs").RuleParams.Command
 	android.AssertStringDoesContain(t, "Expected fully class name", cmd, " com.android.test.RoSystemFeatures ")
 	android.AssertStringDoesContain(t, "Expected readonly flag", cmd, "--readonly=true")
diff --git a/tests/sbom_test.sh b/tests/sbom_test.sh
index 0471853..2ef9e37 100755
--- a/tests/sbom_test.sh
+++ b/tests/sbom_test.sh
@@ -83,6 +83,12 @@
   dump_erofs=$out_dir/host/linux-x86/bin/dump.erofs
   lz4=$out_dir/host/linux-x86/bin/lz4
 
+  declare -A diff_excludes
+  diff_excludes[system]="\
+    -I /etc/NOTICE.xml.gz \
+    -I /odm_dlkm/etc \
+    -I /vendor_dlkm/etc"
+
   # Example output of dump.erofs is as below, and the data used in the test start
   # at line 11. Column 1 is inode id, column 2 is inode type and column 3 is name.
   # Each line is captured in variable "entry", awk is used to get type and name.
@@ -155,7 +161,11 @@
     sort -n -o "$files_in_soong_spdx_file" "$files_in_soong_spdx_file"
 
     echo ============ Diffing files in $f and SBOM created by Soong
-    diff_files "$file_list_file" "$files_in_soong_spdx_file" "$partition_name" ""
+    exclude=
+    if [ -v 'diff_excludes[$partition_name]' ]; then
+     exclude=${diff_excludes[$partition_name]}
+    fi
+    diff_files "$file_list_file" "$files_in_soong_spdx_file" "$partition_name" "$exclude"
   done
 
   RAMDISK_IMAGES="$product_out/ramdisk.img"
@@ -230,10 +240,10 @@
     exit 1
   fi
 
-  # PRODUCT and 7 prebuilt packages have "PackageLicenseDeclared: NOASSERTION"
+  # PRODUCT and 6 prebuilt packages have "PackageLicenseDeclared: NOASSERTION"
   # All other packages have declared licenses
   num_of_packages_with_noassertion_license=$(grep 'PackageLicenseDeclared: NOASSERTION' $sbom_file | wc -l)
-  if [ $num_of_packages_with_noassertion_license = 15 ]
+  if [ $num_of_packages_with_noassertion_license = 13 ]
   then
     echo "Number of packages with NOASSERTION license is correct."
   else
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index e230795..89c69bd 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -160,6 +160,7 @@
 	DeviceTemplate          string
 	HostTemplate            string
 	HostUnitTestTemplate    string
+	StandaloneTest          *bool
 }
 
 func AutoGenTestConfig(ctx android.ModuleContext, options AutoGenTestConfigOptions) android.Path {
@@ -178,6 +179,12 @@
 			autogenTemplate(ctx, name, autogenPath, templatePath.String(), configs, options.TestRunnerOptions, options.OutputFileName, options.TestInstallBase)
 		} else {
 			if ctx.Device() {
+				if Bool(options.StandaloneTest) {
+					options.TestRunnerOptions = append(options.TestRunnerOptions, Option{
+						Name:  "ld-library-path",
+						Value: "{TEST_INSTALL_BASE}/" + name + "/" + ctx.Arch().ArchType.String() + "/standalone-libs",
+					})
+				}
 				autogenTemplate(ctx, name, autogenPath, options.DeviceTemplate, configs, options.TestRunnerOptions, options.OutputFileName, options.TestInstallBase)
 			} else {
 				if Bool(options.UnitTest) {
@@ -190,7 +197,10 @@
 		return autogenPath
 	}
 	if len(options.OptionsForAutogenerated) > 0 {
-		ctx.ModuleErrorf("Extra tradefed configurations were provided for an autogenerated xml file, but the autogenerated xml file was not used.")
+		ctx.ModuleErrorf("You likely need to delete your soong modules local AndroidTest.xml file.  Extra tradefed configurations (%v) were provided for an autogenerated xml file, but the autogenerated xml file was not used.", options.OptionsForAutogenerated)
+	}
+	if len(options.TestRunnerOptions) > 0 {
+		ctx.ModuleErrorf("You likely need to delete your soong modules local AndroidTest.xml file.  Extra test runner options (%v) were provided for an autogenerated xml file, but the autogenerated xml file was not used.", options.TestRunnerOptions)
 	}
 	return path
 }
diff --git a/tradefed_modules/Android.bp b/tradefed_modules/Android.bp
index a765a05..37bae39 100644
--- a/tradefed_modules/Android.bp
+++ b/tradefed_modules/Android.bp
@@ -14,11 +14,9 @@
     ],
     srcs: [
         "test_module_config.go",
-        "test_suite.go",
     ],
     testSrcs: [
         "test_module_config_test.go",
-        "test_suite_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/tradefed_modules/test_module_config.go b/tradefed_modules/test_module_config.go
index 988352c..e833df2 100644
--- a/tradefed_modules/test_module_config.go
+++ b/tradefed_modules/test_module_config.go
@@ -1,14 +1,15 @@
 package tradefed_modules
 
 import (
-	"android/soong/android"
-	"android/soong/tradefed"
 	"encoding/json"
 	"fmt"
 	"io"
 	"slices"
 	"strings"
 
+	"android/soong/android"
+	"android/soong/tradefed"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -162,6 +163,26 @@
 	m.validateBase(ctx, &testModuleConfigTag, "android_test", false)
 	m.generateManifestAndConfig(ctx)
 
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Class = []string{m.provider.MkAppClass}
+	if m.provider.MkAppClass != "NATIVE_TESTS" {
+		moduleInfoJSON.Tags = append(moduleInfoJSON.Tags, "tests")
+	}
+	if m.provider.IsUnitTest {
+		moduleInfoJSON.IsUnitTest = "true"
+	}
+	moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, m.tradefedProperties.Test_suites...)
+	moduleInfoJSON.SystemSharedLibs = []string{"none"}
+	moduleInfoJSON.ExtraRequired = append(moduleInfoJSON.ExtraRequired, m.provider.RequiredModuleNames...)
+	moduleInfoJSON.ExtraRequired = append(moduleInfoJSON.ExtraRequired, *m.Base)
+	moduleInfoJSON.ExtraHostRequired = append(moduleInfoJSON.ExtraRequired, m.provider.HostRequiredModuleNames...)
+	moduleInfoJSON.TestConfig = []string{m.testConfig.String()}
+	moduleInfoJSON.AutoTestConfig = []string{"true"}
+	moduleInfoJSON.TestModuleConfigBase = proptools.String(m.Base)
+
+	android.SetProvider(ctx, android.SupportFilesInfoProvider, android.SupportFilesInfo{
+		SupportFiles: m.supportFiles,
+	})
 }
 
 // Ensure at least one test_suite is listed.  Ideally it should be general-tests
@@ -311,6 +332,9 @@
 func (m *testModuleConfigHostModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	m.validateBase(ctx, &testModuleConfigHostTag, "java_test_host", true)
 	m.generateManifestAndConfig(ctx)
+	android.SetProvider(ctx, android.SupportFilesInfoProvider, android.SupportFilesInfo{
+		SupportFiles: m.supportFiles,
+	})
 }
 
 // Ensure the base listed is the right type by checking that we get the expected provider data.
@@ -410,6 +434,10 @@
 		LocalCertificate:        m.provider.LocalCertificate,
 		IsUnitTest:              m.provider.IsUnitTest,
 	})
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: m.tradefedProperties.Test_suites,
+	})
 }
 
 var _ android.AndroidMkEntriesProvider = (*testModuleConfigHostModule)(nil)
diff --git a/tradefed_modules/test_module_config_test.go b/tradefed_modules/test_module_config_test.go
index efd4a04..302f9a9 100644
--- a/tradefed_modules/test_module_config_test.go
+++ b/tradefed_modules/test_module_config_test.go
@@ -65,21 +65,21 @@
 		android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
 	).RunTestWithBp(t, bp)
 
-	derived := ctx.ModuleForTests("derived_test", variant)
+	derived := ctx.ModuleForTests(t, "derived_test", variant)
 	// Assert there are rules to create these files.
 	derived.Output("test_module_config.manifest")
 	derived.Output("test_config_fixer/derived_test.config")
 
 	// Ensure some basic rules exist.
-	ctx.ModuleForTests("base", "android_common").Output("package-res.apk")
+	ctx.ModuleForTests(t, "base", "android_common").Output("package-res.apk")
 	entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0]
 
 	// Ensure some entries from base are there, specifically support files for data and helper apps.
 	// Do not use LOCAL_COMPATIBILITY_SUPPORT_FILES, but instead use LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES
 	android.AssertStringPathsRelativeToTopEquals(t, "support-files", ctx.Config,
-		[]string{"out/soong/target/product/test_device/testcases/derived_test/arm64/base.apk",
-			"out/soong/target/product/test_device/testcases/derived_test/HelperApp.apk",
-			"out/soong/target/product/test_device/testcases/derived_test/data/testfile"},
+		[]string{"out/target/product/test_device/testcases/derived_test/arm64/base.apk",
+			"out/target/product/test_device/testcases/derived_test/HelperApp.apk",
+			"out/target/product/test_device/testcases/derived_test/data/testfile"},
 		entries.EntryMap["LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES"])
 	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{})
 
@@ -97,15 +97,15 @@
 	convertedActual := make([]string, 5)
 	for i, e := range entries.FooterLinesForTests() {
 		// AssertStringPathsRelativeToTop doesn't replace both instances
-		convertedActual[i] = strings.Replace(e, ctx.Config.SoongOutDir(), "", 2)
+		convertedActual[i] = strings.Replace(e, ctx.Config.OutDir(), "", 2)
 	}
-	android.AssertArrayString(t, fmt.Sprintf("%s", ctx.Config.SoongOutDir()), convertedActual, []string{
+	android.AssertArrayString(t, fmt.Sprintf("%s", ctx.Config.OutDir()), []string{
 		"include $(BUILD_SYSTEM)/soong_app_prebuilt.mk",
 		"/target/product/test_device/testcases/derived_test/arm64/base.apk: /target/product/test_device/testcases/base/arm64/base.apk",
 		"/target/product/test_device/testcases/derived_test/HelperApp.apk: /target/product/test_device/testcases/base/HelperApp.apk",
 		"/target/product/test_device/testcases/derived_test/data/testfile: /target/product/test_device/testcases/base/data/testfile",
 		"",
-	})
+	}, convertedActual)
 }
 
 func TestModuleConfigShTest(t *testing.T) {
@@ -136,7 +136,7 @@
                         options: [{name: "SomeName", value: "OptionValue"}],
                 }
          `)
-	derived := ctx.ModuleForTests("conch", variant) //
+	derived := ctx.ModuleForTests(t, "conch", variant) //
 	conch := derived.Module().(*testModuleConfigModule)
 	android.AssertArrayString(t, "TestcaseRelDataFiles", []string{"arm64/testdata/data1", "arm64/testdata/sub/data2"}, conch.provider.TestcaseRelDataFiles)
 	android.AssertStringEquals(t, "Rel OutputFile", "test.sh", conch.provider.OutputFile.Rel())
@@ -151,8 +151,8 @@
 	// Ensure some entries from base are there, specifically support files for data and helper apps.
 	// Do not use LOCAL_COMPATIBILITY_SUPPORT_FILES, but instead use LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES
 	android.AssertStringPathsRelativeToTopEquals(t, "support-files", ctx.Config,
-		[]string{"out/soong/target/product/test_device/testcases/conch/arm64/testdata/data1",
-			"out/soong/target/product/test_device/testcases/conch/arm64/testdata/sub/data2"},
+		[]string{"out/target/product/test_device/testcases/conch/arm64/testdata/data1",
+			"out/target/product/test_device/testcases/conch/arm64/testdata/sub/data2"},
 		entries.EntryMap["LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES"])
 	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{})
 
@@ -171,14 +171,14 @@
 	convertedActual := make([]string, 4)
 	for i, e := range entries.FooterLinesForTests() {
 		// AssertStringPathsRelativeToTop doesn't replace both instances
-		convertedActual[i] = strings.Replace(e, ctx.Config.SoongOutDir(), "", 2)
+		convertedActual[i] = strings.Replace(e, ctx.Config.OutDir(), "", 2)
 	}
-	android.AssertArrayString(t, fmt.Sprintf("%s", ctx.Config.SoongOutDir()), convertedActual, []string{
+	android.AssertArrayString(t, fmt.Sprintf("%s", ctx.Config.OutDir()), []string{
 		"include $(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk",
 		"/target/product/test_device/testcases/conch/arm64/testdata/data1: /target/product/test_device/testcases/shell_test/arm64/testdata/data1",
 		"/target/product/test_device/testcases/conch/arm64/testdata/sub/data2: /target/product/test_device/testcases/shell_test/arm64/testdata/sub/data2",
 		"",
-	})
+	}, convertedActual)
 
 }
 
@@ -191,7 +191,7 @@
 	).RunTestWithBp(t, bp)
 
 	// Check that we generate a rule to make a new AndroidTest.xml/Module.config file.
-	derived := ctx.ModuleForTests("derived_test", variant)
+	derived := ctx.ModuleForTests(t, "derived_test", variant)
 	rule_cmd := derived.Rule("fix_test_config").RuleParams.Command
 	android.AssertStringDoesContain(t, "Bad FixConfig rule inputs", rule_cmd,
 		`--test-runner-options='[{"Name":"exclude-filter","Key":"","Value":"android.test.example.devcodelab.DevCodelabTest#testHelloFail"},{"Name":"include-annotation","Key":"","Value":"android.platform.test.annotations.LargeTest"}]'`)
@@ -288,7 +288,7 @@
 	).ExtendWithErrorHandler(
 		android.FixtureExpectsAtLeastOneErrorMatchingPattern("Test options must be given")).
 		RunTestWithBp(t, badBp)
-	ctx.ModuleForTests("derived_test", variant)
+	ctx.ModuleForTests(t, "derived_test", variant)
 }
 
 func TestModuleConfigMultipleDerivedTestsWriteDistinctMakeEntries(t *testing.T) {
@@ -326,13 +326,13 @@
 	).RunTestWithBp(t, multiBp)
 
 	{
-		derived := ctx.ModuleForTests("derived_test", variant)
+		derived := ctx.ModuleForTests(t, "derived_test", variant)
 		entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0]
 		// All these should be the same in both derived tests
 		android.AssertStringPathsRelativeToTopEquals(t, "support-files", ctx.Config,
-			[]string{"out/soong/target/product/test_device/testcases/derived_test/arm64/base.apk",
-				"out/soong/target/product/test_device/testcases/derived_test/HelperApp.apk",
-				"out/soong/target/product/test_device/testcases/derived_test/data/testfile"},
+			[]string{"out/target/product/test_device/testcases/derived_test/arm64/base.apk",
+				"out/target/product/test_device/testcases/derived_test/HelperApp.apk",
+				"out/target/product/test_device/testcases/derived_test/data/testfile"},
 			entries.EntryMap["LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES"])
 
 		// Except this one, which points to the updated tradefed xml file.
@@ -342,13 +342,13 @@
 	}
 
 	{
-		derived := ctx.ModuleForTests("another_derived_test", variant)
+		derived := ctx.ModuleForTests(t, "another_derived_test", variant)
 		entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0]
 		// All these should be the same in both derived tests
 		android.AssertStringPathsRelativeToTopEquals(t, "support-files", ctx.Config,
-			[]string{"out/soong/target/product/test_device/testcases/another_derived_test/arm64/base.apk",
-				"out/soong/target/product/test_device/testcases/another_derived_test/HelperApp.apk",
-				"out/soong/target/product/test_device/testcases/another_derived_test/data/testfile"},
+			[]string{"out/target/product/test_device/testcases/another_derived_test/arm64/base.apk",
+				"out/target/product/test_device/testcases/another_derived_test/HelperApp.apk",
+				"out/target/product/test_device/testcases/another_derived_test/data/testfile"},
 			entries.EntryMap["LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES"])
 		// Except this one, which points to the updated tradefed xml file.
 		android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0],
@@ -381,7 +381,7 @@
 	).RunTestWithBp(t, bp)
 
 	variant := ctx.Config.BuildOS.String() + "_common"
-	derived := ctx.ModuleForTests("derived_test", variant)
+	derived := ctx.ModuleForTests(t, "derived_test", variant)
 	mod := derived.Module().(*testModuleConfigHostModule)
 	allEntries := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)
 	entries := allEntries[0]
diff --git a/tradefed_modules/test_suite.go b/tradefed_modules/test_suite.go
deleted file mode 100644
index 00585f5..0000000
--- a/tradefed_modules/test_suite.go
+++ /dev/null
@@ -1,173 +0,0 @@
-// 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 tradefed_modules
-
-import (
-	"encoding/json"
-	"path"
-	"path/filepath"
-
-	"android/soong/android"
-	"android/soong/tradefed"
-	"github.com/google/blueprint"
-)
-
-const testSuiteModuleType = "test_suite"
-
-type testSuiteTag struct{
-	blueprint.BaseDependencyTag
-}
-
-type testSuiteManifest struct {
-	Name  string `json:"name"`
-	Files []string `json:"files"`
-}
-
-func init() {
-	RegisterTestSuiteBuildComponents(android.InitRegistrationContext)
-}
-
-func RegisterTestSuiteBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType(testSuiteModuleType, TestSuiteFactory)
-}
-
-var PrepareForTestWithTestSuiteBuildComponents = android.GroupFixturePreparers(
-	android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
-)
-
-type testSuiteProperties struct {
-	Description string
-	Tests []string `android:"path,arch_variant"`
-}
-
-type testSuiteModule struct {
-	android.ModuleBase
-	android.DefaultableModuleBase
-	testSuiteProperties
-}
-
-func (t *testSuiteModule) DepsMutator(ctx android.BottomUpMutatorContext) {
-	for _, test := range t.Tests {
-		if ctx.OtherModuleDependencyVariantExists(ctx.Config().BuildOSCommonTarget.Variations(), test) {
-			// Host tests.
-			ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), testSuiteTag{}, test)
-		} else {
-			// Target tests.
-			ctx.AddDependency(ctx.Module(), testSuiteTag{}, test)
-		}
-	}
-}
-
-func (t *testSuiteModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	suiteName := ctx.ModuleName()
-	modulesByName := make(map[string]android.Module)
-	ctx.WalkDeps(func(child, parent android.Module) bool {
-		// Recurse into test_suite dependencies.
-		if ctx.OtherModuleType(child) == testSuiteModuleType {
-			ctx.Phony(suiteName, android.PathForPhony(ctx, child.Name()))
-			return true
-		}
-
-		// Only write out top level test suite dependencies here.
-		if _, ok := ctx.OtherModuleDependencyTag(child).(testSuiteTag); !ok {
-			return false
-		}
-
-		if !child.InstallInTestcases() {
-			ctx.ModuleErrorf("test_suite only supports modules installed in testcases. %q is not installed in testcases.", child.Name())
-			return false
-		}
-
-		modulesByName[child.Name()] = child
-		return false
-	})
-
-	var files []string
-	for name, module := range modulesByName {
-		// Get the test provider data from the child.
-		tp, ok := android.OtherModuleProvider(ctx, module, tradefed.BaseTestProviderKey)
-		if !ok {
-			// TODO: Consider printing out a list of all module types.
-			ctx.ModuleErrorf("%q is not a test module.", name)
-			continue
-		}
-
-		files = append(files, packageModuleFiles(ctx, suiteName, module, tp)...)
-		ctx.Phony(suiteName, android.PathForPhony(ctx, name))
-	}
-
-	manifestPath := android.PathForSuiteInstall(ctx, suiteName, suiteName+".json")
-	b, err := json.Marshal(testSuiteManifest{Name: suiteName, Files: files})
-	if err != nil {
-		ctx.ModuleErrorf("Failed to marshal manifest: %v", err)
-		return
-	}
-	android.WriteFileRule(ctx, manifestPath, string(b))
-
-	ctx.Phony(suiteName, manifestPath)
-}
-
-func TestSuiteFactory() android.Module {
-	module := &testSuiteModule{}
-	module.AddProperties(&module.testSuiteProperties)
-
-	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
-	android.InitDefaultableModule(module)
-
-	return module
-}
-
-func packageModuleFiles(ctx android.ModuleContext, suiteName string, module android.Module, tp tradefed.BaseTestProviderData) []string {
-
-	hostOrTarget := "target"
-	if tp.IsHost {
-		hostOrTarget = "host"
-	}
-
-	// suiteRoot at out/soong/packaging/<suiteName>.
-	suiteRoot := android.PathForSuiteInstall(ctx, suiteName)
-
-	var installed android.InstallPaths
-	// Install links to installed files from the module.
-	if installFilesInfo, ok := android.OtherModuleProvider(ctx, module, android.InstallFilesProvider); ok {
-		for _, f := range installFilesInfo.InstallFiles {
-			// rel is anything under .../<partition>, normally under .../testcases.
-			rel := android.Rel(ctx, f.PartitionDir(), f.String())
-
-			// Install the file under <suiteRoot>/<host|target>/<partition>.
-			installDir := suiteRoot.Join(ctx, hostOrTarget, f.Partition(), path.Dir(rel))
-			linkTo, err := filepath.Rel(installDir.String(), f.String())
-			if err != nil {
-				ctx.ModuleErrorf("Failed to get relative path from %s to %s: %v", installDir.String(), f.String(), err)
-				continue
-			}
-			installed = append(installed, ctx.InstallAbsoluteSymlink(installDir, path.Base(rel), linkTo))
-		}
-	}
-
-	// Install config file.
-	if tp.TestConfig != nil {
-		moduleRoot := suiteRoot.Join(ctx, hostOrTarget, "testcases", module.Name())
-		installed = append(installed, ctx.InstallFile(moduleRoot, module.Name() + ".config", tp.TestConfig))
-	}
-
-	// Add to phony and manifest, manifestpaths are relative to suiteRoot.
-	var manifestEntries []string
-	for _, f := range installed {
-		manifestEntries = append(manifestEntries, android.Rel(ctx, suiteRoot.String(), f.String()))
-		ctx.Phony(suiteName, f)
-	}
-	return manifestEntries
-}
diff --git a/tradefed_modules/test_suite_test.go b/tradefed_modules/test_suite_test.go
deleted file mode 100644
index 3c0a9eb..0000000
--- a/tradefed_modules/test_suite_test.go
+++ /dev/null
@@ -1,151 +0,0 @@
-// 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 tradefed_modules
-
-import (
-	"android/soong/android"
-	"android/soong/java"
-	"encoding/json"
-	"slices"
-	"testing"
-)
-
-func TestTestSuites(t *testing.T) {
-	t.Parallel()
-	ctx := android.GroupFixturePreparers(
-		java.PrepareForTestWithJavaDefaultModules,
-		android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
-	).RunTestWithBp(t, `
-		android_test {
-			name: "TestModule1",
-			sdk_version: "current",
-		}
-
-		android_test {
-			name: "TestModule2",
-			sdk_version: "current",
-		}
-
-		test_suite {
-			name: "my-suite",
-			description: "a test suite",
-			tests: [
-				"TestModule1",
-				"TestModule2",
-			]
-		}
-	`)
-	manifestPath := ctx.ModuleForTests("my-suite", "android_common").Output("out/soong/test_suites/my-suite/my-suite.json")
-	var actual testSuiteManifest
-	if err := json.Unmarshal([]byte(android.ContentFromFileRuleForTests(t, ctx.TestContext, manifestPath)), &actual); err != nil {
-		t.Errorf("failed to unmarshal manifest: %v", err)
-	}
-	slices.Sort(actual.Files)
-
-	expected := testSuiteManifest{
-		Name: "my-suite",
-		Files: []string{
-			"target/testcases/TestModule1/TestModule1.config",
-			"target/testcases/TestModule1/arm64/TestModule1.apk",
-			"target/testcases/TestModule2/TestModule2.config",
-			"target/testcases/TestModule2/arm64/TestModule2.apk",
-		},
-	}
-
-	android.AssertDeepEquals(t, "manifests differ", expected, actual)
-}
-
-func TestTestSuitesWithNested(t *testing.T) {
-	t.Parallel()
-	ctx := android.GroupFixturePreparers(
-		java.PrepareForTestWithJavaDefaultModules,
-		android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
-	).RunTestWithBp(t, `
-		android_test {
-			name: "TestModule1",
-			sdk_version: "current",
-		}
-
-		android_test {
-			name: "TestModule2",
-			sdk_version: "current",
-		}
-
-		android_test {
-			name: "TestModule3",
-			sdk_version: "current",
-		}
-
-		test_suite {
-			name: "my-child-suite",
-			description: "a child test suite",
-			tests: [
-				"TestModule1",
-				"TestModule2",
-			]
-		}
-
-		test_suite {
-			name: "my-all-tests-suite",
-			description: "a parent test suite",
-			tests: [
-				"TestModule1",
-				"TestModule3",
-				"my-child-suite",
-			]
-		}
-	`)
-	manifestPath := ctx.ModuleForTests("my-all-tests-suite", "android_common").Output("out/soong/test_suites/my-all-tests-suite/my-all-tests-suite.json")
-	var actual testSuiteManifest
-	if err := json.Unmarshal([]byte(android.ContentFromFileRuleForTests(t, ctx.TestContext, manifestPath)), &actual); err != nil {
-		t.Errorf("failed to unmarshal manifest: %v", err)
-	}
-	slices.Sort(actual.Files)
-
-	expected := testSuiteManifest{
-		Name: "my-all-tests-suite",
-		Files: []string{
-			"target/testcases/TestModule1/TestModule1.config",
-			"target/testcases/TestModule1/arm64/TestModule1.apk",
-			"target/testcases/TestModule2/TestModule2.config",
-			"target/testcases/TestModule2/arm64/TestModule2.apk",
-			"target/testcases/TestModule3/TestModule3.config",
-			"target/testcases/TestModule3/arm64/TestModule3.apk",
-		},
-	}
-
-	android.AssertDeepEquals(t, "manifests differ", expected, actual)
-}
-
-func TestTestSuitesNotInstalledInTestcases(t *testing.T) {
-	t.Parallel()
-	android.GroupFixturePreparers(
-		java.PrepareForTestWithJavaDefaultModules,
-		android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
-	).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern([]string{
-		`"SomeHostTest" is not installed in testcases`,
-	})).RunTestWithBp(t, `
-			java_test_host {
-				name: "SomeHostTest",
-				srcs: ["a.java"],
-			}
-			test_suite {
-				name: "my-suite",
-				description: "a test suite",
-				tests: [
-					"SomeHostTest",
-				]
-			}
-	`)
-}
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index dc1abd9..a868d6a 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -41,6 +41,7 @@
         "soong-remoteexec",
         "soong-shared",
         "soong-ui-build-paths",
+        "soong-ui-execution-metrics",
         "soong-ui-logger",
         "soong-ui-metrics",
         "soong-ui-status",
@@ -54,6 +55,7 @@
         "config.go",
         "context.go",
         "staging_snapshot.go",
+        "source_inputs.go",
         "dumpvars.go",
         "environment.go",
         "exec.go",
diff --git a/ui/build/androidmk_denylist.go b/ui/build/androidmk_denylist.go
index 6f57cb1..8266654 100644
--- a/ui/build/androidmk_denylist.go
+++ b/ui/build/androidmk_denylist.go
@@ -15,20 +15,29 @@
 package build
 
 import (
+	"os"
+	"slices"
 	"strings"
 )
 
 var androidmk_denylist []string = []string{
+	"art/",
 	"bionic/",
-	"chained_build_config/",
+	"bootable/",
+	"build/",
 	"cts/",
 	"dalvik/",
 	"developers/",
 	"development/",
 	"device/common/",
+	"device/generic/",
+	"device/google/",
 	"device/google_car/",
 	"device/sample/",
+	"external/",
 	"frameworks/",
+	"hardware/google/",
+	"hardware/interfaces/",
 	"hardware/libhardware/",
 	"hardware/libhardware_legacy/",
 	"hardware/ril/",
@@ -45,24 +54,48 @@
 	"sdk/",
 	"system/",
 	"test/",
+	"tools/",
 	"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/",
+	"toolchain/",
+}
+
+var androidmk_allowlist []string = []string{
+	"art/Android.mk",
+	"bootable/deprecated-ota/updater/Android.mk",
+	"tools/vendor/google_prebuilts/arc/Android.mk",
+}
+
+func getAllLines(ctx Context, filename string) []string {
+	bytes, err := os.ReadFile(filename)
+	if err != nil {
+		if os.IsNotExist(err) {
+			return []string{}
+		} else {
+			ctx.Fatalf("Could not read %s: %v", filename, err)
+		}
+	}
+	return strings.Split(strings.Trim(string(bytes), " \n"), "\n")
 }
 
 func blockAndroidMks(ctx Context, androidMks []string) {
+	allowlist_files := []string{
+		"vendor/google/build/androidmk/allowlist.txt",
+		"device/google/clockwork/build/androidmk/allowlist.txt",
+		"device/google/sdv/androidmk/allowlist.txt",
+	}
+	for _, allowlist_file := range allowlist_files {
+		allowlist := getAllLines(ctx, allowlist_file)
+		androidmk_allowlist = append(androidmk_allowlist, allowlist...)
+	}
+	slices.Sort(androidmk_allowlist)
+	androidmk_allowlist = slices.Compact(androidmk_allowlist)
+
+	denylist := getAllLines(ctx, "vendor/google/build/androidmk/denylist.txt")
+	androidmk_denylist = append(androidmk_denylist, denylist...)
+
 	for _, mkFile := range androidMks {
 		for _, d := range androidmk_denylist {
-			if strings.HasPrefix(mkFile, d) {
+			if strings.HasPrefix(mkFile, d) && !slices.Contains(androidmk_allowlist, mkFile) {
 				ctx.Fatalf("Found blocked Android.mk file: %s. "+
 					"Please see androidmk_denylist.go for the blocked directories and contact build system team if the file should not be blocked.", mkFile)
 			}
@@ -70,8 +103,8 @@
 	}
 }
 
-// The Android.mk files in these directories are for NDK build system.
-var external_ndk_androidmks []string = []string{
+var external_androidmks []string = []string{
+	// The Android.mk files in these directories are for NDK build system.
 	"external/fmtlib/",
 	"external/google-breakpad/",
 	"external/googletest/",
@@ -83,12 +116,29 @@
 	"external/vulkan-validation-layers/",
 	"external/walt/",
 	"external/webp/",
+	// These directories hold the published Android SDK, used in Unbundled Gradle builds.
+	"prebuilts/fullsdk-darwin",
+	"prebuilts/fullsdk-linux",
+	// wpa_supplicant_8 has been converted to Android.bp and Android.mk files are kept for troubleshooting.
+	"external/wpa_supplicant_8/",
+	// Empty Android.mk in package's top directory
+	"external/proguard/",
+	"external/swig/",
+	"toolchain/",
 }
 
-func ignoreNdkAndroidMks(androidMks []string) (filtered []string) {
-	filter := func(s string) bool {
-		for _, d := range external_ndk_androidmks {
-			if strings.HasPrefix(s, d) {
+var art_androidmks = []string{
+	//"art/",
+}
+
+func ignoreSomeAndroidMks(androidMks []string) (filtered []string) {
+	ignore_androidmks := make([]string, 0, len(external_androidmks)+len(art_androidmks))
+	ignore_androidmks = append(ignore_androidmks, external_androidmks...)
+	ignore_androidmks = append(ignore_androidmks, art_androidmks...)
+
+	shouldKeep := func(androidmk string) bool {
+		for _, prefix := range ignore_androidmks {
+			if strings.HasPrefix(androidmk, prefix) {
 				return false
 			}
 		}
@@ -96,10 +146,9 @@
 	}
 
 	for _, l := range androidMks {
-		if filter(l) {
+		if shouldKeep(l) {
 			filtered = append(filtered, l)
 		}
 	}
-
 	return
 }
diff --git a/ui/build/build.go b/ui/build/build.go
index d5a20b4..781ca18 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -33,26 +33,13 @@
 	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "CleanSpec.mk"))
 	ensureEmptyDirectoriesExist(ctx, config.TempDir())
 
-	// Potentially write a marker file for whether kati is enabled. This is used by soong_build to
-	// potentially run the AndroidMk singleton and postinstall commands.
-	// Note that the absence of the  file does not not preclude running Kati for product
-	// configuration purposes.
-	katiEnabledMarker := filepath.Join(config.SoongOutDir(), ".soong.kati_enabled")
-	if config.SkipKatiNinja() {
-		os.Remove(katiEnabledMarker)
-		// Note that we can not remove the file for SkipKati builds yet -- some continuous builds
-		// --skip-make builds rely on kati targets being defined.
-	} else if !config.SkipKati() {
-		ensureEmptyFileExists(ctx, katiEnabledMarker)
-	}
-
 	// The ninja_build file is used by our buildbots to understand that the output
 	// can be parsed as ninja output.
 	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "ninja_build"))
 	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), ".out-dir"))
 
 	if buildDateTimeFile, ok := config.environ.Get("BUILD_DATETIME_FILE"); ok {
-		err := ioutil.WriteFile(buildDateTimeFile, []byte(config.buildDateTime), 0666) // a+rw
+		err := os.WriteFile(buildDateTimeFile, []byte(config.buildDateTime), 0666) // a+rw
 		if err != nil {
 			ctx.Fatalln("Failed to write BUILD_DATETIME to file:", err)
 		}
@@ -87,6 +74,32 @@
 	// without changing the command line every time.  Avoids rebuilds
 	// when using ninja.
 	writeValueIfChanged(ctx, config, config.SoongOutDir(), "build_number.txt", buildNumber)
+
+	hostname, ok := config.environ.Get("BUILD_HOSTNAME")
+	if !ok {
+		var err error
+		hostname, err = os.Hostname()
+		if err != nil {
+			ctx.Println("Failed to read hostname:", err)
+			hostname = "unknown"
+		}
+	}
+	writeValueIfChanged(ctx, config, config.SoongOutDir(), "build_hostname.txt", hostname)
+}
+
+// SetupKatiEnabledMarker creates or delets a file that tells soong_build if we're running with
+// kati.
+func SetupKatiEnabledMarker(ctx Context, config Config) {
+	// Potentially write a marker file for whether kati is enabled. This is used by soong_build to
+	// potentially run the AndroidMk singleton and postinstall commands.
+	// Note that the absence of the file does not preclude running Kati for product
+	// configuration purposes.
+	katiEnabledMarker := filepath.Join(config.SoongOutDir(), ".soong.kati_enabled")
+	if config.SkipKati() || config.SkipKatiNinja() {
+		os.Remove(katiEnabledMarker)
+	} else {
+		ensureEmptyFileExists(ctx, katiEnabledMarker)
+	}
 }
 
 var combinedBuildNinjaTemplate = template.Must(template.New("combined").Parse(`
@@ -96,8 +109,11 @@
 {{end -}}
 pool highmem_pool
  depth = {{.HighmemParallel}}
-{{if and (not .SkipKatiNinja) .HasKatiSuffix}}subninja {{.KatiBuildNinjaFile}}
+{{if and (not .SkipKatiNinja) .HasKatiSuffix}}
+subninja {{.KatiBuildNinjaFile}}
 subninja {{.KatiPackageNinjaFile}}
+{{else}}
+subninja {{.KatiSoongOnlyPackageNinjaFile}}
 {{end -}}
 subninja {{.SoongNinjaFile}}
 `))
@@ -315,10 +331,16 @@
 
 	if what&RunProductConfig != 0 {
 		runMakeProductConfig(ctx, config)
+
+		// Re-evaluate what to run because there are product variables that control how
+		// soong and make are run.
+		what = evaluateWhatToRun(config, ctx.Verboseln)
 	}
 
 	// Everything below here depends on product config.
 
+	SetupKatiEnabledMarker(ctx, config)
+
 	if inList("installclean", config.Arguments()) ||
 		inList("install-clean", config.Arguments()) {
 		logArgsOtherThan("installclean", "install-clean")
@@ -335,25 +357,31 @@
 		return
 	}
 
+	// Still generate the kati suffix in soong-only builds because soong-only still uses kati for
+	// the packaging step. Also, the kati suffix is used for the combined ninja file.
+	genKatiSuffix(ctx, config)
+
 	if what&RunSoong != 0 {
 		runSoong(ctx, config)
 	}
 
 	if what&RunKati != 0 {
-		genKatiSuffix(ctx, config)
 		runKatiCleanSpec(ctx, config)
 		runKatiBuild(ctx, config)
-		runKatiPackage(ctx, config)
+		runKatiPackage(ctx, config, false)
 
-		ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0666) // a+rw
 	} else if what&RunKatiNinja != 0 {
 		// Load last Kati Suffix if it exists
-		if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil {
+		if katiSuffix, err := os.ReadFile(config.LastKatiSuffixFile()); err == nil {
 			ctx.Verboseln("Loaded previous kati config:", string(katiSuffix))
 			config.SetKatiSuffix(string(katiSuffix))
 		}
+	} else if what&RunSoong != 0 {
+		runKatiPackage(ctx, config, true)
 	}
 
+	os.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0666) // a+rw
+
 	// Write combined ninja file
 	createCombinedBuildNinjaFile(ctx, config)
 
@@ -373,6 +401,7 @@
 		if what&RunKati != 0 {
 			installCleanIfNecessary(ctx, config)
 		}
+		partialCompileCleanIfNecessary(ctx, config)
 		runNinjaForBuild(ctx, config)
 		updateBuildIdDir(ctx, config)
 	}
@@ -399,6 +428,9 @@
 	if config.Checkbuild() {
 		what |= RunBuildTests
 	}
+	if value, ok := config.environ.Get("RUN_BUILD_TESTS"); ok && value == "true" {
+		what |= RunBuildTests
+	}
 	if !config.SkipConfig() {
 		what |= RunProductConfig
 	} else {
@@ -502,4 +534,5 @@
 // Be careful, anything added here slows down EVERY CI build
 func runDistActions(ctx Context, config Config) {
 	runStagingSnapshot(ctx, config)
+	runSourceInputs(ctx, config)
 }
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 46e38c9..3df8b7a 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -219,6 +219,52 @@
 	writeConfig()
 }
 
+// When SOONG_USE_PARTIAL_COMPILE transitions from on to off, we need to remove
+// all files which were potentially built with partial compile, so that they
+// get rebuilt with that turned off.
+func partialCompileCleanIfNecessary(ctx Context, config Config) {
+	configFile := config.DevicePreviousUsePartialCompile()
+	currentValue, _ := config.Environment().Get("SOONG_USE_PARTIAL_COMPILE")
+
+	ensureDirectoriesExist(ctx, filepath.Dir(configFile))
+
+	writeValue := func() {
+		err := ioutil.WriteFile(configFile, []byte(currentValue), 0666) // a+rw
+		if err != nil {
+			ctx.Fatalln("Failed to write use partial compile config:", err)
+		}
+	}
+
+	previousValueBytes, err := ioutil.ReadFile(configFile)
+	if err != nil {
+		if os.IsNotExist(err) {
+			// Just write the new config file, no old config file to worry about.
+			writeValue()
+			return
+		} else {
+			ctx.Fatalln("Failed to read previous use partial compile config:", err)
+		}
+	}
+
+	previousValue := string(previousValueBytes)
+	switch previousValue {
+	case currentValue:
+		// Same value as before - nothing left to do here.
+		return
+	case "true":
+		// Transitioning from on to off.  Build (phony) target: partialcompileclean.
+		ctx.BeginTrace(metrics.PrimaryNinja, "partialcompileclean")
+		defer ctx.EndTrace()
+
+		ctx.Printf("SOONG_USE_PARTIAL_COMPILE turned off, forcing partialcompileclean\n")
+
+		runNinja(ctx, config, []string{"partialcompileclean"})
+	default:
+		// Transitioning from off to on.  Nothing to do in this case.
+	}
+	writeValue()
+}
+
 // cleanOldFiles takes an input file (with all paths relative to basePath), and removes files from
 // the filesystem if they were removed from the input file since the last execution.
 func cleanOldFiles(ctx Context, basePath, newFile string) {
diff --git a/ui/build/config.go b/ui/build/config.go
index 209404e..94b0781 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -63,6 +63,7 @@
 	NINJA_NINJA
 	NINJA_N2
 	NINJA_SISO
+	NINJA_NINJAGO
 )
 
 type Config struct{ *configImpl }
@@ -77,26 +78,31 @@
 	logsPrefix    string
 
 	// From the arguments
-	parallel                 int
-	keepGoing                int
-	verbose                  bool
-	checkbuild               bool
-	dist                     bool
-	jsonModuleGraph          bool
-	reportMkMetrics          bool // Collect and report mk2bp migration progress metrics.
-	soongDocs                bool
-	skipConfig               bool
-	skipKati                 bool
-	skipKatiNinja            bool
-	skipSoong                bool
-	skipNinja                bool
-	skipSoongTests           bool
-	searchApiDir             bool // Scan the Android.bp files generated in out/api_surfaces
-	skipMetricsUpload        bool
-	buildStartedTime         int64 // For metrics-upload-only - manually specify a build-started time
-	buildFromSourceStub      bool
-	incrementalBuildActions  bool
-	ensureAllowlistIntegrity bool // For CI builds - make sure modules are mixed-built
+	parallel        int
+	keepGoing       int
+	verbose         bool
+	checkbuild      bool
+	dist            bool
+	jsonModuleGraph bool
+	reportMkMetrics bool // Collect and report mk2bp migration progress metrics.
+	soongDocs       bool
+	skipConfig      bool
+	// Either the user or product config requested that we skip soong (for the banner). The other
+	// skip flags tell whether *this* soong_ui invocation will skip kati - which will be true
+	// during lunch.
+	soongOnlyRequested        bool
+	skipKati                  bool
+	skipKatiControlledByFlags bool
+	skipKatiNinja             bool
+	skipSoong                 bool
+	skipNinja                 bool
+	skipSoongTests            bool
+	searchApiDir              bool // Scan the Android.bp files generated in out/api_surfaces
+	skipMetricsUpload         bool
+	buildStartedTime          int64 // For metrics-upload-only - manually specify a build-started time
+	buildFromSourceStub       bool
+	incrementalBuildActions   bool
+	ensureAllowlistIntegrity  bool // For CI builds - make sure modules are mixed-built
 
 	// From the product config
 	katiArgs        []string
@@ -250,6 +256,19 @@
 	}
 	ret.parseArgs(ctx, args)
 
+	if value, ok := ret.environ.Get("SOONG_ONLY"); ok && !ret.skipKatiControlledByFlags {
+		if value == "true" || value == "1" || value == "y" || value == "yes" {
+			ret.soongOnlyRequested = true
+			ret.skipKatiControlledByFlags = true
+			ret.skipKati = true
+			ret.skipKatiNinja = true
+		} else {
+			ret.skipKatiControlledByFlags = true
+			ret.skipKati = false
+			ret.skipKatiNinja = false
+		}
+	}
+
 	if ret.ninjaWeightListSource == HINT_FROM_SOONG {
 		ret.environ.Set("SOONG_GENERATES_NINJA_HINT", "always")
 	} else if ret.ninjaWeightListSource == DEFAULT {
@@ -308,16 +327,13 @@
 
 	// If SOONG_USE_PARTIAL_COMPILE is set, make it one of "true" or the empty string.
 	// This simplifies the generated Ninja rules, so that they only need to check for the empty string.
-	if value, ok := os.LookupEnv("SOONG_USE_PARTIAL_COMPILE"); ok {
+	if value, ok := ret.environ.Get("SOONG_USE_PARTIAL_COMPILE"); ok {
 		if value == "true" || value == "1" || value == "y" || value == "yes" {
 			value = "true"
 		} else {
 			value = ""
 		}
-		err = os.Setenv("SOONG_USE_PARTIAL_COMPILE", value)
-		if err != nil {
-			ctx.Fatalln("Failed to set SOONG_USE_PARTIAL_COMPILE: %v", err)
-		}
+		ret.environ.Set("SOONG_USE_PARTIAL_COMPILE", value)
 	}
 
 	ret.ninjaCommand = NINJA_NINJA
@@ -326,6 +342,8 @@
 		ret.ninjaCommand = NINJA_N2
 	case "siso":
 		ret.ninjaCommand = NINJA_SISO
+	case "ninjago":
+		ret.ninjaCommand = NINJA_NINJAGO
 	default:
 		if os.Getenv("SOONG_USE_N2") == "true" {
 			ret.ninjaCommand = NINJA_N2
@@ -392,6 +410,9 @@
 		// Use config.ninjaCommand instead.
 		"SOONG_NINJA",
 		"SOONG_USE_N2",
+
+		// Already incorporated into the config object
+		"SOONG_ONLY",
 	)
 
 	if ret.UseGoma() || ret.ForceUseGoma() {
@@ -598,11 +619,27 @@
 }
 
 func buildConfig(config Config) *smpb.BuildConfig {
+	var soongEnvVars *smpb.SoongEnvVars
+	ensure := func() *smpb.SoongEnvVars {
+		// Create soongEnvVars if it doesn't already exist.
+		if soongEnvVars == nil {
+			soongEnvVars = &smpb.SoongEnvVars{}
+		}
+		return soongEnvVars
+	}
+	if value, ok := config.environ.Get("SOONG_PARTIAL_COMPILE"); ok {
+		ensure().PartialCompile = proto.String(value)
+	}
+	if value, ok := config.environ.Get("SOONG_USE_PARTIAL_COMPILE"); ok {
+		ensure().UsePartialCompile = proto.String(value)
+	}
 	c := &smpb.BuildConfig{
 		ForceUseGoma:          proto.Bool(config.ForceUseGoma()),
 		UseGoma:               proto.Bool(config.UseGoma()),
 		UseRbe:                proto.Bool(config.UseRBE()),
 		NinjaWeightListSource: getNinjaWeightListSourceInMetric(config.NinjaWeightListSource()),
+		SoongEnvVars:          soongEnvVars,
+		SoongOnly:             proto.Bool(config.soongOnlyRequested),
 	}
 	c.Targets = append(c.Targets, config.arguments...)
 
@@ -831,15 +868,21 @@
 			c.emptyNinjaFile = true
 		} else if arg == "--skip-ninja" {
 			c.skipNinja = true
-		} else if arg == "--skip-make" {
-			// TODO(ccross): deprecate this, it has confusing behaviors.  It doesn't run kati,
-			//   but it does run a Kati ninja file if the .kati_enabled marker file was created
-			//   by a previous build.
-			c.skipConfig = true
-			c.skipKati = true
 		} else if arg == "--soong-only" {
+			if c.skipKatiControlledByFlags {
+				ctx.Fatalf("Cannot specify both --soong-only and --no-soong-only")
+			}
+			c.soongOnlyRequested = true
+			c.skipKatiControlledByFlags = true
 			c.skipKati = true
 			c.skipKatiNinja = true
+		} else if arg == "--no-soong-only" {
+			if c.skipKatiControlledByFlags {
+				ctx.Fatalf("Cannot specify both --soong-only and --no-soong-only")
+			}
+			c.skipKatiControlledByFlags = true
+			c.skipKati = false
+			c.skipKatiNinja = false
 		} else if arg == "--config-only" {
 			c.skipKati = true
 			c.skipKatiNinja = true
@@ -1058,7 +1101,7 @@
 
 func (c *configImpl) NinjaArgs() []string {
 	if c.skipKati {
-		return c.arguments
+		return append(c.arguments, c.ninjaArgs...)
 	}
 	return c.ninjaArgs
 }
@@ -1074,11 +1117,18 @@
 func (c *configImpl) PrebuiltOS() string {
 	switch runtime.GOOS {
 	case "linux":
-		return "linux-x86"
+		switch runtime.GOARCH {
+		case "amd64":
+			return "linux-x86"
+		case "arm64":
+			return "linux-arm64"
+		default:
+			panic(fmt.Errorf("Unknown GOARCH %s", runtime.GOARCH))
+		}
 	case "darwin":
 		return "darwin-x86"
 	default:
-		panic("Unknown GOOS")
+		panic(fmt.Errorf("Unknown GOOS %s", runtime.GOOS))
 	}
 }
 
@@ -1327,6 +1377,10 @@
 }
 
 func (c *configImpl) UseABFS() bool {
+	if c.ninjaCommand == NINJA_NINJAGO {
+		return true
+	}
+
 	if v, ok := c.environ.Get("NO_ABFS"); ok {
 		v = strings.ToLower(strings.TrimSpace(v))
 		if v == "true" || v == "1" {
@@ -1580,6 +1634,10 @@
 	return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+katiPackageSuffix+".ninja")
 }
 
+func (c *configImpl) KatiSoongOnlyPackageNinjaFile() string {
+	return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+katiSoongOnlyPackageSuffix+".ninja")
+}
+
 func (c *configImpl) SoongVarsFile() string {
 	targetProduct, err := c.TargetProductOrErr()
 	if err != nil {
@@ -1634,8 +1692,12 @@
 	return filepath.Join(c.ProductOut(), "previous_build_config.mk")
 }
 
+func (c *configImpl) DevicePreviousUsePartialCompile() string {
+	return filepath.Join(c.ProductOut(), "previous_use_partial_compile.txt")
+}
+
 func (c *configImpl) KatiPackageMkDir() string {
-	return filepath.Join(c.ProductOut(), "obj", "CONFIG", "kati_packaging")
+	return filepath.Join(c.SoongOutDir(), "kati_packaging"+c.KatiSuffix())
 }
 
 func (c *configImpl) hostOutRoot() string {
@@ -1656,13 +1718,7 @@
 }
 
 func (c *configImpl) HostPrebuiltTag() string {
-	if runtime.GOOS == "linux" {
-		return "linux-x86"
-	} else if runtime.GOOS == "darwin" {
-		return "darwin-x86"
-	} else {
-		panic("Unsupported OS")
-	}
+	return c.PrebuiltOS()
 }
 
 func (c *configImpl) KatiBin() string {
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index b42edb0..10de1ad 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -30,8 +30,6 @@
 	smpb "android/soong/ui/metrics/metrics_proto"
 	"android/soong/ui/status"
 
-	"google.golang.org/protobuf/encoding/prototext"
-
 	"google.golang.org/protobuf/proto"
 )
 
@@ -1006,6 +1004,12 @@
 	}
 }
 
+func assertEquals[T ~bool | ~int32](t *testing.T, name string, expected, actual T) {
+	if expected != actual {
+		t.Errorf("Expected %s: %#v\nActual %s: %#v", name, expected, name, actual)
+	}
+}
+
 func TestBuildConfig(t *testing.T) {
 	tests := []struct {
 		name                string
@@ -1063,12 +1067,11 @@
 				arguments: tc.arguments,
 			}
 			config := Config{c}
-			actualBuildConfig := buildConfig(config)
-			if expected := tc.expectedBuildConfig; !proto.Equal(expected, actualBuildConfig) {
-				t.Errorf("Build config mismatch.\n"+
-					"Expected build config: %#v\n"+
-					"Actual build config: %#v", prototext.Format(expected), prototext.Format(actualBuildConfig))
-			}
+			actual := buildConfig(config)
+			assertEquals(t, "ForceUseGoma", *tc.expectedBuildConfig.ForceUseGoma, *actual.ForceUseGoma)
+			assertEquals(t, "UseGoma", *tc.expectedBuildConfig.UseGoma, *actual.UseGoma)
+			assertEquals(t, "UseRbe", *tc.expectedBuildConfig.UseRbe, *actual.UseRbe)
+			assertEquals(t, "NinjaWeightListSource", *tc.expectedBuildConfig.NinjaWeightListSource, *actual.NinjaWeightListSource)
 		})
 	}
 }
diff --git a/ui/build/context.go b/ui/build/context.go
index fd20e26..69e5f96 100644
--- a/ui/build/context.go
+++ b/ui/build/context.go
@@ -18,6 +18,7 @@
 	"context"
 	"io"
 
+	"android/soong/ui/execution_metrics"
 	"android/soong/ui/logger"
 	"android/soong/ui/metrics"
 	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
@@ -33,7 +34,8 @@
 	context.Context
 	logger.Logger
 
-	Metrics *metrics.Metrics
+	Metrics          *metrics.Metrics
+	ExecutionMetrics *execution_metrics.ExecutionMetrics
 
 	Writer io.Writer
 	Status *status.Status
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 06b1185..0f1a3dd 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -137,6 +137,12 @@
 		}
 	}
 	if ctx.Metrics != nil {
+		// Also include TARGET_RELEASE in the metrics.  Do this first
+		// so that it gets overwritten if dumpvars ever spits it out.
+		if release, found := os.LookupEnv("TARGET_RELEASE"); found {
+			ctx.Metrics.SetMetadataMetrics(
+				map[string]string{"TARGET_RELEASE": release})
+		}
 		ctx.Metrics.SetMetadataMetrics(ret)
 	}
 
@@ -167,7 +173,7 @@
 	"ROM_BUILDTYPE",
 }
 
-func Banner(make_vars map[string]string) string {
+func Banner(config Config, make_vars map[string]string) string {
 	b := &bytes.Buffer{}
 
 	fmt.Fprintln(b, "============================================")
@@ -176,6 +182,12 @@
 			fmt.Fprintf(b, "%s=%s\n", name, make_vars[name])
 		}
 	}
+	if config.skipKatiControlledByFlags {
+		fmt.Fprintf(b, "SOONG_ONLY=%t\n", config.soongOnlyRequested)
+	} else { // default for this product
+		fmt.Fprintf(b, "SOONG_ONLY=%t\n", make_vars["PRODUCT_SOONG_ONLY"] == "true")
+	}
+
 	fmt.Fprint(b, "============================================")
 
 	return b.String()
@@ -241,6 +253,8 @@
 		// `true` will relegate missing outputs to warnings.
 		"BUILD_BROKEN_MISSING_OUTPUTS",
 
+		"PRODUCT_SOONG_ONLY",
+
 		// Not used, but useful to be in the soong.log
 		"TARGET_BUILD_TYPE",
 		"HOST_ARCH",
@@ -274,6 +288,7 @@
 		"BUILD_BROKEN_USES_BUILD_SHARED_LIBRARY",
 		"BUILD_BROKEN_USES_BUILD_STATIC_JAVA_LIBRARY",
 		"BUILD_BROKEN_USES_BUILD_STATIC_LIBRARY",
+		"RELEASE_BUILD_EXECUTION_METRICS",
 	}, exportEnvVars...), BannerVars...)
 
 	makeVars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true, "")
@@ -281,13 +296,8 @@
 		ctx.Fatalln("Error dumping make vars:", err)
 	}
 
-	env := config.Environment()
-	// Print the banner like make does
-	if !env.IsEnvTrue("ANDROID_QUIET_BUILD") {
-		fmt.Fprintln(ctx.Writer, Banner(makeVars))
-	}
-
 	// Populate the environment
+	env := config.Environment()
 	for _, name := range exportEnvVars {
 		if makeVars[name] == "" {
 			env.Unset(name)
@@ -308,4 +318,17 @@
 	config.SetBuildBrokenNinjaUsesEnvVars(strings.Fields(makeVars["BUILD_BROKEN_NINJA_USES_ENV_VARS"]))
 	config.SetSourceRootDirs(strings.Fields(makeVars["PRODUCT_SOURCE_ROOT_DIRS"]))
 	config.SetBuildBrokenMissingOutputs(makeVars["BUILD_BROKEN_MISSING_OUTPUTS"] == "true")
+
+	if !config.skipKatiControlledByFlags {
+		if makeVars["PRODUCT_SOONG_ONLY"] == "true" {
+			config.soongOnlyRequested = true
+			config.skipKati = true
+			config.skipKatiNinja = true
+		}
+	}
+
+	// Print the banner like make did
+	if !env.IsEnvTrue("ANDROID_QUIET_BUILD") {
+		fmt.Fprintln(ctx.Writer, Banner(config, makeVars))
+	}
 }
diff --git a/ui/build/finder.go b/ui/build/finder.go
index da7f255..ff8908b 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -84,8 +84,14 @@
 			// METADATA file of packages
 			"METADATA",
 		},
-		// .mk files for product/board configuration.
-		IncludeSuffixes: []string{".mk"},
+		IncludeSuffixes: []string{
+			// .mk files for product/board configuration.
+			".mk",
+			// otatools cert files
+			".pk8",
+			".pem",
+			".avbpubkey",
+		},
 	}
 	dumpDir := config.FileListDir()
 	f, err = finder.New(cacheParams, filesystem, logger.New(ioutil.Discard),
@@ -118,6 +124,18 @@
 	return entries.DirNames, matches
 }
 
+func findOtaToolsCertFiles(entries finder.DirEntries) (dirNames []string, fileNames []string) {
+	matches := []string{}
+	for _, foundName := range entries.FileNames {
+		if strings.HasSuffix(foundName, ".pk8") ||
+			strings.HasSuffix(foundName, ".pem") ||
+			strings.HasSuffix(foundName, ".avbpubkey") {
+			matches = append(matches, foundName)
+		}
+	}
+	return entries.DirNames, matches
+}
+
 // FindSources searches for source files known to <f> and writes them to the filesystem for
 // use later.
 func FindSources(ctx Context, config Config, f *finder.Finder) {
@@ -128,7 +146,7 @@
 
 	// Stop searching a subdirectory recursively after finding an Android.mk.
 	androidMks := f.FindFirstNamedAt(".", "Android.mk")
-	androidMks = ignoreNdkAndroidMks(androidMks)
+	androidMks = ignoreSomeAndroidMks(androidMks)
 	blockAndroidMks(ctx, androidMks)
 	err := dumpListToFile(ctx, config, androidMks, filepath.Join(dumpDir, "Android.mk.list"))
 	if err != nil {
@@ -184,6 +202,17 @@
 		ctx.Fatalf("Could not find TEST_MAPPING: %v", err)
 	}
 
+	// Recursively look for all otatools cert files.
+	otatools_cert_files := f.FindMatching("build/make/target/product/security", findOtaToolsCertFiles)
+	otatools_cert_files = append(otatools_cert_files, f.FindMatching("device", findOtaToolsCertFiles)...)
+	otatools_cert_files = append(otatools_cert_files, f.FindMatching("external/avb/test/data", findOtaToolsCertFiles)...)
+	otatools_cert_files = append(otatools_cert_files, f.FindMatching("packages/modules", findOtaToolsCertFiles)...)
+	otatools_cert_files = append(otatools_cert_files, f.FindMatching("vendor", findOtaToolsCertFiles)...)
+	err = dumpListToFile(ctx, config, otatools_cert_files, filepath.Join(dumpDir, "OtaToolsCertFiles.list"))
+	if err != nil {
+		ctx.Fatalf("Could not find otatools cert files: %v", err)
+	}
+
 	// Recursively look for all Android.bp files
 	androidBps := f.FindNamedAt(".", "Android.bp")
 	if len(androidBps) == 0 {
diff --git a/ui/build/kati.go b/ui/build/kati.go
index 4dfb710..6519573 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -31,6 +31,7 @@
 const katiBuildSuffix = ""
 const katiCleanspecSuffix = "-cleanspec"
 const katiPackageSuffix = "-package"
+const katiSoongOnlyPackageSuffix = "-soong-only-package"
 
 // genKatiSuffix creates a filename suffix for kati-generated files so that we
 // can cache them based on their inputs. Such files include the generated Ninja
@@ -40,8 +41,12 @@
 // Currently that includes the TARGET_PRODUCT and kati-processed command line
 // arguments.
 func genKatiSuffix(ctx Context, config Config) {
+	targetProduct := "unknown"
+	if p, err := config.TargetProductOrErr(); err == nil {
+		targetProduct = p
+	}
 	// Construct the base suffix.
-	katiSuffix := "-" + config.TargetProduct() + config.CoverageSuffix()
+	katiSuffix := "-" + targetProduct + config.CoverageSuffix()
 
 	// Append kati arguments to the suffix.
 	if args := config.KatiArgs(); len(args) > 0 {
@@ -68,13 +73,13 @@
 func writeValueIfChanged(ctx Context, config Config, dir string, filename string, value string) {
 	filePath := filepath.Join(dir, filename)
 	previousValue := ""
-	rawPreviousValue, err := ioutil.ReadFile(filePath)
+	rawPreviousValue, err := os.ReadFile(filePath)
 	if err == nil {
 		previousValue = string(rawPreviousValue)
 	}
 
 	if previousValue != value {
-		if err = ioutil.WriteFile(filePath, []byte(value), 0666); err != nil {
+		if err = os.WriteFile(filePath, []byte(value), 0666); err != nil {
 			ctx.Fatalf("Failed to write: %v", err)
 		}
 	}
@@ -200,18 +205,10 @@
 	//     fi
 	cmd.Environment.Unset("SOONG_USE_PARTIAL_COMPILE")
 
-	hostname, ok := cmd.Environment.Get("BUILD_HOSTNAME")
 	// Unset BUILD_HOSTNAME during kati run to avoid kati rerun, kati will use BUILD_HOSTNAME from a file.
 	cmd.Environment.Unset("BUILD_HOSTNAME")
-	if !ok {
-		hostname, err = os.Hostname()
-		if err != nil {
-			ctx.Println("Failed to read hostname:", err)
-			hostname = "unknown"
-		}
-	}
-	writeValueIfChanged(ctx, config, config.SoongOutDir(), "build_hostname.txt", hostname)
-	_, ok = cmd.Environment.Get("BUILD_NUMBER")
+
+	_, ok := cmd.Environment.Get("BUILD_NUMBER")
 	// Unset BUILD_NUMBER during kati run to avoid kati rerun, kati will use BUILD_NUMBER from a file.
 	cmd.Environment.Unset("BUILD_NUMBER")
 	if ok {
@@ -342,10 +339,19 @@
 
 // Generate the Ninja file containing the packaging command lines for the dist
 // dir.
-func runKatiPackage(ctx Context, config Config) {
+func runKatiPackage(ctx Context, config Config, soongOnly bool) {
 	ctx.BeginTrace(metrics.RunKati, "kati package")
 	defer ctx.EndTrace()
 
+	entryPoint := "build/make/packaging/main.mk"
+	suffix := katiPackageSuffix
+	ninjaFile := config.KatiPackageNinjaFile()
+	if soongOnly {
+		entryPoint = "build/make/packaging/main_soong_only.mk"
+		suffix = katiSoongOnlyPackageSuffix
+		ninjaFile = config.KatiSoongOnlyPackageNinjaFile()
+	}
+
 	args := []string{
 		// Mark the dist dir as writable.
 		"--writable", config.DistDir() + "/",
@@ -354,14 +360,14 @@
 		// Fail when redefining / duplicating a target.
 		"--werror_overriding_commands",
 		// Entry point.
-		"-f", "build/make/packaging/main.mk",
+		"-f", entryPoint,
 		// Directory containing .mk files for packaging purposes, such as
 		// the dist.mk file, containing dist-for-goals data.
 		"KATI_PACKAGE_MK_DIR=" + config.KatiPackageMkDir(),
 	}
 
 	// Run Kati against a restricted set of environment variables.
-	runKati(ctx, config, katiPackageSuffix, args, func(env *Environment) {
+	runKati(ctx, config, suffix, args, func(env *Environment) {
 		env.Allow([]string{
 			// Some generic basics
 			"LANG",
@@ -389,7 +395,7 @@
 	})
 
 	// Compress and dist the packaging Ninja file.
-	distGzipFile(ctx, config, config.KatiPackageNinjaFile())
+	distGzipFile(ctx, config, ninjaFile)
 }
 
 // Run Kati on the cleanspec files to clean the build.
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index f5f637f..e2a568f 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"sort"
 	"strconv"
@@ -35,10 +36,16 @@
 	ninjaWeightListFileName = ".ninja_weight_list"
 )
 
+// Runs ninja with the arguments from the command line, as found in
+// config.NinjaArgs().
+func runNinjaForBuild(ctx Context, config Config) {
+	runNinja(ctx, config, config.NinjaArgs())
+}
+
 // Constructs and runs the Ninja command line with a restricted set of
 // environment variables. It's important to restrict the environment Ninja runs
 // for hermeticity reasons, and to avoid spurious rebuilds.
-func runNinjaForBuild(ctx Context, config Config) {
+func runNinja(ctx Context, config Config, ninjaArgs []string) {
 	ctx.BeginTrace(metrics.PrimaryNinja, "ninja")
 	defer ctx.EndTrace()
 
@@ -75,7 +82,7 @@
 			//"--frontend-file", fifo,
 		}
 	default:
-		// NINJA_NINJA is the default.
+		// NINJA_NINJA or NINJA_NINJAGO.
 		executable = config.NinjaBin()
 		args = []string{
 			"-d", "keepdepfile",
@@ -87,7 +94,7 @@
 			"-w", "missingdepfile=err",
 		}
 	}
-	args = append(args, config.NinjaArgs()...)
+	args = append(args, ninjaArgs...)
 
 	var parallel int
 	if config.UseRemoteBuild() {
@@ -243,7 +250,12 @@
 			"RUST_LOG",
 
 			// SOONG_USE_PARTIAL_COMPILE only determines which half of the rule we execute.
+			// When it transitions true => false, we build phony target "partialcompileclean",
+			// which removes all files that could have been created while it was true.
 			"SOONG_USE_PARTIAL_COMPILE",
+
+			// Directory for ExecutionMetrics
+			"SOONG_METRICS_AGGREGATION_DIR",
 		}, config.BuildBrokenNinjaUsesEnvVars()...)...)
 	}
 
@@ -256,6 +268,10 @@
 		// Only set RUST_BACKTRACE for n2.
 	}
 
+	// Set up the metrics aggregation directory.
+	ctx.ExecutionMetrics.SetDir(filepath.Join(config.OutDir(), "soong", "metrics_aggregation"))
+	cmd.Environment.Set("SOONG_METRICS_AGGREGATION_DIR", ctx.ExecutionMetrics.MetricsAggregationDir)
+
 	// Print the environment variables that Ninja is operating in.
 	ctx.Verboseln("Ninja environment: ")
 	envVars := cmd.Environment.Environ()
@@ -300,6 +316,8 @@
 		}
 	}()
 
+	ctx.ExecutionMetrics.Start()
+	defer ctx.ExecutionMetrics.Finish(ctx)
 	ctx.Status.Status("Starting ninja...")
 	cmd.RunAndStreamOrFatal()
 }
@@ -339,3 +357,46 @@
 	}
 	c.prevModTime = newModTime
 }
+
+// Constructs and runs the Ninja command line to get the inputs of a goal.
+// For n2 and siso, this will always run ninja, because they don't have the
+// `-t inputs` command.  This command will use the inputs command's -d option,
+// to use the dep file iff ninja was the executor. For other executors, the
+// results will be wrong.
+func runNinjaInputs(ctx Context, config Config, goal string) ([]string, error) {
+	var executable string
+	switch config.ninjaCommand {
+	case NINJA_N2, NINJA_SISO:
+		executable = config.PrebuiltBuildTool("ninja")
+	default:
+		executable = config.NinjaBin()
+	}
+
+	args := []string{
+		"-f",
+		config.CombinedNinjaFile(),
+		"-t",
+		"inputs",
+	}
+	// Add deps file arg for ninja
+	// TODO: Update as inputs command is implemented
+	if config.ninjaCommand == NINJA_NINJA && !config.UseABFS() {
+		args = append(args, "-d")
+	}
+	args = append(args, goal)
+
+	// This is just ninja -t inputs, so we won't bother running it in the sandbox,
+	// so use exec.Command, not soong_ui's command.
+	cmd := exec.Command(executable, args...)
+
+	cmd.Stdin = os.Stdin
+	cmd.Stderr = os.Stderr
+
+	out, err := cmd.Output()
+	if err != nil {
+		fmt.Printf("Error getting goal inputs for %s: %s\n", goal, err)
+		return nil, err
+	}
+
+	return strings.Split(strings.TrimSpace(string(out)), "\n"), nil
+}
diff --git a/ui/build/path.go b/ui/build/path.go
index cc1d7e9..b92d799 100644
--- a/ui/build/path.go
+++ b/ui/build/path.go
@@ -20,7 +20,6 @@
 	"os"
 	"os/exec"
 	"path/filepath"
-	"runtime"
 	"strings"
 
 	"github.com/google/blueprint/microfactory"
@@ -122,7 +121,7 @@
 	myPath, _ = filepath.Abs(myPath)
 
 	// Set up the checked-in prebuilts path directory for the current host OS.
-	prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
+	prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + config.PrebuiltOS())
 	myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
 
 	// Set $PATH to be the directories containing the host tool symlinks, and
@@ -258,7 +257,7 @@
 
 	// We put some prebuilts in $PATH, since it's infeasible to add dependencies
 	// for all of them.
-	prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
+	prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + config.PrebuiltOS())
 	myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
 
 	// Replace the $PATH variable with the path_interposer symlinks, and
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index a532e6d..bd7f3d0 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -42,7 +42,7 @@
 }
 
 // This tool is specifically disallowed and calling it will result in an
-// "executable no found" error.
+// "executable not found" error.
 var Forbidden = PathConfig{
 	Symlink: false,
 	Log:     true,
@@ -124,6 +124,10 @@
 	"ld.bfd":     Forbidden,
 	"ld.gold":    Forbidden,
 	"pkg-config": Forbidden,
+	"python":     Forbidden,
+	"python2":    Forbidden,
+	"python2.7":  Forbidden,
+	"python3":    Forbidden,
 
 	// These are toybox tools that only work on Linux.
 	"pgrep": LinuxOnlyPrebuilt,
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 0963f76..58334a9 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -197,6 +197,8 @@
 func (pb PrimaryBuilderFactory) primaryBuilderInvocation(config Config) bootstrap.PrimaryBuilderInvocation {
 	commonArgs := make([]string, 0, 0)
 
+	commonArgs = append(commonArgs, "--kati_suffix", config.KatiSuffix())
+
 	if !pb.config.skipSoongTests {
 		commonArgs = append(commonArgs, "-t")
 	}
@@ -501,7 +503,7 @@
 	tf := filepath.Join(outDir, ".top")
 	defer func() {
 		if err := os.WriteFile(tf, []byte(cwd), 0644); err != nil {
-			fmt.Fprintf(os.Stderr, fmt.Sprintf("Unable to log CWD: %v", err))
+			fmt.Fprintf(os.Stderr, "Unable to log CWD: %v", err)
 		}
 	}()
 
diff --git a/ui/build/source_inputs.go b/ui/build/source_inputs.go
new file mode 100644
index 0000000..d1cc1a2
--- /dev/null
+++ b/ui/build/source_inputs.go
@@ -0,0 +1,100 @@
+package build
+
+import (
+	"compress/gzip"
+	"fmt"
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+
+	"android/soong/shared"
+	"android/soong/ui/metrics"
+)
+
+func sortedStringSetKeys(m map[string]bool) []string {
+	result := make([]string, 0, len(m))
+	for key := range m {
+		result = append(result, key)
+	}
+	sort.Strings(result)
+	return result
+}
+
+func addSlash(str string) string {
+	if len(str) == 0 {
+		return ""
+	}
+	if str[len(str)-1] == '/' {
+		return str
+	}
+	return str + "/"
+}
+
+func hasPrefixStrings(str string, prefixes []string) bool {
+	for _, prefix := range prefixes {
+		if strings.HasPrefix(str, prefix) {
+			return true
+		}
+	}
+	return false
+}
+
+// Output DIST_DIR/source_inputs.txt.gz, which will contain a listing of the files
+// in the source tree (not including in the out directory) that were declared as ninja
+// inputs to the build that was just done.
+func runSourceInputs(ctx Context, config Config) {
+	ctx.BeginTrace(metrics.RunSoong, "runSourceInputs")
+	defer ctx.EndTrace()
+
+	success := false
+	outputFilename := shared.JoinPath(config.RealDistDir(), "source_inputs.txt.gz")
+
+	outputFile, err := os.Create(outputFilename)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "source_files_used: unable to open file for write: %s\n", outputFilename)
+		return
+	}
+	defer func() {
+		outputFile.Close()
+		if !success {
+			os.Remove(outputFilename)
+		}
+	}()
+
+	output := gzip.NewWriter(outputFile)
+	defer output.Close()
+
+	// Skip out dir, both absolute and relative. There are some files
+	// generated during analysis that ninja thinks are inputs not intermediates.
+	absOut, _ := filepath.Abs(config.OutDir())
+	excludes := []string{
+		addSlash(config.OutDir()),
+		addSlash(absOut),
+	}
+
+	goals := config.NinjaArgs()
+
+	result := make(map[string]bool)
+	for _, goal := range goals {
+		inputs, err := runNinjaInputs(ctx, config, goal)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "source_files_used: %v\n", err)
+			return
+		}
+
+		for _, filename := range inputs {
+			if !hasPrefixStrings(filename, excludes) {
+				result[filename] = true
+			}
+		}
+	}
+
+	for _, filename := range sortedStringSetKeys(result) {
+		output.Write([]byte(filename))
+		output.Write([]byte("\n"))
+	}
+
+	output.Flush()
+	success = true
+}
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index ba53119..87bec93 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -76,8 +76,10 @@
 	// treated as an source file.
 	dexpreoptConfigFilePath := filepath.Join(outDir, "soong", "dexpreopt.config")
 
-	// out/build_date.txt is considered a "source file"
+	// out/build_(date|hostname|number).txt is considered a "source file"
 	buildDatetimeFilePath := filepath.Join(outDir, "build_date.txt")
+	buildHostnameFilePath := filepath.Join(outDir, "soong", "build_hostname.txt")
+	buildNumberFilePath := filepath.Join(outDir, "soong", "build_number.txt")
 
 	// release-config files are generated from the initial lunch or Kati phase
 	// before running soong and ninja.
@@ -102,6 +104,8 @@
 			line == extraVariablesFilePath ||
 			line == dexpreoptConfigFilePath ||
 			line == buildDatetimeFilePath ||
+			line == buildHostnameFilePath ||
+			line == buildNumberFilePath ||
 			strings.HasPrefix(line, releaseConfigDir) ||
 			buildFingerPrintFilePattern.MatchString(line) {
 			// Leaf node is in one of Soong's bootstrap directories, which do not have
@@ -151,7 +155,7 @@
 
 		ts.FinishAction(status.ActionResult{
 			Action: action,
-			Error:  fmt.Errorf(title),
+			Error:  fmt.Errorf("%s", title),
 			Output: sb.String(),
 		})
 		ctx.Fatal("stopping")
diff --git a/ui/execution_metrics/Android.bp b/ui/execution_metrics/Android.bp
new file mode 100644
index 0000000..542e550
--- /dev/null
+++ b/ui/execution_metrics/Android.bp
@@ -0,0 +1,35 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-ui-execution-metrics",
+    pkgPath: "android/soong/ui/execution_metrics",
+    deps: [
+        "golang-protobuf-proto",
+        "soong-shared",
+        "soong-ui-logger",
+        "soong-ui-execution_metrics_proto",
+        "soong-ui-metrics_proto",
+        "soong-cmd-find_input_delta-proto",
+    ],
+    srcs: [
+        "execution_metrics.go",
+    ],
+    testSrcs: [
+    ],
+}
diff --git a/ui/execution_metrics/execution_metrics.go b/ui/execution_metrics/execution_metrics.go
new file mode 100644
index 0000000..db78449
--- /dev/null
+++ b/ui/execution_metrics/execution_metrics.go
@@ -0,0 +1,279 @@
+// 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 execution_metrics represents the metrics system for Android Platform Build Systems.
+package execution_metrics
+
+// This is the main heart of the metrics system for Android Platform Build Systems.
+// The starting of the soong_ui (cmd/soong_ui/main.go), the metrics system is
+// initialized by the invocation of New and is then stored in the context
+// (ui/build/context.go) to be used throughout the system. During the build
+// initialization phase, several functions in this file are invoked to store
+// information such as the environment, build configuration and build metadata.
+// There are several scoped code that has Begin() and defer End() functions
+// that captures the metrics and is them added as a perfInfo into the set
+// of the collected metrics. Finally, when soong_ui has finished the build,
+// the defer Dump function is invoked to store the collected metrics to the
+// raw protobuf file in the $OUT directory and this raw protobuf file will be
+// uploaded to the destination. See ui/build/upload.go for more details. The
+// filename of the raw protobuf file and the list of files to be uploaded is
+// defined in cmd/soong_ui/main.go. See ui/metrics/event.go for the explanation
+// of what an event is and how the metrics system is a stack based system.
+
+import (
+	"context"
+	"io/fs"
+	"maps"
+	"os"
+	"path/filepath"
+	"slices"
+	"sync"
+
+	"android/soong/ui/logger"
+
+	fid_proto "android/soong/cmd/find_input_delta/find_input_delta_proto"
+	"android/soong/ui/metrics"
+	soong_execution_proto "android/soong/ui/metrics/execution_metrics_proto"
+	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
+	"google.golang.org/protobuf/encoding/protowire"
+	"google.golang.org/protobuf/proto"
+)
+
+type ExecutionMetrics struct {
+	MetricsAggregationDir string
+	ctx                   context.Context
+	logger                logger.Logger
+	waitGroup             sync.WaitGroup
+	fileList              *fileList
+}
+
+type fileList struct {
+	totalChanges uint32
+	changes      fileChanges
+	seenFiles    map[string]bool
+}
+
+type fileChanges struct {
+	additions     changeInfo
+	deletions     changeInfo
+	modifications changeInfo
+}
+
+type fileChangeCounts struct {
+	additions     uint32
+	deletions     uint32
+	modifications uint32
+}
+
+type changeInfo struct {
+	total       uint32
+	list        []string
+	byExtension map[string]uint32
+}
+
+var MAXIMUM_FILES uint32 = 50
+
+// Setup the handler for SoongExecutionMetrics.
+func NewExecutionMetrics(log logger.Logger) *ExecutionMetrics {
+	return &ExecutionMetrics{
+		logger:   log,
+		fileList: &fileList{seenFiles: make(map[string]bool)},
+	}
+}
+
+// Save the path for ExecutionMetrics communications.
+func (c *ExecutionMetrics) SetDir(path string) {
+	c.MetricsAggregationDir = path
+}
+
+// Start collecting SoongExecutionMetrics.
+func (c *ExecutionMetrics) Start() {
+	if c.MetricsAggregationDir == "" {
+		return
+	}
+
+	tmpDir := c.MetricsAggregationDir + ".rm"
+	if _, err := fs.Stat(os.DirFS("."), c.MetricsAggregationDir); err == nil {
+		if err = os.RemoveAll(tmpDir); err != nil {
+			c.logger.Fatalf("Failed to remove %s: %v", tmpDir, err)
+		}
+		if err = os.Rename(c.MetricsAggregationDir, tmpDir); err != nil {
+			c.logger.Fatalf("Failed to rename %s to %s: %v", c.MetricsAggregationDir, tmpDir)
+		}
+	}
+	if err := os.MkdirAll(c.MetricsAggregationDir, 0777); err != nil {
+		c.logger.Fatalf("Failed to create %s: %v", c.MetricsAggregationDir)
+	}
+
+	c.waitGroup.Add(1)
+	go func(d string) {
+		defer c.waitGroup.Done()
+		os.RemoveAll(d)
+	}(tmpDir)
+
+	c.logger.Verbosef("ExecutionMetrics running\n")
+}
+
+type hasTrace interface {
+	BeginTrace(name, desc string)
+	EndTrace()
+}
+
+// Aggregate any execution metrics.
+func (c *ExecutionMetrics) Finish(ctx hasTrace) {
+	ctx.BeginTrace(metrics.RunSoong, "execution_metrics.Finish")
+	defer ctx.EndTrace()
+	if c.MetricsAggregationDir == "" {
+		return
+	}
+	c.waitGroup.Wait()
+
+	// Find and process all of the metrics files.
+	aggFs := os.DirFS(c.MetricsAggregationDir)
+	fs.WalkDir(aggFs, ".", func(path string, d fs.DirEntry, err error) error {
+		if err != nil {
+			c.logger.Fatalf("ExecutionMetrics.Finish: Error walking %s: %v", c.MetricsAggregationDir, err)
+		}
+		if d.IsDir() {
+			return nil
+		}
+		path = filepath.Join(c.MetricsAggregationDir, path)
+		r, err := os.ReadFile(path)
+		if err != nil {
+			c.logger.Fatalf("ExecutionMetrics.Finish: Failed to read %s: %v", path, err)
+		}
+		msg := &soong_execution_proto.SoongExecutionMetrics{}
+		err = proto.Unmarshal(r, msg)
+		if err != nil {
+			c.logger.Verbosef("ExecutionMetrics.Finish: Error unmarshalling SoongExecutionMetrics message: %v\n", err)
+			return nil
+		}
+		switch {
+		case msg.GetFileList() != nil:
+			if err := c.fileList.aggregateFileList(msg.GetFileList()); err != nil {
+				c.logger.Verbosef("ExecutionMetrics.Finish: Error processing SoongExecutionMetrics message: %v\n", err)
+			}
+		// Status update for all others.
+		default:
+			tag, _ := protowire.ConsumeVarint(r)
+			id, _ := protowire.DecodeTag(tag)
+			c.logger.Verbosef("ExecutionMetrics.Finish: Unexpected SoongExecutionMetrics submessage id=%d\n", id)
+		}
+		return nil
+	})
+}
+
+func (fl *fileList) aggregateFileList(msg *fid_proto.FileList) error {
+	fl.updateChangeInfo(msg.GetAdditions(), &fl.changes.additions)
+	fl.updateChangeInfo(msg.GetDeletions(), &fl.changes.deletions)
+	fl.updateChangeInfo(msg.GetChanges(), &fl.changes.modifications)
+	return nil
+}
+
+func (fl *fileList) updateChangeInfo(list []string, info *changeInfo) {
+	for _, filename := range list {
+		if fl.seenFiles[filename] {
+			continue
+		}
+		fl.seenFiles[filename] = true
+		if info.total < MAXIMUM_FILES {
+			info.list = append(info.list, filename)
+		}
+		ext := filepath.Ext(filename)
+		if info.byExtension == nil {
+			info.byExtension = make(map[string]uint32)
+		}
+		info.byExtension[ext] += 1
+		info.total += 1
+		fl.totalChanges += 1
+	}
+}
+
+func (c *ExecutionMetrics) Dump(path string, args []string) error {
+	if c.MetricsAggregationDir == "" {
+		return nil
+	}
+	msg := c.GetMetrics(args)
+
+	if _, err := os.Stat(filepath.Dir(path)); err != nil {
+		if err = os.MkdirAll(filepath.Dir(path), 0775); err != nil {
+			return err
+		}
+	}
+	data, err := proto.Marshal(msg)
+	if err != nil {
+		return err
+	}
+	return os.WriteFile(path, data, 0644)
+}
+
+func (c *ExecutionMetrics) GetMetrics(args []string) *soong_metrics_proto.ExecutionMetrics {
+	return &soong_metrics_proto.ExecutionMetrics{
+		CommandArgs:  args,
+		ChangedFiles: c.getChangedFiles(),
+	}
+}
+
+func (c *ExecutionMetrics) getChangedFiles() *soong_metrics_proto.AggregatedFileList {
+	fl := c.fileList
+	if fl == nil {
+		return nil
+	}
+	var count uint32
+	fileCounts := make(map[string]*soong_metrics_proto.FileCount)
+	ret := &soong_metrics_proto.AggregatedFileList{TotalDelta: proto.Uint32(c.fileList.totalChanges)}
+
+	// MAXIMUM_FILES is the upper bound on total file names reported.
+	if limit := min(MAXIMUM_FILES-min(MAXIMUM_FILES, count), fl.changes.additions.total); limit > 0 {
+		ret.Additions = fl.changes.additions.list[:limit]
+		count += limit
+	}
+	if limit := min(MAXIMUM_FILES-min(MAXIMUM_FILES, count), fl.changes.modifications.total); limit > 0 {
+		ret.Changes = fl.changes.modifications.list[:limit]
+		count += limit
+	}
+	if limit := min(MAXIMUM_FILES-min(MAXIMUM_FILES, count), fl.changes.deletions.total); limit > 0 {
+		ret.Deletions = fl.changes.deletions.list[:limit]
+		count += limit
+	}
+
+	addExt := func(key string) *soong_metrics_proto.FileCount {
+		// Create the fileCounts map entry if needed, and return the address to the caller.
+		if _, ok := fileCounts[key]; !ok {
+			fileCounts[key] = &soong_metrics_proto.FileCount{Extension: proto.String(key)}
+		}
+		return fileCounts[key]
+	}
+	addCount := func(loc **uint32, count uint32) {
+		if *loc == nil {
+			*loc = proto.Uint32(0)
+		}
+		**loc += count
+	}
+	for k, v := range fl.changes.additions.byExtension {
+		addCount(&addExt(k).Additions, v)
+	}
+	for k, v := range fl.changes.modifications.byExtension {
+		addCount(&addExt(k).Modifications, v)
+	}
+	for k, v := range fl.changes.deletions.byExtension {
+		addCount(&addExt(k).Deletions, v)
+	}
+
+	keys := slices.Sorted(maps.Keys(fileCounts))
+	for _, k := range keys {
+		ret.Counts = append(ret.Counts, fileCounts[k])
+	}
+	return ret
+}
diff --git a/ui/execution_metrics/execution_metrics_test.go b/ui/execution_metrics/execution_metrics_test.go
new file mode 100644
index 0000000..28fa973
--- /dev/null
+++ b/ui/execution_metrics/execution_metrics_test.go
@@ -0,0 +1,63 @@
+// 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 execution_metrics represents the metrics system for Android Platform Build Systems.
+package execution_metrics
+
+import (
+	"reflect"
+	"testing"
+
+	fid_proto "android/soong/cmd/find_input_delta/find_input_delta_proto"
+)
+
+func TestUpdateChangeInfo(t *testing.T) {
+	testCases := []struct {
+		Name     string
+		Message  *fid_proto.FileList
+		FileList *fileList
+		Expected *fileList
+	}{
+		{
+			Name: "various",
+			Message: &fid_proto.FileList{
+				Additions: []string{"file1", "file2", "file3", "file2"},
+				Deletions: []string{"file5.go", "file6"},
+			},
+			FileList: &fileList{seenFiles: make(map[string]bool)},
+			Expected: &fileList{
+				seenFiles:    map[string]bool{"file1": true, "file2": true, "file3": true, "file5.go": true, "file6": true},
+				totalChanges: 5,
+				changes: fileChanges{
+					additions: changeInfo{
+						total:       3,
+						list:        []string{"file1", "file2", "file3"},
+						byExtension: map[string]uint32{"": 3},
+					},
+					deletions: changeInfo{
+						total:       2,
+						list:        []string{"file5.go", "file6"},
+						byExtension: map[string]uint32{"": 1, ".go": 1},
+					},
+				},
+			},
+		},
+	}
+	for _, tc := range testCases {
+		tc.FileList.aggregateFileList(tc.Message)
+		if !reflect.DeepEqual(tc.FileList, tc.Expected) {
+			t.Errorf("Expected: %v, Actual: %v", tc.Expected, tc.FileList)
+		}
+	}
+}
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index 591e3cc..2e19f7a 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -26,7 +26,7 @@
         "soong-ui-metrics_proto",
         "soong-ui-mk_metrics_proto",
         "soong-shared",
-        "soong-ui-metrics_combined_proto",
+        "soong-ui-execution_metrics_proto",
     ],
     srcs: [
         "hostinfo.go",
@@ -64,15 +64,15 @@
 }
 
 bootstrap_go_package {
-    name: "soong-ui-metrics_combined_proto",
-    pkgPath: "android/soong/ui/metrics/combined_metrics_proto",
+    name: "soong-ui-execution_metrics_proto",
+    pkgPath: "android/soong/ui/metrics/execution_metrics_proto",
     deps: [
         "golang-protobuf-reflect-protoreflect",
         "golang-protobuf-runtime-protoimpl",
         "soong-cmd-find_input_delta-proto",
     ],
     srcs: [
-        "metrics_proto/metrics.pb.go",
+        "execution_metrics_proto/execution_metrics.pb.go",
     ],
 }
 
diff --git a/ui/metrics/execution_metrics_proto/execution_metrics.pb.go b/ui/metrics/execution_metrics_proto/execution_metrics.pb.go
new file mode 100644
index 0000000..a065dbd
--- /dev/null
+++ b/ui/metrics/execution_metrics_proto/execution_metrics.pb.go
@@ -0,0 +1,240 @@
+// 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.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.30.0
+// 	protoc        v3.21.12
+// source: execution_metrics.proto
+
+package execution_metrics_proto
+
+import (
+	find_input_delta_proto "android/soong/cmd/find_input_delta/find_input_delta_proto"
+	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)
+)
+
+// These field numbers are also found in the inner message declarations.
+// We verify that the values are the same, and that every enum value is checked
+// in execution_metrics_test.go.
+// Do not change this enum without also updating:
+//   - the submessage's .proto file
+//   - execution_metrics_test.go
+type FieldNumbers int32
+
+const (
+	FieldNumbers_FIELD_NUMBERS_UNSPECIFIED FieldNumbers = 0
+	FieldNumbers_FIELD_NUMBERS_FILE_LIST   FieldNumbers = 1
+)
+
+// Enum value maps for FieldNumbers.
+var (
+	FieldNumbers_name = map[int32]string{
+		0: "FIELD_NUMBERS_UNSPECIFIED",
+		1: "FIELD_NUMBERS_FILE_LIST",
+	}
+	FieldNumbers_value = map[string]int32{
+		"FIELD_NUMBERS_UNSPECIFIED": 0,
+		"FIELD_NUMBERS_FILE_LIST":   1,
+	}
+)
+
+func (x FieldNumbers) Enum() *FieldNumbers {
+	p := new(FieldNumbers)
+	*p = x
+	return p
+}
+
+func (x FieldNumbers) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (FieldNumbers) Descriptor() protoreflect.EnumDescriptor {
+	return file_execution_metrics_proto_enumTypes[0].Descriptor()
+}
+
+func (FieldNumbers) Type() protoreflect.EnumType {
+	return &file_execution_metrics_proto_enumTypes[0]
+}
+
+func (x FieldNumbers) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *FieldNumbers) UnmarshalJSON(b []byte) error {
+	num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+	if err != nil {
+		return err
+	}
+	*x = FieldNumbers(num)
+	return nil
+}
+
+// Deprecated: Use FieldNumbers.Descriptor instead.
+func (FieldNumbers) EnumDescriptor() ([]byte, []int) {
+	return file_execution_metrics_proto_rawDescGZIP(), []int{0}
+}
+
+type SoongExecutionMetrics struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// cmd/find_input_delta/find_input_delta_proto.FileList
+	FileList *find_input_delta_proto.FileList `protobuf:"bytes,1,opt,name=file_list,json=fileList" json:"file_list,omitempty"`
+}
+
+func (x *SoongExecutionMetrics) Reset() {
+	*x = SoongExecutionMetrics{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_execution_metrics_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SoongExecutionMetrics) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SoongExecutionMetrics) ProtoMessage() {}
+
+func (x *SoongExecutionMetrics) ProtoReflect() protoreflect.Message {
+	mi := &file_execution_metrics_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 SoongExecutionMetrics.ProtoReflect.Descriptor instead.
+func (*SoongExecutionMetrics) Descriptor() ([]byte, []int) {
+	return file_execution_metrics_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *SoongExecutionMetrics) GetFileList() *find_input_delta_proto.FileList {
+	if x != nil {
+		return x.FileList
+	}
+	return nil
+}
+
+var File_execution_metrics_proto protoreflect.FileDescriptor
+
+var file_execution_metrics_proto_rawDesc = []byte{
+	0x0a, 0x17, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x72,
+	0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
+	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x1a, 0x3b,
+	0x63, 0x6d, 0x64, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64,
+	0x65, 0x6c, 0x74, 0x61, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f,
+	0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x66, 0x69, 0x6c, 0x65,
+	0x5f, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5e, 0x0a, 0x15, 0x53,
+	0x6f, 0x6f, 0x6e, 0x67, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74,
+	0x72, 0x69, 0x63, 0x73, 0x12, 0x45, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6c, 0x69, 0x73,
+	0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+	0x64, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c,
+	0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73,
+	0x74, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x2a, 0x4a, 0x0a, 0x0c, 0x46,
+	0x69, 0x65, 0x6c, 0x64, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x1d, 0x0a, 0x19, 0x46,
+	0x49, 0x45, 0x4c, 0x44, 0x5f, 0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x53, 0x5f, 0x55, 0x4e, 0x53,
+	0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x49,
+	0x45, 0x4c, 0x44, 0x5f, 0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x53, 0x5f, 0x46, 0x49, 0x4c, 0x45,
+	0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x01, 0x42, 0x32, 0x5a, 0x30, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72,
+	0x69, 0x63, 0x73, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65,
+	0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+}
+
+var (
+	file_execution_metrics_proto_rawDescOnce sync.Once
+	file_execution_metrics_proto_rawDescData = file_execution_metrics_proto_rawDesc
+)
+
+func file_execution_metrics_proto_rawDescGZIP() []byte {
+	file_execution_metrics_proto_rawDescOnce.Do(func() {
+		file_execution_metrics_proto_rawDescData = protoimpl.X.CompressGZIP(file_execution_metrics_proto_rawDescData)
+	})
+	return file_execution_metrics_proto_rawDescData
+}
+
+var file_execution_metrics_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_execution_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_execution_metrics_proto_goTypes = []interface{}{
+	(FieldNumbers)(0),                       // 0: soong_build_metrics.FieldNumbers
+	(*SoongExecutionMetrics)(nil),           // 1: soong_build_metrics.SoongExecutionMetrics
+	(*find_input_delta_proto.FileList)(nil), // 2: android.find_input_delta_proto.FileList
+}
+var file_execution_metrics_proto_depIdxs = []int32{
+	2, // 0: soong_build_metrics.SoongExecutionMetrics.file_list:type_name -> android.find_input_delta_proto.FileList
+	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_execution_metrics_proto_init() }
+func file_execution_metrics_proto_init() {
+	if File_execution_metrics_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_execution_metrics_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SoongExecutionMetrics); 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_execution_metrics_proto_rawDesc,
+			NumEnums:      1,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_execution_metrics_proto_goTypes,
+		DependencyIndexes: file_execution_metrics_proto_depIdxs,
+		EnumInfos:         file_execution_metrics_proto_enumTypes,
+		MessageInfos:      file_execution_metrics_proto_msgTypes,
+	}.Build()
+	File_execution_metrics_proto = out.File
+	file_execution_metrics_proto_rawDesc = nil
+	file_execution_metrics_proto_goTypes = nil
+	file_execution_metrics_proto_depIdxs = nil
+}
diff --git a/ui/metrics/metrics_proto/combined_metrics.proto b/ui/metrics/execution_metrics_proto/execution_metrics.proto
similarity index 83%
rename from ui/metrics/metrics_proto/combined_metrics.proto
rename to ui/metrics/execution_metrics_proto/execution_metrics.proto
index 3cd9a53..381dcd1 100644
--- a/ui/metrics/metrics_proto/combined_metrics.proto
+++ b/ui/metrics/execution_metrics_proto/execution_metrics.proto
@@ -1,4 +1,4 @@
-// Copyright 2018 Google Inc. All Rights Reserved.
+// 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.
@@ -15,22 +15,22 @@
 syntax = "proto2";
 
 package soong_build_metrics;
-option go_package = "android/soong/ui/metrics/metrics_proto";
+option go_package = "android/soong/ui/metrics/execution_metrics_proto";
 
 import "cmd/find_input_delta/find_input_delta_proto/file_list.proto";
 
 // These field numbers are also found in the inner message declarations.
 // We verify that the values are the same, and that every enum value is checked
-// in combined_metrics_test.go.
+// in execution_metrics_test.go.
 // Do not change this enum without also updating:
 //  - the submessage's .proto file
-//  - combined_metrics_test.go
+//  - execution_metrics_test.go
 enum FieldNumbers {
   FIELD_NUMBERS_UNSPECIFIED = 0;
   FIELD_NUMBERS_FILE_LIST = 1;
 }
 
-message SoongCombinedMetrics {
+message SoongExecutionMetrics {
   // cmd/find_input_delta/find_input_delta_proto.FileList
   optional android.find_input_delta_proto.FileList file_list = 1;
 }
diff --git a/ui/metrics/metrics_proto/combined_metrics_test.go b/ui/metrics/execution_metrics_proto/execution_metrics_test.go
similarity index 89%
rename from ui/metrics/metrics_proto/combined_metrics_test.go
rename to ui/metrics/execution_metrics_proto/execution_metrics_test.go
index eedb12a..2f71c21 100644
--- a/ui/metrics/metrics_proto/combined_metrics_test.go
+++ b/ui/metrics/execution_metrics_proto/execution_metrics_test.go
@@ -1,4 +1,4 @@
-package metrics_proto
+package execution_metrics_proto
 
 import (
 	"testing"
@@ -6,7 +6,7 @@
 	find_input_delta_proto "android/soong/cmd/find_input_delta/find_input_delta_proto"
 )
 
-func TestCombinedMetricsMessageNums(t *testing.T) {
+func TestExecutionMetricsMessageNums(t *testing.T) {
 	testCases := []struct {
 		Name         string
 		FieldNumbers map[string]int32
diff --git a/ui/metrics/execution_metrics_proto/regen.sh b/ui/metrics/execution_metrics_proto/regen.sh
new file mode 100755
index 0000000..5339a9a
--- /dev/null
+++ b/ui/metrics/execution_metrics_proto/regen.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+# Generates the golang source file of metrics.proto protobuf file.
+
+set -e
+
+function die() { echo "ERROR: $1" >&2; exit 1; }
+
+readonly error_msg="Maybe you need to run 'lunch aosp_arm-eng && m aprotoc blueprint_tools'?"
+
+if ! hash aprotoc &>/dev/null; then
+  die "could not find aprotoc. ${error_msg}"
+fi
+
+if ! aprotoc --go_out=paths=source_relative:. -I .:../../.. execution_metrics.proto; then
+  die "build failed. ${error_msg}"
+fi
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 4a275a8..8d29cfc 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -161,7 +161,7 @@
 }
 
 // SetMetadataMetrics sets information about the build such as the target
-// product, host architecture and out directory.
+// product, host architecture and out directory.  May be called multiple times.
 func (m *Metrics) SetMetadataMetrics(metadata map[string]string) {
 	for k, v := range metadata {
 		switch k {
@@ -171,6 +171,8 @@
 			m.metrics.PlatformVersionCodename = proto.String(v)
 		case "TARGET_PRODUCT":
 			m.metrics.TargetProduct = proto.String(v)
+		case "TARGET_RELEASE":
+			m.metrics.TargetRelease = proto.String(v)
 		case "TARGET_BUILD_VARIANT":
 			switch v {
 			case "user":
diff --git a/ui/metrics/metrics_proto/combined_metrics.pb.go b/ui/metrics/metrics_proto/combined_metrics.pb.go
deleted file mode 100644
index f49d64d..0000000
--- a/ui/metrics/metrics_proto/combined_metrics.pb.go
+++ /dev/null
@@ -1,239 +0,0 @@
-// Copyright 2018 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.
-
-// Code generated by protoc-gen-go. DO NOT EDIT.
-// versions:
-// 	protoc-gen-go v1.33.0
-// 	protoc        v3.21.12
-// source: combined_metrics.proto
-
-package metrics_proto
-
-import (
-	find_input_delta_proto "android/soong/cmd/find_input_delta/find_input_delta_proto"
-	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)
-)
-
-// These field numbers are also found in the inner message declarations.
-// We verify that the values are the same, and that every enum value is checked
-// in combined_metrics_test.go.
-// Do not change this enum without also updating:
-//   - the submessage's .proto file
-//   - combined_metrics_test.go
-type FieldNumbers int32
-
-const (
-	FieldNumbers_FIELD_NUMBERS_UNSPECIFIED FieldNumbers = 0
-	FieldNumbers_FIELD_NUMBERS_FILE_LIST   FieldNumbers = 1
-)
-
-// Enum value maps for FieldNumbers.
-var (
-	FieldNumbers_name = map[int32]string{
-		0: "FIELD_NUMBERS_UNSPECIFIED",
-		1: "FIELD_NUMBERS_FILE_LIST",
-	}
-	FieldNumbers_value = map[string]int32{
-		"FIELD_NUMBERS_UNSPECIFIED": 0,
-		"FIELD_NUMBERS_FILE_LIST":   1,
-	}
-)
-
-func (x FieldNumbers) Enum() *FieldNumbers {
-	p := new(FieldNumbers)
-	*p = x
-	return p
-}
-
-func (x FieldNumbers) String() string {
-	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
-}
-
-func (FieldNumbers) Descriptor() protoreflect.EnumDescriptor {
-	return file_combined_metrics_proto_enumTypes[0].Descriptor()
-}
-
-func (FieldNumbers) Type() protoreflect.EnumType {
-	return &file_combined_metrics_proto_enumTypes[0]
-}
-
-func (x FieldNumbers) Number() protoreflect.EnumNumber {
-	return protoreflect.EnumNumber(x)
-}
-
-// Deprecated: Do not use.
-func (x *FieldNumbers) UnmarshalJSON(b []byte) error {
-	num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
-	if err != nil {
-		return err
-	}
-	*x = FieldNumbers(num)
-	return nil
-}
-
-// Deprecated: Use FieldNumbers.Descriptor instead.
-func (FieldNumbers) EnumDescriptor() ([]byte, []int) {
-	return file_combined_metrics_proto_rawDescGZIP(), []int{0}
-}
-
-type SoongCombinedMetrics struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// cmd/find_input_delta/find_input_delta_proto.FileList
-	FileList *find_input_delta_proto.FileList `protobuf:"bytes,1,opt,name=file_list,json=fileList" json:"file_list,omitempty"`
-}
-
-func (x *SoongCombinedMetrics) Reset() {
-	*x = SoongCombinedMetrics{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_combined_metrics_proto_msgTypes[0]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *SoongCombinedMetrics) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*SoongCombinedMetrics) ProtoMessage() {}
-
-func (x *SoongCombinedMetrics) ProtoReflect() protoreflect.Message {
-	mi := &file_combined_metrics_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 SoongCombinedMetrics.ProtoReflect.Descriptor instead.
-func (*SoongCombinedMetrics) Descriptor() ([]byte, []int) {
-	return file_combined_metrics_proto_rawDescGZIP(), []int{0}
-}
-
-func (x *SoongCombinedMetrics) GetFileList() *find_input_delta_proto.FileList {
-	if x != nil {
-		return x.FileList
-	}
-	return nil
-}
-
-var File_combined_metrics_proto protoreflect.FileDescriptor
-
-var file_combined_metrics_proto_rawDesc = []byte{
-	0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
-	0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
-	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x1a, 0x3b, 0x63,
-	0x6d, 0x64, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65,
-	0x6c, 0x74, 0x61, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64,
-	0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x5f,
-	0x6c, 0x69, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5d, 0x0a, 0x14, 0x53, 0x6f,
-	0x6f, 0x6e, 0x67, 0x43, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69,
-	0x63, 0x73, 0x12, 0x45, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
-	0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61,
-	0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52,
-	0x08, 0x66, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x2a, 0x4a, 0x0a, 0x0c, 0x46, 0x69, 0x65,
-	0x6c, 0x64, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x1d, 0x0a, 0x19, 0x46, 0x49, 0x45,
-	0x4c, 0x44, 0x5f, 0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45,
-	0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x49, 0x45, 0x4c,
-	0x44, 0x5f, 0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x53, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x4c,
-	0x49, 0x53, 0x54, 0x10, 0x01, 0x42, 0x28, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
-	0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
-	0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-}
-
-var (
-	file_combined_metrics_proto_rawDescOnce sync.Once
-	file_combined_metrics_proto_rawDescData = file_combined_metrics_proto_rawDesc
-)
-
-func file_combined_metrics_proto_rawDescGZIP() []byte {
-	file_combined_metrics_proto_rawDescOnce.Do(func() {
-		file_combined_metrics_proto_rawDescData = protoimpl.X.CompressGZIP(file_combined_metrics_proto_rawDescData)
-	})
-	return file_combined_metrics_proto_rawDescData
-}
-
-var file_combined_metrics_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
-var file_combined_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
-var file_combined_metrics_proto_goTypes = []interface{}{
-	(FieldNumbers)(0),                       // 0: soong_build_metrics.FieldNumbers
-	(*SoongCombinedMetrics)(nil),            // 1: soong_build_metrics.SoongCombinedMetrics
-	(*find_input_delta_proto.FileList)(nil), // 2: android.find_input_delta_proto.FileList
-}
-var file_combined_metrics_proto_depIdxs = []int32{
-	2, // 0: soong_build_metrics.SoongCombinedMetrics.file_list:type_name -> android.find_input_delta_proto.FileList
-	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_combined_metrics_proto_init() }
-func file_combined_metrics_proto_init() {
-	if File_combined_metrics_proto != nil {
-		return
-	}
-	if !protoimpl.UnsafeEnabled {
-		file_combined_metrics_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*SoongCombinedMetrics); 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_combined_metrics_proto_rawDesc,
-			NumEnums:      1,
-			NumMessages:   1,
-			NumExtensions: 0,
-			NumServices:   0,
-		},
-		GoTypes:           file_combined_metrics_proto_goTypes,
-		DependencyIndexes: file_combined_metrics_proto_depIdxs,
-		EnumInfos:         file_combined_metrics_proto_enumTypes,
-		MessageInfos:      file_combined_metrics_proto_msgTypes,
-	}.Build()
-	File_combined_metrics_proto = out.File
-	file_combined_metrics_proto_rawDesc = nil
-	file_combined_metrics_proto_goTypes = nil
-	file_combined_metrics_proto_depIdxs = nil
-}
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index 72fdbe8..1ebe911 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.33.0
+// 	protoc-gen-go v1.30.0
 // 	protoc        v3.21.12
 // source: metrics.proto
 
@@ -279,7 +279,7 @@
 
 // Deprecated: Use ModuleTypeInfo_BuildSystem.Descriptor instead.
 func (ModuleTypeInfo_BuildSystem) EnumDescriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{10, 0}
+	return file_metrics_proto_rawDescGZIP(), []int{11, 0}
 }
 
 type ExpConfigFetcher_ConfigStatus int32
@@ -341,7 +341,7 @@
 
 // Deprecated: Use ExpConfigFetcher_ConfigStatus.Descriptor instead.
 func (ExpConfigFetcher_ConfigStatus) EnumDescriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{14, 0}
+	return file_metrics_proto_rawDescGZIP(), []int{15, 0}
 }
 
 type MetricsBase struct {
@@ -427,6 +427,8 @@
 	ChangedEnvironmentVariable []string `protobuf:"bytes,34,rep,name=changed_environment_variable,json=changedEnvironmentVariable" json:"changed_environment_variable,omitempty"`
 	// Metrics related to optimized builds.
 	OptimizedBuildMetrics *OptimizedBuildMetrics `protobuf:"bytes,35,opt,name=optimized_build_metrics,json=optimizedBuildMetrics" json:"optimized_build_metrics,omitempty"`
+	// The target release information. e.g., trunk_staging.
+	TargetRelease *string `protobuf:"bytes,36,opt,name=target_release,json=targetRelease" json:"target_release,omitempty"`
 }
 
 // Default values for MetricsBase fields.
@@ -715,6 +717,13 @@
 	return nil
 }
 
+func (x *MetricsBase) GetTargetRelease() string {
+	if x != nil && x.TargetRelease != nil {
+		return *x.TargetRelease
+	}
+	return ""
+}
+
 type BuildConfig struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -739,6 +748,11 @@
 	// EXTERNAL_FILE - ninja uses an external custom weight list
 	// HINT_FROM_SOONG - ninja uses a prioritized module list from Soong
 	NinjaWeightListSource *BuildConfig_NinjaWeightListSource `protobuf:"varint,8,opt,name=ninja_weight_list_source,json=ninjaWeightListSource,enum=soong_build_metrics.BuildConfig_NinjaWeightListSource,def=0" json:"ninja_weight_list_source,omitempty"`
+	// Values of some build-affecting environment variables.
+	SoongEnvVars *SoongEnvVars `protobuf:"bytes,9,opt,name=soong_env_vars,json=soongEnvVars" json:"soong_env_vars,omitempty"`
+	// Whether this build uses soong-only (no kati) mode (either via environment variable,
+	// command line flag or product config.
+	SoongOnly *bool `protobuf:"varint,10,opt,name=soong_only,json=soongOnly" json:"soong_only,omitempty"`
 }
 
 // Default values for BuildConfig fields.
@@ -834,6 +848,77 @@
 	return Default_BuildConfig_NinjaWeightListSource
 }
 
+func (x *BuildConfig) GetSoongEnvVars() *SoongEnvVars {
+	if x != nil {
+		return x.SoongEnvVars
+	}
+	return nil
+}
+
+func (x *BuildConfig) GetSoongOnly() bool {
+	if x != nil && x.SoongOnly != nil {
+		return *x.SoongOnly
+	}
+	return false
+}
+
+type SoongEnvVars struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// SOONG_PARTIAL_COMPILE
+	PartialCompile *string `protobuf:"bytes,1,opt,name=partial_compile,json=partialCompile" json:"partial_compile,omitempty"`
+	// SOONG_USE_PARTIAL_COMPILE
+	UsePartialCompile *string `protobuf:"bytes,2,opt,name=use_partial_compile,json=usePartialCompile" json:"use_partial_compile,omitempty"`
+}
+
+func (x *SoongEnvVars) Reset() {
+	*x = SoongEnvVars{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SoongEnvVars) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SoongEnvVars) ProtoMessage() {}
+
+func (x *SoongEnvVars) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[2]
+	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 SoongEnvVars.ProtoReflect.Descriptor instead.
+func (*SoongEnvVars) Descriptor() ([]byte, []int) {
+	return file_metrics_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *SoongEnvVars) GetPartialCompile() string {
+	if x != nil && x.PartialCompile != nil {
+		return *x.PartialCompile
+	}
+	return ""
+}
+
+func (x *SoongEnvVars) GetUsePartialCompile() string {
+	if x != nil && x.UsePartialCompile != nil {
+		return *x.UsePartialCompile
+	}
+	return ""
+}
+
 type SystemResourceInfo struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -852,7 +937,7 @@
 func (x *SystemResourceInfo) Reset() {
 	*x = SystemResourceInfo{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[2]
+		mi := &file_metrics_proto_msgTypes[3]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -865,7 +950,7 @@
 func (*SystemResourceInfo) ProtoMessage() {}
 
 func (x *SystemResourceInfo) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[2]
+	mi := &file_metrics_proto_msgTypes[3]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -878,7 +963,7 @@
 
 // Deprecated: Use SystemResourceInfo.ProtoReflect.Descriptor instead.
 func (*SystemResourceInfo) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{2}
+	return file_metrics_proto_rawDescGZIP(), []int{3}
 }
 
 func (x *SystemResourceInfo) GetTotalPhysicalMemory() uint64 {
@@ -927,7 +1012,7 @@
 func (x *SystemCpuInfo) Reset() {
 	*x = SystemCpuInfo{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[3]
+		mi := &file_metrics_proto_msgTypes[4]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -940,7 +1025,7 @@
 func (*SystemCpuInfo) ProtoMessage() {}
 
 func (x *SystemCpuInfo) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[3]
+	mi := &file_metrics_proto_msgTypes[4]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -953,7 +1038,7 @@
 
 // Deprecated: Use SystemCpuInfo.ProtoReflect.Descriptor instead.
 func (*SystemCpuInfo) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{3}
+	return file_metrics_proto_rawDescGZIP(), []int{4}
 }
 
 func (x *SystemCpuInfo) GetVendorId() string {
@@ -1000,7 +1085,7 @@
 func (x *SystemMemInfo) Reset() {
 	*x = SystemMemInfo{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[4]
+		mi := &file_metrics_proto_msgTypes[5]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1013,7 +1098,7 @@
 func (*SystemMemInfo) ProtoMessage() {}
 
 func (x *SystemMemInfo) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[4]
+	mi := &file_metrics_proto_msgTypes[5]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1026,7 +1111,7 @@
 
 // Deprecated: Use SystemMemInfo.ProtoReflect.Descriptor instead.
 func (*SystemMemInfo) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{4}
+	return file_metrics_proto_rawDescGZIP(), []int{5}
 }
 
 func (x *SystemMemInfo) GetMemTotal() uint64 {
@@ -1081,7 +1166,7 @@
 func (x *PerfInfo) Reset() {
 	*x = PerfInfo{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[5]
+		mi := &file_metrics_proto_msgTypes[6]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1094,7 +1179,7 @@
 func (*PerfInfo) ProtoMessage() {}
 
 func (x *PerfInfo) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[5]
+	mi := &file_metrics_proto_msgTypes[6]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1107,7 +1192,7 @@
 
 // Deprecated: Use PerfInfo.ProtoReflect.Descriptor instead.
 func (*PerfInfo) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{5}
+	return file_metrics_proto_rawDescGZIP(), []int{6}
 }
 
 func (x *PerfInfo) GetDescription() string {
@@ -1181,7 +1266,7 @@
 func (x *PerfCounters) Reset() {
 	*x = PerfCounters{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[6]
+		mi := &file_metrics_proto_msgTypes[7]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1194,7 +1279,7 @@
 func (*PerfCounters) ProtoMessage() {}
 
 func (x *PerfCounters) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[6]
+	mi := &file_metrics_proto_msgTypes[7]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1207,7 +1292,7 @@
 
 // Deprecated: Use PerfCounters.ProtoReflect.Descriptor instead.
 func (*PerfCounters) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{6}
+	return file_metrics_proto_rawDescGZIP(), []int{7}
 }
 
 func (x *PerfCounters) GetTime() uint64 {
@@ -1238,7 +1323,7 @@
 func (x *PerfCounterGroup) Reset() {
 	*x = PerfCounterGroup{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[7]
+		mi := &file_metrics_proto_msgTypes[8]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1251,7 +1336,7 @@
 func (*PerfCounterGroup) ProtoMessage() {}
 
 func (x *PerfCounterGroup) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[7]
+	mi := &file_metrics_proto_msgTypes[8]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1264,7 +1349,7 @@
 
 // Deprecated: Use PerfCounterGroup.ProtoReflect.Descriptor instead.
 func (*PerfCounterGroup) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{7}
+	return file_metrics_proto_rawDescGZIP(), []int{8}
 }
 
 func (x *PerfCounterGroup) GetName() string {
@@ -1295,7 +1380,7 @@
 func (x *PerfCounter) Reset() {
 	*x = PerfCounter{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[8]
+		mi := &file_metrics_proto_msgTypes[9]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1308,7 +1393,7 @@
 func (*PerfCounter) ProtoMessage() {}
 
 func (x *PerfCounter) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[8]
+	mi := &file_metrics_proto_msgTypes[9]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1321,7 +1406,7 @@
 
 // Deprecated: Use PerfCounter.ProtoReflect.Descriptor instead.
 func (*PerfCounter) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{8}
+	return file_metrics_proto_rawDescGZIP(), []int{9}
 }
 
 func (x *PerfCounter) GetName() string {
@@ -1368,7 +1453,7 @@
 func (x *ProcessResourceInfo) Reset() {
 	*x = ProcessResourceInfo{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[9]
+		mi := &file_metrics_proto_msgTypes[10]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1381,7 +1466,7 @@
 func (*ProcessResourceInfo) ProtoMessage() {}
 
 func (x *ProcessResourceInfo) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[9]
+	mi := &file_metrics_proto_msgTypes[10]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1394,7 +1479,7 @@
 
 // Deprecated: Use ProcessResourceInfo.ProtoReflect.Descriptor instead.
 func (*ProcessResourceInfo) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{9}
+	return file_metrics_proto_rawDescGZIP(), []int{10}
 }
 
 func (x *ProcessResourceInfo) GetName() string {
@@ -1488,7 +1573,7 @@
 func (x *ModuleTypeInfo) Reset() {
 	*x = ModuleTypeInfo{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[10]
+		mi := &file_metrics_proto_msgTypes[11]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1501,7 +1586,7 @@
 func (*ModuleTypeInfo) ProtoMessage() {}
 
 func (x *ModuleTypeInfo) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[10]
+	mi := &file_metrics_proto_msgTypes[11]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1514,7 +1599,7 @@
 
 // Deprecated: Use ModuleTypeInfo.ProtoReflect.Descriptor instead.
 func (*ModuleTypeInfo) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{10}
+	return file_metrics_proto_rawDescGZIP(), []int{11}
 }
 
 func (x *ModuleTypeInfo) GetBuildSystem() ModuleTypeInfo_BuildSystem {
@@ -1552,7 +1637,7 @@
 func (x *CriticalUserJourneyMetrics) Reset() {
 	*x = CriticalUserJourneyMetrics{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[11]
+		mi := &file_metrics_proto_msgTypes[12]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1565,7 +1650,7 @@
 func (*CriticalUserJourneyMetrics) ProtoMessage() {}
 
 func (x *CriticalUserJourneyMetrics) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[11]
+	mi := &file_metrics_proto_msgTypes[12]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1578,7 +1663,7 @@
 
 // Deprecated: Use CriticalUserJourneyMetrics.ProtoReflect.Descriptor instead.
 func (*CriticalUserJourneyMetrics) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{11}
+	return file_metrics_proto_rawDescGZIP(), []int{12}
 }
 
 func (x *CriticalUserJourneyMetrics) GetName() string {
@@ -1607,7 +1692,7 @@
 func (x *CriticalUserJourneysMetrics) Reset() {
 	*x = CriticalUserJourneysMetrics{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[12]
+		mi := &file_metrics_proto_msgTypes[13]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1620,7 +1705,7 @@
 func (*CriticalUserJourneysMetrics) ProtoMessage() {}
 
 func (x *CriticalUserJourneysMetrics) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[12]
+	mi := &file_metrics_proto_msgTypes[13]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1633,7 +1718,7 @@
 
 // Deprecated: Use CriticalUserJourneysMetrics.ProtoReflect.Descriptor instead.
 func (*CriticalUserJourneysMetrics) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{12}
+	return file_metrics_proto_rawDescGZIP(), []int{13}
 }
 
 func (x *CriticalUserJourneysMetrics) GetCujs() []*CriticalUserJourneyMetrics {
@@ -1669,7 +1754,7 @@
 func (x *SoongBuildMetrics) Reset() {
 	*x = SoongBuildMetrics{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[13]
+		mi := &file_metrics_proto_msgTypes[14]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1682,7 +1767,7 @@
 func (*SoongBuildMetrics) ProtoMessage() {}
 
 func (x *SoongBuildMetrics) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[13]
+	mi := &file_metrics_proto_msgTypes[14]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1695,7 +1780,7 @@
 
 // Deprecated: Use SoongBuildMetrics.ProtoReflect.Descriptor instead.
 func (*SoongBuildMetrics) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{13}
+	return file_metrics_proto_rawDescGZIP(), []int{14}
 }
 
 func (x *SoongBuildMetrics) GetModules() uint32 {
@@ -1773,7 +1858,7 @@
 func (x *ExpConfigFetcher) Reset() {
 	*x = ExpConfigFetcher{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[14]
+		mi := &file_metrics_proto_msgTypes[15]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1786,7 +1871,7 @@
 func (*ExpConfigFetcher) ProtoMessage() {}
 
 func (x *ExpConfigFetcher) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[14]
+	mi := &file_metrics_proto_msgTypes[15]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1799,7 +1884,7 @@
 
 // Deprecated: Use ExpConfigFetcher.ProtoReflect.Descriptor instead.
 func (*ExpConfigFetcher) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{14}
+	return file_metrics_proto_rawDescGZIP(), []int{15}
 }
 
 func (x *ExpConfigFetcher) GetStatus() ExpConfigFetcher_ConfigStatus {
@@ -1837,7 +1922,7 @@
 func (x *MixedBuildsInfo) Reset() {
 	*x = MixedBuildsInfo{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[15]
+		mi := &file_metrics_proto_msgTypes[16]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1850,7 +1935,7 @@
 func (*MixedBuildsInfo) ProtoMessage() {}
 
 func (x *MixedBuildsInfo) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[15]
+	mi := &file_metrics_proto_msgTypes[16]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1863,7 +1948,7 @@
 
 // Deprecated: Use MixedBuildsInfo.ProtoReflect.Descriptor instead.
 func (*MixedBuildsInfo) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{15}
+	return file_metrics_proto_rawDescGZIP(), []int{16}
 }
 
 func (x *MixedBuildsInfo) GetMixedBuildEnabledModules() []string {
@@ -1900,7 +1985,7 @@
 func (x *CriticalPathInfo) Reset() {
 	*x = CriticalPathInfo{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[16]
+		mi := &file_metrics_proto_msgTypes[17]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1913,7 +1998,7 @@
 func (*CriticalPathInfo) ProtoMessage() {}
 
 func (x *CriticalPathInfo) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[16]
+	mi := &file_metrics_proto_msgTypes[17]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1926,7 +2011,7 @@
 
 // Deprecated: Use CriticalPathInfo.ProtoReflect.Descriptor instead.
 func (*CriticalPathInfo) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{16}
+	return file_metrics_proto_rawDescGZIP(), []int{17}
 }
 
 func (x *CriticalPathInfo) GetElapsedTimeMicros() uint64 {
@@ -1971,7 +2056,7 @@
 func (x *JobInfo) Reset() {
 	*x = JobInfo{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[17]
+		mi := &file_metrics_proto_msgTypes[18]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1984,7 +2069,7 @@
 func (*JobInfo) ProtoMessage() {}
 
 func (x *JobInfo) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[17]
+	mi := &file_metrics_proto_msgTypes[18]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1997,7 +2082,7 @@
 
 // Deprecated: Use JobInfo.ProtoReflect.Descriptor instead.
 func (*JobInfo) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{17}
+	return file_metrics_proto_rawDescGZIP(), []int{18}
 }
 
 func (x *JobInfo) GetElapsedTimeMicros() uint64 {
@@ -2030,7 +2115,7 @@
 func (x *OptimizedBuildMetrics) Reset() {
 	*x = OptimizedBuildMetrics{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[18]
+		mi := &file_metrics_proto_msgTypes[19]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2043,7 +2128,7 @@
 func (*OptimizedBuildMetrics) ProtoMessage() {}
 
 func (x *OptimizedBuildMetrics) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[18]
+	mi := &file_metrics_proto_msgTypes[19]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2056,7 +2141,7 @@
 
 // Deprecated: Use OptimizedBuildMetrics.ProtoReflect.Descriptor instead.
 func (*OptimizedBuildMetrics) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{18}
+	return file_metrics_proto_rawDescGZIP(), []int{19}
 }
 
 func (x *OptimizedBuildMetrics) GetAnalysisPerf() *PerfInfo {
@@ -2080,6 +2165,226 @@
 	return nil
 }
 
+// This is created by soong_ui from SoongExexcutionMetrics files.
+type ExecutionMetrics struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The arguments provided on the command line.
+	CommandArgs []string `protobuf:"bytes,1,rep,name=command_args,json=commandArgs" json:"command_args,omitempty"`
+	// Changed files detected by the build.
+	ChangedFiles *AggregatedFileList `protobuf:"bytes,2,opt,name=changed_files,json=changedFiles" json:"changed_files,omitempty"`
+}
+
+func (x *ExecutionMetrics) Reset() {
+	*x = ExecutionMetrics{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[20]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ExecutionMetrics) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ExecutionMetrics) ProtoMessage() {}
+
+func (x *ExecutionMetrics) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[20]
+	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 ExecutionMetrics.ProtoReflect.Descriptor instead.
+func (*ExecutionMetrics) Descriptor() ([]byte, []int) {
+	return file_metrics_proto_rawDescGZIP(), []int{20}
+}
+
+func (x *ExecutionMetrics) GetCommandArgs() []string {
+	if x != nil {
+		return x.CommandArgs
+	}
+	return nil
+}
+
+func (x *ExecutionMetrics) GetChangedFiles() *AggregatedFileList {
+	if x != nil {
+		return x.ChangedFiles
+	}
+	return nil
+}
+
+// This is created by soong_ui from the various
+// android.find_input_delta_proto.FileList metrics provided to it by
+// find_input_delta.
+type AggregatedFileList struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The (possibly truncated list of) added files.
+	Additions []string `protobuf:"bytes,2,rep,name=additions" json:"additions,omitempty"`
+	// The (possibly truncated list of) changed files.
+	Changes []string `protobuf:"bytes,3,rep,name=changes" json:"changes,omitempty"`
+	// The (possibly truncated list of) deleted files.
+	Deletions []string `protobuf:"bytes,4,rep,name=deletions" json:"deletions,omitempty"`
+	// Count of files added/changed/deleted.
+	TotalDelta *uint32 `protobuf:"varint,5,opt,name=total_delta,json=totalDelta" json:"total_delta,omitempty"`
+	// Counts by extension.
+	Counts []*FileCount `protobuf:"bytes,6,rep,name=counts" json:"counts,omitempty"`
+}
+
+func (x *AggregatedFileList) Reset() {
+	*x = AggregatedFileList{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[21]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *AggregatedFileList) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AggregatedFileList) ProtoMessage() {}
+
+func (x *AggregatedFileList) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[21]
+	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 AggregatedFileList.ProtoReflect.Descriptor instead.
+func (*AggregatedFileList) Descriptor() ([]byte, []int) {
+	return file_metrics_proto_rawDescGZIP(), []int{21}
+}
+
+func (x *AggregatedFileList) GetAdditions() []string {
+	if x != nil {
+		return x.Additions
+	}
+	return nil
+}
+
+func (x *AggregatedFileList) GetChanges() []string {
+	if x != nil {
+		return x.Changes
+	}
+	return nil
+}
+
+func (x *AggregatedFileList) GetDeletions() []string {
+	if x != nil {
+		return x.Deletions
+	}
+	return nil
+}
+
+func (x *AggregatedFileList) GetTotalDelta() uint32 {
+	if x != nil && x.TotalDelta != nil {
+		return *x.TotalDelta
+	}
+	return 0
+}
+
+func (x *AggregatedFileList) GetCounts() []*FileCount {
+	if x != nil {
+		return x.Counts
+	}
+	return nil
+}
+
+type FileCount struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The file extension
+	Extension *string `protobuf:"bytes,1,opt,name=extension" json:"extension,omitempty"`
+	// Number of added files with this extension.
+	Additions *uint32 `protobuf:"varint,2,opt,name=additions" json:"additions,omitempty"`
+	// Number of modified files with this extension.
+	Modifications *uint32 `protobuf:"varint,3,opt,name=modifications" json:"modifications,omitempty"`
+	// Number of deleted files with this extension.
+	Deletions *uint32 `protobuf:"varint,4,opt,name=deletions" json:"deletions,omitempty"`
+}
+
+func (x *FileCount) Reset() {
+	*x = FileCount{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[22]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *FileCount) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FileCount) ProtoMessage() {}
+
+func (x *FileCount) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[22]
+	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 FileCount.ProtoReflect.Descriptor instead.
+func (*FileCount) Descriptor() ([]byte, []int) {
+	return file_metrics_proto_rawDescGZIP(), []int{22}
+}
+
+func (x *FileCount) GetExtension() string {
+	if x != nil && x.Extension != nil {
+		return *x.Extension
+	}
+	return ""
+}
+
+func (x *FileCount) GetAdditions() uint32 {
+	if x != nil && x.Additions != nil {
+		return *x.Additions
+	}
+	return 0
+}
+
+func (x *FileCount) GetModifications() uint32 {
+	if x != nil && x.Modifications != nil {
+		return *x.Modifications
+	}
+	return 0
+}
+
+func (x *FileCount) GetDeletions() uint32 {
+	if x != nil && x.Deletions != nil {
+		return *x.Deletions
+	}
+	return 0
+}
+
 type OptimizedBuildMetrics_TargetOptimizationResult struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -2101,7 +2406,7 @@
 func (x *OptimizedBuildMetrics_TargetOptimizationResult) Reset() {
 	*x = OptimizedBuildMetrics_TargetOptimizationResult{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[19]
+		mi := &file_metrics_proto_msgTypes[23]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2114,7 +2419,7 @@
 func (*OptimizedBuildMetrics_TargetOptimizationResult) ProtoMessage() {}
 
 func (x *OptimizedBuildMetrics_TargetOptimizationResult) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[19]
+	mi := &file_metrics_proto_msgTypes[23]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2127,7 +2432,7 @@
 
 // Deprecated: Use OptimizedBuildMetrics_TargetOptimizationResult.ProtoReflect.Descriptor instead.
 func (*OptimizedBuildMetrics_TargetOptimizationResult) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{18, 0}
+	return file_metrics_proto_rawDescGZIP(), []int{19, 0}
 }
 
 func (x *OptimizedBuildMetrics_TargetOptimizationResult) GetName() string {
@@ -2181,7 +2486,7 @@
 func (x *OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact) Reset() {
 	*x = OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[20]
+		mi := &file_metrics_proto_msgTypes[24]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2194,7 +2499,7 @@
 func (*OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact) ProtoMessage() {}
 
 func (x *OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[20]
+	mi := &file_metrics_proto_msgTypes[24]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2207,7 +2512,7 @@
 
 // Deprecated: Use OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact.ProtoReflect.Descriptor instead.
 func (*OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{18, 0, 0}
+	return file_metrics_proto_rawDescGZIP(), []int{19, 0, 0}
 }
 
 func (x *OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact) GetName() string {
@@ -2236,7 +2541,7 @@
 var file_metrics_proto_rawDesc = []byte{
 	0x0a, 0x0d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
 	0x13, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
-	0x72, 0x69, 0x63, 0x73, 0x22, 0xb0, 0x10, 0x0a, 0x0b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x72, 0x69, 0x63, 0x73, 0x22, 0xd7, 0x10, 0x0a, 0x0b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
 	0x42, 0x61, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x64, 0x61,
 	0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01,
 	0x28, 0x03, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d,
@@ -2360,287 +2665,332 @@
 	0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6d,
 	0x69, 0x7a, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
 	0x52, 0x15, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64,
-	0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x30, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64,
-	0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x53, 0x45, 0x52, 0x10,
-	0x00, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x53, 0x45, 0x52, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01,
-	0x12, 0x07, 0x0a, 0x03, 0x45, 0x4e, 0x47, 0x10, 0x02, 0x22, 0x3c, 0x0a, 0x04, 0x41, 0x72, 0x63,
-	0x68, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07,
-	0x0a, 0x03, 0x41, 0x52, 0x4d, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x52, 0x4d, 0x36, 0x34,
-	0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x58, 0x38, 0x36, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x58,
-	0x38, 0x36, 0x5f, 0x36, 0x34, 0x10, 0x04, 0x22, 0x8a, 0x04, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c,
-	0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x5f, 0x67,
-	0x6f, 0x6d, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x75, 0x73, 0x65, 0x47, 0x6f,
-	0x6d, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x5f, 0x72, 0x62, 0x65, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x08, 0x52, 0x06, 0x75, 0x73, 0x65, 0x52, 0x62, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x66,
-	0x6f, 0x72, 0x63, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x5f, 0x67, 0x6f, 0x6d, 0x61, 0x18, 0x03, 0x20,
-	0x01, 0x28, 0x08, 0x52, 0x0c, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x55, 0x73, 0x65, 0x47, 0x6f, 0x6d,
-	0x61, 0x12, 0x24, 0x0a, 0x0e, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x61, 0x73, 0x5f, 0x6e, 0x69,
-	0x6e, 0x6a, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x62, 0x61, 0x7a, 0x65, 0x6c,
-	0x41, 0x73, 0x4e, 0x69, 0x6e, 0x6a, 0x61, 0x12, 0x2a, 0x0a, 0x11, 0x62, 0x61, 0x7a, 0x65, 0x6c,
-	0x5f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x05, 0x20, 0x01,
-	0x28, 0x08, 0x52, 0x0f, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75,
-	0x69, 0x6c, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x06,
-	0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x44, 0x0a,
-	0x1f, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62,
-	0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64,
-	0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x44, 0x69, 0x73,
-	0x61, 0x62, 0x6c, 0x65, 0x42, 0x61, 0x7a, 0x65, 0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75,
-	0x69, 0x6c, 0x64, 0x12, 0x79, 0x0a, 0x18, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x5f, 0x77, 0x65, 0x69,
-	0x67, 0x68, 0x74, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18,
-	0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x36, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
-	0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x75, 0x69, 0x6c,
-	0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4e, 0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69,
-	0x67, 0x68, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x3a, 0x08, 0x4e,
-	0x4f, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x44, 0x52, 0x15, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65,
-	0x69, 0x67, 0x68, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x74,
-	0x0a, 0x15, 0x4e, 0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69, 0x73,
-	0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x4f, 0x54, 0x5f, 0x55,
-	0x53, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x49, 0x4e, 0x4a, 0x41, 0x5f, 0x4c,
-	0x4f, 0x47, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x45, 0x56, 0x45, 0x4e, 0x4c, 0x59, 0x5f, 0x44,
-	0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d,
-	0x45, 0x58, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x03, 0x12,
-	0x13, 0x0a, 0x0f, 0x48, 0x49, 0x4e, 0x54, 0x5f, 0x46, 0x52, 0x4f, 0x4d, 0x5f, 0x53, 0x4f, 0x4f,
-	0x4e, 0x47, 0x10, 0x04, 0x22, 0xed, 0x01, 0x0a, 0x12, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52,
-	0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x32, 0x0a, 0x15, 0x74,
-	0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x6d, 0x65,
-	0x6d, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61,
-	0x6c, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12,
-	0x25, 0x0a, 0x0e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x70, 0x75,
-	0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62,
-	0x6c, 0x65, 0x43, 0x70, 0x75, 0x73, 0x12, 0x3d, 0x0a, 0x08, 0x63, 0x70, 0x75, 0x5f, 0x69, 0x6e,
-	0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
-	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x53,
-	0x79, 0x73, 0x74, 0x65, 0x6d, 0x43, 0x70, 0x75, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x63, 0x70,
-	0x75, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x08, 0x6d, 0x65, 0x6d, 0x5f, 0x69, 0x6e, 0x66,
-	0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
-	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x53, 0x79,
-	0x73, 0x74, 0x65, 0x6d, 0x4d, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x6d, 0x65, 0x6d,
-	0x49, 0x6e, 0x66, 0x6f, 0x22, 0x7e, 0x0a, 0x0d, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x43, 0x70,
-	0x75, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f,
-	0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72,
-	0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x4e, 0x61, 0x6d,
-	0x65, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x03,
-	0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x63, 0x70, 0x75, 0x43, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x14,
-	0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66,
-	0x6c, 0x61, 0x67, 0x73, 0x22, 0x6c, 0x0a, 0x0d, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4d, 0x65,
-	0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x5f, 0x74, 0x6f, 0x74,
-	0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x54, 0x6f, 0x74,
-	0x61, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x65, 0x6d, 0x5f, 0x66, 0x72, 0x65, 0x65, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x46, 0x72, 0x65, 0x65, 0x12, 0x23, 0x0a,
-	0x0d, 0x6d, 0x65, 0x6d, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03,
-	0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6d, 0x65, 0x6d, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62,
-	0x6c, 0x65, 0x22, 0xca, 0x02, 0x0a, 0x08, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x12,
-	0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
-	0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74,
-	0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74,
-	0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d,
-	0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d,
-	0x65, 0x12, 0x21, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x65, 0x18,
-	0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x6d, 0x65, 0x6d, 0x6f, 0x72,
-	0x79, 0x55, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x17, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65,
-	0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18,
-	0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
-	0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x63,
-	0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52,
-	0x15, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
-	0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x6f, 0x6e, 0x5f, 0x7a, 0x65,
-	0x72, 0x6f, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e,
-	0x6f, 0x6e, 0x5a, 0x65, 0x72, 0x6f, 0x45, 0x78, 0x69, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72,
-	0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22,
-	0x61, 0x0a, 0x0c, 0x50, 0x65, 0x72, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12,
-	0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74,
-	0x69, 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20,
-	0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c,
-	0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x43, 0x6f,
-	0x75, 0x6e, 0x74, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75,
-	0x70, 0x73, 0x22, 0x64, 0x0a, 0x10, 0x50, 0x65, 0x72, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65,
-	0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x08, 0x63, 0x6f,
-	0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73,
+	0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65,
+	0x74, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x24, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x22, 0x30,
+	0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x12, 0x08,
+	0x0a, 0x04, 0x55, 0x53, 0x45, 0x52, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x53, 0x45, 0x52,
+	0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x45, 0x4e, 0x47, 0x10, 0x02,
+	0x22, 0x3c, 0x0a, 0x04, 0x41, 0x72, 0x63, 0x68, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e,
+	0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x52, 0x4d, 0x10, 0x01, 0x12, 0x09,
+	0x0a, 0x05, 0x41, 0x52, 0x4d, 0x36, 0x34, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x58, 0x38, 0x36,
+	0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x58, 0x38, 0x36, 0x5f, 0x36, 0x34, 0x10, 0x04, 0x22, 0xf2,
+	0x04, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19,
+	0x0a, 0x08, 0x75, 0x73, 0x65, 0x5f, 0x67, 0x6f, 0x6d, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
+	0x52, 0x07, 0x75, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65,
+	0x5f, 0x72, 0x62, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x75, 0x73, 0x65, 0x52,
+	0x62, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x5f,
+	0x67, 0x6f, 0x6d, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x66, 0x6f, 0x72, 0x63,
+	0x65, 0x55, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12, 0x24, 0x0a, 0x0e, 0x62, 0x61, 0x7a, 0x65,
+	0x6c, 0x5f, 0x61, 0x73, 0x5f, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
+	0x52, 0x0c, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x41, 0x73, 0x4e, 0x69, 0x6e, 0x6a, 0x61, 0x12, 0x2a,
+	0x0a, 0x11, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75,
+	0x69, 0x6c, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x62, 0x61, 0x7a, 0x65, 0x6c,
+	0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x61,
+	0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x74, 0x61, 0x72,
+	0x67, 0x65, 0x74, 0x73, 0x12, 0x44, 0x0a, 0x1f, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x69,
+	0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x69, 0x78, 0x65,
+	0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x66,
+	0x6f, 0x72, 0x63, 0x65, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x61, 0x7a, 0x65, 0x6c,
+	0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x79, 0x0a, 0x18, 0x6e, 0x69,
+	0x6e, 0x6a, 0x61, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x5f,
+	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x36, 0x2e, 0x73,
 	0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
-	0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x08,
-	0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x22, 0x37, 0x0a, 0x0b, 0x50, 0x65, 0x72, 0x66,
-	0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76,
-	0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
-	0x65, 0x22, 0xb9, 0x03, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73,
-	0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
-	0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a,
-	0x10, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f,
-	0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x54, 0x69, 0x6d,
-	0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x79, 0x73, 0x74, 0x65,
-	0x6d, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20,
-	0x01, 0x28, 0x04, 0x52, 0x10, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x4d,
-	0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x73, 0x73,
-	0x5f, 0x6b, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x52, 0x73,
-	0x73, 0x4b, 0x62, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67,
-	0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f,
-	0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12,
-	0x2a, 0x0a, 0x11, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61,
-	0x75, 0x6c, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x61, 0x6a, 0x6f,
-	0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x69,
-	0x6f, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04,
-	0x52, 0x09, 0x69, 0x6f, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c, 0x69,
-	0x6f, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28,
-	0x04, 0x52, 0x0a, 0x69, 0x6f, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x3c, 0x0a,
-	0x1a, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65,
-	0x78, 0x74, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28,
-	0x04, 0x52, 0x18, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74,
-	0x65, 0x78, 0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x69,
-	0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65,
-	0x78, 0x74, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28,
-	0x04, 0x52, 0x1a, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f,
-	0x6e, 0x74, 0x65, 0x78, 0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0xe5, 0x01,
-	0x0a, 0x0e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f,
-	0x12, 0x5b, 0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62,
-	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x6f, 0x64,
-	0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x42, 0x75, 0x69, 0x6c,
-	0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x3a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e,
-	0x52, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x0a,
-	0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24,
-	0x0a, 0x0e, 0x6e, 0x75, 0x6d, 0x5f, 0x6f, 0x66, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73,
-	0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4f, 0x66, 0x4d, 0x6f, 0x64,
-	0x75, 0x6c, 0x65, 0x73, 0x22, 0x2f, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73,
-	0x74, 0x65, 0x6d, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00,
-	0x12, 0x09, 0x0a, 0x05, 0x53, 0x4f, 0x4f, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4d,
-	0x41, 0x4b, 0x45, 0x10, 0x02, 0x22, 0x6c, 0x0a, 0x1a, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61,
-	0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72,
-	0x69, 0x63, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69,
-	0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
-	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d,
-	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x42, 0x61, 0x73, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72,
-	0x69, 0x63, 0x73, 0x22, 0x62, 0x0a, 0x1b, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55,
-	0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69,
-	0x63, 0x73, 0x12, 0x43, 0x0a, 0x04, 0x63, 0x75, 0x6a, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
-	0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d,
-	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55,
-	0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
-	0x73, 0x52, 0x04, 0x63, 0x75, 0x6a, 0x73, 0x22, 0x94, 0x03, 0x0a, 0x11, 0x53, 0x6f, 0x6f, 0x6e,
-	0x67, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x18, 0x0a,
-	0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
-	0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61,
-	0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61,
-	0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c,
-	0x6f, 0x63, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f,
-	0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
-	0x28, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x73,
-	0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c,
-	0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78,
-	0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04,
-	0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x35, 0x0a,
-	0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e,
+	0x63, 0x73, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4e,
+	0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6f,
+	0x75, 0x72, 0x63, 0x65, 0x3a, 0x08, 0x4e, 0x4f, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x44, 0x52, 0x15,
+	0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x53,
+	0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x0e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x65,
+	0x6e, 0x76, 0x5f, 0x76, 0x61, 0x72, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e,
 	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72,
-	0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x65, 0x76,
-	0x65, 0x6e, 0x74, 0x73, 0x12, 0x50, 0x0a, 0x11, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75,
-	0x69, 0x6c, 0x64, 0x73, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32,
-	0x24, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
-	0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64,
-	0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c,
-	0x64, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x46, 0x0a, 0x0d, 0x70, 0x65, 0x72, 0x66, 0x5f, 0x63,
-	0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e,
+	0x69, 0x63, 0x73, 0x2e, 0x53, 0x6f, 0x6f, 0x6e, 0x67, 0x45, 0x6e, 0x76, 0x56, 0x61, 0x72, 0x73,
+	0x52, 0x0c, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x45, 0x6e, 0x76, 0x56, 0x61, 0x72, 0x73, 0x12, 0x1d,
+	0x0a, 0x0a, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x0a, 0x20, 0x01,
+	0x28, 0x08, 0x52, 0x09, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x74, 0x0a,
+	0x15, 0x4e, 0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69, 0x73, 0x74,
+	0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x4f, 0x54, 0x5f, 0x55, 0x53,
+	0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x49, 0x4e, 0x4a, 0x41, 0x5f, 0x4c, 0x4f,
+	0x47, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x45, 0x56, 0x45, 0x4e, 0x4c, 0x59, 0x5f, 0x44, 0x49,
+	0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x45,
+	0x58, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x03, 0x12, 0x13,
+	0x0a, 0x0f, 0x48, 0x49, 0x4e, 0x54, 0x5f, 0x46, 0x52, 0x4f, 0x4d, 0x5f, 0x53, 0x4f, 0x4f, 0x4e,
+	0x47, 0x10, 0x04, 0x22, 0x67, 0x0a, 0x0c, 0x53, 0x6f, 0x6f, 0x6e, 0x67, 0x45, 0x6e, 0x76, 0x56,
+	0x61, 0x72, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x63,
+	0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61,
+	0x72, 0x74, 0x69, 0x61, 0x6c, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x12, 0x2e, 0x0a, 0x13,
+	0x75, 0x73, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x6d, 0x70,
+	0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x75, 0x73, 0x65, 0x50, 0x61,
+	0x72, 0x74, 0x69, 0x61, 0x6c, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x22, 0xed, 0x01, 0x0a,
+	0x12, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49,
+	0x6e, 0x66, 0x6f, 0x12, 0x32, 0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x68, 0x79,
+	0x73, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x04, 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61,
+	0x6c, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x76, 0x61, 0x69, 0x6c,
+	0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x70, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52,
+	0x0d, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x70, 0x75, 0x73, 0x12, 0x3d,
+	0x0a, 0x08, 0x63, 0x70, 0x75, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x22, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d,
+	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x43, 0x70, 0x75,
+	0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x63, 0x70, 0x75, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a,
+	0x08, 0x6d, 0x65, 0x6d, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x22, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
+	0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4d, 0x65, 0x6d, 0x49,
+	0x6e, 0x66, 0x6f, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x7e, 0x0a, 0x0d,
+	0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x43, 0x70, 0x75, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1b, 0x0a,
+	0x09, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x08, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f,
+	0x64, 0x65, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
+	0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x70, 0x75,
+	0x5f, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x63, 0x70,
+	0x75, 0x43, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18,
+	0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x6c, 0x0a, 0x0d,
+	0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4d, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1b, 0x0a,
+	0x09, 0x6d, 0x65, 0x6d, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
+	0x52, 0x08, 0x6d, 0x65, 0x6d, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x65,
+	0x6d, 0x5f, 0x66, 0x72, 0x65, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6d, 0x65,
+	0x6d, 0x46, 0x72, 0x65, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x6d, 0x5f, 0x61, 0x76, 0x61,
+	0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6d, 0x65,
+	0x6d, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x22, 0xca, 0x02, 0x0a, 0x08, 0x50,
+	0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72,
+	0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65,
+	0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
+	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a,
+	0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09,
+	0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52,
+	0x08, 0x72, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0a, 0x6d, 0x65, 0x6d,
+	0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18,
+	0x01, 0x52, 0x09, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x17,
+	0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
+	0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e,
 	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72,
-	0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73,
-	0x52, 0x0c, 0x70, 0x65, 0x72, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x22, 0xdb,
-	0x01, 0x0a, 0x10, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x65, 0x74, 0x63,
-	0x68, 0x65, 0x72, 0x12, 0x4a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c,
-	0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12,
-	0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d,
-	0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6d, 0x69, 0x63,
-	0x72, 0x6f, 0x73, 0x22, 0x47, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x61,
-	0x74, 0x75, 0x73, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47,
-	0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x01, 0x12, 0x09,
-	0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4d, 0x49, 0x53,
-	0x53, 0x49, 0x4e, 0x47, 0x5f, 0x47, 0x43, 0x45, 0x52, 0x54, 0x10, 0x03, 0x22, 0x91, 0x01, 0x0a,
-	0x0f, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49, 0x6e, 0x66, 0x6f,
-	0x12, 0x3d, 0x0a, 0x1b, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
-	0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18,
-	0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c,
-	0x64, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12,
-	0x3f, 0x0a, 0x1c, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x64,
-	0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18,
-	0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x19, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c,
-	0x64, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73,
-	0x22, 0x8a, 0x02, 0x0a, 0x10, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74,
-	0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2e, 0x0a, 0x13, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64,
-	0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x04, 0x52, 0x11, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x4d,
-	0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x39, 0x0a, 0x19, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61,
-	0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72,
-	0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63,
-	0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73,
-	0x12, 0x41, 0x0a, 0x0d, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x74,
-	0x68, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
-	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4a, 0x6f,
-	0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50,
-	0x61, 0x74, 0x68, 0x12, 0x48, 0x0a, 0x11, 0x6c, 0x6f, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6e, 0x6e,
-	0x69, 0x6e, 0x67, 0x5f, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c,
+	0x69, 0x63, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75,
+	0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x15, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
+	0x65, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22,
+	0x0a, 0x0d, 0x6e, 0x6f, 0x6e, 0x5f, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x18,
+	0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e, 0x6f, 0x6e, 0x5a, 0x65, 0x72, 0x6f, 0x45, 0x78,
+	0x69, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73,
+	0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72,
+	0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x61, 0x0a, 0x0c, 0x50, 0x65, 0x72, 0x66, 0x43,
+	0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x06, 0x67,
+	0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x73, 0x6f,
+	0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+	0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x47, 0x72, 0x6f,
+	0x75, 0x70, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x22, 0x64, 0x0a, 0x10, 0x50, 0x65,
+	0x72, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12,
+	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x08, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69,
+	0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x43,
+	0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x08, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73,
+	0x22, 0x37, 0x0a, 0x0b, 0x50, 0x65, 0x72, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12,
+	0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
+	0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xb9, 0x03, 0x0a, 0x13, 0x50, 0x72,
+	0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66,
+	0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x74, 0x69,
+	0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52,
+	0x0e, 0x75, 0x73, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12,
+	0x2c, 0x0a, 0x12, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d,
+	0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x73, 0x79, 0x73,
+	0x74, 0x65, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x1c, 0x0a,
+	0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x73, 0x73, 0x5f, 0x6b, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x52, 0x73, 0x73, 0x4b, 0x62, 0x12, 0x2a, 0x0a, 0x11, 0x6d,
+	0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73,
+	0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x50, 0x61, 0x67,
+	0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x61, 0x6a, 0x6f, 0x72,
+	0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01,
+	0x28, 0x04, 0x52, 0x0f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75,
+	0x6c, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x69, 0x6f, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f,
+	0x6b, 0x62, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6f, 0x49, 0x6e, 0x70, 0x75,
+	0x74, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x6f, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74,
+	0x5f, 0x6b, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x69, 0x6f, 0x4f, 0x75, 0x74,
+	0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x3c, 0x0a, 0x1a, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61,
+	0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63,
+	0x68, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x18, 0x76, 0x6f, 0x6c, 0x75, 0x6e,
+	0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x53, 0x77, 0x69, 0x74, 0x63,
+	0x68, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61,
+	0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63,
+	0x68, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1a, 0x69, 0x6e, 0x76, 0x6f, 0x6c,
+	0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x53, 0x77, 0x69,
+	0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0xe5, 0x01, 0x0a, 0x0e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+	0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x5b, 0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c,
+	0x64, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f,
 	0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
-	0x72, 0x69, 0x63, 0x73, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x6c, 0x6f,
-	0x6e, 0x67, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x62, 0x0a,
-	0x07, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2e, 0x0a, 0x13, 0x65, 0x6c, 0x61, 0x70,
-	0x73, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x54, 0x69,
-	0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6a, 0x6f, 0x62, 0x5f,
-	0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x0e, 0x6a, 0x6f, 0x62, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
-	0x6e, 0x22, 0xb9, 0x05, 0x0a, 0x15, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x42,
-	0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x42, 0x0a, 0x0d, 0x61,
-	0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x66, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64,
-	0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66,
-	0x6f, 0x52, 0x0c, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x50, 0x65, 0x72, 0x66, 0x12,
-	0x44, 0x0a, 0x0e, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x65, 0x72,
-	0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
-	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65,
-	0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x69, 0x6e,
-	0x67, 0x50, 0x65, 0x72, 0x66, 0x12, 0x68, 0x0a, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f,
-	0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x73,
+	0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49,
+	0x6e, 0x66, 0x6f, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x3a,
+	0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x52, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53,
+	0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f,
+	0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x75,
+	0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x75, 0x6d, 0x5f, 0x6f, 0x66,
+	0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c,
+	0x6e, 0x75, 0x6d, 0x4f, 0x66, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x2f, 0x0a, 0x0b,
+	0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x0b, 0x0a, 0x07, 0x55,
+	0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x4f, 0x4f, 0x4e,
+	0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x41, 0x4b, 0x45, 0x10, 0x02, 0x22, 0x6c, 0x0a,
+	0x1a, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75,
+	0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e,
+	0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
+	0x3a, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x20, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d,
+	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x42, 0x61,
+	0x73, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x62, 0x0a, 0x1b, 0x43,
+	0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e,
+	0x65, 0x79, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x43, 0x0a, 0x04, 0x63, 0x75,
+	0x6a, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
+	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x43,
+	0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e,
+	0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x04, 0x63, 0x75, 0x6a, 0x73, 0x22,
+	0x94, 0x03, 0x0a, 0x11, 0x53, 0x6f, 0x6f, 0x6e, 0x67, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65,
+	0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12,
+	0x1a, 0x0a, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x0d, 0x52, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x74,
+	0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c,
+	0x6f, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c,
+	0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x53, 0x69, 0x7a,
+	0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x69,
+	0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, 0x65, 0x61,
+	0x70, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18,
+	0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
+	0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66,
+	0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x50, 0x0a, 0x11,
+	0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x5f, 0x69, 0x6e, 0x66,
+	0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
+	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x69,
+	0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x6d,
+	0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x46,
+	0x0a, 0x0d, 0x70, 0x65, 0x72, 0x66, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18,
+	0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
+	0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66,
+	0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0c, 0x70, 0x65, 0x72, 0x66, 0x43, 0x6f,
+	0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x22, 0xdb, 0x01, 0x0a, 0x10, 0x45, 0x78, 0x70, 0x43, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x12, 0x4a, 0x0a, 0x06, 0x73,
+	0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, 0x73, 0x6f,
+	0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+	0x73, 0x2e, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x65, 0x74, 0x63, 0x68,
+	0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52,
+	0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e,
+	0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e,
+	0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x04, 0x52, 0x06, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x22, 0x47, 0x0a, 0x0c, 0x43,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0d, 0x0a, 0x09, 0x4e,
+	0x4f, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f,
+	0x4e, 0x46, 0x49, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10,
+	0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x5f, 0x47, 0x43, 0x45,
+	0x52, 0x54, 0x10, 0x03, 0x22, 0x91, 0x01, 0x0a, 0x0f, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75,
+	0x69, 0x6c, 0x64, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x1b, 0x6d, 0x69, 0x78, 0x65,
+	0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f,
+	0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x6d,
+	0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
+	0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x6d, 0x69, 0x78, 0x65, 0x64,
+	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f,
+	0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x19, 0x6d,
+	0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65,
+	0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x8a, 0x02, 0x0a, 0x10, 0x43, 0x72, 0x69,
+	0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2e, 0x0a,
+	0x13, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69,
+	0x63, 0x72, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x65, 0x6c, 0x61, 0x70,
+	0x73, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x39, 0x0a,
+	0x19, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74,
+	0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04,
+	0x52, 0x16, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x54, 0x69,
+	0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x41, 0x0a, 0x0d, 0x63, 0x72, 0x69, 0x74,
+	0x69, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
+	0x1c, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
+	0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x63,
+	0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x48, 0x0a, 0x11, 0x6c,
+	0x6f, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x6a, 0x6f, 0x62, 0x73,
+	0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62,
+	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4a, 0x6f, 0x62,
+	0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x6c, 0x6f, 0x6e, 0x67, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e,
+	0x67, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x62, 0x0a, 0x07, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f,
+	0x12, 0x2e, 0x0a, 0x13, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65,
+	0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x65,
+	0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73,
+	0x12, 0x27, 0x0a, 0x0f, 0x6a, 0x6f, 0x62, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6a, 0x6f, 0x62, 0x44, 0x65,
+	0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xb9, 0x05, 0x0a, 0x15, 0x4f, 0x70,
+	0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72,
+	0x69, 0x63, 0x73, 0x12, 0x42, 0x0a, 0x0d, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x5f,
+	0x70, 0x65, 0x72, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f,
+	0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x2e, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x61, 0x6e, 0x61, 0x6c, 0x79,
+	0x73, 0x69, 0x73, 0x50, 0x65, 0x72, 0x66, 0x12, 0x44, 0x0a, 0x0e, 0x70, 0x61, 0x63, 0x6b, 0x61,
+	0x67, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x65, 0x72, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x1d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
+	0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d,
+	0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x50, 0x65, 0x72, 0x66, 0x12, 0x68, 0x0a,
+	0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69,
+	0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6d,
+	0x69, 0x7a, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65,
+	0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x1a, 0xab, 0x03, 0x0a, 0x18, 0x54, 0x61, 0x72, 0x67,
+	0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65,
+	0x73, 0x75, 0x6c, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x70, 0x74, 0x69,
+	0x6d, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6f, 0x70, 0x74,
+	0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x35, 0x0a, 0x16, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69,
+	0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x65,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61,
+	0x74, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x65, 0x12, 0x44, 0x0a,
+	0x0e, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x65, 0x72, 0x66, 0x18,
+	0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
+	0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66,
+	0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x50,
+	0x65, 0x72, 0x66, 0x12, 0x7b, 0x0a, 0x0f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x61, 0x72,
+	0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x52, 0x2e, 0x73,
 	0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
 	0x63, 0x73, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c,
 	0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4f,
 	0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c,
-	0x74, 0x52, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x1a,
-	0xab, 0x03, 0x0a, 0x18, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69,
-	0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x12, 0x0a, 0x04,
-	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
-	0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x08, 0x52, 0x09, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x35,
-	0x0a, 0x16, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72,
-	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15,
-	0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x74, 0x69,
-	0x6f, 0x6e, 0x61, 0x6c, 0x65, 0x12, 0x44, 0x0a, 0x0e, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x69,
-	0x6e, 0x67, 0x5f, 0x70, 0x65, 0x72, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e,
-	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72,
-	0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x70, 0x61,
-	0x63, 0x6b, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x50, 0x65, 0x72, 0x66, 0x12, 0x7b, 0x0a, 0x0f, 0x6f,
-	0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x18, 0x05,
-	0x20, 0x03, 0x28, 0x0b, 0x32, 0x52, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69,
-	0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6d,
-	0x69, 0x7a, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
-	0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74,
-	0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74,
-	0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74,
-	0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x1a, 0x63, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x70,
-	0x75, 0x74, 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, 0x12,
-	0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69,
-	0x7a, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x5f, 0x6d,
-	0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x6e,
-	0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x42, 0x28, 0x5a,
-	0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75,
-	0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
-	0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x74, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74,
+	0x52, 0x0e, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74,
+	0x1a, 0x63, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 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, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e,
+	0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03,
+	0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x4d, 0x6f,
+	0x64, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x83, 0x01, 0x0a, 0x10, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74,
+	0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f,
+	0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09,
+	0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x41, 0x72, 0x67, 0x73, 0x12, 0x4c, 0x0a,
+	0x0d, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69,
+	0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65,
+	0x67, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0c, 0x63,
+	0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22, 0xc9, 0x01, 0x0a, 0x12,
+	0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69,
+	0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+	0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+	0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
+	0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65,
+	0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x64,
+	0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61,
+	0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74,
+	0x6f, 0x74, 0x61, 0x6c, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x36, 0x0a, 0x06, 0x63, 0x6f, 0x75,
+	0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x6f, 0x6f, 0x6e,
+	0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
+	0x46, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x06, 0x63, 0x6f, 0x75, 0x6e, 0x74,
+	0x73, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0x8b, 0x01, 0x0a, 0x09, 0x46, 0x69, 0x6c, 0x65,
+	0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
+	0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73,
+	0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x12, 0x24, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69,
+	0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74,
+	0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65,
+	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x28, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+	0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 }
 
 var (
@@ -2656,7 +3006,7 @@
 }
 
 var file_metrics_proto_enumTypes = make([]protoimpl.EnumInfo, 5)
-var file_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 21)
+var file_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 25)
 var file_metrics_proto_goTypes = []interface{}{
 	(MetricsBase_BuildVariant)(0),                          // 0: soong_build_metrics.MetricsBase.BuildVariant
 	(MetricsBase_Arch)(0),                                  // 1: soong_build_metrics.MetricsBase.Arch
@@ -2665,68 +3015,75 @@
 	(ExpConfigFetcher_ConfigStatus)(0),                     // 4: soong_build_metrics.ExpConfigFetcher.ConfigStatus
 	(*MetricsBase)(nil),                                    // 5: soong_build_metrics.MetricsBase
 	(*BuildConfig)(nil),                                    // 6: soong_build_metrics.BuildConfig
-	(*SystemResourceInfo)(nil),                             // 7: soong_build_metrics.SystemResourceInfo
-	(*SystemCpuInfo)(nil),                                  // 8: soong_build_metrics.SystemCpuInfo
-	(*SystemMemInfo)(nil),                                  // 9: soong_build_metrics.SystemMemInfo
-	(*PerfInfo)(nil),                                       // 10: soong_build_metrics.PerfInfo
-	(*PerfCounters)(nil),                                   // 11: soong_build_metrics.PerfCounters
-	(*PerfCounterGroup)(nil),                               // 12: soong_build_metrics.PerfCounterGroup
-	(*PerfCounter)(nil),                                    // 13: soong_build_metrics.PerfCounter
-	(*ProcessResourceInfo)(nil),                            // 14: soong_build_metrics.ProcessResourceInfo
-	(*ModuleTypeInfo)(nil),                                 // 15: soong_build_metrics.ModuleTypeInfo
-	(*CriticalUserJourneyMetrics)(nil),                     // 16: soong_build_metrics.CriticalUserJourneyMetrics
-	(*CriticalUserJourneysMetrics)(nil),                    // 17: soong_build_metrics.CriticalUserJourneysMetrics
-	(*SoongBuildMetrics)(nil),                              // 18: soong_build_metrics.SoongBuildMetrics
-	(*ExpConfigFetcher)(nil),                               // 19: soong_build_metrics.ExpConfigFetcher
-	(*MixedBuildsInfo)(nil),                                // 20: soong_build_metrics.MixedBuildsInfo
-	(*CriticalPathInfo)(nil),                               // 21: soong_build_metrics.CriticalPathInfo
-	(*JobInfo)(nil),                                        // 22: soong_build_metrics.JobInfo
-	(*OptimizedBuildMetrics)(nil),                          // 23: soong_build_metrics.OptimizedBuildMetrics
-	(*OptimizedBuildMetrics_TargetOptimizationResult)(nil), // 24: soong_build_metrics.OptimizedBuildMetrics.TargetOptimizationResult
-	(*OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact)(nil), // 25: soong_build_metrics.OptimizedBuildMetrics.TargetOptimizationResult.OutputArtifact
+	(*SoongEnvVars)(nil),                                   // 7: soong_build_metrics.SoongEnvVars
+	(*SystemResourceInfo)(nil),                             // 8: soong_build_metrics.SystemResourceInfo
+	(*SystemCpuInfo)(nil),                                  // 9: soong_build_metrics.SystemCpuInfo
+	(*SystemMemInfo)(nil),                                  // 10: soong_build_metrics.SystemMemInfo
+	(*PerfInfo)(nil),                                       // 11: soong_build_metrics.PerfInfo
+	(*PerfCounters)(nil),                                   // 12: soong_build_metrics.PerfCounters
+	(*PerfCounterGroup)(nil),                               // 13: soong_build_metrics.PerfCounterGroup
+	(*PerfCounter)(nil),                                    // 14: soong_build_metrics.PerfCounter
+	(*ProcessResourceInfo)(nil),                            // 15: soong_build_metrics.ProcessResourceInfo
+	(*ModuleTypeInfo)(nil),                                 // 16: soong_build_metrics.ModuleTypeInfo
+	(*CriticalUserJourneyMetrics)(nil),                     // 17: soong_build_metrics.CriticalUserJourneyMetrics
+	(*CriticalUserJourneysMetrics)(nil),                    // 18: soong_build_metrics.CriticalUserJourneysMetrics
+	(*SoongBuildMetrics)(nil),                              // 19: soong_build_metrics.SoongBuildMetrics
+	(*ExpConfigFetcher)(nil),                               // 20: soong_build_metrics.ExpConfigFetcher
+	(*MixedBuildsInfo)(nil),                                // 21: soong_build_metrics.MixedBuildsInfo
+	(*CriticalPathInfo)(nil),                               // 22: soong_build_metrics.CriticalPathInfo
+	(*JobInfo)(nil),                                        // 23: soong_build_metrics.JobInfo
+	(*OptimizedBuildMetrics)(nil),                          // 24: soong_build_metrics.OptimizedBuildMetrics
+	(*ExecutionMetrics)(nil),                               // 25: soong_build_metrics.ExecutionMetrics
+	(*AggregatedFileList)(nil),                             // 26: soong_build_metrics.AggregatedFileList
+	(*FileCount)(nil),                                      // 27: soong_build_metrics.FileCount
+	(*OptimizedBuildMetrics_TargetOptimizationResult)(nil), // 28: soong_build_metrics.OptimizedBuildMetrics.TargetOptimizationResult
+	(*OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact)(nil), // 29: soong_build_metrics.OptimizedBuildMetrics.TargetOptimizationResult.OutputArtifact
 }
 var file_metrics_proto_depIdxs = []int32{
 	0,  // 0: soong_build_metrics.MetricsBase.target_build_variant:type_name -> soong_build_metrics.MetricsBase.BuildVariant
 	1,  // 1: soong_build_metrics.MetricsBase.target_arch:type_name -> soong_build_metrics.MetricsBase.Arch
 	1,  // 2: soong_build_metrics.MetricsBase.host_arch:type_name -> soong_build_metrics.MetricsBase.Arch
 	1,  // 3: soong_build_metrics.MetricsBase.host_2nd_arch:type_name -> soong_build_metrics.MetricsBase.Arch
-	10, // 4: soong_build_metrics.MetricsBase.setup_tools:type_name -> soong_build_metrics.PerfInfo
-	10, // 5: soong_build_metrics.MetricsBase.kati_runs:type_name -> soong_build_metrics.PerfInfo
-	10, // 6: soong_build_metrics.MetricsBase.soong_runs:type_name -> soong_build_metrics.PerfInfo
-	10, // 7: soong_build_metrics.MetricsBase.ninja_runs:type_name -> soong_build_metrics.PerfInfo
-	10, // 8: soong_build_metrics.MetricsBase.total:type_name -> soong_build_metrics.PerfInfo
-	18, // 9: soong_build_metrics.MetricsBase.soong_build_metrics:type_name -> soong_build_metrics.SoongBuildMetrics
+	11, // 4: soong_build_metrics.MetricsBase.setup_tools:type_name -> soong_build_metrics.PerfInfo
+	11, // 5: soong_build_metrics.MetricsBase.kati_runs:type_name -> soong_build_metrics.PerfInfo
+	11, // 6: soong_build_metrics.MetricsBase.soong_runs:type_name -> soong_build_metrics.PerfInfo
+	11, // 7: soong_build_metrics.MetricsBase.ninja_runs:type_name -> soong_build_metrics.PerfInfo
+	11, // 8: soong_build_metrics.MetricsBase.total:type_name -> soong_build_metrics.PerfInfo
+	19, // 9: soong_build_metrics.MetricsBase.soong_build_metrics:type_name -> soong_build_metrics.SoongBuildMetrics
 	6,  // 10: soong_build_metrics.MetricsBase.build_config:type_name -> soong_build_metrics.BuildConfig
-	7,  // 11: soong_build_metrics.MetricsBase.system_resource_info:type_name -> soong_build_metrics.SystemResourceInfo
-	10, // 12: soong_build_metrics.MetricsBase.bazel_runs:type_name -> soong_build_metrics.PerfInfo
-	19, // 13: soong_build_metrics.MetricsBase.exp_config_fetcher:type_name -> soong_build_metrics.ExpConfigFetcher
-	21, // 14: soong_build_metrics.MetricsBase.critical_path_info:type_name -> soong_build_metrics.CriticalPathInfo
-	23, // 15: soong_build_metrics.MetricsBase.optimized_build_metrics:type_name -> soong_build_metrics.OptimizedBuildMetrics
+	8,  // 11: soong_build_metrics.MetricsBase.system_resource_info:type_name -> soong_build_metrics.SystemResourceInfo
+	11, // 12: soong_build_metrics.MetricsBase.bazel_runs:type_name -> soong_build_metrics.PerfInfo
+	20, // 13: soong_build_metrics.MetricsBase.exp_config_fetcher:type_name -> soong_build_metrics.ExpConfigFetcher
+	22, // 14: soong_build_metrics.MetricsBase.critical_path_info:type_name -> soong_build_metrics.CriticalPathInfo
+	24, // 15: soong_build_metrics.MetricsBase.optimized_build_metrics:type_name -> soong_build_metrics.OptimizedBuildMetrics
 	2,  // 16: soong_build_metrics.BuildConfig.ninja_weight_list_source:type_name -> soong_build_metrics.BuildConfig.NinjaWeightListSource
-	8,  // 17: soong_build_metrics.SystemResourceInfo.cpu_info:type_name -> soong_build_metrics.SystemCpuInfo
-	9,  // 18: soong_build_metrics.SystemResourceInfo.mem_info:type_name -> soong_build_metrics.SystemMemInfo
-	14, // 19: soong_build_metrics.PerfInfo.processes_resource_info:type_name -> soong_build_metrics.ProcessResourceInfo
-	12, // 20: soong_build_metrics.PerfCounters.groups:type_name -> soong_build_metrics.PerfCounterGroup
-	13, // 21: soong_build_metrics.PerfCounterGroup.counters:type_name -> soong_build_metrics.PerfCounter
-	3,  // 22: soong_build_metrics.ModuleTypeInfo.build_system:type_name -> soong_build_metrics.ModuleTypeInfo.BuildSystem
-	5,  // 23: soong_build_metrics.CriticalUserJourneyMetrics.metrics:type_name -> soong_build_metrics.MetricsBase
-	16, // 24: soong_build_metrics.CriticalUserJourneysMetrics.cujs:type_name -> soong_build_metrics.CriticalUserJourneyMetrics
-	10, // 25: soong_build_metrics.SoongBuildMetrics.events:type_name -> soong_build_metrics.PerfInfo
-	20, // 26: soong_build_metrics.SoongBuildMetrics.mixed_builds_info:type_name -> soong_build_metrics.MixedBuildsInfo
-	11, // 27: soong_build_metrics.SoongBuildMetrics.perf_counters:type_name -> soong_build_metrics.PerfCounters
-	4,  // 28: soong_build_metrics.ExpConfigFetcher.status:type_name -> soong_build_metrics.ExpConfigFetcher.ConfigStatus
-	22, // 29: soong_build_metrics.CriticalPathInfo.critical_path:type_name -> soong_build_metrics.JobInfo
-	22, // 30: soong_build_metrics.CriticalPathInfo.long_running_jobs:type_name -> soong_build_metrics.JobInfo
-	10, // 31: soong_build_metrics.OptimizedBuildMetrics.analysis_perf:type_name -> soong_build_metrics.PerfInfo
-	10, // 32: soong_build_metrics.OptimizedBuildMetrics.packaging_perf:type_name -> soong_build_metrics.PerfInfo
-	24, // 33: soong_build_metrics.OptimizedBuildMetrics.target_result:type_name -> soong_build_metrics.OptimizedBuildMetrics.TargetOptimizationResult
-	10, // 34: soong_build_metrics.OptimizedBuildMetrics.TargetOptimizationResult.packaging_perf:type_name -> soong_build_metrics.PerfInfo
-	25, // 35: soong_build_metrics.OptimizedBuildMetrics.TargetOptimizationResult.output_artifact:type_name -> soong_build_metrics.OptimizedBuildMetrics.TargetOptimizationResult.OutputArtifact
-	36, // [36:36] is the sub-list for method output_type
-	36, // [36:36] is the sub-list for method input_type
-	36, // [36:36] is the sub-list for extension type_name
-	36, // [36:36] is the sub-list for extension extendee
-	0,  // [0:36] is the sub-list for field type_name
+	7,  // 17: soong_build_metrics.BuildConfig.soong_env_vars:type_name -> soong_build_metrics.SoongEnvVars
+	9,  // 18: soong_build_metrics.SystemResourceInfo.cpu_info:type_name -> soong_build_metrics.SystemCpuInfo
+	10, // 19: soong_build_metrics.SystemResourceInfo.mem_info:type_name -> soong_build_metrics.SystemMemInfo
+	15, // 20: soong_build_metrics.PerfInfo.processes_resource_info:type_name -> soong_build_metrics.ProcessResourceInfo
+	13, // 21: soong_build_metrics.PerfCounters.groups:type_name -> soong_build_metrics.PerfCounterGroup
+	14, // 22: soong_build_metrics.PerfCounterGroup.counters:type_name -> soong_build_metrics.PerfCounter
+	3,  // 23: soong_build_metrics.ModuleTypeInfo.build_system:type_name -> soong_build_metrics.ModuleTypeInfo.BuildSystem
+	5,  // 24: soong_build_metrics.CriticalUserJourneyMetrics.metrics:type_name -> soong_build_metrics.MetricsBase
+	17, // 25: soong_build_metrics.CriticalUserJourneysMetrics.cujs:type_name -> soong_build_metrics.CriticalUserJourneyMetrics
+	11, // 26: soong_build_metrics.SoongBuildMetrics.events:type_name -> soong_build_metrics.PerfInfo
+	21, // 27: soong_build_metrics.SoongBuildMetrics.mixed_builds_info:type_name -> soong_build_metrics.MixedBuildsInfo
+	12, // 28: soong_build_metrics.SoongBuildMetrics.perf_counters:type_name -> soong_build_metrics.PerfCounters
+	4,  // 29: soong_build_metrics.ExpConfigFetcher.status:type_name -> soong_build_metrics.ExpConfigFetcher.ConfigStatus
+	23, // 30: soong_build_metrics.CriticalPathInfo.critical_path:type_name -> soong_build_metrics.JobInfo
+	23, // 31: soong_build_metrics.CriticalPathInfo.long_running_jobs:type_name -> soong_build_metrics.JobInfo
+	11, // 32: soong_build_metrics.OptimizedBuildMetrics.analysis_perf:type_name -> soong_build_metrics.PerfInfo
+	11, // 33: soong_build_metrics.OptimizedBuildMetrics.packaging_perf:type_name -> soong_build_metrics.PerfInfo
+	28, // 34: soong_build_metrics.OptimizedBuildMetrics.target_result:type_name -> soong_build_metrics.OptimizedBuildMetrics.TargetOptimizationResult
+	26, // 35: soong_build_metrics.ExecutionMetrics.changed_files:type_name -> soong_build_metrics.AggregatedFileList
+	27, // 36: soong_build_metrics.AggregatedFileList.counts:type_name -> soong_build_metrics.FileCount
+	11, // 37: soong_build_metrics.OptimizedBuildMetrics.TargetOptimizationResult.packaging_perf:type_name -> soong_build_metrics.PerfInfo
+	29, // 38: soong_build_metrics.OptimizedBuildMetrics.TargetOptimizationResult.output_artifact:type_name -> soong_build_metrics.OptimizedBuildMetrics.TargetOptimizationResult.OutputArtifact
+	39, // [39:39] is the sub-list for method output_type
+	39, // [39:39] is the sub-list for method input_type
+	39, // [39:39] is the sub-list for extension type_name
+	39, // [39:39] is the sub-list for extension extendee
+	0,  // [0:39] is the sub-list for field type_name
 }
 
 func init() { file_metrics_proto_init() }
@@ -2760,7 +3117,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*SystemResourceInfo); i {
+			switch v := v.(*SoongEnvVars); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2772,7 +3129,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*SystemCpuInfo); i {
+			switch v := v.(*SystemResourceInfo); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2784,7 +3141,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*SystemMemInfo); i {
+			switch v := v.(*SystemCpuInfo); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2796,7 +3153,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*PerfInfo); i {
+			switch v := v.(*SystemMemInfo); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2808,7 +3165,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*PerfCounters); i {
+			switch v := v.(*PerfInfo); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2820,7 +3177,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*PerfCounterGroup); i {
+			switch v := v.(*PerfCounters); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2832,7 +3189,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*PerfCounter); i {
+			switch v := v.(*PerfCounterGroup); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2844,7 +3201,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ProcessResourceInfo); i {
+			switch v := v.(*PerfCounter); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2856,7 +3213,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ModuleTypeInfo); i {
+			switch v := v.(*ProcessResourceInfo); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2868,7 +3225,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CriticalUserJourneyMetrics); i {
+			switch v := v.(*ModuleTypeInfo); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2880,7 +3237,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CriticalUserJourneysMetrics); i {
+			switch v := v.(*CriticalUserJourneyMetrics); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2892,7 +3249,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*SoongBuildMetrics); i {
+			switch v := v.(*CriticalUserJourneysMetrics); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2904,7 +3261,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ExpConfigFetcher); i {
+			switch v := v.(*SoongBuildMetrics); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2916,7 +3273,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*MixedBuildsInfo); i {
+			switch v := v.(*ExpConfigFetcher); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2928,7 +3285,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CriticalPathInfo); i {
+			switch v := v.(*MixedBuildsInfo); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2940,7 +3297,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*JobInfo); i {
+			switch v := v.(*CriticalPathInfo); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2952,7 +3309,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*OptimizedBuildMetrics); i {
+			switch v := v.(*JobInfo); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2964,7 +3321,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*OptimizedBuildMetrics_TargetOptimizationResult); i {
+			switch v := v.(*OptimizedBuildMetrics); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2976,6 +3333,54 @@
 			}
 		}
 		file_metrics_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ExecutionMetrics); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_metrics_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*AggregatedFileList); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_metrics_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*FileCount); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_metrics_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OptimizedBuildMetrics_TargetOptimizationResult); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_metrics_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact); i {
 			case 0:
 				return &v.state
@@ -2994,7 +3399,7 @@
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_metrics_proto_rawDesc,
 			NumEnums:      5,
-			NumMessages:   21,
+			NumMessages:   25,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 3fbe97c..7ff389a 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -140,6 +140,9 @@
 
   // Metrics related to optimized builds.
   optional OptimizedBuildMetrics optimized_build_metrics = 35;
+
+  // The target release information. e.g., trunk_staging.
+  optional string target_release = 36;
 }
 
 message BuildConfig {
@@ -177,6 +180,21 @@
   // EXTERNAL_FILE - ninja uses an external custom weight list
   // HINT_FROM_SOONG - ninja uses a prioritized module list from Soong
   optional NinjaWeightListSource ninja_weight_list_source = 8 [default = NOT_USED];
+
+  // Values of some build-affecting environment variables.
+  optional SoongEnvVars soong_env_vars = 9;
+
+  // Whether this build uses soong-only (no kati) mode (either via environment variable,
+  // command line flag or product config.
+  optional bool soong_only = 10;
+}
+
+message SoongEnvVars {
+  // SOONG_PARTIAL_COMPILE
+  optional string partial_compile = 1;
+
+  // SOONG_USE_PARTIAL_COMPILE
+  optional string use_partial_compile = 2;
 }
 
 message SystemResourceInfo {
@@ -451,3 +469,48 @@
     }
   }
 }
+
+// This is created by soong_ui from SoongExexcutionMetrics files.
+message ExecutionMetrics {
+  // The arguments provided on the command line.
+  repeated string command_args = 1;
+
+  // Changed files detected by the build.
+  optional AggregatedFileList changed_files = 2;
+}
+
+// This is created by soong_ui from the various
+// android.find_input_delta_proto.FileList metrics provided to it by
+// find_input_delta.
+message AggregatedFileList {
+  // The (possibly truncated list of) added files.
+  repeated string additions = 2;
+
+  // The (possibly truncated list of) changed files.
+  repeated string changes = 3;
+
+  // The (possibly truncated list of) deleted files.
+  repeated string deletions = 4;
+
+  // Count of files added/changed/deleted.
+  optional uint32 total_delta = 5;
+
+  // Counts by extension.
+  repeated FileCount counts = 6;
+
+  reserved 1;
+}
+
+message FileCount {
+  // The file extension
+  optional string extension = 1;
+
+  // Number of added files with this extension.
+  optional uint32 additions = 2;
+
+  // Number of modified files with this extension.
+  optional uint32 modifications = 3;
+
+  // Number of deleted files with this extension.
+  optional uint32 deletions = 4;
+}
diff --git a/ui/metrics/metrics_proto/regen.sh b/ui/metrics/metrics_proto/regen.sh
index 5e5f9b8..8eb2d74 100755
--- a/ui/metrics/metrics_proto/regen.sh
+++ b/ui/metrics/metrics_proto/regen.sh
@@ -12,6 +12,6 @@
   die "could not find aprotoc. ${error_msg}"
 fi
 
-if ! aprotoc --go_out=paths=source_relative:. -I .:../../.. metrics.proto combined_metrics.proto; then
+if ! aprotoc --go_out=paths=source_relative:. metrics.proto; then
   die "build failed. ${error_msg}"
 fi
diff --git a/ui/metrics/proc/status_linux_test.go b/ui/metrics/proc/status_linux_test.go
index 6709850..0edc400 100644
--- a/ui/metrics/proc/status_linux_test.go
+++ b/ui/metrics/proc/status_linux_test.go
@@ -1,7 +1,6 @@
 package proc
 
 import (
-	"fmt"
 	"path/filepath"
 	"reflect"
 	"strconv"
@@ -29,7 +28,6 @@
 		t.Fatalf("got %v, want nil for error", err)
 	}
 
-	fmt.Printf("%d %d\b", status.VmPeak, expectedStatus.VmPeak)
 	if !reflect.DeepEqual(status, expectedStatus) {
 		t.Errorf("got %v, expecting %v for ProcStatus", status, expectedStatus)
 	}
diff --git a/ui/status/ninja.go b/ui/status/ninja.go
index 8c3ff29..73edbf6 100644
--- a/ui/status/ninja.go
+++ b/ui/status/ninja.go
@@ -46,6 +46,7 @@
 		forceClose: make(chan bool),
 		done:       make(chan bool),
 		cancelOpen: make(chan bool),
+		running:    make(map[uint32]*Action),
 	}
 
 	go n.run()
@@ -59,6 +60,7 @@
 	forceClose chan bool
 	done       chan bool
 	cancelOpen chan bool
+	running    map[uint32]*Action
 }
 
 const NINJA_READER_CLOSE_TIMEOUT = 5 * time.Second
@@ -68,32 +70,47 @@
 	// Signal the goroutine to stop if it is blocking opening the fifo.
 	close(n.cancelOpen)
 
+	closed := false
+
 	// Ninja should already have exited or been killed, wait 5 seconds for the FIFO to be closed and any
 	// remaining messages to be processed through the NinjaReader.run goroutine.
 	timeoutCh := time.After(NINJA_READER_CLOSE_TIMEOUT)
 	select {
 	case <-n.done:
-		return
+		closed = true
 	case <-timeoutCh:
 		// Channel is not closed yet
 	}
 
-	n.status.Error(fmt.Sprintf("ninja fifo didn't finish after %s", NINJA_READER_CLOSE_TIMEOUT.String()))
+	if !closed {
+		n.status.Error(fmt.Sprintf("ninja fifo didn't finish after %s", NINJA_READER_CLOSE_TIMEOUT.String()))
 
-	// Force close the reader even if the FIFO didn't close.
-	close(n.forceClose)
+		// Force close the reader even if the FIFO didn't close.
+		close(n.forceClose)
 
-	// Wait again for the reader thread to acknowledge the close before giving up and assuming it isn't going
-	// to send anything else.
-	timeoutCh = time.After(NINJA_READER_CLOSE_TIMEOUT)
-	select {
-	case <-n.done:
-		return
-	case <-timeoutCh:
-		// Channel is not closed yet
+		// Wait again for the reader thread to acknowledge the close before giving up and assuming it isn't going
+		// to send anything else.
+		timeoutCh = time.After(NINJA_READER_CLOSE_TIMEOUT)
+		select {
+		case <-n.done:
+			closed = true
+		case <-timeoutCh:
+			// Channel is not closed yet
+		}
 	}
 
-	n.status.Verbose(fmt.Sprintf("ninja fifo didn't finish even after force closing after %s", NINJA_READER_CLOSE_TIMEOUT.String()))
+	if !closed {
+		n.status.Verbose(fmt.Sprintf("ninja fifo didn't finish even after force closing after %s", NINJA_READER_CLOSE_TIMEOUT.String()))
+	}
+
+	err := fmt.Errorf("error: action cancelled when ninja exited")
+	for _, action := range n.running {
+		n.status.FinishAction(ActionResult{
+			Action: action,
+			Output: err.Error(),
+			Error:  err,
+		})
+	}
 }
 
 func (n *NinjaReader) run() {
@@ -125,8 +142,6 @@
 
 	r := bufio.NewReader(f)
 
-	running := map[uint32]*Action{}
-
 	msgChan := make(chan *ninja_frontend.Status)
 
 	// Read from the ninja fifo and decode the protobuf in a goroutine so the main NinjaReader.run goroutine
@@ -213,11 +228,11 @@
 				ChangedInputs: msg.EdgeStarted.ChangedInputs,
 			}
 			n.status.StartAction(action)
-			running[msg.EdgeStarted.GetId()] = action
+			n.running[msg.EdgeStarted.GetId()] = action
 		}
 		if msg.EdgeFinished != nil {
-			if started, ok := running[msg.EdgeFinished.GetId()]; ok {
-				delete(running, msg.EdgeFinished.GetId())
+			if started, ok := n.running[msg.EdgeFinished.GetId()]; ok {
+				delete(n.running, msg.EdgeFinished.GetId())
 
 				var err error
 				exitCode := int(msg.EdgeFinished.GetStatus())
diff --git a/vnames.json b/vnames.json
index 096260f..9e006bb 100644
--- a/vnames.json
+++ b/vnames.json
@@ -1,5 +1,17 @@
 [
   {
+    "pattern": "out/(.*)/srcjars.xref/frameworks/base/services/core/(.*)/android/server/am/(.*)",
+    "vname": {
+      "path": "frameworks/base/services/core/@2@/android/server/am/@3@"
+    }
+  },
+  {
+    "pattern": "out/(.*)/srcjars.xref/frameworks/base/services/core/(.*)/android/server/wm/(.*)",
+    "vname": {
+      "path": "frameworks/base/services/core/@2@/android/server/wm/@3@"
+    }
+  },
+  {
     "pattern": "out/(.*)",
     "vname": {
       "root": "out",
diff --git a/xml/xml_test.go b/xml/xml_test.go
index a59a293..212b0c5 100644
--- a/xml/xml_test.go
+++ b/xml/xml_test.go
@@ -71,7 +71,7 @@
 		{rule: "xmllint-minimal", input: "baz.xml"},
 	} {
 		t.Run(tc.schemaType, func(t *testing.T) {
-			rule := result.ModuleForTests(tc.input, "android_arm64_armv8-a").Rule(tc.rule)
+			rule := result.ModuleForTests(t, tc.input, "android_arm64_armv8-a").Rule(tc.rule)
 			android.AssertStringEquals(t, "input", tc.input, rule.Input.String())
 			if tc.schemaType != "" {
 				android.AssertStringEquals(t, "schema", tc.schema, rule.Args[tc.schemaType])
@@ -79,6 +79,6 @@
 		})
 	}
 
-	m := result.ModuleForTests("foo.xml", "android_arm64_armv8-a").Module().(*prebuiltEtcXml)
-	android.AssertPathRelativeToTopEquals(t, "installDir", "out/soong/target/product/test_device/system/etc", m.InstallDirPath())
+	m := result.ModuleForTests(t, "foo.xml", "android_arm64_armv8-a").Module().(*prebuiltEtcXml)
+	android.AssertPathRelativeToTopEquals(t, "installDir", "out/target/product/test_device/system/etc", m.InstallDirPath())
 }
diff --git a/zip/zip.go b/zip/zip.go
index f91a5f2..22b7704 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -475,13 +475,50 @@
 	return nil
 }
 
+func (z *ZipWriter) moveJavaFileBasedOnPackage(mapping *pathMapping) error {
+	src := mapping.src
+	var s os.FileInfo
+	var err error
+	if z.followSymlinks {
+		s, err = z.fs.Stat(src)
+	} else {
+		s, err = z.fs.Lstat(src)
+	}
+	if err != nil {
+		if os.IsNotExist(err) && z.ignoreMissingFiles {
+			return nil
+		}
+		return err
+	}
+	if !s.Mode().IsRegular() {
+		return nil
+	}
+	r, err := z.fs.Open(src)
+	if err != nil {
+		return err
+	}
+	// rewrite the destination using the package path if it can be determined
+	pkg, err := jar.JavaPackage(r, src)
+	err2 := r.Close()
+	if err2 != nil {
+		return err2
+	}
+	if err != nil {
+		// ignore errors for now, leaving the file at in its original location in the zip
+	} else {
+		mapping.dest = filepath.Join(filepath.Join(strings.Split(pkg, ".")...), filepath.Base(src))
+	}
+	return nil
+}
+
 func jarSort(mappings []pathMapping) {
 	sort.SliceStable(mappings, func(i int, j int) bool {
 		return jar.EntryNamesLess(mappings[i].dest, mappings[j].dest)
 	})
 }
 
-func (z *ZipWriter) write(f io.Writer, pathMappings []pathMapping, manifest string, emulateJar, srcJar bool,
+func (z *ZipWriter) write(f io.Writer, pathMappings []pathMapping, manifest string,
+	emulateJar, srcJar bool,
 	parallelJobs int) error {
 
 	z.errors = make(chan error)
@@ -511,11 +548,41 @@
 		return errors.New("must specify --jar when specifying a manifest via -m")
 	}
 
+	// move java source files to the correct folder based on the package statement inside of them.
+	// This is done before the entry sorting so that they're still in the right order.
+	if srcJar {
+		var javaMoveErrors []error
+		var javaMoveErrorsLock sync.Mutex
+		var wg sync.WaitGroup
+		for i := range pathMappings {
+			if filepath.Ext(pathMappings[i].src) == ".java" {
+				wg.Add(1)
+				go func() {
+					err := z.moveJavaFileBasedOnPackage(&pathMappings[i])
+					if err != nil {
+						javaMoveErrorsLock.Lock()
+						javaMoveErrors = append(javaMoveErrors, err)
+						javaMoveErrorsLock.Unlock()
+					}
+					wg.Done()
+				}()
+			}
+		}
+		wg.Wait()
+		if len(javaMoveErrors) > 0 {
+			return errors.Join(javaMoveErrors...)
+		}
+	}
+
 	if emulateJar {
 		// manifest may be empty, in which case addManifest will fill in a default
 		pathMappings = append(pathMappings, pathMapping{jar.ManifestFile, manifest, zip.Deflate})
 
 		jarSort(pathMappings)
+	} else {
+		sort.SliceStable(pathMappings, func(i int, j int) bool {
+			return pathMappings[i].dest < pathMappings[j].dest
+		})
 	}
 
 	go func() {
@@ -526,7 +593,7 @@
 			if emulateJar && ele.dest == jar.ManifestFile {
 				err = z.addManifest(ele.dest, ele.src, ele.zipMethod)
 			} else {
-				err = z.addFile(ele.dest, ele.src, ele.zipMethod, emulateJar, srcJar)
+				err = z.addFile(ele.dest, ele.src, ele.zipMethod, emulateJar)
 			}
 			if err != nil {
 				z.errors <- err
@@ -625,7 +692,7 @@
 }
 
 // imports (possibly with compression) <src> into the zip at sub-path <dest>
-func (z *ZipWriter) addFile(dest, src string, method uint16, emulateJar, srcJar bool) error {
+func (z *ZipWriter) addFile(dest, src string, method uint16, emulateJar bool) error {
 	var fileSize int64
 	var executable bool
 
@@ -699,21 +766,6 @@
 			return err
 		}
 
-		if srcJar && filepath.Ext(src) == ".java" {
-			// rewrite the destination using the package path if it can be determined
-			pkg, err := jar.JavaPackage(r, src)
-			if err != nil {
-				// ignore errors for now, leaving the file at in its original location in the zip
-			} else {
-				dest = filepath.Join(filepath.Join(strings.Split(pkg, ".")...), filepath.Base(src))
-			}
-
-			_, err = r.Seek(0, io.SeekStart)
-			if err != nil {
-				return err
-			}
-		}
-
 		fileSize = s.Size()
 		executable = s.Mode()&0100 != 0
 
diff --git a/zip/zip_test.go b/zip/zip_test.go
index c64c3f4..8f100d8 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -159,10 +159,10 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
+				fh("[", fileEmpty, zip.Store),
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
 				fh("c", fileC, zip.Deflate),
-				fh("[", fileEmpty, zip.Store),
 			},
 		},
 		{
@@ -261,10 +261,10 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
+				fh("[", fileEmpty, zip.Store),
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
 				fh("c", fileC, zip.Deflate),
-				fh("[", fileEmpty, zip.Store),
 			},
 		},
 		{
@@ -274,10 +274,10 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
+				fh("[", fileEmpty, zip.Store),
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
 				fh("c", fileC, zip.Deflate),
-				fh("[", fileEmpty, zip.Store),
 			},
 		},
 		{
@@ -287,11 +287,11 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
+				fh("@", fileC, zip.Deflate),
+				fh("[", fileEmpty, zip.Store),
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
-				fh("@", fileC, zip.Deflate),
 				fh("foo'bar", fileC, zip.Deflate),
-				fh("[", fileEmpty, zip.Store),
 			},
 		},
 		{
@@ -463,8 +463,8 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
-				fh("foo", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
+				fh("foo", fileA, zip.Deflate),
 			},
 		},
 		{
@@ -477,8 +477,8 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
-				fh("prefix/foo", fileA, zip.Deflate),
 				fh("prefix/a/a/b", fileB, zip.Deflate),
+				fh("prefix/foo", fileA, zip.Deflate),
 			},
 		},
 		{
@@ -490,8 +490,8 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
-				fh("foo", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
+				fh("foo", fileA, zip.Deflate),
 			},
 		},
 		{
@@ -504,8 +504,8 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
-				fh("foo/bar", fileA, zip.Deflate),
 				fh("b", fileB, zip.Deflate),
+				fh("foo/bar", fileA, zip.Deflate),
 			},
 		},
 
@@ -688,8 +688,8 @@
 
 	want := []string{
 		"foo/",
-		"foo/wrong_package.java",
 		"foo/correct_package.java",
+		"foo/wrong_package.java",
 		"no_package.java",
 		"src2/",
 		"src2/parse_error.java",