Merge "Upgrade to clang-r547379" into main
diff --git a/Android.bp b/Android.bp
index 98552a7..523f55c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -177,11 +177,10 @@
     ],
     // 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",
     ],
 }
 
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index 402cf16..6e2964a 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -25,7 +25,22 @@
         "aconfig_declarations_test.go",
         "aconfig_values_test.go",
         "aconfig_value_set_test.go",
-        "all_aconfig_declarations_test.go",
     ],
     pluginFor: ["soong_build"],
 }
+
+// All FlaggedApi flags associated with platform API.
+// By default this uses the platform APIs associated with android.jar
+// but other verticals/platforms can override via soong config setting.
+all_aconfig_declarations {
+    name: "all_aconfig_declarations",
+    api_signature_files: select(soong_config_variable("android_aconfig", "opt_platform_api_srcs"), {
+        default: [
+            ":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/all_aconfig_declarations.go b/aconfig/all_aconfig_declarations.go
index 3262493..ec20099 100644
--- a/aconfig/all_aconfig_declarations.go
+++ b/aconfig/all_aconfig_declarations.go
@@ -19,6 +19,8 @@
 	"slices"
 
 	"android/soong/android"
+
+	"github.com/google/blueprint/proptools"
 )
 
 // A singleton module that collects all of the aconfig flags declared in the
@@ -28,8 +30,11 @@
 // 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 allAconfigReleaseDeclarationsSingleton struct {
@@ -37,8 +42,16 @@
 	intermediateTextProtoPath   android.OutputPath
 }
 
+type allAconfigReleaseDeclarationsProperties 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 allAconfigReleaseDeclarationsProperties
 }
 
 func (this *allAconfigDeclarationsSingleton) sortedConfigNames() []string {
@@ -50,7 +63,32 @@
 	return names
 }
 
-func (this *allAconfigDeclarationsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+func (this *allAconfigDeclarationsSingleton) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	apiSignatureFiles := android.Paths{}
+	for _, apiSignatureFile := range this.properties.Api_signature_files.GetOrDefault(ctx, nil) {
+		if path := android.PathForModuleSrc(ctx, apiSignatureFile); path != nil {
+			apiSignatureFiles = append(apiSignatureFiles, path)
+		}
+	}
+	finalizedFlagsFile := android.PathForModuleSrc(ctx, this.properties.Finalized_flags_file)
+	parsedFlagsFile := android.PathForIntermediates(ctx, "all_aconfig_declarations.pb")
+
+	output := android.PathForIntermediates(ctx, "finalized-flags.txt")
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   RecordFinalizedFlagsRule,
+		Inputs: append(apiSignatureFiles, finalizedFlagsFile, parsedFlagsFile),
+		Output: output,
+		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(),
+		},
+	})
+	ctx.Phony("all_aconfig_declarations", output)
+}
+
+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)
@@ -116,4 +154,5 @@
 			ctx.DistForGoalWithFilename(goal, this.releaseMap[rcName].intermediateTextProtoPath, assembleFileName(rcName, "flags.textproto"))
 		}
 	}
+	ctx.DistForGoalWithFilename("sdk", android.PathForIntermediates(ctx, "finalized-flags.txt"), "finalized-flags.txt")
 }
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/codegen/cc_aconfig_library.go b/aconfig/codegen/cc_aconfig_library.go
index f9c7b8c..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
@@ -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..7c7037a 100644
--- a/aconfig/codegen/cc_aconfig_library_test.go
+++ b/aconfig/codegen/cc_aconfig_library_test.go
@@ -255,12 +255,12 @@
 		`))
 
 	module := result.ModuleForTests("my_cc_aconfig_library", "android_arm64_armv8-a_shared").Module()
-	dependOnBaseLib := false
+	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 34fdca3..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` +
@@ -34,14 +35,16 @@
 				`    --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", "new_exported")
+		}, "mode", "debug", "new_exported", "check_api_level")
 
 	// For cc_aconfig_library: Generate C++ library
 	cppRule = pctx.AndroidStaticRule("cc_aconfig_library",
@@ -51,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",
@@ -66,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 cd1767b..7b9da8e 100644
--- a/aconfig/codegen/java_aconfig_library.go
+++ b/aconfig/codegen/java_aconfig_library.go
@@ -112,9 +112,10 @@
 		Output:      srcJarPath,
 		Description: "aconfig.srcjar",
 		Args: map[string]string{
-			"mode":         mode,
-			"debug":        strconv.FormatBool(ctx.Config().ReleaseReadFromNewStorage()),
-			"new_exported": strconv.FormatBool(newExported),
+			"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/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/exported_java_aconfig_library.go b/aconfig/exported_java_aconfig_library.go
index f7e6dcf..dd068d1 100644
--- a/aconfig/exported_java_aconfig_library.go
+++ b/aconfig/exported_java_aconfig_library.go
@@ -59,6 +59,7 @@
 			"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)
diff --git a/aconfig/init.go b/aconfig/init.go
index ab6ee46..3dcec5c 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -70,6 +70,13 @@
 				"${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")
 
 	CreateStorageRule = pctx.AndroidStaticRule("aconfig_create_storage",
 		blueprint.RuleParams{
@@ -88,6 +95,7 @@
 		// 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 ` +
@@ -96,28 +104,31 @@
 				`        --mode=exported` +
 				`        --allow-instrumentation ${use_new_storage}` +
 				`        --new-exported ${use_new_exported}` +
+				`        --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", "use_new_storage", "use_new_exported")
+		}, "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")
 }
 
 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)
 }
diff --git a/android/android_info.go b/android/android_info.go
index 561fa4c..9a68d10 100644
--- a/android/android_info.go
+++ b/android/android_info.go
@@ -58,19 +58,17 @@
 	androidInfoTxtName := proptools.StringDefault(p.properties.Stem, ctx.ModuleName()+".txt")
 	androidInfoTxt := PathForModuleOut(ctx, androidInfoTxtName)
 	androidInfoProp := androidInfoTxt.ReplaceExtension(ctx, "prop")
-	timestamp := PathForModuleOut(ctx, "timestamp")
 
 	if boardInfoFiles := PathsForModuleSrc(ctx, p.properties.Board_info_files); len(boardInfoFiles) > 0 {
 		ctx.Build(pctx, BuildParams{
-			Rule:       mergeAndRemoveComments,
-			Inputs:     boardInfoFiles,
-			Output:     androidInfoTxt,
-			Validation: timestamp,
+			Rule:   mergeAndRemoveComments,
+			Inputs: boardInfoFiles,
+			Output: androidInfoTxt,
 		})
 	} else if bootloaderBoardName := proptools.String(p.properties.Bootloader_board_name); bootloaderBoardName != "" {
-		WriteFileRule(ctx, androidInfoTxt, "board="+bootloaderBoardName, timestamp)
+		WriteFileRule(ctx, androidInfoTxt, "board="+bootloaderBoardName)
 	} else {
-		WriteFileRule(ctx, androidInfoTxt, "", timestamp)
+		WriteFileRule(ctx, androidInfoTxt, "")
 	}
 
 	// Create android_info.prop
diff --git a/android/androidmk.go b/android/androidmk.go
index 87a93e3..f862a96 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -741,8 +741,36 @@
 // defined through the androidmk mechanisms, so this function is an alternate implementation of
 // the androidmk singleton that just focuses on getting the dist contributions
 func (c *androidMkSingleton) soongOnlyBuildActions(ctx SingletonContext, mods []blueprint.Module) {
-	allDistContributions := getDistContributionsFromMods(ctx, mods)
+	allDistContributions, moduleInfoJSONs := getSoongOnlyDataFromMods(ctx, mods)
 
+	// 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
@@ -793,8 +821,11 @@
 	}
 }
 
-func getDistContributionsFromMods(ctx fillInEntriesContext, mods []blueprint.Module) []distContributions {
+// 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 []blueprint.Module) ([]distContributions, []*ModuleInfoJSON) {
 	var allDistContributions []distContributions
+	var moduleInfoJSONs []*ModuleInfoJSON
 	for _, mod := range mods {
 		if amod, ok := mod.(Module); ok && shouldSkipAndroidMkProcessing(ctx, amod.base()) {
 			continue
@@ -806,6 +837,9 @@
 			if info.PrimaryInfo.disabled() {
 				continue
 			}
+			if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+				moduleInfoJSONs = append(moduleInfoJSONs, moduleInfoJSON...)
+			}
 			if contribution := info.PrimaryInfo.getDistContributions(ctx, mod); contribution != nil {
 				allDistContributions = append(allDistContributions, *contribution)
 			}
@@ -831,6 +865,9 @@
 				if data.Entries.disabled() {
 					continue
 				}
+				if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+					moduleInfoJSONs = append(moduleInfoJSONs, moduleInfoJSON...)
+				}
 				if contribution := data.Entries.getDistContributions(mod); contribution != nil {
 					allDistContributions = append(allDistContributions, *contribution)
 				}
@@ -841,6 +878,9 @@
 					if entries.disabled() {
 						continue
 					}
+					if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+						moduleInfoJSONs = append(moduleInfoJSONs, moduleInfoJSON...)
+					}
 					if contribution := entries.getDistContributions(mod); contribution != nil {
 						allDistContributions = append(allDistContributions, *contribution)
 					}
@@ -850,7 +890,7 @@
 			}
 		}
 	}
-	return allDistContributions
+	return allDistContributions, moduleInfoJSONs
 }
 
 func translateAndroidMk(ctx SingletonContext, absMkFile string, moduleInfoJSONPath WritablePath, mods []blueprint.Module) error {
@@ -1037,7 +1077,7 @@
 
 	if !data.Entries.disabled() {
 		if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
-			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
+			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON...)
 		}
 	}
 
@@ -1071,15 +1111,20 @@
 	entriesList := provider.AndroidMkEntries()
 	aconfigUpdateAndroidMkEntries(ctx, mod.(Module), &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)
+				}
+			}
 		}
 	}
 
@@ -1246,7 +1291,7 @@
 
 	if !info.PrimaryInfo.disabled() {
 		if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
-			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
+			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON...)
 		}
 	}
 
diff --git a/android/compliance_metadata.go b/android/compliance_metadata.go
index 2f11df6..dcf393d 100644
--- a/android/compliance_metadata.go
+++ b/android/compliance_metadata.go
@@ -43,6 +43,7 @@
 		STATIC_DEP_FILES       string
 		WHOLE_STATIC_DEPS      string
 		WHOLE_STATIC_DEP_FILES string
+		HEADER_LIBS            string
 		LICENSES               string
 
 		// module_type=package
@@ -71,6 +72,7 @@
 		"static_dep_files",
 		"whole_static_deps",
 		"whole_static_dep_files",
+		"header_libs",
 		"licenses",
 
 		"pkg_default_applicable_licenses",
@@ -106,6 +108,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,
diff --git a/android/config.go b/android/config.go
index 39d6e27..eda8e71 100644
--- a/android/config.go
+++ b/android/config.go
@@ -294,6 +294,10 @@
 	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.
@@ -2175,6 +2179,10 @@
 	return c.productVariables.GetBuildFlagBool("RELEASE_USE_TRANSITIVE_JARS_IN_CLASSPATH")
 }
 
+func (c *config) UseR8FullModeByDefault() bool {
+	return c.productVariables.GetBuildFlagBool("RELEASE_R8_FULL_MODE_BY_DEFAULT")
+}
+
 func (c *config) UseR8OnlyRuntimeVisibleAnnotations() bool {
 	return c.productVariables.GetBuildFlagBool("RELEASE_R8_ONLY_RUNTIME_VISIBLE_ANNOTATIONS")
 }
@@ -2199,7 +2207,7 @@
 		"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",
@@ -2210,9 +2218,11 @@
 		"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",
@@ -2221,6 +2231,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",
 	}
diff --git a/android/container.go b/android/container.go
index 882535e..eb2fc18 100644
--- a/android/container.go
+++ b/android/container.go
@@ -198,7 +198,7 @@
 	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
 				}
diff --git a/android/container_violations.go b/android/container_violations.go
index e1583c5..4c6386d 100644
--- a/android/container_violations.go
+++ b/android/container_violations.go
@@ -997,7 +997,7 @@
 		"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
+		"framework-nfc.impl", // apex [com.android.nfcservices] -> system
 	},
 
 	"okhttp-norepackage": {
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/defs.go b/android/defs.go
index 9f3fb1e..4dd267a 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`,
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/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/licenses.go b/android/licenses.go
index 53d0555..32d12c8 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -227,16 +227,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 +249,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 +274,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 +330,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)
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/module.go b/android/module.go
index b9489b4..39a1654 100644
--- a/android/module.go
+++ b/android/module.go
@@ -94,7 +94,6 @@
 	ReplacedByPrebuilt()
 	IsReplacedByPrebuilt() bool
 	ExportedToMake() bool
-	EffectiveLicenseKinds() []string
 	EffectiveLicenseFiles() Paths
 
 	AddProperties(props ...interface{})
@@ -257,6 +256,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 +316,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 +408,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
@@ -518,6 +508,19 @@
 	Overrides []string
 }
 
+// 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"`
+}
+
 type distProperties struct {
 	// configuration to distribute output files from this module to the distribution
 	// directory (default: $OUT/dist, configurable with $DIST_DIR)
@@ -716,6 +719,7 @@
 	m.AddProperties(
 		&base.nameProperties,
 		&base.commonProperties,
+		&base.baseProperties,
 		&base.distProperties)
 
 	initProductVariableModule(m)
@@ -834,6 +838,7 @@
 
 	nameProperties          nameProperties
 	commonProperties        commonProperties
+	baseProperties          baseProperties
 	distProperties          distProperties
 	variableProperties      interface{}
 	hostAndDeviceProperties hostAndDeviceProperties
@@ -990,8 +995,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.
 	//
@@ -1458,10 +1464,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 {
@@ -1618,15 +1620,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 {
@@ -1654,6 +1656,7 @@
 func (m *ModuleBase) generateModuleTarget(ctx *moduleContext) {
 	var allInstalledFiles InstallPaths
 	var allCheckbuildTargets Paths
+	var alloutputFiles Paths
 	ctx.VisitAllModuleVariantProxies(func(module ModuleProxy) {
 		var checkbuildTarget Path
 		var uncheckedModule bool
@@ -1670,6 +1673,9 @@
 			uncheckedModule = info.UncheckedModule
 			skipAndroidMkProcessing = OtherModuleProviderOrDefault(ctx, module, CommonModuleInfoKey).SkipAndroidMkProcessing
 		}
+		if outputFiles, err := outputFilesForModule(ctx, module, ""); err == nil {
+			alloutputFiles = append(alloutputFiles, outputFiles...)
+		}
 		// 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
@@ -1701,6 +1707,12 @@
 		deps = append(deps, PathForPhony(ctx, name))
 	}
 
+	if len(alloutputFiles) > 0 {
+		name := namespacePrefix + ctx.ModuleName() + "-outputs"
+		ctx.Phony(name, alloutputFiles...)
+		deps = append(deps, PathForPhony(ctx, name))
+	}
+
 	if len(deps) > 0 {
 		suffix := ""
 		if ctx.Config().KatiEnabled() {
@@ -1708,6 +1720,17 @@
 		}
 
 		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)
@@ -2104,47 +2127,84 @@
 	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
+			}
 
-		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)
 	}
 
@@ -2255,7 +2315,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 {
diff --git a/android/module_context.go b/android/module_context.go
index f6a676d..a3dad93 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.
@@ -230,6 +231,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)
@@ -295,7 +300,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.
@@ -586,11 +591,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) {
@@ -610,7 +615,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{
@@ -618,13 +623,15 @@
 		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,
 	}
 	m.packagingSpecs = append(m.packagingSpecs, spec)
 	return spec
@@ -655,22 +662,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 := ""
@@ -690,6 +699,7 @@
 				OrderOnly:   orderOnlyDeps,
 				Args: map[string]string{
 					"extraCmds": extraCmds,
+					"cpFlags":   "-f",
 				},
 			})
 		}
@@ -697,7 +707,7 @@
 		m.installFiles = append(m.installFiles, fullInstallPath)
 	}
 
-	m.packageFile(fullInstallPath, srcPath, executable)
+	m.packageFile(fullInstallPath, srcPath, executable, m.requiresFullInstall())
 
 	if checkbuild {
 		m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
@@ -716,21 +726,23 @@
 	}
 	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,
@@ -745,16 +757,18 @@
 
 	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,
 	})
 
 	return fullInstallPath
@@ -767,15 +781,17 @@
 	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,
 				Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath,
@@ -791,16 +807,18 @@
 
 	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,
 	})
 
 	return fullInstallPath
