Merge "cc/rust: Prevent duplicate obj files in staticlibs" into main
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index 3484b97..6e2964a 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -34,7 +34,7 @@
 // but other verticals/platforms can override via soong config setting.
 all_aconfig_declarations {
     name: "all_aconfig_declarations",
-    api_files: select(soong_config_variable("android_aconfig", "opt_platform_api_srcs"), {
+    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",
@@ -42,4 +42,5 @@
             ":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 9086c93..ec20099 100644
--- a/aconfig/all_aconfig_declarations.go
+++ b/aconfig/all_aconfig_declarations.go
@@ -43,7 +43,8 @@
 }
 
 type allAconfigReleaseDeclarationsProperties struct {
-	Api_files proptools.Configurable[[]string] `android:"arch_variant,path"`
+	Api_signature_files  proptools.Configurable[[]string] `android:"arch_variant,path"`
+	Finalized_flags_file string                           `android:"arch_variant,path"`
 }
 
 type allAconfigDeclarationsSingleton struct {
@@ -63,23 +64,25 @@
 }
 
 func (this *allAconfigDeclarationsSingleton) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	apiFiles := android.Paths{}
-	for _, apiFile := range this.properties.Api_files.GetOrDefault(ctx, nil) {
-		if path := android.PathForModuleSrc(ctx, apiFile); path != nil {
-			apiFiles = append(apiFiles, path)
+	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)
 		}
 	}
-	flagFile := android.PathForIntermediates(ctx, "all_aconfig_declarations.pb")
+	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(apiFiles, flagFile),
+		Inputs: append(apiSignatureFiles, finalizedFlagsFile, parsedFlagsFile),
 		Output: output,
 		Args: map[string]string{
-			"api_files": android.JoinPathsWithPrefix(apiFiles, "--api-file "),
-			"flag_file": "--flag-file " + flagFile.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)
diff --git a/aconfig/init.go b/aconfig/init.go
index 3d7b20d..3dcec5c 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -72,11 +72,11 @@
 		}, "cache_files")
 	RecordFinalizedFlagsRule = pctx.AndroidStaticRule("RecordFinalizedFlagsRule",
 		blueprint.RuleParams{
-			Command: `${record-finalized-flags} ${flag_file} ${api_files} > ${out}`,
+			Command: `${record-finalized-flags} ${parsed_flags_file} ${finalized_flags_file} ${api_signature_files} > ${out}`,
 			CommandDeps: []string{
 				"${record-finalized-flags}",
 			},
-		}, "api_files", "flag_file")
+		}, "api_signature_files", "finalized_flags_file", "parsed_flags_file")
 
 	CreateStorageRule = pctx.AndroidStaticRule("aconfig_create_storage",
 		blueprint.RuleParams{
diff --git a/android/androidmk.go b/android/androidmk.go
index 6f094e5..f862a96 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -838,7 +838,7 @@
 				continue
 			}
 			if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
-				moduleInfoJSONs = append(moduleInfoJSONs, moduleInfoJSON)
+				moduleInfoJSONs = append(moduleInfoJSONs, moduleInfoJSON...)
 			}
 			if contribution := info.PrimaryInfo.getDistContributions(ctx, mod); contribution != nil {
 				allDistContributions = append(allDistContributions, *contribution)
@@ -866,7 +866,7 @@
 					continue
 				}
 				if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
-					moduleInfoJSONs = append(moduleInfoJSONs, moduleInfoJSON)
+					moduleInfoJSONs = append(moduleInfoJSONs, moduleInfoJSON...)
 				}
 				if contribution := data.Entries.getDistContributions(mod); contribution != nil {
 					allDistContributions = append(allDistContributions, *contribution)
@@ -879,7 +879,7 @@
 						continue
 					}
 					if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
-						moduleInfoJSONs = append(moduleInfoJSONs, moduleInfoJSON)
+						moduleInfoJSONs = append(moduleInfoJSONs, moduleInfoJSON...)
 					}
 					if contribution := entries.getDistContributions(mod); contribution != nil {
 						allDistContributions = append(allDistContributions, *contribution)
@@ -1077,7 +1077,7 @@
 
 	if !data.Entries.disabled() {
 		if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
-			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
+			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON...)
 		}
 	}
 
@@ -1111,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)
+				}
+			}
 		}
 	}
 
@@ -1286,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/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/license.go b/android/license.go
index ffda58b..7b4aeeb 100644
--- a/android/license.go
+++ b/android/license.go
@@ -19,7 +19,6 @@
 )
 
 type LicenseInfo struct {
-	EffectiveLicenses          []string
 	PackageName                *string
 	EffectiveLicenseText       NamedPaths
 	EffectiveLicenseKinds      []string
@@ -79,23 +78,23 @@
 
 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)...)
