Merge "Fix typo in symbols_map.proto" 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 4b0aebb..f6d08b8 100644
--- a/android/config.go
+++ b/android/config.go
@@ -2188,6 +2188,10 @@
 	return c.productVariables.GetBuildFlagBool("RELEASE_R8_STORE_STORE_FENCE_CONSTRUCTOR_INLINING")
 }
 
+func (c *config) UseR8GlobalCheckNotNullFlags() bool {
+	return c.productVariables.GetBuildFlagBool("RELEASE_R8_GLOBAL_CHECK_NOT_NULL_FLAGS")
+}
+
 func (c *config) UseDexV41() bool {
 	return c.productVariables.GetBuildFlagBool("RELEASE_USE_DEX_V41")
 }
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..15737f8 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
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/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 4ee5681..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)
@@ -214,7 +230,7 @@
 
 	allImagesZip := android.PathForModuleOut(ctx, "all_images.zip")
 	allImagesZipBuilder := android.NewRuleBuilder(pctx, ctx)
-	cmd := allImagesZipBuilder.Command().BuiltTool("soong_zip").Flag("--sort_entries")
+	cmd := allImagesZipBuilder.Command().BuiltTool("soong_zip")
 	for _, dep := range deps {
 		cmd.FlagWithArg("-e ", dep.Base())
 		cmd.FlagWithInput("-f ", dep)
@@ -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/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 c9d3f37..ed2df21 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -435,6 +435,11 @@
 		android.PathForSource(ctx, "build/make/core/proguard.flags"),
 	}
 
+	if ctx.Config().UseR8GlobalCheckNotNullFlags() {
+		flagFiles = append(flagFiles, android.PathForSource(ctx,
+			"build/make/core/proguard/checknotnull.flags"))
+	}
+
 	flagFiles = append(flagFiles, d.extraProguardFlagsFiles...)
 	// TODO(ccross): static android library proguard files
 
@@ -583,7 +588,6 @@
 	var description string
 	var artProfileOutputPath *android.OutputPath
 	var implicitOutputs android.WritablePaths
-	var flags []string
 	var deps android.Paths
 	args := map[string]string{
 		"zipFlags":       zipFlags,
@@ -610,18 +614,13 @@
 		description = "r8"
 		debugMode := android.InList("--debug", commonFlags)
 		r8Flags, r8Deps, r8ArtProfileOutputPath := d.r8Flags(ctx, dexParams, debugMode)
-		flags = append(flags, r8Flags...)
 		deps = append(deps, r8Deps...)
 		args["r8Flags"] = strings.Join(append(commonFlags, r8Flags...), " ")
 		if r8ArtProfileOutputPath != nil {
 			artProfileOutputPath = r8ArtProfileOutputPath
-			implicitOutputs = append(
-				implicitOutputs,
-				artProfileOutputPath,
-			)
 			// Add the implicit r8 Art profile output to args so that r8RE knows
 			// about this implicit output
-			args["outR8ArtProfile"] = artProfileOutputPath.String()
+			args["outR8ArtProfile"] = r8ArtProfileOutputPath.String()
 		}
 		args["outDict"] = proguardDictionary.String()
 		args["outConfig"] = proguardConfiguration.String()
@@ -642,16 +641,11 @@
 	if useD8 {
 		description = "d8"
 		d8Flags, d8Deps, d8ArtProfileOutputPath := d.d8Flags(ctx, dexParams)
-		flags = append(flags, d8Flags...)
 		deps = append(deps, d8Deps...)
 		deps = append(deps, commonDeps...)
 		args["d8Flags"] = strings.Join(append(commonFlags, d8Flags...), " ")
 		if d8ArtProfileOutputPath != nil {
 			artProfileOutputPath = d8ArtProfileOutputPath
-			implicitOutputs = append(
-				implicitOutputs,
-				artProfileOutputPath,
-			)
 		}
 		// If we are generating both d8 and r8, only use RBE when both are enabled.
 		switch {
@@ -667,6 +661,12 @@
 			rule = d8
 		}
 	}
+	if artProfileOutputPath != nil {
+		implicitOutputs = append(
+			implicitOutputs,
+			artProfileOutputPath,
+		)
+	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:            rule,
 		Description:     description,
@@ -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/jdeps.go b/java/jdeps.go
index 927c169..07f8c43 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -99,7 +99,7 @@
 		Rule:   android.Touch,
 		Output: jfpath,
 	})