@@ -838,11 +856,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
 }
 
diff --git a/android/module_info_json.go b/android/module_info_json.go
index d102dd2..f7bffdb 100644
--- a/android/module_info_json.go
+++ b/android/module_info_json.go
@@ -34,7 +34,7 @@
 	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))
@@ -43,6 +43,12 @@
 	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)
+	ExtraRequired       []string `json:"-"`
+
+	SupportedVariantsOverride []string `json:"-"`
+	Disabled                  bool     `json:"-"`
+	RegisterNameOverride      string   `json:"-"`
+	ModuleNameOverride        string   `json:"-"`
 }
 
 type ModuleInfoJSON struct {
@@ -127,4 +133,4 @@
 	return gobtools.CustomGobDecode[combinedModuleInfoJSON](data, m)
 }
 
-var ModuleInfoJSONProvider = blueprint.NewProvider[*ModuleInfoJSON]()
+var ModuleInfoJSONProvider = blueprint.NewProvider[[]*ModuleInfoJSON]()
diff --git a/android/module_proxy.go b/android/module_proxy.go
index afca0d7..77abc11 100644
--- a/android/module_proxy.go
+++ b/android/module_proxy.go
@@ -164,10 +164,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")
 }
diff --git a/android/mutator.go b/android/mutator.go
index 1523794..1b0700a 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -70,7 +70,8 @@
 	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)
@@ -213,7 +214,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 +230,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 +253,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.
@@ -337,8 +338,22 @@
 	return mutator
 }
 
-func (x *registerMutatorsContext) Transition(name string, m TransitionMutator) TransitionMutatorHandle {
-	atm := &androidTransitionMutator{
+func (x *registerMutatorsContext) Transition(name string, m VariationTransitionMutator) TransitionMutatorHandle {
+	atm := &androidTransitionMutatorAdapter{
+		finalPhase: x.finalPhase,
+		mutator:    variationTransitionMutatorAdapter{m},
+		name:       name,
+	}
+	mutator := &mutator{
+		name:              name,
+		transitionMutator: atm,
+	}
+	x.mutators = append(x.mutators, mutator)
+	return mutator
+}
+
+func (x *registerMutatorsContext) InfoBasedTransition(name string, m androidTransitionMutator) TransitionMutatorHandle {
+	atm := &androidTransitionMutatorAdapter{
 		finalPhase: x.finalPhase,
 		mutator:    m,
 		name:       name,
@@ -524,11 +539,11 @@
 	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")
 }
 
@@ -536,11 +551,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) {
@@ -558,20 +573,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) {
@@ -587,3 +602,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/neverallow.go b/android/neverallow.go
index cf0b297..70af2ac 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -65,6 +65,7 @@
 	AddNeverAllowRules(createKotlinPluginRule()...)
 	AddNeverAllowRules(createPrebuiltEtcBpDefineRule())
 	AddNeverAllowRules(createAutogenRroBpDefineRule())
+	AddNeverAllowRules(createNoSha1HashRule())
 }
 
 // Add a NeverAllow rule to the set of rules to apply.
@@ -297,25 +298,33 @@
 			WithoutMatcher("visibility", InAllowedList([]string{"//trusty/vendor/google/aosp/scripts", "//trusty/vendor/google/proprietary/scripts"})).Because(reason),
 		NeverAllow().
 			ModuleType("genrule").
+			// TODO: remove the 4 below targets once new targets are submitted
 			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").
-			Without("name", "trusty-arm64.wv.lk.elf.gen").
-			Without("name", "trusty-arm64-virt-test-debug.wv.lk.elf.gen").
-			Without("name", "trusty-x86_64.wv.lk.elf.gen").
-			Without("name", "trusty-x86_64-test.wv.lk.elf.gen").
+			// trusty vm target names moving forward
+			Without("name", "trusty-test_vm-arm64.elf.gen").
+			Without("name", "trusty-test_vm-x86.elf.gen").
+			Without("name", "trusty-security_vm-arm64.elf.gen").
+			Without("name", "trusty-security_vm-x86.elf.gen").
+			Without("name", "trusty-widevine_vm-arm64.elf.gen").
+			Without("name", "trusty-widevine_vm-x86.elf.gen").
 			WithMatcher("dir_srcs", isSetMatcherInstance).Because(reason),
 		NeverAllow().
 			ModuleType("genrule").
+			// TODO: remove the 4 below targets once new targets are submitted
 			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").
-			Without("name", "trusty-arm64.wv.lk.elf.gen").
-			Without("name", "trusty-arm64-virt-test-debug.wv.lk.elf.gen").
-			Without("name", "trusty-x86_64.wv.lk.elf.gen").
-			Without("name", "trusty-x86_64-test.wv.lk.elf.gen").
+			// trusty vm target names moving forward
+			Without("name", "trusty-test_vm-arm64.elf.gen").
+			Without("name", "trusty-test_vm-x86.elf.gen").
+			Without("name", "trusty-security_vm-arm64.elf.gen").
+			Without("name", "trusty-security_vm-x86.elf.gen").
+			Without("name", "trusty-widevine_vm-arm64.elf.gen").
+			Without("name", "trusty-widevine_vm-x86.elf.gen").
 			With("keep_gendir", "true").Because(reason),
 	}
 }
@@ -328,6 +337,14 @@
 		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{
 		"external/kotlinc",
diff --git a/android/packaging.go b/android/packaging.go
index d96cccd..d216c0c 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,25 @@
 	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
 }
 
 type packagingSpecGob struct {
@@ -69,12 +79,12 @@
 	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
 }
 
@@ -84,12 +94,12 @@
 		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,
 	}
 }
@@ -99,12 +109,12 @@
 	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
 }
 
@@ -154,10 +164,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 +181,25 @@
 
 // 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
 }
 
 type PackageModule interface {
@@ -507,9 +532,7 @@
 			}
 
 			depNames = append(depNames, child.Name())
-			if ps.overrides != nil {
-				overridden = append(overridden, *ps.overrides...)
-			}
+			overridden = append(overridden, ps.overrides.ToSlice()...)
 		}
 	})
 
diff --git a/android/transition.go b/android/transition.go
index 7c04eff..e1aa891 100644
--- a/android/transition.go
+++ b/android/transition.go
@@ -73,29 +73,56 @@
 // 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 {
+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) []string
+	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, sourceVariation string) string
+	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, incomingVariation string) string
+	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)
 }
 
@@ -108,6 +135,14 @@
 	// 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
 
@@ -128,6 +163,10 @@
 	// 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
@@ -138,68 +177,166 @@
 	DeviceConfig() DeviceConfig
 }
 
-type androidTransitionMutator struct {
+// 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    TransitionMutator
+	mutator    androidTransitionMutator
 	name       string
 }
 
-func (a *androidTransitionMutator) Split(ctx blueprint.BaseModuleContext) []string {
+func (a *androidTransitionMutatorAdapter) Split(ctx blueprint.BaseModuleContext) []blueprint.TransitionInfo {
 	if a.finalPhase {
 		panic("TransitionMutator not allowed in FinalDepsMutators")
 	}
-	if m, ok := ctx.Module().(Module); ok {
-		moduleContext := m.base().baseModuleContextFactory(ctx)
-		return a.mutator.Split(&moduleContext)
-	} else {
-		return []string{""}
-	}
+	m := ctx.Module().(Module)
+	moduleContext := m.base().baseModuleContextFactory(ctx)
+	return a.mutator.Split(&moduleContext)
 }
 
-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 ""
+func (a *androidTransitionMutatorAdapter) OutgoingTransition(bpctx blueprint.OutgoingTransitionContext,
+	sourceTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo {
+	m := bpctx.Module().(Module)
+	ctx := outgoingTransitionContextPool.Get().(*outgoingTransitionContextImpl)
+	defer outgoingTransitionContextPool.Put(ctx)
+	*ctx = outgoingTransitionContextImpl{
+		archModuleContext: m.base().archModuleContextFactory(bpctx),
+		bp:                bpctx,
 	}
+	return a.mutator.OutgoingTransition(ctx, sourceTransitionInfo)
 }
 
-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 *androidTransitionMutatorAdapter) IncomingTransition(bpctx blueprint.IncomingTransitionContext,
+	incomingTransitionInfo blueprint.TransitionInfo) blueprint.TransitionInfo {
+	m := bpctx.Module().(Module)
+	ctx := incomingTransitionContextPool.Get().(*incomingTransitionContextImpl)
+	defer incomingTransitionContextPool.Put(ctx)
+	*ctx = incomingTransitionContextImpl{
+		archModuleContext: m.base().archModuleContextFactory(bpctx),
+		bp:                bpctx,
 	}
+	return a.mutator.IncomingTransition(ctx, incomingTransitionInfo)
 }
 
-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 (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
@@ -209,6 +346,14 @@
 	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)
 }
@@ -233,6 +378,8 @@
 	c.bp.PropertyErrorf(property, fmt, args)
 }
 
+// outgoingTransitionContextImpl wraps a blueprint.OutgoingTransitionContext to convert it to an
+// OutgoingTransitionContext.
 type outgoingTransitionContextImpl struct {
 	archModuleContext
 	bp blueprint.OutgoingTransitionContext
@@ -242,6 +389,10 @@
 	return c.bp.Module().(Module)
 }
 
+func (c *outgoingTransitionContextImpl) ModuleName() string {
+	return c.bp.ModuleName()
+}
+
 func (c *outgoingTransitionContextImpl) DepTag() blueprint.DependencyTag {
 	return c.bp.DepTag()
 }
diff --git a/android/variable.go b/android/variable.go
index 08bcedf..4867067 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -551,10 +551,13 @@
 	SystemExtManifestFiles []string `json:",omitempty"`
 	DeviceManifestFiles    []string `json:",omitempty"`
 	OdmManifestFiles       []string `json:",omitempty"`
+
+	UseSoongNoticeXML *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"`
@@ -623,6 +626,8 @@
 	VendorDlkmSecurityPatch string `json:",omitempty"`
 	OdmDlkmSecurityPatch    string `json:",omitempty"`
 
+	BuildingSystemOtherImage bool `json:",omitempty"`
+
 	// Boot image stuff
 	BuildingRamdiskImage              bool     `json:",omitempty"`
 	ProductBuildBootImage             bool     `json:",omitempty"`
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/apex/apex.go b/apex/apex.go
index 428d57e..d98cfae 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -2570,7 +2570,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
@@ -2579,6 +2579,15 @@
 				return false
 			}
 
+			// 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. "+
diff --git a/apex/apex_test.go b/apex/apex_test.go
index a5b66c1..ffd0606 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -10823,15 +10823,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"],
@@ -10951,8 +10942,8 @@
 	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
 	s := mod.Rule("apexRule").Args["copy_commands"]
 	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
-	if len(copyCmds) != 34 {
-		t.Fatalf("Expected 34 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")
diff --git a/apex/builder.go b/apex/builder.go
index daba6f1..3f2623e 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -552,7 +552,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)
@@ -603,7 +603,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)
 				}
 			}
 
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/cc/builder.go b/cc/builder.go
index 5325116..c94a674 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -474,13 +474,23 @@
 // Generate rules for compiling multiple .c, .cpp, or .S files to individual .o files
 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,14 +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.
 	// 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 {
@@ -574,7 +584,7 @@
 		return "$" + kind + n
 	}
 
-	for i, srcFile := range srcFiles {
+	for i, srcFile := range srcObjFiles {
 		objFile := android.ObjPathWithExt(ctx, subdir, srcFile, "o")
 
 		objFiles[i] = objFile
@@ -852,7 +862,7 @@
 }
 
 func BuildRustStubs(ctx android.ModuleContext, outputFile android.ModuleOutPath,
-	crtBegin, crtEnd android.Paths, stubObjs Objects, ccFlags Flags) {
+	stubObjs Objects, ccFlags Flags) {
 
 	// Instantiate paths
 	sharedLibs := android.Paths{}
@@ -862,6 +872,8 @@
 	deps := android.Paths{}
 	implicitOutputs := android.WritablePaths{}
 	validations := android.Paths{}
+	crtBegin := android.Paths{}
+	crtEnd := android.Paths{}
 	groupLate := false
 
 	builderFlags := flagsToBuilderFlags(ccFlags)
diff --git a/cc/cc.go b/cc/cc.go
index b525ccb..0279928 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -2409,7 +2409,7 @@
 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
@@ -2418,11 +2418,28 @@
 	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))
 
@@ -2439,6 +2456,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() {
diff --git a/cc/config/global.go b/cc/config/global.go
index 19ed8e7..7bea124 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -292,7 +292,6 @@
 		"-Wno-error=deprecated",          // in external/googletest/googletest
 		// 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",
diff --git a/cc/coverage.go b/cc/coverage.go
index c8ff82b..757641c 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -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 {
diff --git a/cc/strip.go b/cc/strip.go
index b1f34bb..36c0c48 100644
--- a/cc/strip.go
+++ b/cc/strip.go
@@ -52,11 +52,7 @@
 // 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())
-	forceEnable := Bool(stripper.StripProperties.Strip.All) ||
-		Bool(stripper.StripProperties.Strip.Keep_symbols) ||
-		Bool(stripper.StripProperties.Strip.Keep_symbols_and_debug_frame)
-	return !forceDisable && (forceEnable || defaultEnable)
+	return !forceDisable
 }
 
 func (stripper *Stripper) strip(actx android.ModuleContext, in android.Path, out android.ModuleOutPath,
diff --git a/cc/test.go b/cc/test.go
index abf9162..dad4afa 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -29,8 +29,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
 }
 
@@ -198,8 +198,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
@@ -397,6 +397,28 @@
 		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)
+			}
+			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)
 }
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/soong_ui/main.go b/cmd/soong_ui/main.go
index 7926292..4f6de82 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -338,7 +338,7 @@
 			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 {
@@ -414,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])
 		}
@@ -471,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/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/filesystem/Android.bp b/filesystem/Android.bp
index 986b72e..cb76df2 100644
--- a/filesystem/Android.bp
+++ b/filesystem/Android.bp
@@ -28,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 9a3ca54..6d03402 100644
--- a/filesystem/aconfig_files.go
+++ b/filesystem/aconfig_files.go
@@ -34,7 +34,13 @@
 
 var importAconfigDependencyTag = interPartitionDepTag{}
 
-func (f *filesystem) buildAconfigFlagsFiles(ctx android.ModuleContext, builder *android.RuleBuilder, specs map[string]android.PackagingSpec, dir android.OutputPath) {
+func (f *filesystem) buildAconfigFlagsFiles(
+	ctx android.ModuleContext,
+	builder *android.RuleBuilder,
+	specs map[string]android.PackagingSpec,
+	dir android.OutputPath,
+	fullInstallPaths *[]FullInstallPathInfo,
+) {
 	var caches []android.Path
 	for _, ps := range specs {
 		caches = append(caches, ps.GetAconfigPaths()...)
@@ -70,6 +76,10 @@
 	for _, cache := range caches {
 		cmd.FlagWithInput("--cache ", cache)
 	}
+	*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+		FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), "etc/aconfig_flags.pb"),
+		SourcePath:      installAconfigFlagsPath,
+	})
 	f.appendToEntry(ctx, installAconfigFlagsPath)
 
 	installAconfigStorageDir := dir.Join(ctx, "etc", "aconfig")
@@ -90,6 +100,10 @@
 			FlagWithOutput("--out ", outputPath).
 			FlagWithArg("--cache ", installAconfigFlagsPath.String()).
 			FlagWithArg("--version ", strconv.Itoa(storageFilesVersion))
+		*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+			FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), "etc/aconfig", fileName),
+			SourcePath:      outputPath,
+		})
 		f.appendToEntry(ctx, outputPath)
 	}
 
diff --git a/filesystem/android_device.go b/filesystem/android_device.go
index e340602..6c04828 100644
--- a/filesystem/android_device.go
+++ b/filesystem/android_device.go
@@ -15,6 +15,7 @@
 package filesystem
 
 import (
+	"fmt"
 	"strings"
 	"sync/atomic"
 
@@ -68,6 +69,8 @@
 	// 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
 }
 
 type androidDevice struct {
@@ -94,9 +97,13 @@
 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) {
@@ -124,6 +131,11 @@
 	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) {
@@ -139,7 +151,7 @@
 		}
 	}
 
-	a.buildTargetFilesZip(ctx)
+	//a.buildTargetFilesZip(ctx) TODO(b/393203512): re-enable target_files.zip
 	var deps []android.Path
 	if proptools.String(a.partitionProps.Super_partition_name) != "" {
 		superImage := ctx.GetDirectDepProxyWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag)