+	var conditions []string
+	var kinds []string
 	for _, module := range ctx.GetDirectDepsProxyWithTag(licenseKindTag) {
 		if lk, ok := OtherModuleProvider(ctx, module, LicenseKindInfoProvider); ok {
-			mergeStringProps(&m.base().commonProperties.Effective_license_conditions, lk.Conditions...)
-			mergeStringProps(&m.base().commonProperties.Effective_license_kinds, ctx.OtherModuleName(module))
+			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{
-		EffectiveLicenses:          m.base().commonProperties.Effective_licenses,
 		PackageName:                m.properties.Package_name,
 		EffectiveLicenseText:       m.base().commonProperties.Effective_license_text,
-		EffectiveLicenseKinds:      m.base().commonProperties.Effective_license_kinds,
-		EffectiveLicenseConditions: m.base().commonProperties.Effective_license_conditions,
+		EffectiveLicenseKinds:      SortedUniqueStrings(kinds),
+		EffectiveLicenseConditions: SortedUniqueStrings(conditions),
 	})
 }
 
diff --git a/android/licenses.go b/android/licenses.go
index 77f563f..32d12c8 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -227,16 +227,18 @@
 	}
 
 	var licenses []string
+	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.PackageName != nil {
 				m.base().commonProperties.Effective_package_name = l.PackageName
 			}
-			mergeStringProps(&m.base().commonProperties.Effective_licenses, l.EffectiveLicenses...)
-			mergeNamedPathProps(&m.base().commonProperties.Effective_license_text, l.EffectiveLicenseText...)
-			mergeStringProps(&m.base().commonProperties.Effective_license_kinds, l.EffectiveLicenseKinds...)
-			mergeStringProps(&m.base().commonProperties.Effective_license_conditions, l.EffectiveLicenseConditions...)
+			texts = append(texts, l.EffectiveLicenseText...)
+			kinds = append(kinds, l.EffectiveLicenseKinds...)
+			conditions = append(conditions, l.EffectiveLicenseConditions...)
 		} else {
 			propertyName := "licenses"
 			primaryProperty := m.base().primaryLicensesProperty
@@ -247,6 +249,10 @@
 		}
 	}
 
+	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 := LicensesInfo{
 		Licenses: licenses,
@@ -254,12 +260,6 @@
 	SetProvider(ctx, LicensesInfoProvider, 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)
-}
-
 // Update a property NamedPath array with a distinct union of its values and a list of new values.
 func namePathProps(prop *NamedPaths, name *string, values ...Path) {
 	if name == nil {
@@ -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) {
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 da75416..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
@@ -1459,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 {
@@ -1619,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 {
@@ -1719,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)
@@ -2115,63 +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,
 			}
 		}
 
-		// 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 := ctx.moduleInfoJSON.CompatibilitySuites
-		if PrefixInList(suites, "mts-") && !InList("mts", suites) {
-			suites = append(suites, "mts")
-		}
-		if PrefixInList(suites, "mcts-") && !InList("mcts", suites) {
-			suites = append(suites, "mcts")
-		}
-		ctx.moduleInfoJSON.CompatibilitySuites = suites
-
-		required := append(m.RequiredModuleNames(ctx), m.VintfFragmentModuleNames(ctx)...)
-		required = append(required, ctx.moduleInfoJSON.ExtraRequired...)
-
-		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:           required,
-		}
 		SetProvider(ctx, ModuleInfoJSONProvider, ctx.moduleInfoJSON)
 	}
 
diff --git a/android/module_context.go b/android/module_context.go
index fd804d0..1f4758c 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.
@@ -589,8 +594,8 @@
 	return m.packageFile(fullInstallPath, srcPath, false)
 }
 
-func (m *moduleContext) getAconfigPaths() *Paths {
-	return &m.aconfigFilePaths
+func (m *moduleContext) getAconfigPaths() Paths {
+	return m.aconfigFilePaths
 }
 
 func (m *moduleContext) setAconfigPaths(paths Paths) {
@@ -618,12 +623,12 @@
 		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,
 	}
 	m.packagingSpecs = append(m.packagingSpecs, spec)
@@ -756,9 +761,9 @@
 		executable:       false,
 		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,
 	})
 
@@ -804,9 +809,9 @@
 		executable:       false,
 		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,
 	})
 