-	ctx.DistForGoal("general-tests", j.outputPath)
+	ctx.DistForGoals([]string{"general-tests", "dist_files"}, j.outputPath)
 }
 
 func createJsonFile(moduleInfos map[string]android.IdeInfo, jfpath android.WritablePath) error {
diff --git a/java/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, "")...)
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index 831f6d4..37537ab 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -164,7 +164,6 @@
 	directories := flags.Bool("d", false, "include directories in zip")
 	compLevel := flags.Int("L", 5, "deflate compression level (0-9)")
 	emulateJar := flags.Bool("jar", false, "modify the resultant .zip to emulate the output of 'jar'")
-	sortEntries := flags.Bool("sort_entries", false, "sort the zip entries")
 	writeIfChanged := flags.Bool("write_if_changed", false, "only update resultant .zip if it has changed")
 	ignoreMissingFiles := flags.Bool("ignore_missing_files", false, "continue if a requested file does not exist")
 	symlinks := flags.Bool("symlinks", true, "store symbolic links in zip instead of following them")
@@ -229,7 +228,6 @@
 		FileArgs:                 fileArgsBuilder.FileArgs(),
 		OutputFilePath:           *out,
 		EmulateJar:               *emulateJar,
-		SortEntries:              *sortEntries,
 		SrcJar:                   *srcJar,
 		AddDirectoryEntriesToZip: *directories,
 		CompressionLevel:         *compLevel,
diff --git a/zip/zip.go b/zip/zip.go
index e4e9585..22b7704 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -272,7 +272,6 @@
 	FileArgs                 []FileArg
 	OutputFilePath           string
 	EmulateJar               bool
-	SortEntries              bool
 	SrcJar                   bool
 	AddDirectoryEntriesToZip bool
 	CompressionLevel         int
@@ -395,7 +394,7 @@
 		}
 	}
 
-	return z.write(w, pathMappings, args.ManifestSourcePath, args.EmulateJar, args.SortEntries, args.SrcJar, args.NumParallelJobs)
+	return z.write(w, pathMappings, args.ManifestSourcePath, args.EmulateJar, args.SrcJar, args.NumParallelJobs)
 }
 
 // Zip creates an output zip archive from given sources.
@@ -476,6 +475,42 @@
 	return nil
 }
 