@@ -198,7 +210,7 @@
 		// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/main.mk;l=1396;drc=6595459cdd8164a6008335f6372c9f97b9094060
 		ctx.Phony("droidcore-unbundled", allImagesStamp)
 
-		validations = append(validations, a.copyFilesToProductOutForSoongOnly(ctx))
+		deps = append(deps, a.copyFilesToProductOutForSoongOnly(ctx))
 	}
 
 	ctx.Build(pctx, android.BuildParams{
@@ -210,6 +222,8 @@
 
 	// Checkbuilding it causes soong to make a phony, so you can say `m <module name>`
 	ctx.CheckbuildFile(allImagesStamp)
+
+	a.setVbmetaPhonyTargets(ctx)
 }
 
 // Helper structs for target_files.zip creation
@@ -326,6 +340,7 @@
 	}
 
 	a.copyImagesToTargetZip(ctx, builder, targetFilesDir)
+	a.copyMetadataToTargetZip(ctx, builder, targetFilesDir)
 
 	builder.Command().
 		BuiltTool("soong_zip").
@@ -373,6 +388,19 @@
 	}
 }
 
+func (a *androidDevice) copyMetadataToTargetZip(ctx android.ModuleContext, builder *android.RuleBuilder, targetFilesDir android.WritablePath) {
+	// 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())
+}
+
 func (a *androidDevice) getFilesystemInfo(ctx android.ModuleContext, depName string) FilesystemInfo {
 	fsMod := ctx.GetDirectDepProxyWithTag(depName, filesystemDepTag)
 	fsInfo, ok := android.OtherModuleProvider(ctx, fsMod, FilesystemProvider)
@@ -381,3 +409,20 @@
 	}
 	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)
+			}
+		}
+	}
+}
diff --git a/filesystem/android_device_product_out.go b/filesystem/android_device_product_out.go
index 916c45a..405d710 100644
--- a/filesystem/android_device_product_out.go
+++ b/filesystem/android_device_product_out.go
@@ -35,18 +35,6 @@
 func (a *androidDevice) copyFilesToProductOutForSoongOnly(ctx android.ModuleContext) android.Path {
 	filesystemInfos := a.getFsInfos(ctx)
 
-	// The current logic to copy the staging directories to PRODUCT_OUT isn't very sound.
-	// We only track dependencies on the image file, so if the image file wasn't changed, the
-	// staging directory won't be re-copied. If you do an installclean, it would remove the copied
-	// staging directories but not affect the intermediates path image file, so the next build
-	// wouldn't re-copy them. As a hack, create a presence detector that would be deleted on
-	// an installclean to use as a dep for the staging dir copies.
-	productOutPresenceDetector := android.PathForModuleInPartitionInstall(ctx, "", "product_out_presence_detector.txt")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:   android.Touch,
-		Output: productOutPresenceDetector,
-	})
-
 	var deps android.Paths
 
 	for _, partition := range android.SortedKeys(filesystemInfos) {
@@ -57,27 +45,53 @@
 			Input:  info.Output,
 			Output: imgInstallPath,
 		})
-		dirStamp := android.PathForModuleOut(ctx, partition+"_staging_dir_copy_stamp.txt")
-		dirInstallPath := android.PathForModuleInPartitionInstall(ctx, "", partition)
-		ctx.Build(pctx, android.BuildParams{
-			Rule:   copyStagingDirRule,
-			Output: dirStamp,
-			Implicits: []android.Path{
-				info.Output,
-				productOutPresenceDetector,
-			},
-			Args: map[string]string{
-				"dir":  info.RebasedDir.String(),
-				"dest": dirInstallPath.String(),
-			},
-		})
 
-		// Make it so doing `m <moduleName>` or `m <partitionType>` will copy the files to
+		// Make it so doing `m <moduleName>` or `m <partitionType>image` will copy the files to
 		// PRODUCT_OUT
-		ctx.Phony(info.ModuleName, dirStamp, imgInstallPath)
-		ctx.Phony(partition, dirStamp, imgInstallPath)
+		if partition == "system_ext" {
+			partition = "systemext"
+		}
+		partition = partition + "imgage"
+		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.Cp,
+							Input:  fip.SourcePath,
+							Output: fip.FullInstallPath,
+						})
+					} 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)
+				ctx.Phony("sync_"+partition, fip.FullInstallPath)
+				ctx.Phony("sync", fip.FullInstallPath)
+			}
+		}
 
-		deps = append(deps, imgInstallPath, dirStamp)
+		deps = append(deps, imgInstallPath)
 	}
 
 	// List all individual files to be copied to PRODUCT_OUT here
diff --git a/filesystem/avb_add_hash_footer.go b/filesystem/avb_add_hash_footer.go
index 9d4ba3e..f32993c 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
@@ -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)
 
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index 98af479..6d6c15c 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
@@ -237,6 +240,27 @@
 		Bootconfig: b.getBootconfigPath(ctx),
 		Output:     output,
 	})
+
+	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,
+	})
 }
 
 var BootimgInfoProvider = blueprint.NewProvider[BootimgInfo]()
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 306710c..0ce31b2 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -20,6 +20,7 @@
 	"io"
 	"path/filepath"
 	"slices"
+	"sort"
 	"strconv"
 	"strings"
 
@@ -78,7 +79,7 @@
 }
 
 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.
@@ -118,7 +119,7 @@
 	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.
@@ -130,6 +131,9 @@
 	// 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 0, 1, 2, etc.
+	Rollback_index_location *int64
+
 	// Name of the partition stored in vbmeta desc. Defaults to the name of this module.
 	Partition_name *string
 
@@ -173,12 +177,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
@@ -384,6 +382,41 @@
 	// 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
+}
+
+// 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]()
@@ -436,6 +469,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+"/")
@@ -470,18 +510,46 @@
 		ctx.PropertyErrorf("include_files_of", "include_files_of is only supported for cpio and compressed cpio filesystem types.")
 	}
 
-	var rootDir android.OutputPath
-	var rebasedDir android.OutputPath
+	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 _, spec := range specs {
+		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)
+
 	var mapFile android.Path
 	var outputHermetic android.Path
+	var buildImagePropFile android.Path
+	var buildImagePropFileDeps android.Paths
 	switch f.fsType(ctx) {
 	case ext4Type, erofsType, f2fsType:
-		f.output, outputHermetic, rootDir, rebasedDir = f.buildImageUsingBuildImage(ctx)
+		f.output, outputHermetic, buildImagePropFile, buildImagePropFileDeps = f.buildImageUsingBuildImage(ctx, builder, rootDir, rebasedDir)
 		mapFile = f.getMapFile(ctx)
 	case compressedCpioType:
-		f.output, rootDir, rebasedDir = f.buildCpioImage(ctx, true)
+		f.output = f.buildCpioImage(ctx, builder, rootDir, true)
 	case cpioType:
-		f.output, rootDir, rebasedDir = f.buildCpioImage(ctx, false)
+		f.output = f.buildCpioImage(ctx, builder, rootDir, false)
 	default:
 		return
 	}
@@ -498,17 +566,17 @@
 	android.WriteFileRule(ctx, fileListFile, f.installedFilesList())
 
 	fsInfo := FilesystemInfo{
-		Output:       f.output,
-		FileListFile: fileListFile,
-		RootDir:      rootDir,
-		RebasedDir:   rebasedDir,
-		ModuleName:   ctx.ModuleName(),
-	}
-	if mapFile != nil {
-		fsInfo.MapFile = mapFile
-	}
-	if outputHermetic != nil {
-		fsInfo.OutputHermetic = outputHermetic
+		Output:                 f.output,
+		OutputHermetic:         outputHermetic,
+		FileListFile:           fileListFile,
+		RootDir:                rootDir,
+		RebasedDir:             rebasedDir,
+		MapFile:                mapFile,
+		ModuleName:             ctx.ModuleName(),
+		BuildImagePropFile:     buildImagePropFile,
+		BuildImagePropFileDeps: buildImagePropFileDeps,
+		SpecsForSystemOther:    f.systemOtherFiles(ctx),
+		FullInstallPaths:       fullInstallPaths,
 	}
 
 	android.SetProvider(ctx, FilesystemProvider, fsInfo)
@@ -518,6 +586,33 @@
 	if proptools.Bool(f.properties.Unchecked_module) {
 		ctx.UncheckedModule()
 	}
+
+	f.setVbmetaPartitionProvider(ctx)
+}
+
+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 {
@@ -596,11 +691,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 {
@@ -623,6 +743,14 @@
 		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)
+		// Only add the fullInstallPath logic for files in the rebased dir. The root dir
+		// is harder to install to.
+		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
@@ -655,25 +783,12 @@
 	return f.partitionName()
 }
 
-func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) (android.Path, android.Path, android.OutputPath, android.OutputPath) {
-	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)
-	f.entries = f.copyPackagingSpecs(ctx, builder, specs, rootDir, rebasedDir)
-
-	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)
-
+func (f *filesystem) buildImageUsingBuildImage(
+	ctx android.ModuleContext,
+	builder *android.RuleBuilder,
+	rootDir android.OutputPath,
+	rebasedDir android.OutputPath,
+) (android.Path, android.Path, android.Path, android.Paths) {
 	// 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.
@@ -702,20 +817,23 @@
 		Output(output).
 		Text(rootDir.String()) // directory where to find fs_config_files|dirs
 
+	// TODO (b/393203512): Re-enable hermetic img file creation for target_files.zip
 	// Add an additional cmd to create a hermetic img file. This will contain pinned timestamps e.g.
-	propFilePinnedTimestamp := android.PathForModuleOut(ctx, "for_target_files", "prop")
-	builder.Command().Textf("cat").Input(propFile).Flag(">").Output(propFilePinnedTimestamp).Textf(" && echo use_fixed_timestamp=true >> %s", propFilePinnedTimestamp)
+	//propFilePinnedTimestamp := android.PathForModuleOut(ctx, "for_target_files", "prop")
+	//builder.Command().Textf("cat").Input(propFile).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
 
-	outputHermetic := android.PathForModuleOut(ctx, "for_target_files", f.installFileName())
-	builder.Command().
-		Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
-		BuiltTool("build_image").
-		Text(rootDir.String()). // input directory
-		Flag(propFilePinnedTimestamp.String()).
-		Implicits(toolDeps).
-		Implicit(fec).
-		Output(outputHermetic).
-		Text(rootDir.String()) // directory where to find fs_config_files|dirs
+	//outputHermetic := android.PathForModuleOut(ctx, "for_target_files", f.installFileName())
+	//builder.Command().
+	//	Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
+	//	BuiltTool("build_image").
+	//	Text(rootDir.String()). // input directory
+	//	Flag(propFilePinnedTimestamp.String()).
+	//	Implicits(toolDeps).
+	//	Implicit(fec).
+	//	Output(outputHermetic).
+	//	Text(rootDir.String()) // directory where to find fs_config_files|dirs
 
 	if f.properties.Partition_size != nil {
 		assertMaxImageSize(builder, output, *f.properties.Partition_size, false)
@@ -724,7 +842,7 @@
 	// rootDir is not deleted. Might be useful for quick inspection.
 	builder.Build("build_filesystem_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
 
-	return output, outputHermetic, rootDir, rebasedDir
+	return output, nil, propFile, toolDeps
 }
 
 func (f *filesystem) buildFileContexts(ctx android.ModuleContext) android.Path {
@@ -739,12 +857,9 @@
 
 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())
@@ -764,7 +879,6 @@
 		}
 		panic(fmt.Errorf("unsupported fs type %v", t))
 	}
-	addStr("block_list", f.getMapFile(ctx).String()) // This will be an implicit output
 
 	addStr("fs_type", fsTypeStr(f.fsType(ctx)))
 	addStr("mount_point", proptools.StringDefault(f.properties.Mount_point, "/"))
@@ -789,9 +903,8 @@
 		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
-		}
+		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 {
@@ -874,8 +987,10 @@
 		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,
@@ -918,7 +1033,12 @@
 	return rootDirs, partitions
 }
 
-func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool) (android.Path, android.OutputPath, android.OutputPath) {
+func (f *filesystem) buildCpioImage(
+	ctx android.ModuleContext,
+	builder *android.RuleBuilder,
+	rootDir android.OutputPath,
+	compressed bool,
+) android.Path {
 	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.")
@@ -928,27 +1048,6 @@
 		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, 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)
-	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)
-
 	rootDirs, partitions := includeFilesRootDir(ctx)
 
 	output := android.PathForModuleOut(ctx, f.installFileName())
@@ -978,7 +1077,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, rootDir, rebasedDir
+	return output
 }
 
 var validPartitions = []string{
@@ -998,28 +1097,12 @@
 	"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
 	}
@@ -1029,10 +1112,20 @@
 	builder.Command().Text("mkdir").Flag("-p").Text(etcPath.String())
 	builder.Command().Text("cp").Input(android.MergedLogtagsPath(ctx)).Text(eventLogtagsPath.String())
 
+	*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
 	}
@@ -1043,6 +1136,11 @@
 	output := rebasedDir.Join(ctx, "etc", "linker.config.pb")
 	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)
 }
 
@@ -1105,8 +1203,21 @@
 // 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)
+}
+
+// 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 {
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index 47d06f6..d9bf242 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -136,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 {
diff --git a/filesystem/fsverity_metadata.go b/filesystem/fsverity_metadata.go
index c3f1936..a3a2086 100644
--- a/filesystem/fsverity_metadata.go
+++ b/filesystem/fsverity_metadata.go
@@ -44,7 +44,14 @@
 	android.WriteFileRuleVerbatim(ctx, outputPath, buf.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) 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 {
@@ -82,9 +89,13 @@
 			FlagWithInput("--fsverity-path ", fsverityPath).
 			FlagWithArg("--signature ", "none").
 			FlagWithArg("--hash-alg ", "sha256").
-			FlagWithArg("--output ", destPath.String()).
+			FlagWithOutput("--output ", destPath).
 			Text(srcPath.String())
 		f.appendToEntry(ctx, destPath)
+		*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+			SourcePath:      destPath,
+			FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), spec.RelPathInPackage()+".fsv_meta"),
+		})
 	}
 
 	fsVerityBaseDir := rootDir.String()
@@ -148,6 +159,10 @@
 		FlagWithArg("--version-name ", ctx.Config().AppsDefaultVersionName()).
 		FlagWithInput("--manifest ", manifestTemplatePath).
 		Text(" --rename-manifest-package com.android.security.fsverity_metadata." + f.partitionName())
+	*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+		SourcePath:      apkPath,
+		FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), fmt.Sprintf("etc/security/fsverity/BuildManifest%s.apk", apkNameSuffix)),
+	})
 
 	f.appendToEntry(ctx, apkPath)
 
@@ -160,6 +175,10 @@
 		FlagWithInput("--cert ", pemPath).
 		FlagWithInput("--key ", keyPath).
 		ImplicitOutput(idsigPath)
+	*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, idsigPath)
 }
diff --git a/filesystem/super_image.go b/filesystem/super_image.go
index f73f0dc..5332462 100644
--- a/filesystem/super_image.go
+++ b/filesystem/super_image.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"path/filepath"
 	"regexp"
+	"slices"
 	"strconv"
 	"strings"
 
@@ -55,6 +56,9 @@
 	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 {
@@ -127,6 +131,12 @@
 
 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 {
@@ -143,6 +153,9 @@
 	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) {
@@ -232,7 +245,7 @@
 
 	} else {
 		if s.properties.Virtual_ab.Retrofit != nil {
-			ctx.PropertyErrorf("virtual_ab.retrofix", "This property cannot be set when virtual_ab is disabled")
+			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")
@@ -299,6 +312,20 @@
 		}
 	}
 
+	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 {
+			addStr("system_other_image", systemOtherFiles[0].String())
+			deps = append(deps, systemOtherFiles[0])
+		}
+	}
+
 	// 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.
diff --git a/filesystem/system_image.go b/filesystem/system_image.go
index 874d20d..cc9093f 100644
--- a/filesystem/system_image.go
+++ b/filesystem/system_image.go
@@ -44,7 +44,12 @@
 	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
 	}
@@ -55,6 +60,11 @@
 		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
@@ -87,6 +97,11 @@
 			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..28fe1ce
