Merge "Add support for prebuilts in /usr/odml." into main
diff --git a/android/Android.bp b/android/Android.bp
index aef18fe..1cc7ffe 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -112,6 +112,7 @@
         "soong_config_modules.go",
         "team.go",
         "test_asserts.go",
+        "test_mapping_zip.go",
         "test_suites.go",
         "testing.go",
         "transition.go",
diff --git a/android/aconfig_providers.go b/android/aconfig_providers.go
index b698d24..205b855 100644
--- a/android/aconfig_providers.go
+++ b/android/aconfig_providers.go
@@ -136,7 +136,7 @@
 			AconfigFiles: mergedAconfigFiles,
 			ModeInfos:    mergedModeInfos,
 		})
-		ctx.setAconfigPaths(getAconfigFilePaths(ctx.Module().base(), mergedAconfigFiles))
+		ctx.setAconfigPaths(getAconfigFilePaths(getContainer(ctx.Module()), mergedAconfigFiles))
 	}
 }
 
@@ -147,7 +147,8 @@
 		return
 	}
 	data.Extra = append(data.Extra, func(w io.Writer, outputFile Path) {
-		AndroidMkEmitAssignList(w, "LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles).Strings())
+		AndroidMkEmitAssignList(w, "LOCAL_ACONFIG_FILES", getAconfigFilePaths(
+			getContainerUsingProviders(ctx, mod), info.AconfigFiles).Strings())
 	})
 	// If there is a Custom writer, it needs to support this provider.
 	if data.Custom != nil {
@@ -179,24 +180,29 @@
 	// All of the files in the module potentially depend on the aconfig flag values.
 	for idx, _ := range *entries {
 		(*entries)[idx].ExtraEntries = append((*entries)[idx].ExtraEntries,
-			func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
-				entries.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles))
+			func(_ AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
+				entries.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(
+					getContainerUsingProviders(ctx, mod), info.AconfigFiles))
 			},
 		)
 
 	}
 }
 
+// TODO(b/397766191): Change the signature to take ModuleProxy
+// Please only access the module's internal data through providers.
 func aconfigUpdateAndroidMkInfos(ctx fillInEntriesContext, mod Module, infos *AndroidMkProviderInfo) {
 	info, ok := OtherModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
 	if !ok || len(info.AconfigFiles) == 0 {
 		return
 	}
 	// All of the files in the module potentially depend on the aconfig flag values.
-	infos.PrimaryInfo.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles))
+	infos.PrimaryInfo.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(
+		getContainerUsingProviders(ctx, mod), info.AconfigFiles))
 	if len(infos.ExtraInfo) > 0 {
 		for _, ei := range (*infos).ExtraInfo {
-			ei.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles))
+			ei.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(
+				getContainerUsingProviders(ctx, mod), info.AconfigFiles))
 		}
 	}
 }
@@ -224,19 +230,39 @@
 	return Paths{output}
 }
 