+func (z *ZipWriter) moveJavaFileBasedOnPackage(mapping *pathMapping) error {
+	src := mapping.src
+	var s os.FileInfo
+	var err error
+	if z.followSymlinks {
+		s, err = z.fs.Stat(src)
+	} else {
+		s, err = z.fs.Lstat(src)
+	}
+	if err != nil {
+		if os.IsNotExist(err) && z.ignoreMissingFiles {
+			return nil
+		}
+		return err
+	}
+	if !s.Mode().IsRegular() {
+		return nil
+	}
+	r, err := z.fs.Open(src)
+	if err != nil {
+		return err
+	}
+	// rewrite the destination using the package path if it can be determined
+	pkg, err := jar.JavaPackage(r, src)
+	err2 := r.Close()
+	if err2 != nil {
+		return err2
+	}
+	if err != nil {
+		// ignore errors for now, leaving the file at in its original location in the zip
+	} else {
+		mapping.dest = filepath.Join(filepath.Join(strings.Split(pkg, ".")...), filepath.Base(src))
+	}
+	return nil
+}
+
 func jarSort(mappings []pathMapping) {
 	sort.SliceStable(mappings, func(i int, j int) bool {
 		return jar.EntryNamesLess(mappings[i].dest, mappings[j].dest)
@@ -483,7 +518,7 @@
 }
 
 func (z *ZipWriter) write(f io.Writer, pathMappings []pathMapping, manifest string,
-	emulateJar, sortEntries, srcJar bool,
+	emulateJar, srcJar bool,
 	parallelJobs int) error {
 
 	z.errors = make(chan error)
@@ -513,16 +548,38 @@
 		return errors.New("must specify --jar when specifying a manifest via -m")
 	}
 
-	if emulateJar && sortEntries {
-		return errors.New("Cannot specify both --jar and --sort_entries (--jar implies sorting with a different algorithm)")
+	// move java source files to the correct folder based on the package statement inside of them.
+	// This is done before the entry sorting so that they're still in the right order.
+	if srcJar {
+		var javaMoveErrors []error
+		var javaMoveErrorsLock sync.Mutex
+		var wg sync.WaitGroup
+		for i := range pathMappings {
+			if filepath.Ext(pathMappings[i].src) == ".java" {
+				wg.Add(1)
+				go func() {
+					err := z.moveJavaFileBasedOnPackage(&pathMappings[i])
+					if err != nil {
+						javaMoveErrorsLock.Lock()
+						javaMoveErrors = append(javaMoveErrors, err)
+						javaMoveErrorsLock.Unlock()
+					}
+					wg.Done()
+				}()
+			}
+		}
+		wg.Wait()
+		if len(javaMoveErrors) > 0 {
+			return errors.Join(javaMoveErrors...)
+		}
 	}
+
 	if emulateJar {
 		// manifest may be empty, in which case addManifest will fill in a default
 		pathMappings = append(pathMappings, pathMapping{jar.ManifestFile, manifest, zip.Deflate})
 
 		jarSort(pathMappings)
-	}
-	if sortEntries {
+	} else {
 		sort.SliceStable(pathMappings, func(i int, j int) bool {
 			return pathMappings[i].dest < pathMappings[j].dest
 		})
@@ -536,7 +593,7 @@
 			if emulateJar && ele.dest == jar.ManifestFile {
 				err = z.addManifest(ele.dest, ele.src, ele.zipMethod)
 			} else {
-				err = z.addFile(ele.dest, ele.src, ele.zipMethod, emulateJar, srcJar)
+				err = z.addFile(ele.dest, ele.src, ele.zipMethod, emulateJar)
 			}
 			if err != nil {
 				z.errors <- err
@@ -635,7 +692,7 @@
 }
 
 // imports (possibly with compression) <src> into the zip at sub-path <dest>
-func (z *ZipWriter) addFile(dest, src string, method uint16, emulateJar, srcJar bool) error {
+func (z *ZipWriter) addFile(dest, src string, method uint16, emulateJar bool) error {
 	var fileSize int64
 	var executable bool
 
@@ -709,21 +766,6 @@
 			return err
 		}
 
-		if srcJar && filepath.Ext(src) == ".java" {
-			// rewrite the destination using the package path if it can be determined
-			pkg, err := jar.JavaPackage(r, src)
-			if err != nil {
-				// ignore errors for now, leaving the file at in its original location in the zip
-			} else {
-				dest = filepath.Join(filepath.Join(strings.Split(pkg, ".")...), filepath.Base(src))
-			}
-
-			_, err = r.Seek(0, io.SeekStart)
-			if err != nil {
-				return err
-			}
-		}
-
 		fileSize = s.Size()
 		executable = s.Mode()&0100 != 0
 
diff --git a/zip/zip_test.go b/zip/zip_test.go
index c64c3f4..8f100d8 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -159,10 +159,10 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
+				fh("[", fileEmpty, zip.Store),
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
 				fh("c", fileC, zip.Deflate),
-				fh("[", fileEmpty, zip.Store),
 			},
 		},
 		{
@@ -261,10 +261,10 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
+				fh("[", fileEmpty, zip.Store),
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
 				fh("c", fileC, zip.Deflate),
-				fh("[", fileEmpty, zip.Store),
 			},
 		},
 		{
@@ -274,10 +274,10 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
+				fh("[", fileEmpty, zip.Store),
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
 				fh("c", fileC, zip.Deflate),
-				fh("[", fileEmpty, zip.Store),
 			},
 		},
 		{
@@ -287,11 +287,11 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
+				fh("@", fileC, zip.Deflate),
+				fh("[", fileEmpty, zip.Store),
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
-				fh("@", fileC, zip.Deflate),
 				fh("foo'bar", fileC, zip.Deflate),
-				fh("[", fileEmpty, zip.Store),
 			},
 		},
 		{
@@ -463,8 +463,8 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
-				fh("foo", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
+				fh("foo", fileA, zip.Deflate),
 			},
 		},
 		{
@@ -477,8 +477,8 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
-				fh("prefix/foo", fileA, zip.Deflate),
 				fh("prefix/a/a/b", fileB, zip.Deflate),
+				fh("prefix/foo", fileA, zip.Deflate),
 			},
 		},
 		{
@@ -490,8 +490,8 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
-				fh("foo", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
+				fh("foo", fileA, zip.Deflate),
 			},
 		},
 		{
@@ -504,8 +504,8 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
-				fh("foo/bar", fileA, zip.Deflate),
 				fh("b", fileB, zip.Deflate),
+				fh("foo/bar", fileA, zip.Deflate),
 			},
 		},
 
@@ -688,8 +688,8 @@
 
 	want := []string{
 		"foo/",
-		"foo/wrong_package.java",
 		"foo/correct_package.java",
+		"foo/wrong_package.java",
 		"no_package.java",
 		"src2/",
 		"src2/parse_error.java",