--- /dev/null
+++ b/filesystem/system_other.go
@@ -0,0 +1,137 @@
+// 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"
+	"path/filepath"
+	"strings"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+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")
+
+	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)
+
+	if len(m.properties.Preinstall_dexpreopt_files_from) > 0 {
+		builder.Command().Textf("touch %s", filepath.Join(stagingDir.String(), "system-other-odex-marker"))
+	}
+
+	// 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())}
+
+	builder.Command().
+		Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
+		BuiltTool("build_image").
+		Text(stagingDir.String()). // input directory
+		Input(systemInfo.BuildImagePropFile).
+		Implicits(systemInfo.BuildImagePropFileDeps).
+		Implicit(fec).
+		Output(output).
+		Text(stagingDir.String())
+
+	builder.Build("build_system_other", "build system other")
+
+	ctx.SetOutputFiles(android.Paths{output}, "")
+	ctx.CheckbuildFile(output)
+}
diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go
index b7f312c..91826b2 100644
--- a/filesystem/vbmeta.go
+++ b/filesystem/vbmeta.go
@@ -148,8 +148,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 {
diff --git a/fsgen/boot_imgs.go b/fsgen/boot_imgs.go
index 889a4c2..58ebcc4 100644
--- a/fsgen/boot_imgs.go
+++ b/fsgen/boot_imgs.go
@@ -69,23 +69,26 @@
 	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,
-			Stem:               proptools.StringPtr("boot.img"),
+			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
@@ -123,23 +126,26 @@
 	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),
-			Partition_size:     partitionSize,
-			Use_avb:            avbInfo.avbEnable,
-			Avb_mode:           avbInfo.avbMode,
-			Avb_private_key:    avbInfo.avbkeyFilegroup,
-			Avb_rollback_index: avbInfo.avbRollbackIndex,
-			Dtb_prebuilt:       dtbPrebuilt,
-			Cmdline:            cmdline,
-			Bootconfig:         vendorBootConfigImg,
-			Stem:               proptools.StringPtr("vendor_boot.img"),
+			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
@@ -172,22 +178,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,
-			Stem:               proptools.StringPtr("init_boot.img"),
+			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/filesystem_creator.go b/fsgen/filesystem_creator.go
index d00ebb2..9dcbec1 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,55 +155,71 @@
 	return module
 }
 
-func generatedPartitions(ctx android.EarlyModuleContext) []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)
@@ -156,19 +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) {
-		superImageSubpartitions = createSuperImage(ctx, finalSoongGeneratedPartitions, partitionVars)
+		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, superImageSubpartitions)
+	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 {
@@ -183,6 +294,12 @@
 	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 {
@@ -205,7 +322,7 @@
 
 func (f *filesystemCreator) createDeviceModule(
 	ctx android.LoadHookContext,
-	generatedPartitionTypes []string,
+	partitions allGeneratedPartitionData,
 	vbmetaPartitions []string,
 	superImageSubPartitions []string,
 ) {
@@ -222,35 +339,35 @@
 	if f.properties.Super_image != "" {
 		partitionProps.Super_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "super"))
 	}
-	if android.InList("system", generatedPartitionTypes) && !android.InList("system", superImageSubPartitions) {
-		partitionProps.System_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
+	if modName := partitions.nameForType("system"); modName != "" && !android.InList("system", superImageSubPartitions) {
+		partitionProps.System_partition_name = proptools.StringPtr(modName)
 	}
-	if android.InList("system_ext", generatedPartitionTypes) && !android.InList("system_ext", superImageSubPartitions) {
-		partitionProps.System_ext_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
+	if modName := partitions.nameForType("system_ext"); modName != "" && !android.InList("system_ext", superImageSubPartitions) {
+		partitionProps.System_ext_partition_name = proptools.StringPtr(modName)
 	}
-	if android.InList("vendor", generatedPartitionTypes) && !android.InList("vendor", superImageSubPartitions) {
-		partitionProps.Vendor_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor"))
+	if modName := partitions.nameForType("vendor"); modName != "" && !android.InList("vendor", superImageSubPartitions) {
+		partitionProps.Vendor_partition_name = proptools.StringPtr(modName)
 	}
-	if android.InList("product", generatedPartitionTypes) && !android.InList("product", superImageSubPartitions) {
-		partitionProps.Product_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "product"))
+	if modName := partitions.nameForType("product"); modName != "" && !android.InList("product", superImageSubPartitions) {
+		partitionProps.Product_partition_name = proptools.StringPtr(modName)
 	}
-	if android.InList("odm", generatedPartitionTypes) && !android.InList("odm", superImageSubPartitions) {
-		partitionProps.Odm_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "odm"))
+	if modName := partitions.nameForType("odm"); modName != "" && !android.InList("odm", superImageSubPartitions) {
+		partitionProps.Odm_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("userdata"); modName != "" {
+		partitionProps.Userdata_partition_name = proptools.StringPtr(modName)
 	}
-	if android.InList("recovery", f.properties.Generated_partition_types) {
-		partitionProps.Recovery_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "recovery"))
+	if modName := partitions.nameForType("recovery"); modName != "" {
+		partitionProps.Recovery_partition_name = proptools.StringPtr(modName)
 	}
-	if android.InList("system_dlkm", f.properties.Generated_partition_types) && !android.InList("system_dlkm", superImageSubPartitions) {
-		partitionProps.System_dlkm_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_dlkm"))
+	if modName := partitions.nameForType("system_dlkm"); modName != "" && !android.InList("system_dlkm", superImageSubPartitions) {
+		partitionProps.System_dlkm_partition_name = proptools.StringPtr(modName)
 	}
-	if android.InList("vendor_dlkm", f.properties.Generated_partition_types) && !android.InList("vendor_dlkm", superImageSubPartitions) {
-		partitionProps.Vendor_dlkm_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_dlkm"))
+	if modName := partitions.nameForType("vendor_dlkm"); modName != "" && !android.InList("vendor_dlkm", superImageSubPartitions) {
+		partitionProps.Vendor_dlkm_partition_name = proptools.StringPtr(modName)
 	}
-	if android.InList("odm_dlkm", f.properties.Generated_partition_types) && !android.InList("odm_dlkm", superImageSubPartitions) {
-		partitionProps.Odm_dlkm_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "odm_dlkm"))
+	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"))
@@ -264,7 +381,8 @@
 	partitionProps.Vbmeta_partitions = vbmetaPartitions
 
 	deviceProps := &filesystem.DeviceProperties{
-		Main_device: proptools.BoolPtr(true),
+		Main_device:    proptools.BoolPtr(true),
+		Ab_ota_updater: proptools.BoolPtr(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.AbOtaUpdater),
 	}
 	if bootloader, ok := f.createBootloaderFilegroup(ctx); ok {
 		deviceProps.Bootloader = proptools.StringPtr(":" + bootloader)
@@ -273,7 +391,8 @@
 	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)
@@ -325,8 +444,8 @@
 		fsProps.Dirs = proptools.NewSimpleConfigurable(commonPartitionDirs)
 		fsProps.Security_patch = proptools.StringPtr(ctx.Config().PlatformSecurityPatch())
 
-		if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
-			fsProps.Import_aconfig_flags_from = []string{generatedModuleNameForPartition(ctx.Config(), "system_ext")}
+		if systemExtName := partitions.nameForType("system_ext"); systemExtName != "" {
+			fsProps.Import_aconfig_flags_from = []string{systemExtName}
 		}
 		fsProps.Stem = proptools.StringPtr("system.img")
 	case "system_ext":
@@ -342,9 +461,9 @@
 		fsProps.Stem = proptools.StringPtr("system_ext.img")
 	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")
@@ -360,9 +479,9 @@
 				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")
@@ -452,8 +571,8 @@
 		fsProps.Security_patch = proptools.StringPtr(partitionVars.OdmDlkmSecurityPatch)
 		fsProps.Stem = proptools.StringPtr("odm_dlkm.img")
 	case "vendor_ramdisk":
-		if android.InList("recovery", generatedPartitions(ctx)) {
-			fsProps.Include_files_of = []string{generatedModuleNameForPartition(ctx.Config(), "recovery")}
+		if recoveryName := partitions.nameForType("recovery"); recoveryName != "" {
+			fsProps.Include_files_of = []string{recoveryName}
 		}
 		fsProps.Stem = proptools.StringPtr("vendor_ramdisk.img")
 	}
@@ -467,16 +586,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" {
@@ -500,7 +625,6 @@
 	if partitionType == "vendor" {
 		f.createVendorBuildProp(ctx)
 	}
-	return true
 }
 
 // Creates filegroups for the files specified in BOARD_(partition_)AVB_KEY_PATH
@@ -702,7 +826,7 @@
 	moduleName := generatedModuleName(ctx.Config(), "recovery-prop.default")
 
 	var vendorBuildProp *string
-	if android.InList("vendor", generatedPartitions(ctx)) {
+	if ctx.DeviceConfig().BuildingVendorImage() && ctx.DeviceConfig().VendorPath() == "vendor" {
 		vendorBuildProp = proptools.StringPtr(":" + generatedModuleName(ctx.Config(), "vendor-build.prop"))
 	}
 
@@ -792,7 +916,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
@@ -836,6 +960,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)
@@ -858,19 +984,20 @@
 
 	}
 
-	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 {
@@ -917,6 +1044,13 @@
 			}
 			result.avbRollbackIndex = &parsed
 		}
+		if specificPartitionVars.BoardAvbRollbackIndexLocation != "" {
+			parsed, err := strconv.ParseInt(specificPartitionVars.BoardAvbRollbackIndexLocation, 10, 64)
+			if err != nil {
+				panic(fmt.Sprintf("Rollback index location must be an int, got %s", specificPartitionVars.BoardAvbRollbackIndexLocation))
+			}
+			result.avbRollbackIndexLocation = &parsed
+		}
 
 		// Make allows you to pass arbitrary arguments to avbtool via this variable, but in practice
 		// it's only used for --hash_algorithm. The soong module has a dedicated property for the
@@ -937,12 +1071,12 @@
 	return result
 }
 
-func (f *filesystemCreator) createFileListDiffTest(ctx android.ModuleContext, partitionType string) android.Path {
-	partitionModuleName := generatedModuleNameForPartition(ctx.Config(), partitionType)
+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))
@@ -1003,8 +1137,8 @@
 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)
@@ -1017,9 +1151,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")
 	}
@@ -1028,12 +1164,12 @@
 	ctx.Phony("product_config_to_bp", generatedBp)
 
 	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)
@@ -1079,13 +1215,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/fsgen_mutators.go b/fsgen/fsgen_mutators.go
index 9327669..9b25e77 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
@@ -282,9 +282,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/super_img.go b/fsgen/super_img.go
index 23d331d..569f780 100644
--- a/fsgen/super_img.go
+++ b/fsgen/super_img.go
@@ -27,7 +27,12 @@
 	return partitionVars.ProductBuildSuperPartition
 }
 
-func createSuperImage(ctx android.LoadHookContext, partitions []string, partitionVars android.PartitionVariables) []string {
+func createSuperImage(
+	ctx android.LoadHookContext,
+	partitions allGeneratedPartitionData,
+	partitionVars android.PartitionVariables,
+	systemOtherImageName string,
+) []string {
 	baseProps := &struct {
 		Name *string
 	}{
@@ -58,7 +63,7 @@
 		if partitionVars.ProductVirtualAbCowVersion != "" {
 			version, err := strconv.ParseInt(partitionVars.ProductVirtualAbCowVersion, 10, 32)
 			if err != nil {
-				ctx.ModuleErrorf("Compression factor must be an int, got %q", partitionVars.ProductVirtualAbCowVersion)
+				ctx.ModuleErrorf("COW version must be an int, got %q", partitionVars.ProductVirtualAbCowVersion)
 			}
 			superImageProps.Virtual_ab.Cow_version = proptools.Int64Ptr(version)
 		}
@@ -79,42 +84,46 @@
 	}
 	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")
 	}
 
diff --git a/fsgen/vbmeta_partitions.go b/fsgen/vbmeta_partitions.go
index a75f59c..11f4bd0 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,16 @@
 
 	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]
 		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,15 +113,12 @@
 
 		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
@@ -119,15 +137,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 +166,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") || partitionType == "userdata" {
-			// ramdisk and userdata are 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 +253,8 @@
 			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/golang/golang.go b/golang/golang.go
index d33f5e0..3422f8b 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 (!ctx.Config().KatiEnabled() || 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/java/aar.go b/java/aar.go
index b982b95..0a5a4c4 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -1040,7 +1040,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...)
 		}
@@ -1063,6 +1063,8 @@
 	}
 
 	a.setOutputFiles(ctx)
+
+	buildComplianceMetadata(ctx)
 }
 
 func (a *AndroidLibrary) setOutputFiles(ctx android.ModuleContext) {
@@ -1594,6 +1596,8 @@
 
 	ctx.SetOutputFiles([]android.Path{a.implementationAndResourcesJarFile}, "")
 	ctx.SetOutputFiles([]android.Path{a.aarPath}, ".aar")
+
+	buildComplianceMetadata(ctx)
 }
 
 func (a *AARImport) HeaderJars() android.Paths {
diff --git a/java/aar_test.go b/java/aar_test.go
index aa4f0af..3ac228d 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,6 +51,7 @@
 
 	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")
 
@@ -84,6 +86,7 @@
 }
 
 func TestLibraryFlagsPackages(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 	).RunTestWithBp(t, `
@@ -133,6 +136,7 @@
 }
 
 func TestAndroidLibraryOutputFilesRel(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
diff --git a/java/android_manifest_test.go b/java/android_manifest_test.go
index 7c91884..edb22fc 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",
@@ -100,6 +101,7 @@
 }
 
 func TestManifestValuesApplicationIdSetsPackageName(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_test {
 			name: "test",
diff --git a/java/androidmk.go b/java/androidmk.go
index 039e847..fe3c7a2 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()
@@ -555,73 +554,13 @@
 		},
 		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..9306e72 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",
@@ -42,6 +43,7 @@
 }
 
 func TestHostdex(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -72,6 +74,7 @@
 }
 
 func TestHostdexRequired(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -103,6 +106,7 @@
 }
 
 func TestHostdexSpecificRequired(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -136,6 +140,7 @@
 }
 
 func TestJavaSdkLibrary_RequireXmlPermissionFile(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -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",
@@ -209,6 +216,7 @@
 }
 
 func TestGetOverriddenPackages(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(
 		t, `
 		android_app {
@@ -255,6 +263,7 @@
 }
 
 func TestJniAsRequiredDeps(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		cc.PrepareForTestWithCcDefaultModules,
diff --git a/java/app.go b/java/app.go
index 34a172e..abbf034 100644
--- a/java/app.go
+++ b/java/app.go
@@ -70,6 +70,8 @@
 
 	// EmbeddedJNILibs is the list of paths to JNI libraries that were embedded in the APK.
 	EmbeddedJNILibs android.Paths
+
+	MergedManifestFile android.Path
 }
 
 var AppInfoProvider = blueprint.NewProvider[*AppInfo]()
@@ -403,6 +405,14 @@
 		Updatable:     Bool(a.appProperties.Updatable),
 		TestHelperApp: true,
 	})
+
+	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")
+	}
 }
 
 func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -419,9 +429,10 @@
 		}
 	}
 	android.SetProvider(ctx, AppInfoProvider, &AppInfo{
-		Updatable:       Bool(a.appProperties.Updatable),
-		TestHelperApp:   false,
-		EmbeddedJNILibs: embeddedJniLibs,
+		Updatable:          Bool(a.appProperties.Updatable),
+		TestHelperApp:      false,
+		EmbeddedJNILibs:    embeddedJniLibs,
+		MergedManifestFile: a.mergedManifest,
 	})
 
 	a.requiredModuleNames = a.getRequiredModuleNames(ctx)
@@ -707,7 +718,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 {
@@ -753,12 +764,8 @@
 			packageResources = binaryResources
 		}
 	}
-	if javaInfo != nil {
-		setExtraJavaInfo(ctx, a, javaInfo)
-		android.SetProvider(ctx, JavaInfoProvider, javaInfo)
-	}
 
-	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 {
@@ -965,7 +972,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)
@@ -1081,7 +1088,23 @@
 		},
 	)
 
+	if javaInfo != nil {
+		javaInfo.OutputFile = a.outputFile
+		setExtraJavaInfo(ctx, a, javaInfo)
+		android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+	}
+
+	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) {
@@ -1595,6 +1618,19 @@
 	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)...)
 
+	// 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)
+		}
+		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),
 		OutputFile:              a.OutputFile(),
@@ -1612,6 +1648,22 @@
 		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...)
 }
 
 func testcaseRel(paths android.Paths) []string {
diff --git a/java/app_import.go b/java/app_import.go
index a997e35..352e995 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -531,6 +531,8 @@
 
 	ctx.SetOutputFiles([]android.Path{a.outputFile}, "")
 
+	buildComplianceMetadata(ctx)
+
 	// TODO: androidmk converter jni libs
 }
 