-func getAconfigFilePaths(m *ModuleBase, aconfigFiles map[string]Paths) (paths Paths) {
-	// TODO(b/311155208): The default container here should be system.
+func getContainer(m Module) string {
 	container := "system"
-
-	if m.SocSpecific() {
+	base := m.base()
+	if base.SocSpecific() {
 		container = "vendor"
-	} else if m.ProductSpecific() {
+	} else if base.ProductSpecific() {
 		container = "product"
-	} else if m.SystemExtSpecific() {
+	} else if base.SystemExtSpecific() {
 		// system_ext and system partitions should be treated as one container
 		container = "system"
 	}
 
+	return container
+}
+
+// TODO(b/397766191): Change the signature to take ModuleProxy
+// Please only access the module's internal data through providers.
+func getContainerUsingProviders(ctx OtherModuleProviderContext, m Module) string {
+	container := "system"
+	commonInfo, _ := OtherModuleProvider(ctx, m, CommonModuleInfoKey)
+	if commonInfo.Vendor || commonInfo.Proprietary || commonInfo.SocSpecific {
+		container = "vendor"
+	} else if commonInfo.ProductSpecific {
+		container = "product"
+	} else if commonInfo.SystemExtSpecific {
+		// system_ext and system partitions should be treated as one container
+		container = "system"
+	}
+
+	return container
+}
+
+func getAconfigFilePaths(container string, aconfigFiles map[string]Paths) (paths Paths) {
 	paths = append(paths, aconfigFiles[container]...)
 	if container == "system" {
 		// TODO(b/311155208): Once the default container is system, we can drop this.
diff --git a/android/androidmk.go b/android/androidmk.go
index c081ba3..d9d78f3 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -775,6 +775,8 @@
 // In soong-only mode, we don't do most of the androidmk stuff. But disted files are still largely
 // defined through the androidmk mechanisms, so this function is an alternate implementation of
 // the androidmk singleton that just focuses on getting the dist contributions
+// TODO(b/397766191): Change the signature to take ModuleProxy
+// Please only access the module's internal data through providers.
 func (so *soongOnlyAndroidMkSingleton) soongOnlyBuildActions(ctx SingletonContext, mods []Module) {
 	allDistContributions, moduleInfoJSONs := getSoongOnlyDataFromMods(ctx, mods)
 
@@ -895,28 +897,29 @@
 			}
 		}
 
-		if shouldSkipAndroidMkProcessing(ctx, mod.base()) {
+		commonInfo, _ := OtherModuleProvider(ctx, mod, CommonModuleInfoKey)
+		if commonInfo.SkipAndroidMkProcessing {
 			continue
 		}
 		if info, ok := OtherModuleProvider(ctx, mod, AndroidMkInfoProvider); ok {
 			// Deep copy the provider info since we need to modify the info later
 			info := deepCopyAndroidMkProviderInfo(info)
-			info.PrimaryInfo.fillInEntries(ctx, mod)
+			info.PrimaryInfo.fillInEntries(ctx, mod, &commonInfo)
 			if info.PrimaryInfo.disabled() {
 				continue
 			}
 			if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
 				moduleInfoJSONs = append(moduleInfoJSONs, moduleInfoJSON...)
 			}
-			if contribution := info.PrimaryInfo.getDistContributions(ctx, mod); contribution != nil {
+			if contribution := info.PrimaryInfo.getDistContributions(ctx, mod, &commonInfo); contribution != nil {
 				allDistContributions = append(allDistContributions, *contribution)
 			}
 			for _, ei := range info.ExtraInfo {
-				ei.fillInEntries(ctx, mod)
+				ei.fillInEntries(ctx, mod, &commonInfo)
 				if ei.disabled() {
 					continue
 				}
-				if contribution := ei.getDistContributions(ctx, mod); contribution != nil {
+				if contribution := ei.getDistContributions(ctx, mod, &commonInfo); contribution != nil {
 					allDistContributions = append(allDistContributions, *contribution)
 				}
 			}
@@ -1333,9 +1336,12 @@
 // TODO: rename it to AndroidMkEntriesProvider after AndroidMkEntriesProvider interface is gone.
 var AndroidMkInfoProvider = blueprint.NewProvider[*AndroidMkProviderInfo]()
 
+// TODO(b/397766191): Change the signature to take ModuleProxy
+// Please only access the module's internal data through providers.
 func translateAndroidMkEntriesInfoModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON,
 	mod Module, providerInfo *AndroidMkProviderInfo) error {
-	if shouldSkipAndroidMkProcessing(ctx, mod.base()) {
+	commonInfo, _ := OtherModuleProvider(ctx, mod, CommonModuleInfoKey)
+	if commonInfo.SkipAndroidMkProcessing {
 		return nil
 	}
 
@@ -1345,11 +1351,11 @@
 	aconfigUpdateAndroidMkInfos(ctx, mod, &info)
 
 	// Any new or special cases here need review to verify correct propagation of license information.
-	info.PrimaryInfo.fillInEntries(ctx, mod)
+	info.PrimaryInfo.fillInEntries(ctx, mod, &commonInfo)
 	info.PrimaryInfo.write(w)
 	if len(info.ExtraInfo) > 0 {
 		for _, ei := range info.ExtraInfo {
-			ei.fillInEntries(ctx, mod)
+			ei.fillInEntries(ctx, mod, &commonInfo)
 			ei.write(w)
 		}
 	}
@@ -1478,13 +1484,14 @@
 	a.AddStrings("LOCAL_COMPATIBILITY_SUITE", suites...)
 }
 
-func (a *AndroidMkInfo) fillInEntries(ctx fillInEntriesContext, mod Module) {
+// TODO(b/397766191): Change the signature to take ModuleProxy
+// Please only access the module's internal data through providers.
+func (a *AndroidMkInfo) fillInEntries(ctx fillInEntriesContext, mod Module, commonInfo *CommonModuleInfo) {
 	helperInfo := AndroidMkInfo{
 		EntryMap: make(map[string][]string),
 	}
 
-	base := mod.base()
-	name := base.BaseModuleName()
+	name := commonInfo.BaseModuleName
 	if a.OverrideName != "" {
 		name = a.OverrideName
 	}
@@ -1492,16 +1499,16 @@
 	if a.Include == "" {
 		a.Include = "$(BUILD_PREBUILT)"
 	}
-	a.Required = append(a.Required, mod.RequiredModuleNames(ctx)...)
-	a.Required = append(a.Required, mod.VintfFragmentModuleNames(ctx)...)
-	a.Host_required = append(a.Host_required, mod.HostRequiredModuleNames()...)
-	a.Target_required = append(a.Target_required, mod.TargetRequiredModuleNames()...)
+	a.Required = append(a.Required, commonInfo.RequiredModuleNames...)
+	a.Required = append(a.Required, commonInfo.VintfFragmentModuleNames...)
+	a.Host_required = append(a.Host_required, commonInfo.HostRequiredModuleNames...)
+	a.Target_required = append(a.Target_required, commonInfo.TargetRequiredModuleNames...)
 
-	for _, distString := range a.GetDistForGoals(ctx, mod) {
+	for _, distString := range a.GetDistForGoals(ctx, mod, commonInfo) {
 		a.HeaderStrings = append(a.HeaderStrings, distString)
 	}
 
-	a.HeaderStrings = append(a.HeaderStrings, fmt.Sprintf("\ninclude $(CLEAR_VARS)  # type: %s, name: %s, variant: %s", ctx.ModuleType(mod), base.BaseModuleName(), ctx.ModuleSubDir(mod)))
+	a.HeaderStrings = append(a.HeaderStrings, fmt.Sprintf("\ninclude $(CLEAR_VARS)  # type: %s, name: %s, variant: %s", ctx.ModuleType(mod), commonInfo.BaseModuleName, ctx.ModuleSubDir(mod)))
 
 	// Collect make variable assignment entries.
 	helperInfo.SetString("LOCAL_PATH", ctx.ModuleDir(mod))
@@ -1526,7 +1533,7 @@
 		// Soong may not have generated the install rule also when `no_full_install: true`.
 		// Mark this module as uninstallable in order to prevent Make from creating an
 		// install rule there.
-		helperInfo.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", proptools.Bool(base.commonProperties.No_full_install))
+		helperInfo.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", commonInfo.NoFullInstall)
 	}
 
 	if info.UncheckedModule {
@@ -1541,31 +1548,31 @@
 		helperInfo.AddStrings("LOCAL_TEST_DATA", androidMkDataPaths(info.TestData)...)
 	}
 
-	if am, ok := mod.(ApexModule); ok {
-		helperInfo.SetBoolIfTrue("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", am.NotAvailableForPlatform())
+	if commonInfo.IsApexModule {
+		helperInfo.SetBoolIfTrue("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", commonInfo.NotAvailableForPlatform)
 	}
 
-	archStr := base.Arch().ArchType.String()
+	archStr := commonInfo.Target.Arch.ArchType.String()
 	host := false
-	switch base.Os().Class {
+	switch commonInfo.Target.Os.Class {
 	case Host:
-		if base.Target().HostCross {
+		if commonInfo.Target.HostCross {
 			// Make cannot identify LOCAL_MODULE_HOST_CROSS_ARCH:= common.
-			if base.Arch().ArchType != Common {
+			if commonInfo.Target.Arch.ArchType != Common {
 				helperInfo.SetString("LOCAL_MODULE_HOST_CROSS_ARCH", archStr)
 			}
 		} else {
 			// Make cannot identify LOCAL_MODULE_HOST_ARCH:= common.
-			if base.Arch().ArchType != Common {
+			if commonInfo.Target.Arch.ArchType != Common {
 				helperInfo.SetString("LOCAL_MODULE_HOST_ARCH", archStr)
 			}
 		}
 		host = true
 	case Device:
 		// Make cannot identify LOCAL_MODULE_TARGET_ARCH:= common.
-		if base.Arch().ArchType != Common {
-			if base.Target().NativeBridge {
-				hostArchStr := base.Target().NativeBridgeHostArchName
+		if commonInfo.Target.Arch.ArchType != Common {
+			if commonInfo.Target.NativeBridge {
+				hostArchStr := commonInfo.Target.NativeBridgeHostArchName
 				if hostArchStr != "" {
 					helperInfo.SetString("LOCAL_MODULE_TARGET_ARCH", hostArchStr)
 				}
@@ -1574,27 +1581,28 @@
 			}
 		}
 
-		if !base.InVendorRamdisk() {
+		if !commonInfo.InVendorRamdisk {
 			helperInfo.AddPaths("LOCAL_FULL_INIT_RC", info.InitRcPaths)
 		}
 		if len(info.VintfFragmentsPaths) > 0 {
 			helperInfo.AddPaths("LOCAL_FULL_VINTF_FRAGMENTS", info.VintfFragmentsPaths)
 		}
-		helperInfo.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(base.commonProperties.Proprietary))
-		if Bool(base.commonProperties.Vendor) || Bool(base.commonProperties.Soc_specific) {
+		helperInfo.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", commonInfo.Proprietary)
+		if commonInfo.Vendor || commonInfo.SocSpecific {
 			helperInfo.SetString("LOCAL_VENDOR_MODULE", "true")
 		}
-		helperInfo.SetBoolIfTrue("LOCAL_ODM_MODULE", Bool(base.commonProperties.Device_specific))
-		helperInfo.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", Bool(base.commonProperties.Product_specific))
-		helperInfo.SetBoolIfTrue("LOCAL_SYSTEM_EXT_MODULE", Bool(base.commonProperties.System_ext_specific))
-		if base.commonProperties.Owner != nil {
-			helperInfo.SetString("LOCAL_MODULE_OWNER", *base.commonProperties.Owner)
+		helperInfo.SetBoolIfTrue("LOCAL_ODM_MODULE", commonInfo.DeviceSpecific)
+		helperInfo.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", commonInfo.ProductSpecific)
+		helperInfo.SetBoolIfTrue("LOCAL_SYSTEM_EXT_MODULE", commonInfo.SystemExtSpecific)
+		if commonInfo.Owner != "" {
+			helperInfo.SetString("LOCAL_MODULE_OWNER", commonInfo.Owner)
 		}
 	}
 
 	if host {
-		makeOs := base.Os().String()
-		if base.Os() == Linux || base.Os() == LinuxBionic || base.Os() == LinuxMusl {
+		os := commonInfo.Target.Os
+		makeOs := os.String()
+		if os == Linux || os == LinuxBionic || os == LinuxMusl {
 			makeOs = "linux"
 		}
 		helperInfo.SetString("LOCAL_MODULE_HOST_OS", makeOs)
@@ -1652,8 +1660,10 @@
 
 // Compute the list of Make strings to declare phony goals and dist-for-goals
 // calls from the module's dist and dists properties.
-func (a *AndroidMkInfo) GetDistForGoals(ctx fillInEntriesContext, mod Module) []string {
-	distContributions := a.getDistContributions(ctx, mod)
+// TODO(b/397766191): Change the signature to take ModuleProxy
+// Please only access the module's internal data through providers.
+func (a *AndroidMkInfo) GetDistForGoals(ctx fillInEntriesContext, mod Module, commonInfo *CommonModuleInfo) []string {
+	distContributions := a.getDistContributions(ctx, mod, commonInfo)
 	if distContributions == nil {
 		return nil
 	}
@@ -1662,9 +1672,11 @@
 }
 
 // Compute the contributions that the module makes to the dist.
-func (a *AndroidMkInfo) getDistContributions(ctx fillInEntriesContext, mod Module) *distContributions {
-	amod := mod.base()
-	name := amod.BaseModuleName()
+// TODO(b/397766191): Change the signature to take ModuleProxy
+// Please only access the module's internal data through providers.
+func (a *AndroidMkInfo) getDistContributions(ctx fillInEntriesContext, mod Module,
+	commonInfo *CommonModuleInfo) *distContributions {
+	name := commonInfo.BaseModuleName
 
 	// Collate the set of associated tag/paths available for copying to the dist.
 	// Start with an empty (nil) set.
@@ -1700,12 +1712,12 @@
 	// Collate the contributions this module makes to the dist.
 	distContributions := &distContributions{}
 
-	if !exemptFromRequiredApplicableLicensesProperty(mod) {
+	if !commonInfo.ExemptFromRequiredApplicableLicensesProperty {
 		distContributions.licenseMetadataFile = info.LicenseMetadataFile
 	}
 
 	// Iterate over this module's dist structs, merged from the dist and dists properties.
-	for _, dist := range amod.Dists() {
+	for _, dist := range commonInfo.Dists {
 		// Get the list of goals this dist should be enabled for. e.g. sdk, droidcore
 		goals := strings.Join(dist.Targets, " ")
 
diff --git a/android/config.go b/android/config.go
index f6d08b8..a5edf0d 100644
--- a/android/config.go
+++ b/android/config.go
@@ -2275,10 +2275,18 @@
 }
 
 func (c *config) UseDebugArt() bool {
+	// If the ArtTargetIncludeDebugBuild product variable is set then return its value.
 	if c.productVariables.ArtTargetIncludeDebugBuild != nil {
 		return Bool(c.productVariables.ArtTargetIncludeDebugBuild)
 	}
 
+	// If the RELEASE_APEX_CONTRIBUTIONS_ART build flag is set to use a prebuilt ART apex
+	// then don't use the debug apex.
+	if val, ok := c.GetBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART"); ok && val != "" {
+		return false
+	}
+
+	// Default to the debug apex for eng builds.
 	return Bool(c.productVariables.Eng)
 }
 
diff --git a/android/module.go b/android/module.go
index f359e9f..996c64e 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1922,6 +1922,23 @@
 	// The primary licenses property, may be nil, records license metadata for the module.
 	PrimaryLicensesProperty applicableLicensesProperty
 	Owner                   string
+	Vendor                  bool
+	Proprietary             bool
+	SocSpecific             bool
+	ProductSpecific         bool
+	SystemExtSpecific       bool
+	DeviceSpecific          bool
+	// When set to true, this module is not installed to the full install path (ex: under
+	// out/target/product/<name>/<partition>). It can be installed only to the packaging
+	// modules like android_filesystem.
+	NoFullInstall                                bool
+	InVendorRamdisk                              bool
+	ExemptFromRequiredApplicableLicensesProperty bool
+	RequiredModuleNames                          []string
+	HostRequiredModuleNames                      []string
+	TargetRequiredModuleNames                    []string
+	VintfFragmentModuleNames                     []string
+	Dists                                        []Dist
 }
 
 type ApiLevelOrPlatform struct {
@@ -2266,7 +2283,21 @@
 		SkipInstall:                      m.commonProperties.SkipInstall,
 		Host:                             m.Host(),
 		PrimaryLicensesProperty:          m.primaryLicensesProperty,
-		Owner:                            m.Owner(),
+		Owner:                            m.module.Owner(),
+		SocSpecific:                      Bool(m.commonProperties.Soc_specific),
+		Vendor:                           Bool(m.commonProperties.Vendor),
+		Proprietary:                      Bool(m.commonProperties.Proprietary),
+		ProductSpecific:                  Bool(m.commonProperties.Product_specific),
+		SystemExtSpecific:                Bool(m.commonProperties.System_ext_specific),
+		DeviceSpecific:                   Bool(m.commonProperties.Device_specific),
+		NoFullInstall:                    proptools.Bool(m.commonProperties.No_full_install),
+		InVendorRamdisk:                  m.InVendorRamdisk(),
+		ExemptFromRequiredApplicableLicensesProperty: exemptFromRequiredApplicableLicensesProperty(m.module),
+		RequiredModuleNames:                          m.module.RequiredModuleNames(ctx),
+		HostRequiredModuleNames:                      m.module.HostRequiredModuleNames(),
+		TargetRequiredModuleNames:                    m.module.TargetRequiredModuleNames(),
+		VintfFragmentModuleNames:                     m.module.VintfFragmentModuleNames(ctx),
+		Dists:                                        m.Dists(),
 	}
 	if mm, ok := m.module.(interface {
 		MinSdkVersion(ctx EarlyModuleContext) ApiLevel
diff --git a/android/path_properties.go b/android/path_properties.go
index 55a4dc0..d769d58 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -54,12 +54,14 @@
 	var pathDeviceFirstPrefer32Properties []string
 	var pathDeviceCommonProperties []string
 	var pathCommonOsProperties []string
+	var pathHostCommonProperties []string
 	for _, ps := range props {
 		pathProperties = append(pathProperties, taggedPropertiesForPropertyStruct(ctx, ps, "path")...)
 		pathDeviceFirstProperties = append(pathDeviceFirstProperties, taggedPropertiesForPropertyStruct(ctx, ps, "path_device_first")...)
 		pathDeviceFirstPrefer32Properties = append(pathDeviceFirstPrefer32Properties, taggedPropertiesForPropertyStruct(ctx, ps, "path_device_first_prefer32")...)
 		pathDeviceCommonProperties = append(pathDeviceCommonProperties, taggedPropertiesForPropertyStruct(ctx, ps, "path_device_common")...)
 		pathCommonOsProperties = append(pathCommonOsProperties, taggedPropertiesForPropertyStruct(ctx, ps, "path_common_os")...)
+		pathHostCommonProperties = append(pathHostCommonProperties, taggedPropertiesForPropertyStruct(ctx, ps, "path_host_common")...)
 	}
 
 	// Remove duplicates to avoid multiple dependencies.
@@ -68,6 +70,7 @@
 	pathDeviceFirstPrefer32Properties = FirstUniqueStrings(pathDeviceFirstPrefer32Properties)
 	pathDeviceCommonProperties = FirstUniqueStrings(pathDeviceCommonProperties)
 	pathCommonOsProperties = FirstUniqueStrings(pathCommonOsProperties)
+	pathHostCommonProperties = FirstUniqueStrings(pathHostCommonProperties)
 
 	// Add dependencies to anything that is a module reference.
 	for _, s := range pathProperties {
@@ -108,6 +111,12 @@
 			ctx.AddVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), sourceOrOutputDepTag(m, t), m)
 		}
 	}
+	// properties tagged "path_host_common" get the host common variant
+	for _, s := range pathHostCommonProperties {
+		if m, t := SrcIsModuleWithTag(s); m != "" {
+			ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), sourceOrOutputDepTag(m, t), m)
+		}
+	}
 	// properties tagged "path_common_os" get the CommonOs variant
 	for _, s := range pathCommonOsProperties {
 		if m, t := SrcIsModuleWithTag(s); m != "" {
diff --git a/android/test_mapping_zip.go b/android/test_mapping_zip.go
new file mode 100644
index 0000000..8dc70d7
--- /dev/null
+++ b/android/test_mapping_zip.go
@@ -0,0 +1,49 @@
+// Copyright 2025 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+func init() {
+	InitRegistrationContext.RegisterSingletonType("test_mapping_zip_singleton", testMappingZipSingletonFactory)
+}
+
+func testMappingZipSingletonFactory() Singleton {
+	return &testMappingZipSingleton{}
+}
+
+type testMappingZipSingleton struct{}
+
+func (s *testMappingZipSingleton) GenerateBuildActions(ctx SingletonContext) {
+	fileListFile := PathForArbitraryOutput(ctx, ".module_paths", "TEST_MAPPING.list")
+	out := PathForOutput(ctx, "test_mappings.zip")
+	dep := PathForOutput(ctx, "test_mappings.zip.d")
+
+	// disabled-presubmit-tests used to be filled out based on modules that set
+	// LOCAL_PRESUBMIT_DISABLED. But that's no longer used and there was never a soong equivalent
+	// anyways, so just always create an empty file.
+	disabledPresubmitTestsFile := PathForOutput(ctx, "disabled-presubmit-tests")
+	WriteFileRule(ctx, disabledPresubmitTestsFile, "")
+
+	builder := NewRuleBuilder(pctx, ctx)
+	builder.Command().BuiltTool("soong_zip").
+		FlagWithOutput("-o ", out).
+		FlagWithInput("-l ", fileListFile).
+		FlagWithArg("-e ", "disabled-presubmit-tests").
+		FlagWithInput("-f ", disabledPresubmitTestsFile)
+	builder.Command().Textf("echo '%s : ' $(cat %s) > ", out, fileListFile).DepFile(dep)
+	builder.Build("test_mappings_zip", "build TEST_MAPPING zip")
+
+	ctx.Phony("test_mapping", out)
+	ctx.DistForGoals([]string{"dist_files", "test_mapping"}, out)
+}
diff --git a/android/testing.go b/android/testing.go
index 0cab0ab..1962fde 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -1197,10 +1197,11 @@
 
 	info := OtherModuleProviderOrDefault(ctx, mod, AndroidMkInfoProvider)
 	aconfigUpdateAndroidMkInfos(ctx, mod, info)
-	info.PrimaryInfo.fillInEntries(ctx, mod)
+	commonInfo, _ := OtherModuleProvider(ctx, mod, CommonModuleInfoKey)
+	info.PrimaryInfo.fillInEntries(ctx, mod, &commonInfo)
 	if len(info.ExtraInfo) > 0 {
 		for _, ei := range info.ExtraInfo {
-			ei.fillInEntries(ctx, mod)
+			ei.fillInEntries(ctx, mod, &commonInfo)
 		}
 	}
 
diff --git a/apex/apex.go b/apex/apex.go
index 4b510f8..6e4685b 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -82,10 +82,6 @@
 	// /system/sepolicy/apex/<module_name>_file_contexts.
 	File_contexts *string `android:"path"`
 
-	// By default, file_contexts is amended by force-labelling / and /apex_manifest.pb as system_file
-	// to avoid mistakes. When set as true, no force-labelling.
-	Use_file_contexts_as_is *bool
-
 	// Path to the canned fs config file for customizing file's
 	// uid/gid/mod/capabilities. The content of this file is appended to the
 	// default config, so that the custom entries are preferred. The format is
diff --git a/apex/builder.go b/apex/builder.go
index 8427719..2fc4902 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -428,8 +428,6 @@
 		ctx.PropertyErrorf("file_contexts", "cannot find file_contexts file: %q", fileContexts.String())
 	}
 
-	useFileContextsAsIs := proptools.Bool(a.properties.Use_file_contexts_as_is)
-
 	output := android.PathForModuleOut(ctx, "file_contexts")
 	rule := android.NewRuleBuilder(pctx, ctx)
 
@@ -446,11 +444,9 @@
 	rule.Command().Text("cat").Input(fileContexts).Text(">>").Output(output)
 	// new line
 	rule.Command().Text("echo").Text(">>").Output(output)
-	if !useFileContextsAsIs {
-		// force-label /apex_manifest.pb and /
-		rule.Command().Text("echo").Text("/apex_manifest\\\\.pb").Text(labelForManifest).Text(">>").Output(output)
-		rule.Command().Text("echo").Text("/").Text(labelForRoot).Text(">>").Output(output)
-	}
+	// force-label /apex_manifest.pb and /
+	rule.Command().Text("echo").Text("/apex_manifest\\\\.pb").Text(labelForManifest).Text(">>").Output(output)
+	rule.Command().Text("echo").Text("/").Text(labelForRoot).Text(">>").Output(output)
 
 	rule.Build("file_contexts."+a.Name(), "Generate file_contexts")
 	return output
@@ -592,6 +588,7 @@
 			}
 			a.extraInstalledFiles = append(a.extraInstalledFiles, installedFile)
 			a.extraInstalledPairs = append(a.extraInstalledPairs, installPair{install.OutputPathOnHost, installedFile})
+			ctx.PackageFile(install.InstallDirOnDevice, install.InstallFileOnDevice, install.OutputPathOnHost)
 		}
 		if performInstalls {
 			for _, dexJar := range fi.systemServerDexJars {
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 3daa4f8..0a97027 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -245,6 +245,7 @@
 		}
 		p.extraInstalledFiles = append(p.extraInstalledFiles, installedFile)
 		p.extraInstalledPairs = append(p.extraInstalledPairs, installPair{install.OutputPathOnHost, installedFile})
+		ctx.PackageFile(install.InstallDirOnDevice, install.InstallFileOnDevice, install.OutputPathOnHost)
 	}
 
 	for _, dexJar := range p.systemServerDexJars {
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index cf7ea8a..61f79d6 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -471,3 +471,120 @@
 		t.Fatalf("Expected profile-guided to be %v, got %v", expected, actual)
 	}
 }
+
+func TestCheckSystemServerOrderWithArtApex(t *testing.T) {
+	preparers := android.GroupFixturePreparers(
+		java.PrepareForTestWithDexpreopt,
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		PrepareForTestWithApexBuildComponents,
+		prepareForTestWithArtApex,
+		java.FixtureConfigureBootJars("com.android.art:framework-art"),
+		dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-apex1", "com.android.art:service-art"),
+		java.FixtureWithLastReleaseApis("baz"),
+	)
+
+	// Creates a com.android.art apex with a bootclasspath fragment and a systemserverclasspath fragment, and a
+	// com.android.apex1 prebuilt whose bootclasspath fragment depends on the com.android.art bootclasspath fragment.
+	// Verifies that the checkSystemServerOrder doesn't get confused by the bootclasspath dependencies and report
+	// that service-apex1 depends on service-art.
+	result := preparers.RunTestWithBp(t, `
+		apex {
+			name: "com.android.art",
+			key: "com.android.art.key",
+			bootclasspath_fragments: ["art-bootclasspath-fragment"],
+			systemserverclasspath_fragments: ["art-systemserverclasspath-fragment"],
+			updatable: false,
+		}
+
+		apex_key {
+			name: "com.android.art.key",
+			public_key: "com.android.art.avbpubkey",
+			private_key: "com.android.art.pem",
+		}
+
+		bootclasspath_fragment {
+			name: "art-bootclasspath-fragment",
+			image_name: "art",
+			contents: ["framework-art"],
+			apex_available: [
+				"com.android.art",
+			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+
+		java_library {
+			name: "framework-art",
+			apex_available: ["com.android.art"],
+			srcs: ["a.java"],
+			compile_dex: true,
+		}
+
+		systemserverclasspath_fragment {
+			name: "art-systemserverclasspath-fragment",
+			apex_available: ["com.android.art"],
+			contents: ["service-art"],
+		}
+
+		java_library {
+			name: "service-art",
+			srcs: ["a.java"],
+			apex_available: ["com.android.art"],
+			compile_dex: true,
+		}
+
+		prebuilt_apex {
+			name: "com.android.apex1",
+			arch: {
+				arm64: {
+					src: "myapex-arm64.apex",
+				},
+				arm: {
+					src: "myapex-arm.apex",
+				},
+			},
+			exported_bootclasspath_fragments: ["com.android.apex1-bootclasspath-fragment"],
+			exported_systemserverclasspath_fragments: ["com.android.apex1-systemserverclasspath-fragment"],
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "com.android.apex1-bootclasspath-fragment",
+			visibility: ["//visibility:public"],
+			apex_available: ["com.android.apex1"],
+			contents: ["framework-apex1"],
+			fragments: [
+				{
+					apex: "com.android.art",
+					module: "art-bootclasspath-fragment",
+				},
+			],
+			hidden_api: {
+				annotation_flags: "hiddenapi/annotation-flags.csv",
+				metadata: "hiddenapi/metadata.csv",
+				index: "hiddenapi/index.csv",
+				stub_flags: "hiddenapi/stub-flags.csv",
+				all_flags: "hiddenapi/all-flags.csv",
+			},
+		}
+
+		java_import {
+			name: "framework-apex1",
+			apex_available: ["com.android.apex1"],
+		}
+
+		prebuilt_systemserverclasspath_fragment {
+			name: "com.android.apex1-systemserverclasspath-fragment",
+			apex_available: ["com.android.apex1"],
+			contents: ["service-apex1"],
+		}
+
+		java_import {
+			name: "service-apex1",
+			installable: true,
+			apex_available: ["com.android.apex1"],
+			sdk_version: "current",
+		}`)
+
+	_ = result
+}
diff --git a/cc/Android.bp b/cc/Android.bp
index 3b29ae8..1ac5a4a 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -36,6 +36,7 @@
         "linkable.go",
         "lto.go",
         "makevars.go",
+        "misc_disted_files.go",
         "orderfile.go",
         "prebuilt.go",
         "proto.go",
diff --git a/cc/fuzz.go b/cc/fuzz.go
index a8e4cb7..bd3d8e4 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -353,6 +353,7 @@
 	fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Data)
 	fuzzPackagedModule.Data = append(fuzzPackagedModule.Data, android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Device_common_data)...)
 	fuzzPackagedModule.Data = append(fuzzPackagedModule.Data, android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Device_first_data)...)
+	fuzzPackagedModule.Data = append(fuzzPackagedModule.Data, android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Host_common_data)...)
 
 	if fuzzPackagedModule.FuzzProperties.Dictionary != nil {
 		fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *fuzzPackagedModule.FuzzProperties.Dictionary)
diff --git a/cc/makevars.go b/cc/makevars.go
index c945102..9358755 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -100,15 +100,7 @@
 
 	// Filter vendor_public_library that are exported to make
 	var exportedVendorPublicLibraries []string
-	var warningsAllowed []string
-	var usingWnoErrors []string
-	var missingProfiles []string
 	ctx.VisitAllModules(func(module android.Module) {
-		if v, ok := android.OtherModuleProvider(ctx, module, CcMakeVarsInfoProvider); ok {
-			warningsAllowed = android.AppendIfNotZero(warningsAllowed, v.WarningsAllowed)
-			usingWnoErrors = android.AppendIfNotZero(usingWnoErrors, v.UsingWnoError)
-			missingProfiles = android.AppendIfNotZero(missingProfiles, v.MissingProfile)
-		}
 		if ccModule, ok := module.(*Module); ok {
 			baseName := ccModule.BaseModuleName()
 			if ccModule.IsVendorPublicLibrary() && module.ExportedToMake() {
@@ -123,9 +115,6 @@
 	ctx.Strict("LSDUMP_PATHS", strings.Join(lsdumpPaths, " "))
 
 	ctx.Strict("ANDROID_WARNING_ALLOWED_PROJECTS", makeStringOfWarningAllowedProjects())
-	ctx.Strict("SOONG_MODULES_WARNINGS_ALLOWED", makeVarsString(warningsAllowed))
-	ctx.Strict("SOONG_MODULES_USING_WNO_ERROR", makeVarsString(usingWnoErrors))
-	ctx.Strict("SOONG_MODULES_MISSING_PGO_PROFILE_FILE", makeVarsString(missingProfiles))
 
 	ctx.Strict("CLANG_COVERAGE_CONFIG_CFLAGS", strings.Join(clangCoverageCFlags, " "))
 	ctx.Strict("CLANG_COVERAGE_CONFIG_COMMFLAGS", strings.Join(clangCoverageCommonFlags, " "))
diff --git a/cc/misc_disted_files.go b/cc/misc_disted_files.go
new file mode 100644
index 0000000..4bdffaa
--- /dev/null
+++ b/cc/misc_disted_files.go
@@ -0,0 +1,89 @@
+// Copyright 2025 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+	"android/soong/android"
+	"strings"
+)
+
+func init() {
+	android.InitRegistrationContext.RegisterSingletonType("cc_misc_disted_files", ccMiscDistedFilesSingletonFactory)
+}
+
+func ccMiscDistedFilesSingletonFactory() android.Singleton {
+	return &ccMiscDistedFilesSingleton{}
+}
+
+type ccMiscDistedFilesSingleton struct {
+	warningsAllowed []string
+	usingWnoErrors  []string
+}
+
+func (s *ccMiscDistedFilesSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	var warningsAllowed []string
+	var usingWnoErrors []string
+	var missingProfiles []string
+	ctx.VisitAllModules(func(module android.Module) {
+		if v, ok := android.OtherModuleProvider(ctx, module, CcMakeVarsInfoProvider); ok {
+			warningsAllowed = android.AppendIfNotZero(warningsAllowed, v.WarningsAllowed)
+			usingWnoErrors = android.AppendIfNotZero(usingWnoErrors, v.UsingWnoError)
+			missingProfiles = android.AppendIfNotZero(missingProfiles, v.MissingProfile)
+		}
+	})
+
+	warningsAllowed = android.SortedUniqueStrings(warningsAllowed)
+	usingWnoErrors = android.SortedUniqueStrings(usingWnoErrors)
+	missingProfiles = android.SortedUniqueStrings(missingProfiles)
+
+	s.warningsAllowed = warningsAllowed
+	s.usingWnoErrors = usingWnoErrors
+
+	var sb strings.Builder
+	sb.WriteString("# Modules using -Wno-error\n")
+	for _, nwe := range usingWnoErrors {
+		sb.WriteString(nwe)
+		sb.WriteString("\n")
+	}
+	sb.WriteString("# Modules that allow warnings\n")
+	for _, wa := range warningsAllowed {
+		sb.WriteString(wa)
+		sb.WriteString("\n")
+	}
+	wallWerrFile := android.PathForOutput(ctx, "wall_werror.txt")
+	android.WriteFileRuleVerbatim(ctx, wallWerrFile, sb.String())
+
+	// Only dist this file in soong-only builds. In soong+make builds, it contains information
+	// from make modules, so we'll still rely on make to build and dist it.
+	if !ctx.Config().KatiEnabled() {
+		ctx.DistForGoal("droidcore-unbundled", wallWerrFile)
+	}
+
+	var sb2 strings.Builder
+	sb2.WriteString("# Modules missing PGO profile files\n")
+	for _, mp := range missingProfiles {
+		sb2.WriteString(mp)
+		sb2.WriteString("\n")
+	}
+	profileMissingFile := android.PathForOutput(ctx, "pgo_profile_file_missing.txt")
+	android.WriteFileRuleVerbatim(ctx, profileMissingFile, sb2.String())
+
+	ctx.DistForGoal("droidcore-unbundled", profileMissingFile)
+}
+
+func (s *ccMiscDistedFilesSingleton) MakeVars(ctx android.MakeVarsContext) {
+	ctx.Strict("SOONG_MODULES_WARNINGS_ALLOWED", strings.Join(s.warningsAllowed, " "))
+	ctx.Strict("SOONG_MODULES_USING_WNO_ERROR", strings.Join(s.usingWnoErrors, " "))
+}
diff --git a/cc/test.go b/cc/test.go
index 2c5c36e..d2c4b28 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -94,6 +94,11 @@
 	// of a host test.
 	Device_first_data []string `android:"path_device_first"`
 
+	// Same as data, but will add dependencies on modules using the host's os variation and
+	// the common arch variation. Useful for a device test that wants to depend on a host
+	// module, for example to include a custom Tradefed test runner.
+	Host_common_data []string `android:"path_host_common"`
+
 	// list of shared library modules that should be installed alongside the test
 	Data_libs []string `android:"arch_variant"`
 
@@ -345,6 +350,7 @@
 	dataSrcPaths := android.PathsForModuleSrc(ctx, test.Properties.Data)
 	dataSrcPaths = append(dataSrcPaths, android.PathsForModuleSrc(ctx, test.Properties.Device_common_data)...)
 	dataSrcPaths = append(dataSrcPaths, android.PathsForModuleSrc(ctx, test.Properties.Device_first_data)...)
+	dataSrcPaths = append(dataSrcPaths, android.PathsForModuleSrc(ctx, test.Properties.Host_common_data)...)
 
 	for _, dataSrcPath := range dataSrcPaths {
 		test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
diff --git a/ci_tests/ci_test_package_zip.go b/ci_tests/ci_test_package_zip.go
index d9573a3..acfa8e0 100644
--- a/ci_tests/ci_test_package_zip.go
+++ b/ci_tests/ci_test_package_zip.go
@@ -190,6 +190,10 @@
 		}
 
 		for _, installedFile := range installedFilesInfo.InstallFiles {
+			// there are additional installed files for some app-class modules, we only need the .apk files in the test package
+			if class == "app" && installedFile.Ext() != ".apk" {
+				continue
+			}
 			name := removeFileExtension(installedFile.Base())
 			f := strings.TrimPrefix(installedFile.String(), productOut+"/")
 			if strings.HasPrefix(f, "out") {
diff --git a/cmd/symbols_map/symbols_map.go b/cmd/symbols_map/symbols_map.go
index c56cf93..3955c8a 100644
--- a/cmd/symbols_map/symbols_map.go
+++ b/cmd/symbols_map/symbols_map.go
@@ -72,6 +72,7 @@
 
 	elfFile := flags.String("elf", "", "extract identifier from an elf file")
 	r8File := flags.String("r8", "", "extract identifier from an r8 dictionary")
+	locationFlag := flags.String("location", "", "an override for the value of the location field in the proto. If not specified, the filename will be used")
 	merge := flags.String("merge", "", "merge multiple identifier protos")
 
 	writeIfChanged := flags.Bool("write_if_changed", false, "only write output file if it is modified")
@@ -134,6 +135,10 @@
 		panic("shouldn't get here")
 	}
 
+	if *locationFlag != "" {
+		location = *locationFlag
+	}
+
 	mapping := symbols_map_proto.Mapping{
 		Identifier: proto.String(identifier),
 		Location:   proto.String(location),
diff --git a/cmd/symbols_map/symbols_map_proto/regen.sh b/cmd/symbols_map/symbols_map_proto/regen.sh
new file mode 100755
index 0000000..3c189d1
--- /dev/null
+++ b/cmd/symbols_map/symbols_map_proto/regen.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. symbols_map.proto
+
diff --git a/cmd/symbols_map/symbols_map_proto/symbols_map.pb.go b/cmd/symbols_map/symbols_map_proto/symbols_map.pb.go
index f9c0ce5..07f4b39 100644
--- a/cmd/symbols_map/symbols_map_proto/symbols_map.pb.go
+++ b/cmd/symbols_map/symbols_map_proto/symbols_map.pb.go
@@ -14,8 +14,8 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.27.1
-// 	protoc        v3.9.1
+// 	protoc-gen-go v1.30.0
+// 	protoc        v3.21.12
 // source: symbols_map.proto
 
 package symbols_map_proto
@@ -93,6 +93,68 @@
 	return file_symbols_map_proto_rawDescGZIP(), []int{0, 0}
 }
 
+// LocationType is the place where to look for the file with the given
+// identifier.
+type Mapping_LocationType int32
+
+const (
+	// ZIP denotes the file with the given identifier is in the distribuited
+	// symbols.zip or proguard_dict.zip files, or the local disc.
+	Mapping_ZIP Mapping_LocationType = 0
+	// AB denotes the file with the given identifier is in the AB artifacts but
+	// not in a symbols.zip or proguard_dict.zip.
+	Mapping_AB Mapping_LocationType = 1
+)
+
+// Enum value maps for Mapping_LocationType.
+var (
+	Mapping_LocationType_name = map[int32]string{
+		0: "ZIP",
+		1: "AB",
+	}
+	Mapping_LocationType_value = map[string]int32{
+		"ZIP": 0,
+		"AB":  1,
+	}
+)
+
+func (x Mapping_LocationType) Enum() *Mapping_LocationType {
+	p := new(Mapping_LocationType)
+	*p = x
+	return p
+}
+
+func (x Mapping_LocationType) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (Mapping_LocationType) Descriptor() protoreflect.EnumDescriptor {
+	return file_symbols_map_proto_enumTypes[1].Descriptor()
+}
+
+func (Mapping_LocationType) Type() protoreflect.EnumType {
+	return &file_symbols_map_proto_enumTypes[1]
+}
+
+func (x Mapping_LocationType) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *Mapping_LocationType) UnmarshalJSON(b []byte) error {
+	num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+	if err != nil {
+		return err
+	}
+	*x = Mapping_LocationType(num)
+	return nil
+}
+
+// Deprecated: Use Mapping_LocationType.Descriptor instead.
+func (Mapping_LocationType) EnumDescriptor() ([]byte, []int) {
+	return file_symbols_map_proto_rawDescGZIP(), []int{0, 1}
+}
+
 type Mapping struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -107,6 +169,9 @@
 	Location *string `protobuf:"bytes,2,opt,name=location" json:"location,omitempty"`
 	// type is the type of the mapping, either ELF or R8.
 	Type *Mapping_Type `protobuf:"varint,3,opt,name=type,enum=symbols_map.Mapping_Type" json:"type,omitempty"`
+	// location_type is the Location Type that dictates where to search for the
+	// file with the given identifier. Defaults to ZIP if not present.
+	LocationType *Mapping_LocationType `protobuf:"varint,4,opt,name=location_type,json=locationType,enum=symbols_map.Mapping_LocationType" json:"location_type,omitempty"`
 }
 
 func (x *Mapping) Reset() {
@@ -162,6 +227,13 @@
 	return Mapping_ELF
 }
 
+func (x *Mapping) GetLocationType() Mapping_LocationType {
+	if x != nil && x.LocationType != nil {
+		return *x.LocationType
+	}
+	return Mapping_ZIP
+}
+
 type Mappings struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -214,23 +286,29 @@
 var file_symbols_map_proto_rawDesc = []byte{
 	0x0a, 0x11, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x2e, 0x70, 0x72,
 	0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70,
-	0x22, 0x8d, 0x01, 0x0a, 0x07, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x1e, 0x0a, 0x0a,
+	0x22, 0xf6, 0x01, 0x0a, 0x07, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x1e, 0x0a, 0x0a,
 	0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
 	0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08,
 	0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
 	0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
 	0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73,
 	0x5f, 0x6d, 0x61, 0x70, 0x2e, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x54, 0x79, 0x70,
-	0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x17, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,
-	0x07, 0x0a, 0x03, 0x45, 0x4c, 0x46, 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x52, 0x38, 0x10, 0x01,
-	0x22, 0x3c, 0x0a, 0x08, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x30, 0x0a, 0x08,
-	0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14,
+	0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x46, 0x0a, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21,
 	0x2e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x2e, 0x4d, 0x61, 0x70,
-	0x70, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x31,
-	0x5a, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f,
-	0x63, 0x6d, 0x64, 0x2f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x2f,
-	0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x74,
-	0x6f,
+	0x70, 0x69, 0x6e, 0x67, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70,
+	0x65, 0x52, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x22,
+	0x17, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x45, 0x4c, 0x46, 0x10, 0x00,
+	0x12, 0x06, 0x0a, 0x02, 0x52, 0x38, 0x10, 0x01, 0x22, 0x1f, 0x0a, 0x0c, 0x4c, 0x6f, 0x63, 0x61,
+	0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x5a, 0x49, 0x50, 0x10,
+	0x00, 0x12, 0x06, 0x0a, 0x02, 0x41, 0x42, 0x10, 0x01, 0x22, 0x3c, 0x0a, 0x08, 0x4d, 0x61, 0x70,
+	0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x30, 0x0a, 0x08, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67,
+	0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c,
+	0x73, 0x5f, 0x6d, 0x61, 0x70, 0x2e, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x6d,
+	0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x31, 0x5a, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x73, 0x79, 0x6d,
+	0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x2f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73,
+	0x5f, 0x6d, 0x61, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 }
 
 var (
@@ -245,21 +323,23 @@
 	return file_symbols_map_proto_rawDescData
 }
 
-var file_symbols_map_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_symbols_map_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
 var file_symbols_map_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
 var file_symbols_map_proto_goTypes = []interface{}{
-	(Mapping_Type)(0), // 0: symbols_map.Mapping.Type
-	(*Mapping)(nil),   // 1: symbols_map.Mapping
-	(*Mappings)(nil),  // 2: symbols_map.Mappings
+	(Mapping_Type)(0),         // 0: symbols_map.Mapping.Type
+	(Mapping_LocationType)(0), // 1: symbols_map.Mapping.LocationType
+	(*Mapping)(nil),           // 2: symbols_map.Mapping
+	(*Mappings)(nil),          // 3: symbols_map.Mappings
 }
 var file_symbols_map_proto_depIdxs = []int32{
 	0, // 0: symbols_map.Mapping.type:type_name -> symbols_map.Mapping.Type
-	1, // 1: symbols_map.Mappings.mappings:type_name -> symbols_map.Mapping
-	2, // [2:2] is the sub-list for method output_type
-	2, // [2:2] is the sub-list for method input_type
-	2, // [2:2] is the sub-list for extension type_name
-	2, // [2:2] is the sub-list for extension extendee
-	0, // [0:2] is the sub-list for field type_name
+	1, // 1: symbols_map.Mapping.location_type:type_name -> symbols_map.Mapping.LocationType
+	2, // 2: symbols_map.Mappings.mappings:type_name -> symbols_map.Mapping
+	3, // [3:3] is the sub-list for method output_type
+	3, // [3:3] is the sub-list for method input_type
+	3, // [3:3] is the sub-list for extension type_name
+	3, // [3:3] is the sub-list for extension extendee
+	0, // [0:3] is the sub-list for field type_name
 }
 
 func init() { file_symbols_map_proto_init() }
@@ -298,7 +378,7 @@
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_symbols_map_proto_rawDesc,
-			NumEnums:      1,
+			NumEnums:      2,
 			NumMessages:   2,
 			NumExtensions: 0,
 			NumServices:   0,
diff --git a/cmd/symbols_map/symbols_map_proto/symbols_map.proto b/cmd/symbols_map/symbols_map_proto/symbols_map.proto
index a76d171..a52f75c 100644
--- a/cmd/symbols_map/symbols_map_proto/symbols_map.proto
+++ b/cmd/symbols_map/symbols_map_proto/symbols_map.proto
@@ -40,7 +40,7 @@
 
   // LocationType is the place where to look for the file with the given
   // identifier.
-  Enum LocationType {
+  enum LocationType {
     // ZIP denotes the file with the given identifier is in the distribuited
     // symbols.zip or proguard_dict.zip files, or the local disc.
     ZIP = 0;
@@ -56,4 +56,4 @@
 
 message Mappings {
   repeated Mapping mappings = 4;
-}
\ No newline at end of file
+}
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 1452b41..699a675 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -283,10 +283,7 @@
 		clcHostString := "PCL[" + strings.Join(clcHost.Strings(), ":") + "]"
 		clcTargetString := "PCL[" + strings.Join(clcTarget, ":") + "]"
 
-		if systemServerClasspathJars.ContainsJar(module.Name) {
-			// TODO(b/397461231): renable this check
-			//checkSystemServerOrder(ctx, jarIndex)
-		} else {
+		if !systemServerClasspathJars.ContainsJar(module.Name) {
 			// Standalone jars are loaded by separate class loaders with SYSTEMSERVERCLASSPATH as the
 			// parent.
 			clcHostString = "PCL[];" + clcHostString
@@ -578,29 +575,6 @@
 	}
 }
 
-// Check the order of jars on the system server classpath and give a warning/error if a jar precedes
-// one of its dependencies. This is not an error, but a missed optimization, as dexpreopt won't
-// have the dependency jar in the class loader context, and it won't be able to resolve any
-// references to its classes and methods.
-func checkSystemServerOrder(ctx android.PathContext, jarIndex int) {
-	mctx, isModule := ctx.(android.ModuleContext)
-	if isModule {
-		config := GetGlobalConfig(ctx)
-		jars := config.AllSystemServerClasspathJars(ctx)
-		mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
-			depIndex := jars.IndexOfJar(dep.Name())
-			if jarIndex < depIndex && !config.BrokenSuboptimalOrderOfSystemServerJars {
-				jar := jars.Jar(jarIndex)
-				dep := jars.Jar(depIndex)
-				mctx.ModuleErrorf("non-optimal order of jars on the system server classpath:"+
-					" '%s' precedes its dependency '%s', so dexpreopt is unable to resolve any"+
-					" references from '%s' to '%s'.\n", jar, dep, jar, dep)
-			}
-			return true
-		})
-	}
-}
-
 // Returns path to a file containing the reult of verify_uses_libraries check (empty if the check
 // has succeeded, or an error message if it failed).
 func UsesLibrariesStatusFile(ctx android.ModuleContext) android.WritablePath {
diff --git a/filesystem/android_device.go b/filesystem/android_device.go
index 959ef37..fef4aeb 100644
--- a/filesystem/android_device.go
+++ b/filesystem/android_device.go
@@ -17,16 +17,24 @@
 import (
 	"cmp"
 	"fmt"
+	"path/filepath"
 	"slices"
 	"strings"
 	"sync/atomic"
 
 	"android/soong/android"
+	"android/soong/java"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
+var proguardDictToProto = pctx.AndroidStaticRule("proguard_dict_to_proto", blueprint.RuleParams{
+	Command:     `${symbols_map} -r8 $in -location $location -write_if_changed $out`,
+	Restat:      true,
+	CommandDeps: []string{"${symbols_map}"},
+}, "location")
+
 type PartitionNameProperties struct {
 	// Name of the super partition filesystem module
 	Super_partition_name *string
@@ -89,6 +97,10 @@
 	deviceProps DeviceProperties
 
 	allImagesZip android.Path
+
+	proguardDictZip     android.Path
+	proguardDictMapping android.Path
+	proguardUsageZip    android.Path
 }
 
 func AndroidDeviceFactory() android.Module {
@@ -161,7 +173,11 @@
 		}
 	}
 
+	allInstalledModules := a.allInstalledModules(ctx)
+
 	a.buildTargetFilesZip(ctx)
+	a.buildProguardZips(ctx, allInstalledModules)
+
 	var deps []android.Path
 	if proptools.String(a.partitionProps.Super_partition_name) != "" {
 		superImage := ctx.GetDirectDepProxyWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag)
@@ -281,22 +297,32 @@
 	return ret
 }
 
+func insertBeforeExtension(file, insertion string) string {
+	ext := filepath.Ext(file)
+	return strings.TrimSuffix(file, ext) + insertion + ext
+}
+
 func (a *androidDevice) distFiles(ctx android.ModuleContext) {
-	if !ctx.Config().KatiEnabled() {
-		if proptools.Bool(a.deviceProps.Main_device) {
-			fsInfoMap := a.getFsInfos(ctx)
-			for _, partition := range android.SortedKeys(fsInfoMap) {
-				fsInfo := fsInfoMap[partition]
-				if fsInfo.InstalledFiles.Json != nil {
-					ctx.DistForGoal("droidcore-unbundled", fsInfo.InstalledFiles.Json)
-				}
-				if fsInfo.InstalledFiles.Txt != nil {
-					ctx.DistForGoal("droidcore-unbundled", fsInfo.InstalledFiles.Txt)
-				}
+	if !ctx.Config().KatiEnabled() && proptools.Bool(a.deviceProps.Main_device) {
+		fsInfoMap := a.getFsInfos(ctx)
+		for _, partition := range android.SortedKeys(fsInfoMap) {
+			fsInfo := fsInfoMap[partition]
+			if fsInfo.InstalledFiles.Json != nil {
+				ctx.DistForGoal("droidcore-unbundled", fsInfo.InstalledFiles.Json)
+			}
+			if fsInfo.InstalledFiles.Txt != nil {
+				ctx.DistForGoal("droidcore-unbundled", fsInfo.InstalledFiles.Txt)
 			}
 		}
-	}
 
+		namePrefix := ""
+		if ctx.Config().HasDeviceProduct() {
+			namePrefix = ctx.Config().DeviceProduct() + "-"
+		}
+		ctx.DistForGoalWithFilename("droidcore-unbundled", a.proguardDictZip, namePrefix+insertBeforeExtension(a.proguardDictZip.Base(), "-FILE_NAME_TAG_PLACEHOLDER"))
+		ctx.DistForGoalWithFilename("droidcore-unbundled", a.proguardDictMapping, namePrefix+insertBeforeExtension(a.proguardDictMapping.Base(), "-FILE_NAME_TAG_PLACEHOLDER"))
+		ctx.DistForGoalWithFilename("droidcore-unbundled", a.proguardUsageZip, namePrefix+insertBeforeExtension(a.proguardUsageZip.Base(), "-FILE_NAME_TAG_PLACEHOLDER"))
+	}
 }
 
 func (a *androidDevice) MakeVars(ctx android.MakeVarsModuleContext) {
@@ -305,6 +331,56 @@
 	}
 }
 
+func (a *androidDevice) buildProguardZips(ctx android.ModuleContext, allInstalledModules []android.Module) {
+	dictZip := android.PathForModuleOut(ctx, "proguard-dict.zip")
+	dictZipBuilder := android.NewRuleBuilder(pctx, ctx)
+	dictZipCmd := dictZipBuilder.Command().BuiltTool("soong_zip").Flag("-d").FlagWithOutput("-o ", dictZip)
+
+	dictMapping := android.PathForModuleOut(ctx, "proguard-dict-mapping.textproto")
+	dictMappingBuilder := android.NewRuleBuilder(pctx, ctx)
+	dictMappingCmd := dictMappingBuilder.Command().BuiltTool("symbols_map").Flag("-merge").Output(dictMapping)
+
+	protosDir := android.PathForModuleOut(ctx, "proguard_mapping_protos")
+
+	usageZip := android.PathForModuleOut(ctx, "proguard-usage.zip")
+	usageZipBuilder := android.NewRuleBuilder(pctx, ctx)
+	usageZipCmd := usageZipBuilder.Command().BuiltTool("merge_zips").Output(usageZip)
+
+	for _, mod := range allInstalledModules {
+		if proguardInfo, ok := android.OtherModuleProvider(ctx, mod, java.ProguardProvider); ok {
+			// Maintain these out/target/common paths for backwards compatibility. They may be able
+			// to be changed if tools look up file locations from the protobuf, but I'm not
+			// exactly sure how that works.
+			dictionaryFakePath := fmt.Sprintf("out/target/common/obj/%s/%s_intermediates/proguard_dictionary", proguardInfo.Class, proguardInfo.ModuleName)
+			dictZipCmd.FlagWithArg("-e ", dictionaryFakePath)
+			dictZipCmd.FlagWithInput("-f ", proguardInfo.ProguardDictionary)
+			dictZipCmd.Textf("-e out/target/common/obj/%s/%s_intermediates/classes.jar", proguardInfo.Class, proguardInfo.ModuleName)
+			dictZipCmd.FlagWithInput("-f ", proguardInfo.ClassesJar)
+
+			protoFile := protosDir.Join(ctx, filepath.Dir(dictionaryFakePath), "proguard_dictionary.textproto")
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   proguardDictToProto,
+				Input:  proguardInfo.ProguardDictionary,
+				Output: protoFile,
+				Args: map[string]string{
+					"location": dictionaryFakePath,
+				},
+			})
+			dictMappingCmd.Input(protoFile)
+
+			usageZipCmd.Input(proguardInfo.ProguardUsageZip)
+		}
+	}
+
+	dictZipBuilder.Build("proguard_dict_zip", "Building proguard dictionary zip")
+	dictMappingBuilder.Build("proguard_dict_mapping_proto", "Building proguard mapping proto")
+	usageZipBuilder.Build("proguard_usage_zip", "Building proguard usage zip")
+
+	a.proguardDictZip = dictZip
+	a.proguardDictMapping = dictMapping
+	a.proguardUsageZip = usageZip
+}
+
 // Helper structs for target_files.zip creation
 type targetFilesZipCopy struct {
 	srcModule  *string
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 1ce6131..28eb36d 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -33,11 +33,14 @@
 	"github.com/google/blueprint/proptools"
 )
 
+var pctx = android.NewPackageContext("android/soong/filesystem")
+
 func init() {
 	registerBuildComponents(android.InitRegistrationContext)
 	registerMutators(android.InitRegistrationContext)
 	pctx.HostBinToolVariable("fileslist", "fileslist")
 	pctx.HostBinToolVariable("fs_config", "fs_config")
+	pctx.HostBinToolVariable("symbols_map", "symbols_map")
 }
 
 func registerBuildComponents(ctx android.RegistrationContext) {
@@ -576,8 +579,6 @@
 	return txt, json
 }
 
-var pctx = android.NewPackageContext("android/soong/filesystem")
-
 func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	validatePartitionType(ctx, f)
 	if f.filesystemBuilder.ShouldUseVintfFragmentModuleOnly() {
diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
index e2485a1..3d83706 100644
--- a/fsgen/filesystem_creator.go
+++ b/fsgen/filesystem_creator.go
@@ -347,11 +347,9 @@
 	superImageSubPartitions []string,
 ) {
 	baseProps := &struct {
-		Name         *string
-		Android_info *string
+		Name *string
 	}{
-		Name:         proptools.StringPtr(generatedModuleName(ctx.Config(), "device")),
-		Android_info: proptools.StringPtr(":" + generatedModuleName(ctx.Config(), "android_info.prop{.txt}")),
+		Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "device")),
 	}
 
 	// Currently, only the system and system_ext partition module is created.
@@ -406,6 +404,7 @@
 		Ab_ota_partitions:         ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.AbOtaPartitions,
 		Ab_ota_postinstall_config: ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.AbOtaPostInstallConfig,
 		Ramdisk_node_list:         proptools.StringPtr(":ramdisk_node_list"),
+		Android_info:              proptools.StringPtr(":" + generatedModuleName(ctx.Config(), "android_info.prop{.txt}")),
 	}
 
 	if bootloader, ok := f.createBootloaderFilegroup(ctx); ok {
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index 3fd79a7..83ccd89 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -427,6 +427,12 @@
 	// device's first architecture's variant. Can be useful to add device-built apps to the data
 	// of a host test.
 	Device_first_data []string `android:"path_device_first"`
+
+	// Same as data, but will add dependencies on modules using the host's os variation and
+	// the common arch variation. Useful for a device test that wants to depend on a host
+	// module, for example to include a custom Tradefed test runner.
+	Host_common_data []string `android:"path_host_common"`
+
 	// Optional dictionary to be installed to the fuzz target's output directory.
 	Dictionary *string `android:"path"`
 	// Define the fuzzing frameworks this fuzz target can be built for. If
diff --git a/java/app.go b/java/app.go
index 9b10bf3..17548e7 100644
--- a/java/app.go
+++ b/java/app.go
@@ -452,6 +452,16 @@
 	android.SetProvider(ctx, AppInfoProvider, appInfo)
 
 	a.requiredModuleNames = a.getRequiredModuleNames(ctx)
+
+	if a.dexer.proguardDictionary.Valid() {
+		android.SetProvider(ctx, ProguardProvider, ProguardInfo{
+			ModuleName:         ctx.ModuleName(),
+			Class:              "APPS",
+			ProguardDictionary: a.dexer.proguardDictionary.Path(),
+			ProguardUsageZip:   a.dexer.proguardUsageZip.Path(),
+			ClassesJar:         a.implementationAndResourcesJar,
+		})
+	}
 }
 
 func (a *AndroidApp) getRequiredModuleNames(ctx android.ModuleContext) []string {
@@ -1636,6 +1646,7 @@
 	a.data = append(a.data, android.PathsForModuleSrc(ctx, a.testProperties.Device_common_data)...)
 	a.data = append(a.data, android.PathsForModuleSrc(ctx, a.testProperties.Device_first_data)...)
 	a.data = append(a.data, android.PathsForModuleSrc(ctx, a.testProperties.Device_first_prefer32_data)...)
+	a.data = append(a.data, android.PathsForModuleSrc(ctx, a.testProperties.Host_common_data)...)
 
 	// Install test deps
 	if !ctx.Config().KatiEnabled() {
diff --git a/java/dex.go b/java/dex.go
index 311657f..ed2df21 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -697,3 +697,13 @@
 
 	return javalibJar, artProfileOutputPath
 }
+
+type ProguardInfo struct {
+	ModuleName         string
+	Class              string
+	ProguardDictionary android.Path
+	ProguardUsageZip   android.Path
+	ClassesJar         android.Path
+}
+
+var ProguardProvider = blueprint.NewProvider[ProguardInfo]()
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 5755dec..b21cfc9 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -565,6 +565,10 @@
 	if !isApexSystemServerJar {
 		d.builtInstalled = dexpreoptRule.Installs().String()
 	}
+
+	if isSystemServerJar {
+		checkSystemServerOrder(ctx, libName)
+	}
 }
 
 func getModuleInstallPathInfo(ctx android.ModuleContext, fullInstallPath string) (android.InstallPath, string, string) {
@@ -631,3 +635,30 @@
 func (d *dexpreopter) SetRewrittenProfile(p android.Path) {
 	d.rewrittenProfile = p
 }
+
+// Check the order of jars on the system server classpath and give a warning/error if a jar precedes
+// one of its dependencies. This is not an error, but a missed optimization, as dexpreopt won't
+// have the dependency jar in the class loader context, and it won't be able to resolve any
+// references to its classes and methods.
+func checkSystemServerOrder(ctx android.ModuleContext, libName string) {
+	config := dexpreopt.GetGlobalConfig(ctx)
+	jars := config.AllSystemServerClasspathJars(ctx)
+	jarIndex := config.AllSystemServerJars(ctx).IndexOfJar(libName)
+	ctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
+		tag := ctx.OtherModuleDependencyTag(dep)
+		// Ideally this should only be walking relevant dependencies, but to maintain existing behavior
+		// for now just exclude any known irrelevant dependencies that would lead to incorrect errors.
+		if _, ok := tag.(bootclasspathDependencyTag); ok {
+			return false
+		}
+		depIndex := jars.IndexOfJar(dep.Name())
+		if jarIndex < depIndex && !config.BrokenSuboptimalOrderOfSystemServerJars {
+			jar := jars.Jar(jarIndex)
+			dep := jars.Jar(depIndex)
+			ctx.ModuleErrorf("non-optimal order of jars on the system server classpath:"+
+				" '%s' precedes its dependency '%s', so dexpreopt is unable to resolve any"+
+				" references from '%s' to '%s'.\n", jar, dep, jar, dep)
+		}
+		return true
+	})
+}
diff --git a/java/java.go b/java/java.go
index b9109ee..b18c561 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1172,6 +1172,16 @@
 	buildComplianceMetadata(ctx)
 
 	j.createApiXmlFile(ctx)
+
+	if j.dexer.proguardDictionary.Valid() {
+		android.SetProvider(ctx, ProguardProvider, ProguardInfo{
+			ModuleName:         ctx.ModuleName(),
+			Class:              "JAVA_LIBRARIES",
+			ProguardDictionary: j.dexer.proguardDictionary.Path(),
+			ProguardUsageZip:   j.dexer.proguardUsageZip.Path(),
+			ClassesJar:         j.implementationAndResourcesJar,
+		})
+	}
 }
 
 func (j *Library) javaLibraryModuleInfoJSON(ctx android.ModuleContext) *android.ModuleInfoJSON {
@@ -1539,6 +1549,11 @@
 	// host test.
 	Device_first_prefer32_data []string `android:"path_device_first_prefer32"`
 
+	// Same as data, but will add dependencies on modules using the host's os variation and
+	// the common arch variation. Useful for a device test that wants to depend on a host
+	// module, for example to include a custom Tradefed test runner.
+	Host_common_data []string `android:"path_host_common"`
+
 	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
 	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
 	// explicitly.
@@ -1837,6 +1852,7 @@
 	j.data = append(j.data, android.PathsForModuleSrc(ctx, j.testProperties.Device_common_data)...)
 	j.data = append(j.data, android.PathsForModuleSrc(ctx, j.testProperties.Device_first_data)...)
 	j.data = append(j.data, android.PathsForModuleSrc(ctx, j.testProperties.Device_first_prefer32_data)...)
+	j.data = append(j.data, android.PathsForModuleSrc(ctx, j.testProperties.Host_common_data)...)
 
 	j.extraTestConfigs = android.PathsForModuleSrc(ctx, j.testProperties.Test_options.Extra_test_configs)
 
diff --git a/java/java_test.go b/java/java_test.go
index 636a0c8..a6290a6 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -585,6 +585,29 @@
 	}
 }
 
+func TestHostCommonData(t *testing.T) {
+	t.Parallel()
+	ctx, _ := testJava(t, `
+		java_library_host {
+			name: "host",
+			srcs: ["a.java"],
+		}
+
+		java_test {
+			name: "foo",
+			srcs: ["a.java"],
+			host_common_data: [":host"],
+		}
+	`)
+
+	foo := ctx.ModuleForTests(t, "foo", "android_common").Module().(*Test)
+	host := ctx.ModuleForTests(t, "host", ctx.Config().BuildOSCommonTarget.String()).Module().(*Library)
+
+	if g, w := foo.data.RelativeToTop().Strings(), []string{host.outputFile.RelativeToTop().String()}; !slices.Equal(g, w) {
+		t.Errorf("expected test data %q, got %q\n", w, g)
+	}
+}
+
 func TestHostBinaryNoJavaDebugInfoOverride(t *testing.T) {
 	t.Parallel()
 	bp := `
diff --git a/java/robolectric.go b/java/robolectric.go
index 5dcc7dd..be369f7 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -168,6 +168,7 @@
 	r.data = append(r.data, android.PathsForModuleSrc(ctx, r.testProperties.Device_common_data)...)
 	r.data = append(r.data, android.PathsForModuleSrc(ctx, r.testProperties.Device_first_data)...)
 	r.data = append(r.data, android.PathsForModuleSrc(ctx, r.testProperties.Device_first_prefer32_data)...)
+	r.data = append(r.data, android.PathsForModuleSrc(ctx, r.testProperties.Host_common_data)...)
 
 	var ok bool
 	var instrumentedApp *JavaInfo
diff --git a/rust/test.go b/rust/test.go
index 5c183bc..9833ffd 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -46,9 +46,16 @@
 	// the test
 	Data []string `android:"path,arch_variant"`
 
-	// Same as data, but will add dependencies on the device's
+	// Same as data, but adds dependencies on modules using the device's os variant, and common
+	// architecture's variant. Can be useful to add device-built apps to the data of a host
+	// test.
 	Device_common_data []string `android:"path_device_common"`
 
+	// Same as data, but will add dependencies on modules using the host's os variation and
+	// the common arch variation. Useful for a device test that wants to depend on a host
+	// module, for example to include a custom Tradefed test runner.
+	Host_common_data []string `android:"path_host_common"`
+
 	// list of shared library modules that should be installed alongside the test
 	Data_libs []string `android:"arch_variant"`
 
@@ -147,6 +154,7 @@
 
 	dataSrcPaths := android.PathsForModuleSrc(ctx, test.Properties.Data)
 	dataSrcPaths = append(dataSrcPaths, android.PathsForModuleSrc(ctx, test.Properties.Device_common_data)...)
+	dataSrcPaths = append(dataSrcPaths, android.PathsForModuleSrc(ctx, test.Properties.Host_common_data)...)
 
 	ctx.VisitDirectDepsProxyWithTag(dataLibDepTag, func(dep android.ModuleProxy) {
 		depName := ctx.OtherModuleName(dep)
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index c0c6ff2..f8d1ce5 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -138,6 +138,11 @@
 	// host test.
 	Device_first_data []string `android:"path_device_first"`
 
+	// Same as data, but will add dependencies on modules using the host's os variation and
+	// the common arch variation. Useful for a device test that wants to depend on a host
+	// module, for example to include a custom Tradefed test runner.
+	Host_common_data []string `android:"path_host_common"`
+
 	// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
 	// with root permission.
 	Require_root *bool
@@ -436,6 +441,7 @@
 	expandedData := android.PathsForModuleSrc(ctx, s.testProperties.Data)
 	expandedData = append(expandedData, android.PathsForModuleSrc(ctx, s.testProperties.Device_common_data)...)
 	expandedData = append(expandedData, android.PathsForModuleSrc(ctx, s.testProperties.Device_first_data)...)
+	expandedData = append(expandedData, android.PathsForModuleSrc(ctx, s.testProperties.Host_common_data)...)
 	// Emulate the data property for java_data dependencies.
 	for _, javaData := range ctx.GetDirectDepsProxyWithTag(shTestJavaDataTag) {
 		expandedData = append(expandedData, android.OutputFilesForModule(ctx, javaData, "")...)