@@ -845,11 +850,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 27120ef..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))
@@ -44,6 +44,11 @@
 	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 {
@@ -128,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/neverallow.go b/android/neverallow.go
index cf0b297..7615ca8 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -301,10 +301,8 @@
 			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").
+			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").
@@ -312,10 +310,8 @@
 			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").
+			Without("name", "trusty-widevine_vm-arm64.elf.gen").
+			Without("name", "trusty-widevine_vm-x86.elf.gen").
 			With("keep_gendir", "true").Because(reason),
 	}
 }
diff --git a/android/packaging.go b/android/packaging.go
index d96cccd..738f215 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,13 +53,13 @@
 	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
@@ -69,12 +70,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 +85,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 +100,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 +155,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 +172,7 @@
 
 // Paths of aconfig files for the built artifact
 func (p *PackagingSpec) GetAconfigPaths() Paths {
-	return *p.aconfigPaths
+	return p.aconfigPaths.ToSlice()
 }
 
 type PackageModule interface {
@@ -507,9 +505,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/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/cc/builder.go b/cc/builder.go
index 3a435cd..c94a674 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -862,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{}
@@ -872,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/test.go b/cc/test.go
index 32b1551..dad4afa 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -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/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
index ebcc68b..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,59 +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)
-		}
-	}
-	finalSoongGeneratedPartitionNames := make([]string, 0, len(finalSoongGeneratedPartitions))
-	for _, partitionType := range finalSoongGeneratedPartitions {
-		finalSoongGeneratedPartitionNames = append(finalSoongGeneratedPartitionNames, generatedModuleNameForPartition(ctx.Config(), partitionType))
+	partitions := generatedPartitions(ctx)
+	for i := range partitions {
+		f.createPartition(ctx, partitions, &partitions[i])
 	}
 	// Create android_info.prop
 	f.createAndroidInfo(ctx)
@@ -162,13 +251,13 @@
 
 	var systemOtherImageName string
 	if buildingSystemOtherImage(partitionVars) {
-		systemModule := generatedModuleNameForPartition(ctx.Config(), "system")
+		systemModule := partitions.nameForType("system")
 		systemOtherImageName = generatedModuleNameForPartition(ctx.Config(), "system_other")
 		ctx.CreateModule(
 			filesystem.SystemOtherImageFactory,
 			&filesystem.SystemOtherImageProperties{
 				System_image:                    &systemModule,
-				Preinstall_dexpreopt_files_from: finalSoongGeneratedPartitionNames,
+				Preinstall_dexpreopt_files_from: partitions.moduleNames(),
 			},
 			&struct {
 				Name *string
@@ -178,19 +267,19 @@
 		)
 	}
 
-	for _, x := range f.createVbmetaPartitions(ctx, finalSoongGeneratedPartitions) {
+	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, systemOtherImageName)
+		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 {
@@ -233,7 +322,7 @@
 
 func (f *filesystemCreator) createDeviceModule(
 	ctx android.LoadHookContext,
-	generatedPartitionTypes []string,
+	partitions allGeneratedPartitionData,
 	vbmetaPartitions []string,
 	superImageSubPartitions []string,
 ) {
@@ -250,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"))
@@ -302,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)
@@ -354,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":
@@ -371,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")
@@ -389,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")
@@ -481,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")
 	}
@@ -496,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" {
@@ -529,7 +625,6 @@
 	if partitionType == "vendor" {
 		f.createVendorBuildProp(ctx)
 	}
-	return true
 }
 
 // Creates filegroups for the files specified in BOARD_(partition_)AVB_KEY_PATH
@@ -731,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"))
 	}
 
@@ -821,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
@@ -889,7 +984,7 @@
 
 	}
 
-	partitionSpecificFsProps(ctx, fsProps, partitionVars, partitionType)
+	partitionSpecificFsProps(ctx, partitions, fsProps, partitionType)
 
 	return fsProps, true
 }
@@ -976,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))
@@ -1042,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)
@@ -1056,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")
 	}