diff --git a/java/app_import_test.go b/java/app_import_test.go
index a28c28b..52ae719 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",
@@ -59,7 +60,48 @@
 	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("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 TestAndroidAppImport_NoDexPreopt(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "foo",
@@ -87,6 +129,7 @@
 }
 
 func TestAndroidAppImport_Presigned(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "foo",
@@ -121,6 +164,7 @@
 }
 
 func TestAndroidAppImport_SigningLineage(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 	  android_app_import {
 			name: "foo",
@@ -164,6 +208,7 @@
 }
 
 func TestAndroidAppImport_SigningLineageFilegroup(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 	  android_app_import {
 			name: "foo",
@@ -196,6 +241,7 @@
 }
 
 func TestAndroidAppImport_DefaultDevCert(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "foo",
@@ -231,6 +277,7 @@
 }
 
 func TestAndroidAppImport_DpiVariants(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app_import {
 			name: "foo",
@@ -317,6 +364,7 @@
 }
 
 func TestAndroidAppImport_Filename(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "foo",
@@ -409,6 +457,7 @@
 }
 
 func TestAndroidAppImport_ArchVariants(t *testing.T) {
+	t.Parallel()
 	// The test config's target arch is ARM64.
 	testCases := []struct {
 		name         string
@@ -534,6 +583,7 @@
 
 	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")
@@ -559,6 +609,7 @@
 }
 
 func TestAndroidAppImport_SoongConfigVariables(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name         string
 		bp           string
@@ -601,6 +652,7 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			ctx := android.GroupFixturePreparers(
 				prepareForJavaTest,
 				android.PrepareForTestWithSoongConfigModuleBuildComponents,
@@ -636,6 +688,7 @@
 }
 
 func TestAndroidAppImport_overridesDisabledAndroidApp(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app {
 			name: "foo",
@@ -664,6 +717,7 @@
 }
 
 func TestAndroidAppImport_relativeInstallPath(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app_import {
 			name: "no_relative_install_path",
@@ -708,13 +762,17 @@
 		},
 	}
 	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(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",
@@ -731,6 +789,7 @@
 	}
 }
 func TestAndroidTestImport(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_test_import {
 			name: "foo",
@@ -759,6 +818,7 @@
 }
 
 func TestAndroidTestImport_NoJinUncompressForPresigned(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_test_import {
 			name: "foo",
@@ -796,6 +856,7 @@
 }
 
 func TestAndroidTestImport_Preprocessed(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_test_import {
 			name: "foo",
@@ -822,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) {
@@ -899,6 +962,7 @@
 }
 
 func TestAndroidTestImport_UncompressDex(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name string
 		bp   string
@@ -962,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)
 				})
 			}
@@ -970,6 +1035,7 @@
 }
 
 func TestAppImportMissingCertificateAllowMissingDependencies(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.PrepareForTestWithAllowMissingDependencies,
diff --git a/java/app_set_test.go b/java/app_set_test.go
index c02b359..cc7af04 100644
--- a/java/app_set_test.go
+++ b/java/app_set_test.go
@@ -24,6 +24,7 @@
 )
 
 func TestAndroidAppSet(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app_set {
 			name: "foo",
@@ -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("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 bde801b..701fc35 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -42,6 +42,7 @@
 }
 
 func TestApp(t *testing.T) {
+	t.Parallel()
 	resourceFiles := []string{
 		"res/layout/layout.xml",
 		"res/values/strings.xml",
@@ -56,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) {
@@ -101,6 +103,7 @@
 }
 
 func TestAppSplits(t *testing.T) {
+	t.Parallel()
 	ctx := testApp(t, `
 				android_app {
 					name: "foo",
@@ -125,6 +128,7 @@
 }
 
 func TestPlatformAPIs(t *testing.T) {
+	t.Parallel()
 	testJava(t, `
 		android_app {
 			name: "foo",
@@ -159,6 +163,7 @@
 }
 
 func TestAndroidAppLinkType(t *testing.T) {
+	t.Parallel()
 	testJava(t, `
 		android_app {
 			name: "foo",
@@ -248,6 +253,7 @@
 }
 
 func TestUpdatableApps(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name          string
 		bp            string
@@ -359,6 +365,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)
@@ -373,6 +380,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",
@@ -391,6 +399,7 @@
 }
 
 func TestUpdatableApps_JniLibsShouldShouldSupportMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		android_app {
 			name: "foo",
@@ -411,6 +420,7 @@
 }
 
 func TestUpdatableApps_JniLibShouldBeBuiltAgainstMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	bp := cc.GatherRequiredDepsForTest(android.Android) + `
 		android_app {
 			name: "foo",
@@ -466,6 +476,7 @@
 }
 
 func TestUpdatableApps_ErrorIfJniLibDoesntSupportMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	bp := cc.GatherRequiredDepsForTest(android.Android) + `
 		android_app {
 			name: "foo",
@@ -487,6 +498,7 @@
 }
 
 func TestUpdatableApps_ErrorIfDepMinSdkVersionIsHigher(t *testing.T) {
+	t.Parallel()
 	bp := cc.GatherRequiredDepsForTest(android.Android) + `
 		android_app {
 			name: "foo",
@@ -518,6 +530,7 @@
 }
 
 func TestUpdatableApps_ApplyDefaultUpdatableModuleVersion(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -538,6 +551,7 @@
 }
 
 func TestUpdatableApps_ApplyOverrideApexManifestDefaultVersion(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.FixtureMergeEnv(map[string]string{
@@ -561,6 +575,7 @@
 }
 
 func TestResourceDirs(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name      string
 		prop      string
@@ -597,6 +612,7 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 				fs.AddToFixture(),
@@ -618,6 +634,7 @@
 }
 
 func TestLibraryAssets(t *testing.T) {
+	t.Parallel()
 	bp := `
 			android_app {
 				name: "foo",
@@ -712,6 +729,7 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			m := ctx.ModuleForTests(test.name, "android_common")
 
 			// Check asset flag in aapt2 link flags
@@ -747,6 +765,7 @@
 }
 
 func TestAppJavaResources(t *testing.T) {
+	t.Parallel()
 	bp := `
 			android_app {
 				name: "foo",
@@ -792,6 +811,7 @@
 }
 
 func TestAndroidResourceProcessor(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name                            string
 		appUsesRP                       bool
@@ -1224,6 +1244,7 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
 			bp := fmt.Sprintf(`
 				android_app {
 					name: "app",
@@ -1420,6 +1441,7 @@
 }
 
 func TestAndroidResourceOverlays(t *testing.T) {
+	t.Parallel()
 	type moduleAndVariant struct {
 		module  string
 		variant string
@@ -1616,6 +1638,7 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 				fs.AddToFixture(),
@@ -1725,6 +1748,7 @@
 }
 
 func TestAppSdkVersion(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name                  string
 		sdkVersion            string
@@ -1792,6 +1816,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,"
@@ -1828,6 +1853,7 @@
 }
 
 func TestVendorAppSdkVersion(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name                                  string
 		sdkVersion                            string
@@ -1870,6 +1896,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"],
@@ -1901,6 +1928,7 @@
 }
 
 func TestJNIABI(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
 			name: "libjni",
@@ -1957,6 +1985,7 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			app := ctx.ModuleForTests(test.name, "android_common")
 			jniLibZip := app.Output("jnilibs.zip")
 			var abis []string
@@ -1975,6 +2004,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",
@@ -2019,6 +2049,7 @@
 }
 
 func TestJNIPackaging(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
 			name: "libjni",
@@ -2090,6 +2121,7 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			app := ctx.ModuleForTests(test.name, "android_common")
 			jniLibZip := app.MaybeOutput("jnilibs.zip")
 			if g, w := (jniLibZip.Rule != nil), test.packaged; g != w {
@@ -2110,6 +2142,7 @@
 }
 
 func TestJNISDK(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
 			name: "libjni",
@@ -2180,6 +2213,7 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			app := ctx.ModuleForTests(test.name, "android_common")
 
 			jniLibZip := app.MaybeOutput("jnilibs.zip")
@@ -2205,6 +2239,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",
@@ -2215,6 +2250,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",
@@ -2227,6 +2263,7 @@
 }
 
 func TestCertificates(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name                     string
 		bp                       string
@@ -2364,6 +2401,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) {
@@ -2401,6 +2439,7 @@
 }
 
 func TestRequestV4SigningFlag(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name     string
 		bp       string
@@ -2445,6 +2484,7 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 			).RunTestWithBp(t, test.bp)
@@ -2459,6 +2499,7 @@
 }
 
 func TestPackageNameOverride(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name                string
 		bp                  string
@@ -2516,6 +2557,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) {
@@ -2544,6 +2586,7 @@
 }
 
 func TestInstrumentationTargetOverridden(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app {
 			name: "foo",
@@ -2575,6 +2618,7 @@
 }
 
 func TestOverrideAndroidApp(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(
 		t, `
 		android_app {
@@ -2757,6 +2801,7 @@
 }
 
 func TestOverrideAndroidAppOverrides(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(
 		t, `
 		android_app {
@@ -2815,6 +2860,7 @@
 }
 
 func TestOverrideAndroidAppWithPrebuilt(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(
 		t, `
 		android_app {
@@ -2850,6 +2896,7 @@
 }
 
 func TestOverrideAndroidAppStem(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app {
 			name: "foo",
@@ -2924,6 +2971,7 @@
 }
 
 func TestOverrideAndroidAppDependency(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app {
 			name: "foo",
@@ -3159,6 +3207,7 @@
 }
 
 func TestStl(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
 			name: "libjni",
@@ -3201,6 +3250,7 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			app := ctx.ModuleForTests(test.name, "android_common")
 			jniLibZip := app.Output("jnilibs.zip")
 			var jnis []string
@@ -3431,6 +3481,7 @@
 }
 
 func TestDexpreoptBcp(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_sdk_library {
 			name: "foo",
@@ -3473,6 +3524,7 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				prepareForJavaTest,
 				PrepareForTestWithJavaSdkLibraryFiles,
@@ -3491,6 +3543,7 @@
 }
 
 func TestCodelessApp(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name   string
 		bp     string
@@ -3555,6 +3608,7 @@
 
 	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")
@@ -3567,6 +3621,7 @@
 }
 
 func TestUncompressDex(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name string
 		bp   string
@@ -3666,10 +3721,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)
 			})
 		})
@@ -3691,6 +3749,7 @@
 }
 
 func TestExportedProguardFlagFiles(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app {
 			name: "foo",
@@ -3752,6 +3811,7 @@
 }
 
 func TestTargetSdkVersionManifestFixer(t *testing.T) {
+	t.Parallel()
 	platform_sdk_codename := "Tiramisu"
 	platform_sdk_version := 33
 	testCases := []struct {
@@ -3804,43 +3864,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("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 {
@@ -3896,11 +3960,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",
@@ -3911,30 +3977,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("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 {
@@ -3979,8 +4047,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,
@@ -3991,35 +4061,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("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 {
@@ -4052,8 +4124,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,
@@ -4062,35 +4136,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("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,
@@ -4120,6 +4196,7 @@
 }
 
 func TestAppIncludesJniPackages(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -4182,6 +4259,7 @@
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
 			app := ctx.ModuleForTests(tc.name, "android_common")
 
 			outputFile := "jnilibs.zip"
@@ -4206,6 +4284,7 @@
 }
 
 func TestTargetSdkVersionMtsTests(t *testing.T) {
+	t.Parallel()
 	platformSdkCodename := "Tiramisu"
 	android_test := "android_test"
 	android_test_helper_app := "android_test_helper_app"
@@ -4261,14 +4340,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("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",
@@ -4311,6 +4394,7 @@
 }
 
 func TestPrivappAllowlistAndroidMk(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.PrepareForTestWithAndroidMk,
@@ -4390,6 +4474,7 @@
 }
 
 func TestAppFlagsPackages(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		android.FixtureMergeMockFs(
@@ -4454,6 +4539,7 @@
 }
 
 func TestAppFlagsPackagesPropagation(t *testing.T) {
+	t.Parallel()
 	ctx := testApp(t, `
 		aconfig_declarations {
 			name: "foo",
@@ -4531,6 +4617,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",
@@ -4660,6 +4747,7 @@
 }
 
 func TestAppStem(t *testing.T) {
+	t.Parallel()
 	ctx := testApp(t, `
 				android_app {
 					name: "foo",
@@ -4677,6 +4765,7 @@
 }
 
 func TestAppMinSdkVersionOverride(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -4710,6 +4799,7 @@
 }
 
 func TestNotApplyDefaultUpdatableModuleVersion(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -4729,6 +4819,7 @@
 }
 
 func TestNotApplyOverrideApexManifestDefaultVersion(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.FixtureMergeEnv(map[string]string{
@@ -4751,6 +4842,7 @@
 }
 
 func TestResourcesWithFlagDirectories(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.FixtureMergeMockFs(android.MockFS{
@@ -4855,17 +4947,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("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)
+		})
 	}
 }
 
@@ -4922,22 +5017,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("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 8e013b9..d89c324 100644
--- a/java/base.go
+++ b/java/base.go
@@ -1336,14 +1336,24 @@
 		kotlincFlags := j.properties.Kotlincflags
 		CheckKotlincFlags(ctx, kotlincFlags)
 
-		kotlin_lang_version := proptools.StringDefault(j.properties.Kotlin_lang_version, "1.9")
-		if kotlin_lang_version == "1.9" {
+		// 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")
-		} else if kotlin_lang_version == "2" {
+		case "2":
 			kotlincFlags = append(kotlincFlags, "-Xsuppress-version-warnings", "-Xconsistent-data-class-copy-visibility")
-		} else {
+		default:
 			ctx.PropertyErrorf("kotlin_lang_version", "Must be one of `1.9` or `2`")
-
 		}
 
 		// Workaround for KT-46512
@@ -1970,6 +1980,7 @@
 		StubsLinkType:                       j.stubsLinkType,
 		AconfigIntermediateCacheOutputPaths: j.aconfigCacheFiles,
 		SdkVersion:                          j.SdkVersion(ctx),
+		OutputFile:                          j.outputFile,
 	}
 }
 
@@ -1978,7 +1989,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)
 
@@ -2219,6 +2230,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())
 	}
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 3aa1258..d181ce0 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,
diff --git a/java/builder.go b/java/builder.go
index f1d5e99..22dad10 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -750,9 +750,9 @@
 	jar android.Path) {
 
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        extractR8Rules,
-		Output:      outputFile,
-		Input:       jar,
+		Rule:   extractR8Rules,
+		Output: outputFile,
+		Input:  jar,
 	})
 }
 
@@ -796,7 +796,7 @@
 	totalStr := strconv.Itoa(totalShards)
 	for i := 0; i < totalShards; i++ {
 		iStr := strconv.Itoa(i)
-		tempOut := android.PathForOutput(ctx, outputFile.String()+"-"+iStr+".jar")
+		tempOut := outputFile.ReplaceExtension(ctx, "-"+iStr+".jar")
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        jarjar,
 			Description: "jarjar (" + iStr + "/" + totalStr + ")",
diff --git a/java/container_test.go b/java/container_test.go
index 25cfa4c..515236d 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, `
diff --git a/java/device_host_converter_test.go b/java/device_host_converter_test.go
index 6ccc5c1..45369e2 100644
--- a/java/device_host_converter_test.go
+++ b/java/device_host_converter_test.go
@@ -22,6 +22,7 @@
 )
 
 func TestDeviceForHost(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "device_module",
@@ -102,6 +103,7 @@
 }
 
 func TestHostForDevice(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library_host {
 			name: "host_module",
diff --git a/java/dex.go b/java/dex.go
index bc14290..00a0537 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -378,7 +378,7 @@
 		r8Flags = append(r8Flags, "--keep-runtime-invisible-annotations")
 	}
 
-	if BoolDefault(opt.Proguard_compatibility, true) {
+	if BoolDefault(opt.Proguard_compatibility, !ctx.Config().UseR8FullModeByDefault()) {
 		r8Flags = append(r8Flags, "--force-proguard-compatibility")
 	}
 
diff --git a/java/dex_test.go b/java/dex_test.go
index 8bc28e6..f261066 100644
--- a/java/dex_test.go
+++ b/java/dex_test.go
@@ -24,6 +24,7 @@
 )
 
 func TestR8(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app {
 			name: "app",
@@ -91,6 +92,7 @@
 }
 
 func TestR8TransitiveDeps(t *testing.T) {
+	t.Parallel()
 	bp := `
 		override_android_app {
 			name: "override_app",
@@ -192,6 +194,7 @@
 
 	for _, tc := range testcases {
 		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
 			fixturePreparer := PrepareForTestWithJavaDefaultModules
 			if tc.unbundled {
 				fixturePreparer = android.GroupFixturePreparers(
@@ -259,6 +262,7 @@
 }
 
 func TestR8Flags(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app {
 			name: "app",
@@ -288,6 +292,7 @@
 }
 
 func TestD8(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -329,6 +334,7 @@
 }
 
 func TestProguardFlagsInheritanceStatic(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app {
 			name: "app",
@@ -383,6 +389,7 @@
 }
 
 func TestProguardFlagsInheritance(t *testing.T) {
+	t.Parallel()
 	directDepFlagsFileName := "direct_dep.flags"
 	transitiveDepFlagsFileName := "transitive_dep.flags"
 
@@ -601,6 +608,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{
@@ -642,6 +650,7 @@
 }
 
 func TestProguardFlagsInheritanceAppImport(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app {
 			name: "app",
@@ -664,6 +673,7 @@
 }
 
 func TestR8FlagsArtProfile(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app {
 			name: "app",
@@ -696,6 +706,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,6 +726,7 @@
 }
 
 func TestDebugReleaseFlags(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app {
 			name: "app",
@@ -771,6 +783,7 @@
 
 	for _, tc := range testcases {
 		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
 			fixturePreparer := PrepareForTestWithJavaDefaultModules
 			fixturePreparer = android.GroupFixturePreparers(
 				fixturePreparer,
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index efca913..f52ce5d 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -80,11 +80,11 @@
 // safe to use in `android.AndroidMkExtraEntriesFunc`.
 func (install dexpreopterInstall) ToMakeEntries() android.AndroidMkEntries {
 	return android.AndroidMkEntries{
-		Class:      "ETC",
-		OutputFile: android.OptionalPathForPath(install.outputPathOnHost),
+		OverrideName: install.FullModuleName(),
+		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")
@@ -93,6 +93,14 @@
 	}
 }
 
+func (install dexpreopterInstall) AddModuleInfoJSONForApex(ctx android.ModuleContext) {
+	moduleInfoJSON := ctx.ExtraModuleInfoJSON()
+	moduleInfoJSON.RegisterNameOverride = install.FullModuleName()
+	moduleInfoJSON.ModuleNameOverride = install.FullModuleName()
+	moduleInfoJSON.Class = []string{"ETC"}
+	moduleInfoJSON.SystemSharedLibs = []string{"none"}
+}
+
 type Dexpreopter struct {
 	dexpreopter
 }
@@ -204,7 +212,7 @@
 	}
 	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
 		}
@@ -660,6 +668,12 @@
 	return entries
 }
 
+func (d *dexpreopter) ModuleInfoJSONForApex(ctx android.ModuleContext) {
+	for _, install := range d.builtInstalledForApex {
+		install.AddModuleInfoJSONForApex(ctx)
+	}
+}
+
 func (d *dexpreopter) OutputProfilePathOnHost() android.Path {
 	return d.outputProfilePathOnHost
 }
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..fbdb7b0 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)
 	})
 }
@@ -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)
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index 207ff65..53a0d71 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -30,6 +30,7 @@
 }
 
 func TestDexpreoptEnabled(t *testing.T) {
+	t.Parallel()
 	tests := []struct {
 		name        string
 		bp          string
@@ -219,6 +220,7 @@
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			preparers := android.GroupFixturePreparers(
 				PrepareForTestWithDexpreopt,
 				PrepareForTestWithFakeApexMutator,
@@ -258,6 +260,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 +276,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",
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 49674b9..225f201 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"
@@ -874,6 +875,13 @@
 	Path *string
 }
 
+type ExportedDroiddocDirInfo struct {
+	Deps android.Paths
+	Dir  android.Path
+}
+
+var ExportedDroiddocDirInfoProvider = blueprint.NewProvider[ExportedDroiddocDirInfo]()
+
 type ExportedDroiddocDir struct {
 	android.ModuleBase
 
@@ -897,6 +905,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..2256f1e 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",
@@ -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 fa1fb86..22f4d98 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())
 				}
@@ -603,18 +626,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 +645,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-{version:level}/android.jar
-					//  prebuilts/sdk/{version:level}/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))
@@ -650,7 +667,7 @@
 	for _, sdkDir := range sdkDirs {
 		for _, dir := range dirs {
 			addPattern := func(jarFilename string) {
-				cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/{version:level}/%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 +682,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 +694,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 +720,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 +767,8 @@
 
 	addMetalavaConfigFilesToCmd(cmd, configFiles)
 
+	addOptionalApiSurfaceToCmd(cmd, apiSurface)
+
 	return cmd
 }
 
@@ -764,6 +787,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
@@ -838,7 +869,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 +1209,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)
 
@@ -1220,6 +1280,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 +1364,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 +1412,39 @@
 		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)
+}
+
+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 +1612,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 37740ae..faa9a15 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",
@@ -88,7 +89,7 @@
 		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 ./{version:level}/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)
 			}
@@ -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/{version:level}/public/android.jar",
-		"--android-jar-pattern someotherdir/{version:level}/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/{version:level}/system/android.jar",
-		"--android-jar-pattern someotherdir/{version:level}/system/android.jar",
-		"--android-jar-pattern somedir/{version:level}/public/android.jar",
-		"--android-jar-pattern someotherdir/{version:level}/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/{version:level}/module-lib/android.jar",
-		"--android-jar-pattern someotherdir/{version:level}/module-lib/android.jar",
-		"--android-jar-pattern somedir/{version:level}/system/android.jar",
-		"--android-jar-pattern someotherdir/{version:level}/system/android.jar",
-		"--android-jar-pattern somedir/{version:level}/public/android.jar",
-		"--android-jar-pattern someotherdir/{version:level}/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/{version:level}/system-server/android.jar",
-		"--android-jar-pattern someotherdir/{version:level}/system-server/android.jar",
-		"--android-jar-pattern somedir/{version:level}/module-lib/android.jar",
-		"--android-jar-pattern someotherdir/{version:level}/module-lib/android.jar",
-		"--android-jar-pattern somedir/{version:level}/system/android.jar",
-		"--android-jar-pattern someotherdir/{version:level}/system/android.jar",
-		"--android-jar-pattern somedir/{version:level}/public/android.jar",
-		"--android-jar-pattern someotherdir/{version:level}/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)
 }
 
@@ -303,7 +307,7 @@
 	m := ctx.ModuleForTests("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")
 }
 
diff --git a/java/fuzz_test.go b/java/fuzz_test.go
index f29c913..40adae0 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",
diff --git a/java/generated_java_library_test.go b/java/generated_java_library_test.go
index a5c4be1..7efd54b 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",
diff --git a/java/genrule_test.go b/java/genrule_test.go
index 1c294b2..b4e9d21 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,
@@ -64,6 +65,7 @@
 }
 
 func TestJarGenrules(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index afe8b4c..c14fdb7 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"),
@@ -63,6 +64,7 @@
 }
 
 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"),
@@ -105,6 +108,7 @@
 }
 
 func TestHiddenAPISingletonWithPrebuiltUseSource(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
@@ -134,6 +138,7 @@
 }
 
 func TestHiddenAPISingletonWithPrebuiltOverrideSource(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
@@ -163,6 +168,7 @@
 }
 
 func TestHiddenAPISingletonSdks(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name             string
 		unbundledBuild   bool
@@ -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
@@ -289,6 +296,7 @@
 }
 
 func TestHiddenAPIEncoding_JavaSdkLibrary(t *testing.T) {
+	t.Parallel()
 
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
@@ -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/java.go b/java/java.go
index 0a9381d..9e158d1 100644
--- a/java/java.go
+++ b/java/java.go
@@ -360,6 +360,11 @@
 
 	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
@@ -371,6 +376,62 @@
 	ModuleWithUsesLibraryInfo *ModuleWithUsesLibraryInfo
 
 	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
+
+	BuiltInstalledForApex []dexpreopterInstall
+
+	// 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
+
+	// The path to the profile on host that dexpreopter generates. This is used as the input for
+	// dex2oat.
+	OutputProfilePathOnHost android.Path
+
+	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
 }
 
 var JavaInfoProvider = blueprint.NewProvider[*JavaInfo]()
@@ -529,7 +590,7 @@
 )
 
 func IsLibDepTag(depTag blueprint.DependencyTag) bool {
-	return depTag == libTag || depTag == sdkLibTag
+	return depTag == libTag
 }
 
 func IsStaticLibDepTag(depTag blueprint.DependencyTag) bool {
@@ -1062,10 +1123,82 @@
 
 	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.BuiltInstalledForApex = j.builtInstalledForApex
+		javaInfo.BuiltInstalled = j.builtInstalled
+		javaInfo.ConfigPath = j.configPath
+		javaInfo.OutputProfilePathOnHost = j.outputProfilePathOnHost
+		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)
+}
+
+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
+		j.dexpreopter.ModuleInfoJSONForApex(ctx)
+	}
+	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.FirstUniqueStrings(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.FirstUniqueStrings(staticDepNames))
+	complianceMetadataInfo.SetListValue(android.ComplianceMetadataProp.STATIC_DEP_FILES, android.FirstUniqueStrings(staticDepFiles.Strings()))
 }
 
 func (j *Library) getJarInstallDir(ctx android.ModuleContext) android.InstallPath {
@@ -1624,6 +1757,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) {
@@ -1658,17 +1796,17 @@
 
 	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,
@@ -1701,10 +1839,70 @@
 	})
 
 	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)
+		}
+		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.installFile != nil {
+			ctx.InstallFile(pathInTestCases, ctx.ModuleName()+".jar", j.installFile)
+		}
+	}
 }
 
 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())
+	}
 }
 
 func (j *JavaTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -1958,13 +2156,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.OtherModuleProvider(ctx, jni, android.CommonModuleInfoKey)
+		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())
 		}
@@ -2183,7 +2381,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())
 
@@ -2224,6 +2422,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
@@ -2307,6 +2507,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 == "" {
@@ -2355,7 +2566,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:
@@ -2384,8 +2595,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 {
@@ -2417,7 +2628,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)
 
@@ -2988,6 +3199,8 @@
 
 	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) {
@@ -3358,6 +3571,8 @@
 		&bootclasspathFragmentProperties{},
 		&SourceOnlyBootclasspathProperties{},
 		&ravenwoodTestProperties{},
+		&AndroidAppImportProperties{},
+		&UsesLibraryProperties{},
 	)
 
 	android.InitDefaultsModule(module)
diff --git a/java/java_test.go b/java/java_test.go
index d415679..de58237 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -117,6 +117,7 @@
 // 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",
@@ -341,6 +344,7 @@
 
 	for _, tt := range testCases {
 		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 				tt.preparer,
@@ -378,6 +382,7 @@
 }
 
 func TestExportedPlugins(t *testing.T) {
+	t.Parallel()
 	type Result struct {
 		library        string
 		processors     string
@@ -456,6 +461,7 @@
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			ctx, _ := testJava(t, `
 				java_plugin {
 					name: "plugin",
@@ -484,6 +490,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 +532,7 @@
 }
 
 func TestArchSpecific(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -544,6 +552,7 @@
 }
 
 func TestBinary(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library_host {
 			name: "foo",
@@ -586,6 +595,7 @@
 }
 
 func TestTest(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_test_host {
 			name: "foo",
@@ -618,6 +628,7 @@
 }
 
 func TestHostBinaryNoJavaDebugInfoOverride(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "target_library",
@@ -665,6 +676,7 @@
 var _ android.ModuleErrorfContext = (*moduleErrorfTestCtx)(nil)
 
 func TestPrebuilts(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -765,6 +777,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 {
@@ -782,10 +795,12 @@
 	}
 
 	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 +809,7 @@
 }
 
 func TestDefaults(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_defaults {
 			name: "defaults",
@@ -869,6 +885,7 @@
 }
 
 func TestResources(t *testing.T) {
+	t.Parallel()
 	var table = []struct {
 		name  string
 		prop  string
@@ -940,6 +957,7 @@
 
 	for _, test := range table {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			ctx, _ := testJavaWithFS(t, `
 				java_library {
 					name: "foo",
@@ -975,6 +993,7 @@
 }
 
 func TestIncludeSrcs(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJavaWithFS(t, `
 		java_library {
 			name: "foo",
@@ -1042,6 +1061,7 @@
 }
 
 func TestGeneratedSources(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJavaWithFS(t, `
 		java_library {
 			name: "foo",
@@ -1078,6 +1098,7 @@
 }
 
 func TestTurbine(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{"14": {"foo"}})).
 		RunTestWithBp(t, `
@@ -1119,6 +1140,7 @@
 }
 
 func TestSharding(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "bar",
@@ -1137,6 +1159,7 @@
 }
 
 func TestExcludeFileGroupInSrcs(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -1163,6 +1186,7 @@
 }
 
 func TestJavaLibraryOutputFiles(t *testing.T) {
+	t.Parallel()
 	testJavaWithFS(t, "", map[string][]byte{
 		"libcore/Android.bp": []byte(`
 				java_library {
@@ -1180,6 +1204,7 @@
 }
 
 func TestJavaImportOutputFiles(t *testing.T) {
+	t.Parallel()
 	testJavaWithFS(t, "", map[string][]byte{
 		"libcore/Android.bp": []byte(`
 				java_import {
@@ -1196,6 +1221,7 @@
 }
 
 func TestJavaImport(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "source_library",
@@ -1323,6 +1349,7 @@
 }
 
 func TestCompilerFlags(t *testing.T) {
+	t.Parallel()
 	for _, testCase := range compilerFlagsTestCases {
 		ctx := &mockContext{result: true}
 		CheckKotlincFlags(ctx, []string{testCase.in})
@@ -1353,7 +1380,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 +1415,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 +1456,7 @@
 }
 
 func TestJavaLibraryWithSystemModules(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 		    name: "lib-with-source-system-modules",
@@ -1482,6 +1513,7 @@
 }
 
 func TestAidlExportIncludeDirsFromImports(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -1506,6 +1538,7 @@
 }
 
 func TestAidlFlagsArePassedToTheAidlCompiler(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -1522,6 +1555,7 @@
 }
 
 func TestAidlFlagsWithMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	fixture := android.GroupFixturePreparers(
 		prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{"14": {"foo"}}))
 
@@ -1535,6 +1569,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",
@@ -1552,6 +1587,7 @@
 }
 
 func TestAidlFlagsMinSdkVersionDroidstubs(t *testing.T) {
+	t.Parallel()
 	bpTemplate := `
 	droidstubs {
 		name: "foo-stubs",
@@ -1585,6 +1621,7 @@
 }
 
 func TestAidlEnforcePermissions(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -1601,6 +1638,7 @@
 }
 
 func TestAidlEnforcePermissionsException(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -1621,6 +1659,7 @@
 }
 
 func TestDataNativeBinaries(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		android.PrepareForTestWithAllowMissingDependencies).RunTestWithBp(t, `
@@ -1646,6 +1685,7 @@
 }
 
 func TestDefaultInstallable(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_test_host {
 			name: "foo"
@@ -1659,6 +1699,7 @@
 }
 
 func TestErrorproneEnabled(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -1687,6 +1728,7 @@
 }
 
 func TestErrorproneDisabled(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "foo",
@@ -1721,6 +1763,7 @@
 }
 
 func TestErrorproneEnabledOnlyByEnvironmentVariable(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "foo",
@@ -1751,6 +1794,7 @@
 }
 
 func TestDataDeviceBinsBuildsDeviceBinary(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		dataDeviceBinType  string
 		depCompileMultilib string
@@ -1887,6 +1931,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)
@@ -1916,12 +1961,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 {
@@ -1945,6 +1991,7 @@
 }
 
 func TestJavaApiContributionEmptyApiFile(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		android.FixtureMergeEnv(
@@ -1968,6 +2015,7 @@
 }
 
 func TestJavaApiLibraryAndProviderLink(t *testing.T) {
+	t.Parallel()
 	provider_bp_a := `
 	java_api_contribution {
 		name: "foo1",
@@ -2034,6 +2082,7 @@
 }
 
 func TestJavaApiLibraryAndDefaultsLink(t *testing.T) {
+	t.Parallel()
 	provider_bp_a := `
 	java_api_contribution {
 		name: "foo1",
@@ -2142,6 +2191,7 @@
 }
 
 func TestJavaApiLibraryJarGeneration(t *testing.T) {
+	t.Parallel()
 	provider_bp_a := `
 	java_api_contribution {
 		name: "foo1",
@@ -2208,6 +2258,7 @@
 }
 
 func TestJavaApiLibraryLibsLink(t *testing.T) {
+	t.Parallel()
 	provider_bp_a := `
 	java_api_contribution {
 		name: "foo1",
@@ -2296,6 +2347,7 @@
 }
 
 func TestJavaApiLibraryStaticLibsLink(t *testing.T) {
+	t.Parallel()
 	provider_bp_a := `
 	java_api_contribution {
 		name: "foo1",
@@ -2383,6 +2435,7 @@
 }
 
 func TestTransitiveSrcFiles(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "a",
@@ -2406,6 +2459,7 @@
 }
 
 func TestTradefedOptions(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaBuildComponents.RunTestWithBp(t, `
 java_test_host {
 	name: "foo",
@@ -2430,6 +2484,7 @@
 }
 
 func TestTestRunnerOptions(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaBuildComponents.RunTestWithBp(t, `
 java_test_host {
 	name: "foo",
@@ -2454,6 +2509,7 @@
 }
 
 func TestJavaLibraryWithResourcesStem(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJavaWithFS(t, `
     java_library {
         name: "foo",
@@ -2473,6 +2529,7 @@
 }
 
 func TestHeadersOnly(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -2491,6 +2548,7 @@
 }
 
 func TestJavaApiContributionImport(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		android.FixtureMergeEnv(
@@ -2519,6 +2577,7 @@
 }
 
 func TestJavaApiLibraryApiFilesSorting(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_api_library {
 			name: "foo",
@@ -2547,6 +2606,7 @@
 }
 
 func TestSdkLibraryProvidesSystemModulesToApiLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -2576,6 +2636,7 @@
 }
 
 func TestApiLibraryDroidstubsDependency(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -2622,6 +2683,7 @@
 }
 
 func TestDisableFromTextStubForCoverageBuild(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -2651,6 +2713,7 @@
 }
 
 func TestMultiplePrebuilts(t *testing.T) {
+	t.Parallel()
 	bp := `
 		// an rdep
 		java_library {
@@ -2749,6 +2812,7 @@
 }
 
 func TestMultiplePlatformCompatConfigPrebuilts(t *testing.T) {
+	t.Parallel()
 	bp := `
 		// multiple variations of platform_compat_config
 		// source
@@ -2809,6 +2873,7 @@
 }
 
 func TestApiLibraryAconfigDeclarations(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -2919,6 +2984,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 +3020,7 @@
 }
 
 func TestJavaLibHostWithStem(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library_host {
 			name: "foo",
@@ -2972,6 +3039,7 @@
 }
 
 func TestJavaLibWithStem(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -2989,6 +3057,7 @@
 }
 
 func TestJavaLibraryOutputFilesRel(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -3034,6 +3103,7 @@
 }
 
 func TestCoverage(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		prepareForTestWithFrameworkJacocoInstrumentation,
@@ -3104,6 +3174,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) {
diff --git a/java/jdeps_test.go b/java/jdeps_test.go
index 1435000..2cbf75b 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"}
@@ -42,6 +43,7 @@
 }
 
 func TestCollectJavaLibraryPropertiesAddStaticLibsDeps(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t,
 		`
 		java_library {name: "Foo"}
@@ -62,6 +64,7 @@
 }
 
 func TestCollectJavaLibraryPropertiesAddScrs(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t,
 		`
 		java_library {
@@ -79,6 +82,7 @@
 }
 
 func TestCollectJavaLibraryPropertiesAddAidlIncludeDirs(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t,
 		`
 		java_library {
@@ -98,6 +102,7 @@
 }
 
 func TestCollectJavaLibraryWithJarJarRules(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t,
 		`
 		java_library {
@@ -117,6 +122,7 @@
 }
 
 func TestCollectJavaLibraryLinkingAgainstVersionedSdk(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		FixtureWithPrebuiltApis(map[string][]string{
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index ad8734d..3a20335 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",
@@ -234,6 +235,7 @@
 
 	for _, tt := range testCases {
 		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 				tt.preparer,
@@ -275,6 +277,7 @@
 }
 
 func TestKapt(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "foo",
@@ -303,6 +306,7 @@
 		}
 	`
 	t.Run("", func(t *testing.T) {
+		t.Parallel()
 		ctx, _ := testJava(t, bp)
 
 		buildOS := ctx.Config().BuildOS.String()
@@ -384,6 +388,7 @@
 	})
 
 	t.Run("errorprone", func(t *testing.T) {
+		t.Parallel()
 		env := map[string]string{
 			"RUN_ERROR_PRONE": "true",
 		}
@@ -434,6 +439,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 +490,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 +500,7 @@
 }
 
 func TestKotlinCompose(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -544,6 +552,7 @@
 }
 
 func TestKotlinPlugin(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
diff --git a/java/lint_test.go b/java/lint_test.go
index 617dc54..f7d3229 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",
@@ -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",
@@ -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",
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_test.go b/java/platform_bootclasspath_test.go
index 1f691a0..db85579 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -27,6 +27,7 @@
 )
 
 func TestPlatformBootclasspath(t *testing.T) {
+	t.Parallel()
 	preparer := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		FixtureConfigureBootJars("platform:foo", "system_ext:bar"),
@@ -81,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)
@@ -96,6 +98,7 @@
 		android.AssertArrayString(t, "srcjar inputs", expected, srcjar.Implicits.Strings())
 	}
 	t.Run("source", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			preparer,
 			addSourceBootclassPathModule,
@@ -112,6 +115,7 @@
 	})
 
 	t.Run("prebuilt", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			preparer,
 			addPrebuiltBootclassPathModule,
@@ -128,6 +132,7 @@
 	})
 
 	t.Run("source+prebuilt - source preferred", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			preparer,
 			addSourceBootclassPathModule,
@@ -145,6 +150,7 @@
 	})
 
 	t.Run("source+prebuilt - prebuilt preferred", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			preparer,
 			addSourceBootclassPathModule,
@@ -162,6 +168,7 @@
 	})
 
 	t.Run("dex import", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			preparer,
 			android.FixtureAddTextFile("deximport/Android.bp", `
@@ -184,6 +191,7 @@
 }
 
 func TestPlatformBootclasspathVariant(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		android.FixtureWithRootAndroidBp(`
@@ -198,6 +206,7 @@
 }
 
 func TestPlatformBootclasspath_ClasspathFragmentPaths(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		android.FixtureWithRootAndroidBp(`
@@ -213,6 +222,7 @@
 }
 
 func TestPlatformBootclasspathModule_AndroidMkEntries(t *testing.T) {
+	t.Parallel()
 	preparer := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		android.FixtureWithRootAndroidBp(`
@@ -223,6 +233,7 @@
 	)
 
 	t.Run("AndroidMkEntries", func(t *testing.T) {
+		t.Parallel()
 		result := preparer.RunTest(t)
 
 		p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
@@ -232,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)
@@ -243,6 +255,7 @@
 	})
 
 	t.Run("classpath-fragment-entry", func(t *testing.T) {
+		t.Parallel()
 		result := preparer.RunTest(t)
 
 		want := map[string][]string{
@@ -267,6 +280,7 @@
 }
 
 func TestPlatformBootclasspath_Dist(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		FixtureConfigureBootJars("platform:foo", "platform:bar"),
@@ -310,6 +324,7 @@
 }
 
 func TestPlatformBootclasspath_HiddenAPIMonolithicFiles(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		PrepareForTestWithJavaSdkLibraryFiles,
diff --git a/java/platform_compat_config_test.go b/java/platform_compat_config_test.go
index f7529a7..72f81e0 100644
--- a/java/platform_compat_config_test.go
+++ b/java/platform_compat_config_test.go
@@ -21,6 +21,7 @@
 )
 
 func TestPlatformCompatConfig(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithPlatformCompatConfig,
 		android.FixtureWithRootAndroidBp(`
diff --git a/java/plugin_test.go b/java/plugin_test.go
index dc29b1c..95f4aca 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",
@@ -43,6 +44,7 @@
 }
 
 func TestPlugin(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -82,6 +84,7 @@
 }
 
 func TestPluginGeneratesApi(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
diff --git a/java/prebuilt_apis_test.go b/java/prebuilt_apis_test.go
index b6fb2c6..c6a5913 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,
@@ -101,6 +103,7 @@
 }
 
 func TestPrebuiltApis_WithIncrementalApi(t *testing.T) {
+	t.Parallel()
 	runTestWithIncrementalApi := func() (foo_input, bar_input, baz_input string) {
 		result := android.GroupFixturePreparers(
 			prepareForJavaTest,
diff --git a/java/proto_test.go b/java/proto_test.go
index d1cb714..5b184b6 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",
diff --git a/java/ravenwood.go b/java/ravenwood.go
index 84d6a9f..3b6c80b 100644
--- a/java/ravenwood.go
+++ b/java/ravenwood.go
@@ -185,26 +185,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 +226,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 +258,15 @@
 
 	// 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"}
 }
 
 func (r *ravenwoodTest) AndroidMkEntries() []android.AndroidMkEntries {
@@ -345,7 +352,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})
diff --git a/java/ravenwood_test.go b/java/ravenwood_test.go
index f7fe8e3..ac4f147 100644
--- a/java/ravenwood_test.go
+++ b/java/ravenwood_test.go
@@ -103,6 +103,7 @@
 var installPathPrefix = "out/host/linux-x86/testcases"
 
 func TestRavenwoodRuntime(t *testing.T) {
+	t.Parallel()
 	if runtime.GOOS != "linux" {
 		t.Skip("requires linux")
 	}
@@ -133,6 +134,7 @@
 }
 
 func TestRavenwoodTest(t *testing.T) {
+	t.Parallel()
 	if runtime.GOOS != "linux" {
 		t.Skip("requires linux")
 	}
diff --git a/java/robolectric.go b/java/robolectric.go
index 6c74d08..43e17f9 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -29,6 +29,12 @@
 	RegisterRobolectricBuildComponents(android.InitRegistrationContext)
 }
 
+type RobolectricRuntimesInfo struct {
+	Runtimes []android.InstallPath
+}
+
+var RobolectricRuntimesInfoProvider = blueprint.NewProvider[RobolectricRuntimesInfo]()
+
 type roboRuntimeOnlyDependencyTag struct {
 	blueprint.BaseDependencyTag
 }
@@ -138,20 +144,25 @@
 	r.forceOSType = ctx.Config().BuildOS
 	r.forceArchType = ctx.Config().BuildArch
 
-	var options []tradefed.Option
-	options = append(options, tradefed.Option{Name: "java-flags", Value: "-Drobolectric=true"})
+	var extraTestRunnerOptions []tradefed.Option
+	extraTestRunnerOptions = append(extraTestRunnerOptions, tradefed.Option{Name: "java-flags", Value: "-Drobolectric=true"})
 	if proptools.BoolDefault(r.robolectricProperties.Strict_mode, true) {
-	    options = append(options, tradefed.Option{Name: "java-flags", Value: "-Drobolectric.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,
-		TestRunnerOptions:      options,
-		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)...)
@@ -159,25 +170,27 @@
 	r.data = append(r.data, android.PathsForModuleSrc(ctx, r.testProperties.Device_first_prefer32_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")
@@ -185,7 +198,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...)
@@ -193,19 +206,19 @@
 		}
 	}
 
-	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(roboRuntimeOnlyDepTag) {
+	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())
@@ -233,8 +246,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)
 	}
 
@@ -253,6 +266,19 @@
 		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")
+	}
 }
 
 func generateSameDirRoboTestConfigJar(ctx android.ModuleContext, outputFile android.ModuleOutPath) {
@@ -368,7 +394,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)})
@@ -386,6 +412,10 @@
 		installedRuntime := ctx.InstallFile(androidAllDir, runtimeName, runtimeFromSourceJar)
 		r.runtimes = append(r.runtimes, installedRuntime)
 	}
+
+	android.SetProvider(ctx, RobolectricRuntimesInfoProvider, RobolectricRuntimesInfo{
+		Runtimes: r.runtimes,
+	})
 }
 
 func (r *robolectricRuntimes) InstallInTestcases() bool { return true }
diff --git a/java/robolectric_test.go b/java/robolectric_test.go
index 78326ab..ad0613e 100644
--- a/java/robolectric_test.go
+++ b/java/robolectric_test.go
@@ -71,6 +71,7 @@
 )
 
 func TestRobolectricJniTest(t *testing.T) {
+	t.Parallel()
 	if runtime.GOOS != "linux" {
 		t.Skip("requires linux")
 	}
diff --git a/java/rro.go b/java/rro.go
index 44d5564..d9f4ff7 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -206,6 +206,8 @@
 	android.SetProvider(ctx, FlagsPackagesProvider, FlagsPackages{
 		AconfigTextFiles: aconfigTextFilePaths,
 	})
+
+	buildComplianceMetadata(ctx)
 }
 
 func (r *RuntimeResourceOverlay) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
diff --git a/java/rro_test.go b/java/rro_test.go
index b946511..1978ad6 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,
@@ -129,6 +130,7 @@
 }
 
 func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.FixtureModifyConfig(android.SetKatiEnabledForTests),
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 155bea4..05a5b49 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -480,6 +480,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 +693,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).UsesLibraryDependencyInfo
+		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 +709,8 @@
 			paths.stubsImplPath = lib.ImplementationJars
 		}
 
-		libDep := dep.(UsesLibraryDependency)
-		paths.stubsDexJarPath = libDep.DexJarBuildPath(ctx)
+		libDep := android.OtherModuleProviderOrDefault(ctx, dep, JavaInfoProvider).UsesLibraryDependencyInfo
+		paths.stubsDexJarPath = libDep.DexJarBuildPath
 		return nil
 	} else {
 		return fmt.Errorf("expected module that has JavaInfoProvider, e.g. java_library")
@@ -720,58 +723,67 @@
 			paths.stubsImplPath = lib.ImplementationJars
 		}
 
-		libDep := dep.(UsesLibraryDependency)
-		paths.exportableStubsDexJarPath = libDep.DexJarBuildPath(ctx)
+		libDep := android.OtherModuleProviderOrDefault(ctx, dep, JavaInfoProvider).UsesLibraryDependencyInfo
+		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 +793,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 +803,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 +821,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.
@@ -908,9 +950,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) {
@@ -1215,16 +1257,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
 }
@@ -1411,11 +1453,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.
@@ -1435,7 +1477,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
 			}
 		}
 	})
@@ -1446,39 +1489,39 @@
 		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.builtInstalledForApex = module.implLibraryInfo.BuiltInstalledForApex
+		module.dexpreopter.configPath = module.implLibraryInfo.ConfigPath
+		module.dexpreopter.outputProfilePathOnHost = module.implLibraryInfo.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)
 			}
@@ -1521,15 +1564,26 @@
 		}
 	}
 
-	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)
 	}
 
 	sdkLibInfo.GeneratingLibs = generatingLibs
 	android.SetProvider(ctx, SdkLibraryInfoProvider, sdkLibInfo)
 }
 
+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) BuiltInstalledForApex() []dexpreopterInstall {
 	return module.builtInstalledForApex
 }
@@ -1903,10 +1957,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
@@ -2095,7 +2145,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.
@@ -2107,17 +2157,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)
@@ -2157,9 +2201,9 @@
 	}
 
 	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)
 	}
 
 	sdkLibInfo.GeneratingLibs = generatingLibs
@@ -2178,10 +2222,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
 	}
 }
 
@@ -2197,10 +2241,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
 	}
 }
 
@@ -2213,19 +2257,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
 	}
 }
 
@@ -2390,8 +2434,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 ec9c160..db9cd24 100644
--- a/java/sdk_library_internal.go
+++ b/java/sdk_library_internal.go
@@ -174,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).
@@ -227,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
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 6031d72..0aed4b8 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,
@@ -187,6 +188,7 @@
 }
 
 func TestJavaSdkLibrary_UpdatableLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -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,
@@ -370,6 +377,7 @@
 }
 
 func TestJavaSdkLibrary_StubOrImplOnlyLibs(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -426,6 +434,7 @@
 }
 
 func TestJavaSdkLibrary_DoNotAccessImplWhenItIsNotBuilt(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -455,6 +464,7 @@
 }
 
 func TestJavaSdkLibrary_AccessOutputFiles(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -478,6 +488,7 @@
 }
 
 func TestJavaSdkLibrary_AccessOutputFiles_NoAnnotations(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -503,6 +514,7 @@
 }
 
 func TestJavaSdkLibrary_AccessOutputFiles_MissingScope(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -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,
@@ -788,6 +811,7 @@
 }
 
 func TestJavaSdkLibraryImport(t *testing.T) {
+	t.Parallel()
 	result := prepareForJavaTest.RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -844,6 +868,7 @@
 }
 
 func TestJavaSdkLibraryImport_WithSource(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -986,7 +1011,9 @@
 }
 
 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 +1021,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",
@@ -1113,6 +1141,7 @@
 }
 
 func TestJavaSdkLibraryDist(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaBuildComponents,
 		PrepareForTestWithJavaDefaultModules,
@@ -1179,6 +1208,7 @@
 
 	for _, tt := range testCases {
 		t.Run(tt.module, func(t *testing.T) {
+			t.Parallel()
 			m := result.ModuleForTests(apiScopePublic.exportableStubsLibraryModuleName(tt.module), "android_common").Module().(*Library)
 			dists := m.Dists()
 			if len(dists) != 1 {
@@ -1195,6 +1225,7 @@
 }
 
 func TestSdkLibrary_CheckMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	preparer := android.GroupFixturePreparers(
 		PrepareForTestWithJavaBuildComponents,
 		PrepareForTestWithJavaDefaultModules,
@@ -1279,6 +1310,7 @@
 }
 
 func TestJavaSdkLibrary_StubOnlyLibs_PassedToDroidstubs(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1306,6 +1338,7 @@
 }
 
 func TestJavaSdkLibrary_Scope_Libs_PassedToDroidstubs(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1333,6 +1366,7 @@
 }
 
 func TestJavaSdkLibrary_ApiLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1383,6 +1417,7 @@
 }
 
 func TestStaticDepStubLibrariesVisibility(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1412,6 +1447,7 @@
 }
 
 func TestSdkLibraryDependency(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1444,6 +1480,7 @@
 }
 
 func TestSdkLibraryExportableStubsLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1494,6 +1531,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",
@@ -1547,6 +1585,7 @@
 
 // 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",
@@ -1632,6 +1671,7 @@
 }
 
 func TestStubLinkType(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1668,6 +1708,7 @@
 }
 
 func TestSdkLibDirectDependency(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1732,6 +1773,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..e926307 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,17 +389,18 @@
 		},
 	}
 
-	t.Parallel()
 	t.Run("basic", func(t *testing.T) {
 		t.Parallel()
 		testClasspathTestCases(t, classpathTestcases, false, false)
 	})
 
 	t.Run("Always_use_prebuilt_sdks=true", func(t *testing.T) {
+		t.Parallel()
 		testClasspathTestCases(t, classpathTestcases, true, false)
 	})
 
 	t.Run("UseTransitiveJarsInClasspath", func(t *testing.T) {
+		t.Parallel()
 		testClasspathTestCases(t, classpathTestcases, false, true)
 	})
 }
@@ -571,6 +573,7 @@
 
 			// 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 */)
@@ -584,6 +587,7 @@
 
 			// 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 */)
@@ -602,6 +606,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 +615,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..6f0370a 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) {
diff --git a/java/system_modules_test.go b/java/system_modules_test.go
index b05b0e4..b7a99b5 100644
--- a/java/system_modules_test.go
+++ b/java/system_modules_test.go
@@ -51,6 +51,7 @@
 `)
 
 func TestJavaSystemModules(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForJavaTest, addSourceSystemModules).RunTest(t)
 
 	// check the existence of the source module
@@ -78,6 +79,7 @@
 `)
 
 func TestJavaSystemModulesImport(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForJavaTest, addPrebuiltSystemModules).RunTest(t)
 
 	// check the existence of the renamed prebuilt module
@@ -90,6 +92,7 @@
 }
 
 func TestJavaSystemModulesMixSourceAndPrebuilt(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		addSourceSystemModules,
@@ -114,6 +117,7 @@
 }
 
 func TestMultipleSystemModulesPrebuilts(t *testing.T) {
+	t.Parallel()
 	bp := `
 		// an rdep
 		java_library {
diff --git a/java/systemserver_classpath_fragment_test.go b/java/systemserver_classpath_fragment_test.go
index 2a1728b..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(`
@@ -54,6 +56,7 @@
 }
 
 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/python/binary.go b/python/binary.go
index 5f60761..a3acb34 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -22,8 +22,14 @@
 	"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,6 +109,9 @@
 	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}, "")
 }
 
@@ -116,13 +125,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
 			}
 		})
 	}
@@ -140,7 +149,7 @@
 	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
diff --git a/python/python.go b/python/python.go
index 914b77e..09af62e 100644
--- a/python/python.go
+++ b/python/python.go
@@ -22,12 +22,23 @@
 	"regexp"
 	"strings"
 
+	"android/soong/cc"
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
 )
 
+type PythonLibraryInfo struct {
+	SrcsPathMappings   []pathMapping
+	DataPathMappings   []pathMapping
+	SrcsZip            android.Path
+	PrecompiledSrcsZip android.Path
+	PkgPath            string
+}
+
+var PythonLibraryInfoProvider = blueprint.NewProvider[PythonLibraryInfo]()
+
 func init() {
 	registerPythonMutators(android.InitRegistrationContext)
 }
@@ -173,16 +184,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
@@ -212,8 +213,6 @@
 	return &p.properties
 }
 
-var _ pythonDependency = (*PythonLibraryModule)(nil)
-
 func (p *PythonLibraryModule) init() android.Module {
 	p.AddProperties(&p.properties, &p.protoProperties, &p.sourceProperties)
 	android.InitAndroidArchModule(p, p.hod, p.multilib)
@@ -464,7 +463,7 @@
 	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, "")...)
 	}
 
@@ -492,6 +491,14 @@
 	// 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(),
+	})
 }
 
 func isValidPythonPath(path string) error {
@@ -657,16 +664,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()
 			}
@@ -674,9 +681,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()))
@@ -707,16 +714,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.
@@ -737,7 +734,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
@@ -747,27 +744,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
diff --git a/python/test.go b/python/test.go
index 37947dd..5e70fc1 100644
--- a/python/test.go
+++ b/python/test.go
@@ -199,13 +199,13 @@
 	}
 
 	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})
 		}
@@ -214,6 +214,46 @@
 	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)
+			}
+		}
+		// 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/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/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/benchmark.go b/rust/benchmark.go
index eaa2176..daba964 100644
--- a/rust/benchmark.go
+++ b/rust/benchmark.go
@@ -130,3 +130,20 @@
 
 	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")
+	}
+}
diff --git a/rust/binary.go b/rust/binary.go
index d22041b..3c7a482 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -183,3 +183,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/bindgen.go b/rust/bindgen.go
index 898e792..8accd03 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"
diff --git a/rust/compiler.go b/rust/compiler.go
index 1d2fb58..f186ef3 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -78,6 +78,8 @@
 	checkedCrateRootPath() (android.Path, error)
 
 	Aliases() map[string]string
+
+	moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON)
 }
 
 func (compiler *baseCompiler) edition() string {
@@ -327,6 +329,31 @@
 	}
 }
 
+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 {
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..ae95e46 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -15,6 +15,7 @@
 package rust
 
 import (
+	"android/soong/android"
 	"github.com/google/blueprint"
 
 	"android/soong/cc"
@@ -65,16 +66,18 @@
 		flags.RustFlags = append(flags.RustFlags,
 			"-C instrument-coverage", "-g")
 		if ctx.Device() {
-			coverage := ctx.GetDirectDepWithTag(CovLibraryName, cc.CoverageDepTag).(cc.LinkableInterface)
+			m := ctx.GetDirectDepProxyWithTag(CovLibraryName, 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/library.go b/rust/library.go
index 49169ac..3686bf9 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -202,6 +202,9 @@
 }
 
 func (library *libraryDecorator) nativeCoverage() bool {
+	if library.BuildStubs() {
+		return false
+	}
 	return true
 }
 
@@ -708,7 +711,7 @@
 	if library.stubs() {
 		ccFlags := library.getApiStubsCcFlags(ctx)
 		stubObjs := library.compileModuleLibApiStubs(ctx, ccFlags)
-		cc.BuildRustStubs(ctx, outputFile, deps.CrtBegin, deps.CrtEnd, stubObjs, ccFlags)
+		cc.BuildRustStubs(ctx, outputFile, stubObjs, ccFlags)
 	} else if library.rlib() {
 		ret.kytheFile = TransformSrctoRlib(ctx, crateRootPath, deps, flags, outputFile).kytheFile
 	} else if library.dylib() {
@@ -858,6 +861,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) {
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index 28ed68b..837e1a6 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -100,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/rust.go b/rust/rust.go
index 6428859..5cc8c07 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -49,6 +49,7 @@
 type ProtobufDecoratorInfo struct{}
 
 type SourceProviderInfo struct {
+	Srcs                  android.Paths
 	ProtobufDecoratorInfo *ProtobufDecoratorInfo
 }
 
@@ -1065,9 +1066,9 @@
 			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()...)
 	}
@@ -1155,10 +1156,11 @@
 		}
 	}
 	if mod.sourceProvider != nil {
+		rustInfo.SourceProviderInfo = &SourceProviderInfo{
+			Srcs: mod.sourceProvider.Srcs(),
+		}
 		if _, ok := mod.sourceProvider.(*protobufDecorator); ok {
-			rustInfo.SourceProviderInfo = &SourceProviderInfo{
-				ProtobufDecoratorInfo: &ProtobufDecoratorInfo{},
-			}
+			rustInfo.SourceProviderInfo.ProtobufDecoratorInfo = &ProtobufDecoratorInfo{}
 		}
 	}
 	android.SetProvider(ctx, RustInfoProvider, rustInfo)
@@ -1181,6 +1183,11 @@
 	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) {
@@ -1203,12 +1210,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())
 	}
@@ -1226,7 +1233,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())
@@ -1767,6 +1774,7 @@
 	depPaths.depClangFlags = android.FirstUniqueStrings(depPaths.depClangFlags)
 	depPaths.depIncludePaths = android.FirstUniquePaths(depPaths.depIncludePaths)
 	depPaths.depSystemIncludePaths = android.FirstUniquePaths(depPaths.depSystemIncludePaths)
+	depPaths.depLinkFlags = android.FirstUniqueStrings(depPaths.depLinkFlags)
 
 	return depPaths
 }
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 dce5e03..b658ae2 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -148,35 +148,36 @@
 	dataSrcPaths := android.PathsForModuleSrc(ctx, test.Properties.Data)
 	dataSrcPaths = append(dataSrcPaths, android.PathsForModuleSrc(ctx, test.Properties.Device_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.OtherModuleProviderOrDefault(ctx, dep, android.CommonModuleInfoKey)
 			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 +195,27 @@
 	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)
+			}
+		}
+		// 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,6 +224,7 @@
 	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")
@@ -257,6 +280,32 @@
 	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")
+	}
+}
+
 func rustTestHostMultilib(ctx android.LoadHookContext) {
 	type props struct {
 		Target struct {
diff --git a/scripts/Android.bp b/scripts/Android.bp
index d39c84a..94163a5 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -293,14 +293,6 @@
 }
 
 python_binary_host {
-    name: "merge_directories",
-    main: "merge_directories.py",
-    srcs: [
-        "merge_directories.py",
-    ],
-}
-
-python_binary_host {
     name: "merge_json",
     main: "merge_json.py",
     srcs: [
diff --git a/scripts/gen_build_prop.py b/scripts/gen_build_prop.py
index 430e613..355f33d 100644
--- a/scripts/gen_build_prop.py
+++ b/scripts/gen_build_prop.py
@@ -308,7 +308,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
   #
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/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/sdk/testing.go b/sdk/testing.go
index f4e2b03..21d457c 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -144,7 +144,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()
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 60c5317..d753d24 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -423,7 +423,7 @@
 	expandedData = append(expandedData, android.PathsForModuleSrc(ctx, s.testProperties.Device_common_data)...)
 	expandedData = append(expandedData, android.PathsForModuleSrc(ctx, s.testProperties.Device_first_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 {
@@ -476,21 +476,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.OtherModuleProviderOrDefault(ctx, dep, android.CommonModuleInfoKey)
+
+				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
@@ -498,7 +501,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)
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/tradefed/autogen.go b/tradefed/autogen.go
index e230795..8dd7381 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -190,7 +190,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("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("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/test_suite.go b/tradefed_modules/test_suite.go
index 00585f5..8b7babf 100644
--- a/tradefed_modules/test_suite.go
+++ b/tradefed_modules/test_suite.go
@@ -26,12 +26,12 @@
 
 const testSuiteModuleType = "test_suite"
 
-type testSuiteTag struct{
+type testSuiteTag struct {
 	blueprint.BaseDependencyTag
 }
 
 type testSuiteManifest struct {
-	Name  string `json:"name"`
+	Name  string   `json:"name"`
 	Files []string `json:"files"`
 }
 
@@ -49,7 +49,7 @@
 
 type testSuiteProperties struct {
 	Description string
-	Tests []string `android:"path,arch_variant"`
+	Tests       []string `android:"path,arch_variant"`
 }
 
 type testSuiteModule struct {
@@ -109,7 +109,7 @@
 	}
 
 	manifestPath := android.PathForSuiteInstall(ctx, suiteName, suiteName+".json")
-	b, err := json.Marshal(testSuiteManifest{Name: suiteName, Files: files})
+	b, err := json.Marshal(testSuiteManifest{Name: suiteName, Files: android.SortedUniqueStrings(files)})
 	if err != nil {
 		ctx.ModuleErrorf("Failed to marshal manifest: %v", err)
 		return
@@ -160,7 +160,7 @@
 	// 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))
+		installed = append(installed, ctx.InstallFile(moduleRoot, module.Name()+".config", tp.TestConfig))
 	}
 
 	// Add to phony and manifest, manifestpaths are relative to suiteRoot.
diff --git a/ui/build/build.go b/ui/build/build.go
index 26f5969..ea86782 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)
 		}
@@ -100,6 +87,21 @@
 	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(`
 builddir = {{.OutDir}}
 {{if .UseRemoteBuild }}pool local_pool
@@ -329,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")
diff --git a/ui/build/config.go b/ui/build/config.go
index 4f2d213..2a00c41 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 {
@@ -323,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
@@ -389,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() {
@@ -615,6 +639,7 @@
 		UseRbe:                proto.Bool(config.UseRBE()),
 		NinjaWeightListSource: getNinjaWeightListSourceInMetric(config.NinjaWeightListSource()),
 		SoongEnvVars:          soongEnvVars,
+		SoongOnly:             proto.Bool(config.soongOnlyRequested),
 	}
 	c.Targets = append(c.Targets, config.arguments...)
 
@@ -843,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
@@ -1070,7 +1101,7 @@
 
 func (c *configImpl) NinjaArgs() []string {
 	if c.skipKati {
-		return c.arguments
+		return append(c.arguments, c.ninjaArgs...)
 	}
 	return c.ninjaArgs
 }
@@ -1339,6 +1370,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" {
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/dumpvars.go b/ui/build/dumpvars.go
index b592f11..16a3db8 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -172,7 +172,7 @@
 	"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE",
 }
 
-func Banner(make_vars map[string]string) string {
+func Banner(config Config, make_vars map[string]string) string {
 	b := &bytes.Buffer{}
 
 	fmt.Fprintln(b, "============================================")
@@ -181,6 +181,7 @@
 			fmt.Fprintf(b, "%s=%s\n", name, make_vars[name])
 		}
 	}
+	fmt.Fprintf(b, "SOONG_ONLY=%t\n", config.soongOnlyRequested)
 	fmt.Fprint(b, "============================================")
 
 	return b.String()
@@ -246,6 +247,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",
@@ -287,13 +290,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)
@@ -314,4 +312,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/ninja.go b/ui/build/ninja.go
index b0c9c07..1d4285f 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -76,7 +76,7 @@
 			//"--frontend-file", fifo,
 		}
 	default:
-		// NINJA_NINJA is the default.
+		// NINJA_NINJA or NINJA_NINJAGO.
 		executable = config.NinjaBin()
 		args = []string{
 			"-d", "keepdepfile",
@@ -351,12 +351,18 @@
 }
 
 // Constructs and runs the Ninja command line to get the inputs of a goal.
-// For now, this will always run ninja, because ninjago, n2 and siso don't have the
+// 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) {
-	executable := config.PrebuiltBuildTool("ninja")
+	var executable string
+	switch config.ninjaCommand {
+	case NINJA_N2, NINJA_SISO:
+		executable = config.PrebuiltBuildTool("ninja")
+	default:
+		executable = config.NinjaBin()
+	}
 
 	args := []string{
 		"-f",
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index 6c9a1eb..110ddee 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,
@@ -122,6 +122,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/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index f9c6e01..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
 
@@ -750,6 +750,9 @@
 	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.
@@ -852,6 +855,13 @@
 	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
@@ -2664,7 +2674,7 @@
 	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, 0xd3,
+	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,
@@ -2694,291 +2704,293 @@
 	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, 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, 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,
+	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, 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, 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, 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,
+	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, 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, 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,
+	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, 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, 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,
+	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, 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 (
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 415b0fb..7ff389a 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -183,6 +183,10 @@
 
   // 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 {
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",