@@ -1067,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)
@@ -1118,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 e353688..569f780 100644
--- a/fsgen/super_img.go
+++ b/fsgen/super_img.go
@@ -29,7 +29,7 @@
 
 func createSuperImage(
 	ctx android.LoadHookContext,
-	partitions []string,
+	partitions allGeneratedPartitionData,
 	partitionVars android.PartitionVariables,
 	systemOtherImageName string,
 ) []string {
@@ -90,40 +90,40 @@
 
 	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 be738ea..93425ae 100644
--- a/fsgen/vbmeta_partitions.go
+++ b/fsgen/vbmeta_partitions.go
@@ -17,7 +17,6 @@
 import (
 	"android/soong/android"
 	"android/soong/filesystem"
-	"slices"
 	"strconv"
 
 	"github.com/google/blueprint/proptools"
@@ -63,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 (f *filesystemCreator) 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 {
@@ -114,11 +113,9 @@
 
 		var partitionModules []string
 		for _, partition := range props.Partitions {
-			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 := generatedModuleNameForPartition(ctx.Config(), chainedName)
@@ -218,7 +215,7 @@
 
 	var chainedPartitionModules []string
 	var includePartitionModules []string
-	allGeneratedPartitionTypes := append(generatedPartitionTypes,
+	allGeneratedPartitionTypes := append(partitions.types(),
 		chainedPartitionTypes...,
 	)
 	if len(f.properties.Boot_image) > 0 {
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/app.go b/java/app.go
index 17c7b22..da7eb02 100644
--- a/java/app.go
+++ b/java/app.go
@@ -1616,6 +1616,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(),
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/dexpreopt.go b/java/dexpreopt.go
index 24e5f50..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
 }
@@ -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/droidstubs.go b/java/droidstubs.go
index e0c2e63..22f4d98 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -28,18 +28,28 @@
 	"android/soong/remoteexec"
 )
 
-type StubsArtifactsInfo struct {
-	ApiVersionsXml android.WritablePath
+type StubsInfo struct {
+	ApiVersionsXml android.Path
+	AnnotationsZip android.Path
+	ApiFile        android.Path
+	RemovedApiFile android.Path
 }
 
 type DroidStubsInfo struct {
 	CurrentApiTimestamp android.Path
-	EverythingArtifacts StubsArtifactsInfo
-	ExportableArtifacts StubsArtifactsInfo
+	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"}
 
@@ -541,9 +551,9 @@
 		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())
 				}
@@ -710,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())
 
@@ -756,6 +767,8 @@
 
 	addMetalavaConfigFilesToCmd(cmd, configFiles)
 
+	addOptionalApiSurfaceToCmd(cmd, apiSurface)
+
 	return cmd
 }
 
@@ -774,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
@@ -848,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)
@@ -1187,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)
 
@@ -1230,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 {
@@ -1279,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")
@@ -1315,52 +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")
-	}
-
-	android.SetProvider(ctx, DroidStubsInfoProvider, DroidStubsInfo{
+	droidInfo := DroidStubsInfo{
 		CurrentApiTimestamp: d.CurrentApiTimestamp(),
-		EverythingArtifacts: StubsArtifactsInfo{
-			ApiVersionsXml: d.everythingArtifacts.apiVersionsXml,
-		},
-		ExportableArtifacts: StubsArtifactsInfo{
-			ApiVersionsXml: d.exportableArtifacts.apiVersionsXml,
-		},
+		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
@@ -1528,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/java.go b/java/java.go
index fafe9f9..8d10e3b 100644
--- a/java/java.go
+++ b/java/java.go
@@ -363,6 +363,8 @@
 	// 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
@@ -374,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]()
@@ -1065,10 +1123,56 @@
 
 	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)
+}
+
+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 (j *Library) getJarInstallDir(ctx android.ModuleContext) android.InstallPath {
@@ -1627,6 +1731,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) {
@@ -1704,10 +1813,54 @@
 	})
 
 	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...)
 }
 
 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) {
@@ -2186,7 +2339,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())
 
@@ -2227,6 +2380,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
@@ -2310,6 +2465,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 == "" {
@@ -2420,7 +2586,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)
 
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/ravenwood.go b/java/ravenwood.go
index 8c8d8e9..3b6c80b 100644
--- a/java/ravenwood.go
+++ b/java/ravenwood.go
@@ -258,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 {
diff --git a/java/robolectric.go b/java/robolectric.go
index ff0c850..ed3fc9a 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -261,6 +261,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) {
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/python/test.go b/python/test.go
index c780a6f..5e70fc1 100644
--- a/python/test.go
+++ b/python/test.go
@@ -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/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/library.go b/rust/library.go
index 49169ac..77280d9 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -708,7 +708,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() {
diff --git a/rust/test.go b/rust/test.go
index 4fd1da0..5e42c3f 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -203,6 +203,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")
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_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/config.go b/ui/build/config.go
index 84e4005..9fbbc48 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -252,6 +252,18 @@
 	}
 	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.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 {
@@ -393,6 +405,9 @@
 		// Use config.ninjaCommand instead.
 		"SOONG_NINJA",
 		"SOONG_USE_N2",
+
+		// Already incorporated into the config object
+		"SOONG_ONLY",
 	)
 
 	if ret.UseGoma() || ret.ForceUseGoma() {