Merge "Update test for go modules with mutliple variants" into main
diff --git a/aconfig/build_flags/build_flags_singleton.go b/aconfig/build_flags/build_flags_singleton.go
index ba27a85..3b40755 100644
--- a/aconfig/build_flags/build_flags_singleton.go
+++ b/aconfig/build_flags/build_flags_singleton.go
@@ -114,7 +114,7 @@
 
 func (this *allBuildFlagDeclarationsSingleton) MakeVars(ctx android.MakeVarsContext) {
 	ctx.DistForGoal("droid", this.flagsBinaryProtoPath)
-	for _, goal := range []string{"docs", "droid", "sdk"} {
+	for _, goal := range []string{"docs", "droid", "sdk", "release_config_metadata"} {
 		ctx.DistForGoalWithFilename(goal, this.flagsBinaryProtoPath, "build_flags/all_flags.pb")
 		ctx.DistForGoalWithFilename(goal, this.flagsTextProtoPath, "build_flags/all_flags.textproto")
 		ctx.DistForGoalWithFilename(goal, this.configsBinaryProtoPath, "build_flags/all_release_config_contributions.pb")
diff --git a/android/Android.bp b/android/Android.bp
index c2bef0b..eb8c64d 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -71,6 +71,7 @@
         "module.go",
         "module_context.go",
         "module_info_json.go",
+        "module_proxy.go",
         "mutator.go",
         "namespace.go",
         "neverallow.go",
diff --git a/android/arch.go b/android/arch.go
index 1ec971d..e2d0d0d 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -1369,7 +1369,7 @@
 
 // Returns the structs corresponding to the properties specific to the given
 // architecture and OS in archProperties.
-func getArchProperties(ctx BaseMutatorContext, archProperties interface{}, arch Arch, os OsType, nativeBridgeEnabled bool) []reflect.Value {
+func getArchProperties(ctx BaseModuleContext, archProperties interface{}, arch Arch, os OsType, nativeBridgeEnabled bool) []reflect.Value {
 	result := make([]reflect.Value, 0)
 	archPropValues := reflect.ValueOf(archProperties).Elem()
 
diff --git a/android/base_module_context.go b/android/base_module_context.go
index c7d7573..670537f 100644
--- a/android/base_module_context.go
+++ b/android/base_module_context.go
@@ -33,6 +33,8 @@
 
 	blueprintBaseModuleContext() blueprint.BaseModuleContext
 
+	EqualModules(m1, m2 Module) bool
+
 	// OtherModuleName returns the name of another Module.  See BaseModuleContext.ModuleName for more information.
 	// It is intended for use inside the visit functions of Visit* and WalkDeps.
 	OtherModuleName(m blueprint.Module) string
@@ -130,6 +132,14 @@
 	// function, it may be invalidated by future mutators.
 	VisitDirectDepsAllowDisabled(visit func(Module))
 
+	// VisitDirectDepsProxyAllowDisabled calls visit for each direct dependency.  If there are
+	// multiple direct dependencies on the same module visit will be called multiple times on
+	// that module and OtherModuleDependencyTag will return a different tag for each.
+	//
+	// The Module passed to the visit function should not be retained outside of the visit function, it may be
+	// invalidated by future mutators.
+	VisitDirectDepsProxyAllowDisabled(visit func(proxy Module))
+
 	VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module))
 
 	// VisitDirectDepsIf calls pred for each direct dependency, and if pred returns true calls visit.  If there are
@@ -155,6 +165,16 @@
 	// invalidated by future mutators.
 	WalkDeps(visit func(child, parent Module) bool)
 
+	// WalkDeps calls visit for each transitive dependency, traversing the dependency tree in top down order.  visit may
+	// be called multiple times for the same (child, parent) pair if there are multiple direct dependencies between the
+	// child and parent with different tags.  OtherModuleDependencyTag will return the tag for the currently visited
+	// (child, parent) pair.  If visit returns false WalkDeps will not continue recursing down to child.  It skips
+	// any dependencies that are not an android.Module.
+	//
+	// The Modules passed to the visit function should not be retained outside of the visit function, they may be
+	// invalidated by future mutators.
+	WalkDepsProxy(visit func(child, parent Module) bool)
+
 	// GetWalkPath is supposed to be called in visit function passed in WalkDeps()
 	// and returns a top-down dependency path from a start module to current child module.
 	GetWalkPath() []Module
@@ -214,15 +234,26 @@
 
 }
 
+func getWrappedModule(module blueprint.Module) blueprint.Module {
+	if mp, isProxy := module.(ModuleProxy); isProxy {
+		return mp.module
+	}
+	return module
+}
+
+func (b *baseModuleContext) EqualModules(m1, m2 Module) bool {
+	return b.bp.EqualModules(getWrappedModule(m1), getWrappedModule(m2))
+}
+
 func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string {
-	return b.bp.OtherModuleName(m)
+	return b.bp.OtherModuleName(getWrappedModule(m))
 }
 func (b *baseModuleContext) OtherModuleDir(m blueprint.Module) string { return b.bp.OtherModuleDir(m) }
 func (b *baseModuleContext) OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{}) {
 	b.bp.OtherModuleErrorf(m, fmt, args...)
 }
 func (b *baseModuleContext) OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag {
-	return b.bp.OtherModuleDependencyTag(m)
+	return b.bp.OtherModuleDependencyTag(getWrappedModule(m))
 }
 func (b *baseModuleContext) OtherModuleExists(name string) bool { return b.bp.OtherModuleExists(name) }
 func (b *baseModuleContext) OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool {
@@ -395,6 +426,14 @@
 	})
 }
 
+func (b *baseModuleContext) VisitDirectDepsProxyAllowDisabled(visit func(proxy Module)) {
+	b.bp.VisitDirectDepsProxy(func(module blueprint.ModuleProxy) {
+		visit(ModuleProxy{
+			module: module,
+		})
+	})
+}
+
 func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) {
 	b.bp.VisitDirectDeps(func(module blueprint.Module) {
 		if b.bp.OtherModuleDependencyTag(module) == tag {
@@ -466,6 +505,23 @@
 	})
 }
 
+func (b *baseModuleContext) WalkDepsProxy(visit func(Module, Module) bool) {
+	b.walkPath = []Module{ModuleProxy{blueprint.CreateModuleProxy(b.Module())}}
+	b.tagPath = []blueprint.DependencyTag{}
+	b.bp.WalkDepsProxy(func(child, parent blueprint.ModuleProxy) bool {
+		childAndroidModule := ModuleProxy{child}
+		parentAndroidModule := ModuleProxy{parent}
+		// record walkPath before visit
+		for b.walkPath[len(b.walkPath)-1] != parentAndroidModule {
+			b.walkPath = b.walkPath[0 : len(b.walkPath)-1]
+			b.tagPath = b.tagPath[0 : len(b.tagPath)-1]
+		}
+		b.walkPath = append(b.walkPath, childAndroidModule)
+		b.tagPath = append(b.tagPath, b.OtherModuleDependencyTag(childAndroidModule))
+		return visit(childAndroidModule, parentAndroidModule)
+	})
+}
+
 func (b *baseModuleContext) GetWalkPath() []Module {
 	return b.walkPath
 }
diff --git a/android/compliance_metadata.go b/android/compliance_metadata.go
index 38f1382..d28831e 100644
--- a/android/compliance_metadata.go
+++ b/android/compliance_metadata.go
@@ -17,7 +17,6 @@
 import (
 	"bytes"
 	"encoding/csv"
-	"encoding/gob"
 	"fmt"
 	"slices"
 	"strconv"
@@ -126,32 +125,32 @@
 	properties map[string]string
 }
 
+type complianceMetadataInfoGob struct {
+	Properties map[string]string
+}
+
 func NewComplianceMetadataInfo() *ComplianceMetadataInfo {
 	return &ComplianceMetadataInfo{
 		properties: map[string]string{},
 	}
 }
 
-func (c *ComplianceMetadataInfo) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := encoder.Encode(c.properties)
-	if err != nil {
-		return nil, err
+func (m *ComplianceMetadataInfo) ToGob() *complianceMetadataInfoGob {
+	return &complianceMetadataInfoGob{
+		Properties: m.properties,
 	}
+}
 
-	return w.Bytes(), nil
+func (m *ComplianceMetadataInfo) FromGob(data *complianceMetadataInfoGob) {
+	m.properties = data.Properties
+}
+
+func (c *ComplianceMetadataInfo) GobEncode() ([]byte, error) {
+	return blueprint.CustomGobEncode[complianceMetadataInfoGob](c)
 }
 
 func (c *ComplianceMetadataInfo) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := decoder.Decode(&c.properties)
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return blueprint.CustomGobDecode[complianceMetadataInfoGob](data, c)
 }
 
 func (c *ComplianceMetadataInfo) SetStringValue(propertyName string, value string) {
diff --git a/android/config.go b/android/config.go
index 10e43ce..06d71c0 100644
--- a/android/config.go
+++ b/android/config.go
@@ -287,6 +287,10 @@
 	return c.config.productVariables.GetBuildFlagBool("RELEASE_READ_FROM_NEW_STORAGE")
 }
 
+func (c Config) ReleaseCreateAconfigStorageFile() bool {
+	return c.config.productVariables.GetBuildFlagBool("RELEASE_CREATE_ACONFIG_STORAGE_FILE")
+}
+
 // A DeviceConfig object represents the configuration for a particular device
 // being built. For now there will only be one of these, but in the future there
 // may be multiple devices being built.
@@ -2086,6 +2090,10 @@
 	return PathsForSource(ctx, c.productVariables.OdmPropFiles)
 }
 
+func (c *config) ExtraAllowedDepsTxt() string {
+	return String(c.productVariables.ExtraAllowedDepsTxt)
+}
+
 func (c *config) EnableUffdGc() string {
 	return String(c.productVariables.EnableUffdGc)
 }
@@ -2105,3 +2113,10 @@
 func (c *config) BoardAvbSystemAddHashtreeFooterArgs() []string {
 	return c.productVariables.BoardAvbSystemAddHashtreeFooterArgs
 }
+
+// Returns true if RELEASE_INSTALL_APEX_SYSTEMSERVER_DEXPREOPT_SAME_PARTITION is set to true.
+// If true, dexpreopt files of apex system server jars will be installed in the same partition as the parent apex.
+// If false, all these files will be installed in /system partition.
+func (c Config) InstallApexSystemServerDexpreoptSamePartition() bool {
+	return c.config.productVariables.GetBuildFlagBool("RELEASE_INSTALL_APEX_SYSTEMSERVER_DEXPREOPT_SAME_PARTITION")
+}
diff --git a/android/defaults.go b/android/defaults.go
index 3d06c69..8fe2879 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -274,7 +274,7 @@
 
 func RegisterDefaultsPreArchMutators(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("defaults_deps", defaultsDepsMutator).Parallel()
-	ctx.BottomUp("defaults", defaultsMutator).Parallel()
+	ctx.BottomUp("defaults", defaultsMutator).Parallel().UsesCreateModule()
 }
 
 func defaultsDepsMutator(ctx BottomUpMutatorContext) {
diff --git a/android/depset_generic.go b/android/depset_generic.go
index 690987a..d04f88b 100644
--- a/android/depset_generic.go
+++ b/android/depset_generic.go
@@ -15,10 +15,9 @@
 package android
 
 import (
-	"bytes"
-	"encoding/gob"
-	"errors"
 	"fmt"
+
+	"github.com/google/blueprint"
 )
 
 // DepSet is designed to be conceptually compatible with Bazel's depsets:
@@ -68,28 +67,38 @@
 	transitive []*DepSet[T]
 }
 
-func (d *DepSet[T]) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(d.preorder), encoder.Encode(d.reverse),
-		encoder.Encode(d.order), encoder.Encode(d.direct), encoder.Encode(d.transitive))
-	if err != nil {
-		return nil, err
-	}
+type depSetGob[T depSettableType] struct {
+	Preorder   bool
+	Reverse    bool
+	Order      DepSetOrder
+	Direct     []T
+	Transitive []*DepSet[T]
+}
 
-	return w.Bytes(), nil
+func (d *DepSet[T]) ToGob() *depSetGob[T] {
+	return &depSetGob[T]{
+		Preorder:   d.preorder,
+		Reverse:    d.reverse,
+		Order:      d.order,
+		Direct:     d.direct,
+		Transitive: d.transitive,
+	}
+}
+
+func (d *DepSet[T]) FromGob(data *depSetGob[T]) {
+	d.preorder = data.Preorder
+	d.reverse = data.Reverse
+	d.order = data.Order
+	d.direct = data.Direct
+	d.transitive = data.Transitive
+}
+
+func (d *DepSet[T]) GobEncode() ([]byte, error) {
+	return blueprint.CustomGobEncode[depSetGob[T]](d)
 }
 
 func (d *DepSet[T]) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&d.preorder), decoder.Decode(&d.reverse),
-		decoder.Decode(&d.order), decoder.Decode(&d.direct), decoder.Decode(&d.transitive))
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return blueprint.CustomGobDecode[depSetGob[T]](data, d)
 }
 
 // NewDepSet returns an immutable DepSet with the given order, direct and transitive contents.
diff --git a/android/early_module_context.go b/android/early_module_context.go
index 11de771..9b1a9ea 100644
--- a/android/early_module_context.go
+++ b/android/early_module_context.go
@@ -29,7 +29,7 @@
 	Module() Module
 
 	// ModuleName returns the name of the module.  This is generally the value that was returned by Module.Name() when
-	// the module was created, but may have been modified by calls to BaseMutatorContext.Rename.
+	// the module was created, but may have been modified by calls to BottomUpMutatorContext.Rename.
 	ModuleName() string
 
 	// ModuleDir returns the path to the directory that contains the definition of the module.
diff --git a/android/init.go b/android/init.go
index b462292..1ace344 100644
--- a/android/init.go
+++ b/android/init.go
@@ -17,7 +17,12 @@
 import "encoding/gob"
 
 func init() {
+	gob.Register(extraFilesZip{})
+	gob.Register(InstallPath{})
+	gob.Register(ModuleGenPath{})
 	gob.Register(ModuleOutPath{})
+	gob.Register(OutputPath{})
 	gob.Register(PhonyPath{})
+	gob.Register(SourcePath{})
 	gob.Register(unstableInfo{})
 }
diff --git a/android/module.go b/android/module.go
index 20caae2..44f7583 100644
--- a/android/module.go
+++ b/android/module.go
@@ -15,9 +15,6 @@
 package android
 
 import (
-	"bytes"
-	"encoding/gob"
-	"errors"
 	"fmt"
 	"net/url"
 	"path/filepath"
@@ -81,6 +78,7 @@
 	InstallInOdm() bool
 	InstallInProduct() bool
 	InstallInVendor() bool
+	InstallInSystemExt() bool
 	InstallForceOS() (*OsType, *ArchType)
 	PartitionTag(DeviceConfig) string
 	HideFromMake()
@@ -1005,14 +1003,6 @@
 			return
 		}
 
-		// Do not create a dependency from common variant to arch variant for `common_first` modules
-		if multilib, _ := decodeMultilib(ctx, ctx.Module().base()); multilib == string(MultilibCommonFirst) {
-			commonVariant := ctx.Arch().ArchType.Multilib == ""
-			if bothInAndroid && commonVariant && InList(target.Arch.ArchType.Multilib, []string{"lib32", "lib64"}) {
-				return
-			}
-		}
-
 		variation := target.Variations()
 		if ctx.OtherModuleFarDependencyVariantExists(variation, depName) {
 			ctx.AddFarVariationDependencies(variation, RequiredDepTag, depName)
@@ -1514,6 +1504,10 @@
 	return Bool(m.commonProperties.Vendor) || Bool(m.commonProperties.Soc_specific) || Bool(m.commonProperties.Proprietary)
 }
 
+func (m *ModuleBase) InstallInSystemExt() bool {
+	return Bool(m.commonProperties.System_ext_specific)
+}
+
 func (m *ModuleBase) InstallInRoot() bool {
 	return false
 }
@@ -1801,6 +1795,26 @@
 
 var FinalModuleBuildTargetsProvider = blueprint.NewProvider[FinalModuleBuildTargetsInfo]()
 
+type CommonPropertiesProviderData struct {
+	Enabled bool
+	// Whether the module has been replaced by a prebuilt
+	ReplacedByPrebuilt bool
+}
+
+var CommonPropertiesProviderKey = blueprint.NewProvider[CommonPropertiesProviderData]()
+
+type PrebuiltModuleProviderData struct {
+	// Empty for now
+}
+
+var PrebuiltModuleProviderKey = blueprint.NewProvider[PrebuiltModuleProviderData]()
+
+type HostToolProviderData struct {
+	HostToolPath OptionalPath
+}
+
+var HostToolProviderKey = blueprint.NewProvider[HostToolProviderData]()
+
 func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) {
 	ctx := &moduleContext{
 		module:            m.module,
@@ -2046,6 +2060,23 @@
 		})
 	}
 	buildComplianceMetadataProvider(ctx, m)
+
+	commonData := CommonPropertiesProviderData{
+		ReplacedByPrebuilt: m.commonProperties.ReplacedByPrebuilt,
+	}
+	if m.commonProperties.ForcedDisabled {
+		commonData.Enabled = false
+	} else {
+		commonData.Enabled = m.commonProperties.Enabled.GetOrDefault(m.ConfigurableEvaluator(ctx), !m.Os().DefaultDisabled)
+	}
+	SetProvider(ctx, CommonPropertiesProviderKey, commonData)
+	if p, ok := m.module.(PrebuiltInterface); ok && p.Prebuilt() != nil {
+		SetProvider(ctx, PrebuiltModuleProviderKey, PrebuiltModuleProviderData{})
+	}
+	if h, ok := m.module.(HostToolProvider); ok {
+		SetProvider(ctx, HostToolProviderKey, HostToolProviderData{
+			HostToolPath: h.HostToolPath()})
+	}
 }
 
 func SetJarJarPrefixHandler(handler func(ModuleContext)) {
@@ -2125,36 +2156,47 @@
 	orderOnlyDeps Paths
 	executable    bool
 	extraFiles    *extraFilesZip
-
-	absFrom string
+	absFrom       string
 }
 
-func (p *katiInstall) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.from), encoder.Encode(p.to),
-		encoder.Encode(p.implicitDeps), encoder.Encode(p.orderOnlyDeps),
-		encoder.Encode(p.executable), encoder.Encode(p.extraFiles),
-		encoder.Encode(p.absFrom))
-	if err != nil {
-		return nil, err
-	}
-
-	return w.Bytes(), nil
+type katiInstallGob struct {
+	From          Path
+	To            InstallPath
+	ImplicitDeps  Paths
+	OrderOnlyDeps Paths
+	Executable    bool
+	ExtraFiles    *extraFilesZip
+	AbsFrom       string
 }
 
-func (p *katiInstall) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.from), decoder.Decode(&p.to),
-		decoder.Decode(&p.implicitDeps), decoder.Decode(&p.orderOnlyDeps),
-		decoder.Decode(&p.executable), decoder.Decode(&p.extraFiles),
-		decoder.Decode(&p.absFrom))
-	if err != nil {
-		return err
+func (k *katiInstall) ToGob() *katiInstallGob {
+	return &katiInstallGob{
+		From:          k.from,
+		To:            k.to,
+		ImplicitDeps:  k.implicitDeps,
+		OrderOnlyDeps: k.orderOnlyDeps,
+		Executable:    k.executable,
+		ExtraFiles:    k.extraFiles,
+		AbsFrom:       k.absFrom,
 	}
+}
 
-	return nil
+func (k *katiInstall) FromGob(data *katiInstallGob) {
+	k.from = data.From
+	k.to = data.To
+	k.implicitDeps = data.ImplicitDeps
+	k.orderOnlyDeps = data.OrderOnlyDeps
+	k.executable = data.Executable
+	k.extraFiles = data.ExtraFiles
+	k.absFrom = data.AbsFrom
+}
+
+func (k *katiInstall) GobEncode() ([]byte, error) {
+	return blueprint.CustomGobEncode[katiInstallGob](k)
+}
+
+func (k *katiInstall) GobDecode(data []byte) error {
+	return blueprint.CustomGobDecode[katiInstallGob](data, k)
 }
 
 type extraFilesZip struct {
@@ -2162,26 +2204,29 @@
 	dir InstallPath
 }
 
-func (p *extraFilesZip) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.zip), encoder.Encode(p.dir))
-	if err != nil {
-		return nil, err
-	}
-
-	return w.Bytes(), nil
+type extraFilesZipGob struct {
+	Zip Path
+	Dir InstallPath
 }
 
-func (p *extraFilesZip) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.zip), decoder.Decode(&p.dir))
-	if err != nil {
-		return err
+func (e *extraFilesZip) ToGob() *extraFilesZipGob {
+	return &extraFilesZipGob{
+		Zip: e.zip,
+		Dir: e.dir,
 	}
+}
 
-	return nil
+func (e *extraFilesZip) FromGob(data *extraFilesZipGob) {
+	e.zip = data.Zip
+	e.dir = data.Dir
+}
+
+func (e *extraFilesZip) GobEncode() ([]byte, error) {
+	return blueprint.CustomGobEncode[extraFilesZipGob](e)
+}
+
+func (e *extraFilesZip) GobDecode(data []byte) error {
+	return blueprint.CustomGobDecode[extraFilesZipGob](data, e)
 }
 
 type katiInstalls []katiInstall
diff --git a/android/module_proxy.go b/android/module_proxy.go
new file mode 100644
index 0000000..bc5090e
--- /dev/null
+++ b/android/module_proxy.go
@@ -0,0 +1,203 @@
+package android
+
+import (
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+type ModuleProxy struct {
+	module blueprint.ModuleProxy
+}
+
+func (m ModuleProxy) Name() string {
+	return m.module.Name()
+}
+
+func (m ModuleProxy) GenerateBuildActions(context blueprint.ModuleContext) {
+	m.module.GenerateBuildActions(context)
+}
+
+func (m ModuleProxy) GenerateAndroidBuildActions(context ModuleContext) {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) ComponentDepsMutator(ctx BottomUpMutatorContext) {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) DepsMutator(context BottomUpMutatorContext) {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) base() *ModuleBase {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) Disable() {
+
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) Enabled(ctx ConfigurableEvaluatorContext) bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) Target() Target {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) MultiTargets() []Target {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) ImageVariation() blueprint.Variation {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) Owner() string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInData() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInTestcases() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInSanitizerDir() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInRamdisk() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInVendorRamdisk() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInDebugRamdisk() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInRecovery() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInRoot() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInOdm() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInProduct() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInVendor() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInSystemExt() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallForceOS() (*OsType, *ArchType) {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) PartitionTag(d DeviceConfig) string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) HideFromMake() {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) IsHideFromMake() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) IsSkipInstall() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) MakeUninstallable() {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) ReplacedByPrebuilt() {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) IsReplacedByPrebuilt() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) ExportedToMake() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) EffectiveLicenseKinds() []string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) EffectiveLicenseFiles() Paths {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) AddProperties(props ...interface{}) {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) GetProperties() []interface{} {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) BuildParamsForTests() []BuildParams {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) VariablesForTests() map[string]string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) String() string {
+	return m.module.Name()
+}
+
+func (m ModuleProxy) qualifiedModuleId(ctx BaseModuleContext) qualifiedModuleName {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) visibilityProperties() []visibilityProperty {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) HostRequiredModuleNames() []string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) TargetRequiredModuleNames() []string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) VintfFragmentModuleNames(ctx ConfigurableEvaluatorContext) []string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) ConfigurableEvaluator(ctx ConfigurableEvaluatorContext) proptools.ConfigurableEvaluator {
+	panic("method is not implemented on ModuleProxy")
+}
diff --git a/android/mutator.go b/android/mutator.go
index a8b5c7d..0da3ec7 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -32,11 +32,11 @@
 // collateGloballyRegisteredMutators constructs the list of mutators that have been registered
 // with the InitRegistrationContext and will be used at runtime.
 func collateGloballyRegisteredMutators() sortableComponents {
-	return collateRegisteredMutators(preArch, preDeps, postDeps, finalDeps)
+	return collateRegisteredMutators(preArch, preDeps, postDeps, postApex, finalDeps)
 }
 
 // collateRegisteredMutators constructs a single list of mutators from the separate lists.
-func collateRegisteredMutators(preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc) sortableComponents {
+func collateRegisteredMutators(preArch, preDeps, postDeps, postApex, finalDeps []RegisterMutatorFunc) sortableComponents {
 	mctx := &registerMutatorsContext{}
 
 	register := func(funcs []RegisterMutatorFunc) {
@@ -53,6 +53,8 @@
 
 	register(postDeps)
 
+	register(postApex)
+
 	mctx.finalPhase = true
 	register(finalDeps)
 
@@ -166,6 +168,8 @@
 	RegisterOverridePostDepsMutators,
 }
 
+var postApex = []RegisterMutatorFunc{}
+
 var finalDeps = []RegisterMutatorFunc{}
 
 func PreArchMutators(f RegisterMutatorFunc) {
@@ -180,29 +184,18 @@
 	postDeps = append(postDeps, f)
 }
 
-func FinalDepsMutators(f RegisterMutatorFunc) {
-	finalDeps = append(finalDeps, f)
+func PostApexMutators(f RegisterMutatorFunc) {
+	postApex = append(postApex, f)
 }
 
-type BaseMutatorContext interface {
-	BaseModuleContext
-
-	// MutatorName returns the name that this mutator was registered with.
-	MutatorName() string
-
-	// Rename all variants of a module.  The new name is not visible to calls to ModuleName,
-	// AddDependency or OtherModuleName until after this mutator pass is complete.
-	Rename(name string)
-
-	// CreateModule creates a new module by calling the factory method for the specified moduleType, and applies
-	// the specified property structs to it as if the properties were set in a blueprint file.
-	CreateModule(ModuleFactory, ...interface{}) Module
+func FinalDepsMutators(f RegisterMutatorFunc) {
+	finalDeps = append(finalDeps, f)
 }
 
 type TopDownMutator func(TopDownMutatorContext)
 
 type TopDownMutatorContext interface {
-	BaseMutatorContext
+	BaseModuleContext
 }
 
 type topDownMutatorContext struct {
@@ -213,7 +206,7 @@
 type BottomUpMutator func(BottomUpMutatorContext)
 
 type BottomUpMutatorContext interface {
-	BaseMutatorContext
+	BaseModuleContext
 
 	// AddDependency adds a dependency to the given module.  It returns a slice of modules for each
 	// dependency (some entries may be nil).
@@ -228,7 +221,8 @@
 	// Does not affect the ordering of the current mutator pass, but will be ordered
 	// correctly for all future mutator passes.  All reverse dependencies for a destination module are
 	// collected until the end of the mutator pass, sorted by name, and then appended to the destination
-	// module's dependency list.
+	// module's dependency list.  May only  be called by mutators that were marked with
+	// UsesReverseDependencies during registration.
 	AddReverseDependency(module blueprint.Module, tag blueprint.DependencyTag, name string)
 
 	// AddVariationDependencies adds deps as dependencies of the current module, but uses the variations
@@ -242,14 +236,15 @@
 	// be ordered correctly for all future mutator passes.
 	AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag, names ...string) []blueprint.Module
 
-	// AddReverseVariationDependencies adds a dependency from the named module to the current
+	// AddReverseVariationDependency adds a dependency from the named module to the current
 	// module. The given variations will be added to the current module's varations, and then the
 	// result will be used to find the correct variation of the depending module, which must exist.
 	//
 	// Does not affect the ordering of the current mutator pass, but will be ordered
 	// correctly for all future mutator passes.  All reverse dependencies for a destination module are
 	// collected until the end of the mutator pass, sorted by name, and then appended to the destination
-	// module's dependency list.
+	// module's dependency list.  May only  be called by mutators that were marked with
+	// UsesReverseDependencies during registration.
 	AddReverseVariationDependency([]blueprint.Variation, blueprint.DependencyTag, string)
 
 	// AddFarVariationDependencies adds deps as dependencies of the current module, but uses the
@@ -269,14 +264,26 @@
 
 	// ReplaceDependencies finds all the variants of the module with the specified name, then
 	// replaces all dependencies onto those variants with the current variant of this module.
-	// Replacements don't take effect until after the mutator pass is finished.
+	// Replacements don't take effect until after the mutator pass is finished.  May only
+	// be called by mutators that were marked with UsesReplaceDependencies during registration.
 	ReplaceDependencies(string)
 
 	// ReplaceDependenciesIf finds all the variants of the module with the specified name, then
 	// replaces all dependencies onto those variants with the current variant of this module
 	// as long as the supplied predicate returns true.
-	// Replacements don't take effect until after the mutator pass is finished.
+	// Replacements don't take effect until after the mutator pass is finished.  May only
+	// be called by mutators that were marked with UsesReplaceDependencies during registration.
 	ReplaceDependenciesIf(string, blueprint.ReplaceDependencyPredicate)
+
+	// Rename all variants of a module.  The new name is not visible to calls to ModuleName,
+	// AddDependency or OtherModuleName until after this mutator pass is complete.  May only be called
+	// by mutators that were marked with UsesRename during registration.
+	Rename(name string)
+
+	// CreateModule creates a new module by calling the factory method for the specified moduleType, and applies
+	// the specified property structs to it as if the properties were set in a blueprint file.  May only
+	// be called by mutators that were marked with UsesCreateModule during registration.
+	CreateModule(ModuleFactory, ...interface{}) Module
 }
 
 // An outgoingTransitionContextImpl and incomingTransitionContextImpl is created for every dependency of every module
@@ -619,13 +626,60 @@
 	} else if mutator.transitionMutator != nil {
 		blueprintCtx.RegisterTransitionMutator(mutator.name, mutator.transitionMutator)
 	}
+
+	// Forward booleans set on the MutatorHandle to the blueprint.MutatorHandle.
 	if mutator.parallel {
 		handle.Parallel()
 	}
+	if mutator.usesRename {
+		handle.UsesRename()
+	}
+	if mutator.usesReverseDependencies {
+		handle.UsesReverseDependencies()
+	}
+	if mutator.usesReplaceDependencies {
+		handle.UsesReplaceDependencies()
+	}
+	if mutator.usesCreateModule {
+		handle.UsesCreateModule()
+	}
+	if mutator.mutatesDependencies {
+		handle.MutatesDependencies()
+	}
+	if mutator.mutatesGlobalState {
+		handle.MutatesGlobalState()
+	}
 }
 
 type MutatorHandle interface {
+	// Parallel sets the mutator to visit modules in parallel while maintaining ordering.  Calling any
+	// method on the mutator context is thread-safe, but the mutator must handle synchronization
+	// for any modifications to global state or any modules outside the one it was invoked on.
 	Parallel() MutatorHandle
+
+	// UsesRename marks the mutator as using the BottomUpMutatorContext.Rename method, which prevents
+	// coalescing adjacent mutators into a single mutator pass.
+	UsesRename() MutatorHandle
+
+	// UsesReverseDependencies marks the mutator as using the BottomUpMutatorContext.AddReverseDependency
+	// method, which prevents coalescing adjacent mutators into a single mutator pass.
+	UsesReverseDependencies() MutatorHandle
+
+	// UsesReplaceDependencies marks the mutator as using the BottomUpMutatorContext.ReplaceDependencies
+	// method, which prevents coalescing adjacent mutators into a single mutator pass.
+	UsesReplaceDependencies() MutatorHandle
+
+	// UsesCreateModule marks the mutator as using the BottomUpMutatorContext.CreateModule method,
+	// which prevents coalescing adjacent mutators into a single mutator pass.
+	UsesCreateModule() MutatorHandle
+
+	// MutatesDependencies marks the mutator as modifying properties in dependencies, which prevents
+	// coalescing adjacent mutators into a single mutator pass.
+	MutatesDependencies() MutatorHandle
+
+	// MutatesGlobalState marks the mutator as modifying global state, which prevents coalescing
+	// adjacent mutators into a single mutator pass.
+	MutatesGlobalState() MutatorHandle
 }
 
 func (mutator *mutator) Parallel() MutatorHandle {
@@ -633,6 +687,36 @@
 	return mutator
 }
 
+func (mutator *mutator) UsesRename() MutatorHandle {
+	mutator.usesRename = true
+	return mutator
+}
+
+func (mutator *mutator) UsesReverseDependencies() MutatorHandle {
+	mutator.usesReverseDependencies = true
+	return mutator
+}
+
+func (mutator *mutator) UsesReplaceDependencies() MutatorHandle {
+	mutator.usesReplaceDependencies = true
+	return mutator
+}
+
+func (mutator *mutator) UsesCreateModule() MutatorHandle {
+	mutator.usesCreateModule = true
+	return mutator
+}
+
+func (mutator *mutator) MutatesDependencies() MutatorHandle {
+	mutator.mutatesDependencies = true
+	return mutator
+}
+
+func (mutator *mutator) MutatesGlobalState() MutatorHandle {
+	mutator.mutatesGlobalState = true
+	return mutator
+}
+
 func RegisterComponentsMutator(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("component-deps", componentDepsMutator).Parallel()
 }
@@ -652,7 +736,7 @@
 }
 
 func registerDepsMutator(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("deps", depsMutator).Parallel()
+	ctx.BottomUp("deps", depsMutator).Parallel().UsesReverseDependencies()
 }
 
 // android.topDownMutatorContext either has to embed blueprint.TopDownMutatorContext, in which case every method that
@@ -661,32 +745,6 @@
 // non-overridden method has to be forwarded.  There are fewer non-overridden methods, so use the latter.  The following
 // methods forward to the identical blueprint versions for topDownMutatorContext and bottomUpMutatorContext.
 
-func (t *topDownMutatorContext) MutatorName() string {
-	return t.bp.MutatorName()
-}
-
-func (t *topDownMutatorContext) Rename(name string) {
-	t.bp.Rename(name)
-	t.Module().base().commonProperties.DebugName = name
-}
-
-func (t *topDownMutatorContext) createModule(factory blueprint.ModuleFactory, name string, props ...interface{}) blueprint.Module {
-	return t.bp.CreateModule(factory, name, props...)
-}
-
-func (t *topDownMutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
-	return createModule(t, factory, "_topDownMutatorModule", props...)
-}
-
-func (t *topDownMutatorContext) createModuleWithoutInheritance(factory ModuleFactory, props ...interface{}) Module {
-	module := t.bp.CreateModule(ModuleFactoryAdaptor(factory), "", props...).(Module)
-	return module
-}
-
-func (b *bottomUpMutatorContext) MutatorName() string {
-	return b.bp.MutatorName()
-}
-
 func (b *bottomUpMutatorContext) Rename(name string) {
 	b.bp.Rename(name)
 	b.Module().base().commonProperties.DebugName = name
diff --git a/android/mutator_test.go b/android/mutator_test.go
index 5d4074a..33fca9e 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -134,10 +134,6 @@
 						return []string{"a", "b"}
 					},
 				})
-				ctx.TopDown("rename_top_down", func(ctx TopDownMutatorContext) {
-					moduleStrings = append(moduleStrings, ctx.Module().String())
-					ctx.Rename(ctx.Module().base().Name() + "_renamed1")
-				})
 			})
 
 			ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
@@ -161,8 +157,8 @@
 				})
 				ctx.BottomUp("rename_bottom_up", func(ctx BottomUpMutatorContext) {
 					moduleStrings = append(moduleStrings, ctx.Module().String())
-					ctx.Rename(ctx.Module().base().Name() + "_renamed2")
-				})
+					ctx.Rename(ctx.Module().base().Name() + "_renamed1")
+				}).UsesRename()
 				ctx.BottomUp("final", func(ctx BottomUpMutatorContext) {
 					moduleStrings = append(moduleStrings, ctx.Module().String())
 				})
@@ -181,17 +177,23 @@
 		"foo{pre_arch:b}",
 		"foo{pre_arch:a}",
 
-		// After rename_top_down (reversed because pre_deps TransitionMutator.Split is TopDown).
-		"foo_renamed1{pre_arch:b}",
-		"foo_renamed1{pre_arch:a}",
-
 		// After pre_deps (reversed because post_deps TransitionMutator.Split is TopDown).
-		"foo_renamed1{pre_arch:b,pre_deps:d}",
-		"foo_renamed1{pre_arch:b,pre_deps:c}",
-		"foo_renamed1{pre_arch:a,pre_deps:d}",
-		"foo_renamed1{pre_arch:a,pre_deps:c}",
+		"foo{pre_arch:b,pre_deps:d}",
+		"foo{pre_arch:b,pre_deps:c}",
+		"foo{pre_arch:a,pre_deps:d}",
+		"foo{pre_arch:a,pre_deps:c}",
 
 		// After post_deps.
+		"foo{pre_arch:a,pre_deps:c,post_deps:e}",
+		"foo{pre_arch:a,pre_deps:c,post_deps:f}",
+		"foo{pre_arch:a,pre_deps:d,post_deps:e}",
+		"foo{pre_arch:a,pre_deps:d,post_deps:f}",
+		"foo{pre_arch:b,pre_deps:c,post_deps:e}",
+		"foo{pre_arch:b,pre_deps:c,post_deps:f}",
+		"foo{pre_arch:b,pre_deps:d,post_deps:e}",
+		"foo{pre_arch:b,pre_deps:d,post_deps:f}",
+
+		// After rename_bottom_up.
 		"foo_renamed1{pre_arch:a,pre_deps:c,post_deps:e}",
 		"foo_renamed1{pre_arch:a,pre_deps:c,post_deps:f}",
 		"foo_renamed1{pre_arch:a,pre_deps:d,post_deps:e}",
@@ -200,16 +202,6 @@
 		"foo_renamed1{pre_arch:b,pre_deps:c,post_deps:f}",
 		"foo_renamed1{pre_arch:b,pre_deps:d,post_deps:e}",
 		"foo_renamed1{pre_arch:b,pre_deps:d,post_deps:f}",
-
-		// After rename_bottom_up.
-		"foo_renamed2{pre_arch:a,pre_deps:c,post_deps:e}",
-		"foo_renamed2{pre_arch:a,pre_deps:c,post_deps:f}",
-		"foo_renamed2{pre_arch:a,pre_deps:d,post_deps:e}",
-		"foo_renamed2{pre_arch:a,pre_deps:d,post_deps:f}",
-		"foo_renamed2{pre_arch:b,pre_deps:c,post_deps:e}",
-		"foo_renamed2{pre_arch:b,pre_deps:c,post_deps:f}",
-		"foo_renamed2{pre_arch:b,pre_deps:d,post_deps:e}",
-		"foo_renamed2{pre_arch:b,pre_deps:d,post_deps:f}",
 	}
 
 	AssertDeepEquals(t, "module String() values", want, moduleStrings)
diff --git a/android/namespace.go b/android/namespace.go
index ebf85a1..866d125 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -457,7 +457,7 @@
 }
 
 func RegisterNamespaceMutator(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("namespace_deps", namespaceMutator).Parallel()
+	ctx.BottomUp("namespace_deps", namespaceMutator).Parallel().MutatesGlobalState()
 }
 
 func namespaceMutator(ctx BottomUpMutatorContext) {
diff --git a/android/namespace_test.go b/android/namespace_test.go
index ea51c6e..0327e78 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -646,7 +646,7 @@
 		ctx.RegisterModuleType("test_module", newTestModule)
 		ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
 		ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-			ctx.BottomUp("rename", renameMutator)
+			ctx.BottomUp("rename", renameMutator).UsesRename()
 		})
 	}),
 )
@@ -709,9 +709,6 @@
 }
 
 func (m *testModule) DepsMutator(ctx BottomUpMutatorContext) {
-	if m.properties.Rename != "" {
-		ctx.Rename(m.properties.Rename)
-	}
 	for _, d := range m.properties.Deps {
 		ctx.AddDependency(ctx.Module(), nil, d)
 	}
diff --git a/android/override_module.go b/android/override_module.go
index f69f963..d844da6 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -234,8 +234,9 @@
 // Mutators for override/overridable modules. All the fun happens in these functions. It is critical
 // to keep them in this order and not put any order mutators between them.
 func RegisterOverridePostDepsMutators(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("override_deps", overrideModuleDepsMutator).Parallel()
+	ctx.BottomUp("override_deps", overrideModuleDepsMutator).Parallel().MutatesDependencies() // modifies deps via addOverride
 	ctx.Transition("override", &overrideTransitionMutator{})
+	ctx.BottomUp("override_apply", overrideApplyMutator).Parallel().MutatesDependencies()
 	// overridableModuleDepsMutator calls OverridablePropertiesDepsMutator so that overridable modules can
 	// add deps from overridable properties.
 	ctx.BottomUp("overridable_deps", overridableModuleDepsMutator).Parallel()
@@ -243,8 +244,8 @@
 	// prebuilt's ReplaceDependencies doesn't affect to those deps added by overridable properties.
 	// By running PrebuiltPostDepsMutator again after overridableModuleDepsMutator, deps via overridable properties
 	// can be replaced with prebuilts.
-	ctx.BottomUp("replace_deps_on_prebuilts_for_overridable_deps_again", PrebuiltPostDepsMutator).Parallel()
-	ctx.BottomUp("replace_deps_on_override", replaceDepsOnOverridingModuleMutator).Parallel()
+	ctx.BottomUp("replace_deps_on_prebuilts_for_overridable_deps_again", PrebuiltPostDepsMutator).Parallel().UsesReplaceDependencies()
+	ctx.BottomUp("replace_deps_on_override", replaceDepsOnOverridingModuleMutator).Parallel().UsesReplaceDependencies()
 }
 
 type overrideBaseDependencyTag struct {
@@ -330,6 +331,9 @@
 }
 
 func (overrideTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
+}
+
+func overrideApplyMutator(ctx BottomUpMutatorContext) {
 	if o, ok := ctx.Module().(OverrideModule); ok {
 		overridableDeps := ctx.GetDirectDepsWithTag(overrideBaseDepTag)
 		if len(overridableDeps) > 1 {
diff --git a/android/packaging.go b/android/packaging.go
index 0909936..3c64d56 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -15,9 +15,6 @@
 package android
 
 import (
-	"bytes"
-	"encoding/gob"
-	"errors"
 	"fmt"
 	"path/filepath"
 	"sort"
@@ -67,34 +64,53 @@
 	owner string
 }
 
-func (p *PackagingSpec) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.relPathInPackage), encoder.Encode(p.srcPath),
-		encoder.Encode(p.symlinkTarget), encoder.Encode(p.executable),
-		encoder.Encode(p.effectiveLicenseFiles), encoder.Encode(p.partition),
-		encoder.Encode(p.skipInstall), encoder.Encode(p.aconfigPaths),
-		encoder.Encode(p.archType))
-	if err != nil {
-		return nil, err
-	}
+type packagingSpecGob struct {
+	RelPathInPackage string
+	SrcPath          Path
+	SymlinkTarget    string
+	Executable       bool
+	Partition        string
+	SkipInstall      bool
+	AconfigPaths     *Paths
+	ArchType         ArchType
+	Overrides        *[]string
+	Owner            string
+}
 
-	return w.Bytes(), nil
+func (p *PackagingSpec) ToGob() *packagingSpecGob {
+	return &packagingSpecGob{
+		RelPathInPackage: p.relPathInPackage,
+		SrcPath:          p.srcPath,
+		SymlinkTarget:    p.symlinkTarget,
+		Executable:       p.executable,
+		Partition:        p.partition,
+		SkipInstall:      p.skipInstall,
+		AconfigPaths:     p.aconfigPaths,
+		ArchType:         p.archType,
+		Overrides:        p.overrides,
+		Owner:            p.owner,
+	}
+}
+
+func (p *PackagingSpec) FromGob(data *packagingSpecGob) {
+	p.relPathInPackage = data.RelPathInPackage
+	p.srcPath = data.SrcPath
+	p.symlinkTarget = data.SymlinkTarget
+	p.executable = data.Executable
+	p.partition = data.Partition
+	p.skipInstall = data.SkipInstall
+	p.aconfigPaths = data.AconfigPaths
+	p.archType = data.ArchType
+	p.overrides = data.Overrides
+	p.owner = data.Owner
+}
+
+func (p *PackagingSpec) GobEncode() ([]byte, error) {
+	return blueprint.CustomGobEncode[packagingSpecGob](p)
 }
 
 func (p *PackagingSpec) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.relPathInPackage), decoder.Decode(&p.srcPath),
-		decoder.Decode(&p.symlinkTarget), decoder.Decode(&p.executable),
-		decoder.Decode(&p.effectiveLicenseFiles), decoder.Decode(&p.partition),
-		decoder.Decode(&p.skipInstall), decoder.Decode(&p.aconfigPaths),
-		decoder.Decode(&p.archType))
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return blueprint.CustomGobDecode[packagingSpecGob](data, p)
 }
 
 func (p *PackagingSpec) Equals(other *PackagingSpec) bool {
diff --git a/android/paths.go b/android/paths.go
index 1c8258e..9c2df65 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -15,9 +15,6 @@
 package android
 
 import (
-	"bytes"
-	"encoding/gob"
-	"errors"
 	"fmt"
 	"os"
 	"path/filepath"
@@ -342,6 +339,11 @@
 	invalidReason string // Not applicable if path != nil. "" if the reason is unknown.
 }
 
+type optionalPathGob struct {
+	Path          Path
+	InvalidReason string
+}
+
 // OptionalPathForPath returns an OptionalPath containing the path.
 func OptionalPathForPath(path Path) OptionalPath {
 	return OptionalPath{path: path}
@@ -353,6 +355,26 @@
 	return OptionalPath{invalidReason: reason}
 }
 
+func (p *OptionalPath) ToGob() *optionalPathGob {
+	return &optionalPathGob{
+		Path:          p.path,
+		InvalidReason: p.invalidReason,
+	}
+}
+
+func (p *OptionalPath) FromGob(data *optionalPathGob) {
+	p.path = data.Path
+	p.invalidReason = data.InvalidReason
+}
+
+func (p OptionalPath) GobEncode() ([]byte, error) {
+	return blueprint.CustomGobEncode[optionalPathGob](&p)
+}
+
+func (p *OptionalPath) GobDecode(data []byte) error {
+	return blueprint.CustomGobDecode[optionalPathGob](data, p)
+}
+
 // Valid returns whether there is a valid path
 func (p OptionalPath) Valid() bool {
 	return p.path != nil
@@ -1065,26 +1087,29 @@
 	rel  string
 }
 
-func (p basePath) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.path), encoder.Encode(p.rel))
-	if err != nil {
-		return nil, err
-	}
+type basePathGob struct {
+	Path string
+	Rel  string
+}
 
-	return w.Bytes(), nil
+func (p *basePath) ToGob() *basePathGob {
+	return &basePathGob{
+		Path: p.path,
+		Rel:  p.rel,
+	}
+}
+
+func (p *basePath) FromGob(data *basePathGob) {
+	p.path = data.Path
+	p.rel = data.Rel
+}
+
+func (p basePath) GobEncode() ([]byte, error) {
+	return blueprint.CustomGobEncode[basePathGob](&p)
 }
 
 func (p *basePath) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.path), decoder.Decode(&p.rel))
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return blueprint.CustomGobDecode[basePathGob](data, p)
 }
 
 func (p basePath) Ext() string {
@@ -1337,26 +1362,32 @@
 	fullPath string
 }
 
-func (p OutputPath) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.basePath), encoder.Encode(p.outDir), encoder.Encode(p.fullPath))
-	if err != nil {
-		return nil, err
-	}
+type outputPathGob struct {
+	basePath
+	OutDir   string
+	FullPath string
+}
 
-	return w.Bytes(), nil
+func (p *OutputPath) ToGob() *outputPathGob {
+	return &outputPathGob{
+		basePath: p.basePath,
+		OutDir:   p.outDir,
+		FullPath: p.fullPath,
+	}
+}
+
+func (p *OutputPath) FromGob(data *outputPathGob) {
+	p.basePath = data.basePath
+	p.outDir = data.OutDir
+	p.fullPath = data.FullPath
+}
+
+func (p OutputPath) GobEncode() ([]byte, error) {
+	return blueprint.CustomGobEncode[outputPathGob](&p)
 }
 
 func (p *OutputPath) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.basePath), decoder.Decode(&p.outDir), decoder.Decode(&p.fullPath))
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return blueprint.CustomGobDecode[outputPathGob](data, p)
 }
 
 func (p OutputPath) withRel(rel string) OutputPath {
@@ -1756,30 +1787,41 @@
 	fullPath string
 }
 
-func (p *InstallPath) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.basePath), encoder.Encode(p.soongOutDir),
-		encoder.Encode(p.partitionDir), encoder.Encode(p.partition),
-		encoder.Encode(p.makePath), encoder.Encode(p.fullPath))
-	if err != nil {
-		return nil, err
-	}
+type installPathGob struct {
+	basePath
+	SoongOutDir  string
+	PartitionDir string
+	Partition    string
+	MakePath     bool
+	FullPath     string
+}
 
-	return w.Bytes(), nil
+func (p *InstallPath) ToGob() *installPathGob {
+	return &installPathGob{
+		basePath:     p.basePath,
+		SoongOutDir:  p.soongOutDir,
+		PartitionDir: p.partitionDir,
+		Partition:    p.partition,
+		MakePath:     p.makePath,
+		FullPath:     p.fullPath,
+	}
+}
+
+func (p *InstallPath) FromGob(data *installPathGob) {
+	p.basePath = data.basePath
+	p.soongOutDir = data.SoongOutDir
+	p.partitionDir = data.PartitionDir
+	p.partition = data.Partition
+	p.makePath = data.MakePath
+	p.fullPath = data.FullPath
+}
+
+func (p InstallPath) GobEncode() ([]byte, error) {
+	return blueprint.CustomGobEncode[installPathGob](&p)
 }
 
 func (p *InstallPath) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.basePath), decoder.Decode(&p.soongOutDir),
-		decoder.Decode(&p.partitionDir), decoder.Decode(&p.partition),
-		decoder.Decode(&p.makePath), decoder.Decode(&p.fullPath))
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return blueprint.CustomGobDecode[installPathGob](data, p)
 }
 
 // Will panic if called from outside a test environment.
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 4f04d05..017ba76 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -400,13 +400,13 @@
 }
 
 func RegisterPrebuiltsPreArchMutators(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("prebuilt_rename", PrebuiltRenameMutator).Parallel()
+	ctx.BottomUp("prebuilt_rename", PrebuiltRenameMutator).Parallel().UsesRename()
 }
 
 func RegisterPrebuiltsPostDepsMutators(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("prebuilt_source", PrebuiltSourceDepsMutator).Parallel()
+	ctx.BottomUp("prebuilt_source", PrebuiltSourceDepsMutator).Parallel().UsesReverseDependencies()
 	ctx.BottomUp("prebuilt_select", PrebuiltSelectModuleMutator).Parallel()
-	ctx.BottomUp("prebuilt_postdeps", PrebuiltPostDepsMutator).Parallel()
+	ctx.BottomUp("prebuilt_postdeps", PrebuiltPostDepsMutator).Parallel().UsesReplaceDependencies()
 }
 
 // Returns the name of the source module corresponding to a prebuilt module
@@ -677,7 +677,7 @@
 //
 // Even though this is a cc_prebuilt_library_shared, we create both the variants today
 // https://source.corp.google.com/h/googleplex-android/platform/build/soong/+/e08e32b45a18a77bc3c3e751f730539b1b374f1b:cc/library.go;l=2113-2116;drc=2c4a9779cd1921d0397a12b3d3521f4c9b30d747;bpv=1;bpt=0
-func (p *Prebuilt) variantIsDisabled(ctx BaseMutatorContext, prebuilt Module) bool {
+func (p *Prebuilt) variantIsDisabled(ctx BaseModuleContext, prebuilt Module) bool {
 	return p.srcsSupplier != nil && len(p.srcsSupplier(ctx, prebuilt)) == 0
 }
 
@@ -687,7 +687,7 @@
 
 // usePrebuilt returns true if a prebuilt should be used instead of the source module.  The prebuilt
 // will be used if it is marked "prefer" or if the source module is disabled.
-func (p *Prebuilt) usePrebuilt(ctx BaseMutatorContext, source Module, prebuilt Module) bool {
+func (p *Prebuilt) usePrebuilt(ctx BaseModuleContext, source Module, prebuilt Module) bool {
 	isMainlinePrebuilt := func(prebuilt Module) bool {
 		apex, ok := prebuilt.(apexVariationName)
 		if !ok {
diff --git a/android/provider.go b/android/provider.go
index 5ded4cc..81d17a1 100644
--- a/android/provider.go
+++ b/android/provider.go
@@ -24,7 +24,7 @@
 // OtherModuleProviderContext is a helper interface that accepts ModuleContext, BottomUpMutatorContext, or
 // TopDownMutatorContext.
 func OtherModuleProvider[K any](ctx OtherModuleProviderContext, module blueprint.Module, provider blueprint.ProviderKey[K]) (K, bool) {
-	value, ok := ctx.otherModuleProvider(module, provider)
+	value, ok := ctx.otherModuleProvider(getWrappedModule(module), provider)
 	if !ok {
 		var k K
 		return k, false
diff --git a/android/register.go b/android/register.go
index eb6a35e..94d875d 100644
--- a/android/register.go
+++ b/android/register.go
@@ -91,7 +91,14 @@
 	bottomUpMutator   blueprint.BottomUpMutator
 	topDownMutator    blueprint.TopDownMutator
 	transitionMutator blueprint.TransitionMutator
-	parallel          bool
+
+	parallel                bool
+	usesRename              bool
+	usesReverseDependencies bool
+	usesReplaceDependencies bool
+	usesCreateModule        bool
+	mutatesDependencies     bool
+	mutatesGlobalState      bool
 }
 
 var _ sortableComponent = &mutator{}
@@ -235,6 +242,7 @@
 
 	PreDepsMutators(f RegisterMutatorFunc)
 	PostDepsMutators(f RegisterMutatorFunc)
+	PostApexMutators(f RegisterMutatorFunc)
 	FinalDepsMutators(f RegisterMutatorFunc)
 }
 
@@ -326,6 +334,10 @@
 	PostDepsMutators(f)
 }
 
+func (ctx *initRegistrationContext) PostApexMutators(f RegisterMutatorFunc) {
+	PostApexMutators(f)
+}
+
 func (ctx *initRegistrationContext) FinalDepsMutators(f RegisterMutatorFunc) {
 	FinalDepsMutators(f)
 }
diff --git a/android/testing.go b/android/testing.go
index 196b22e..7440869 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -197,8 +197,8 @@
 
 type TestContext struct {
 	*Context
-	preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc
-	NameResolver                          *NameResolver
+	preArch, preDeps, postDeps, postApex, finalDeps []RegisterMutatorFunc
+	NameResolver                                    *NameResolver
 
 	// The list of singletons registered for the test.
 	singletons sortableComponents
@@ -229,6 +229,10 @@
 	ctx.postDeps = append(ctx.postDeps, f)
 }
 
+func (ctx *TestContext) PostApexMutators(f RegisterMutatorFunc) {
+	ctx.postApex = append(ctx.postApex, f)
+}
+
 func (ctx *TestContext) FinalDepsMutators(f RegisterMutatorFunc) {
 	ctx.finalDeps = append(ctx.finalDeps, f)
 }
@@ -449,7 +453,7 @@
 func (ctx *TestContext) Register() {
 	globalOrder := globallyRegisteredComponentsOrder()
 
-	mutators := collateRegisteredMutators(ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.finalDeps)
+	mutators := collateRegisteredMutators(ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.postApex, ctx.finalDeps)
 	// Ensure that the mutators used in the test are in the same order as they are used at runtime.
 	globalOrder.mutatorOrder.enforceOrdering(mutators)
 	mutators.registerAll(ctx.Context)
diff --git a/android/variable.go b/android/variable.go
index 1aaa70a..417ba89 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -522,6 +522,10 @@
 	DeviceProductCompatibilityMatrixFile   []string `json:",omitempty"`
 
 	PartitionVarsForSoongMigrationOnlyDoNotUse PartitionVariables
+
+	ExtraAllowedDepsTxt *string `json:",omitempty"`
+
+	AdbKeys *string `json:",omitempty"`
 }
 
 type PartitionQualifiedVariablesType struct {
diff --git a/androidmk/parser/parser_test.go b/androidmk/parser/parser_test.go
index e238f8b..21baf6b 100644
--- a/androidmk/parser/parser_test.go
+++ b/androidmk/parser/parser_test.go
@@ -142,7 +142,7 @@
 		t.Fatalf("Unexpected errors while parsing: %v", errs)
 	}
 
-	if got[0].End() < got[len(got) -1].Pos() {
-		t.Errorf("Rule's end (%d) is smaller than directive that inside of rule's start (%v)\n", got[0].End(), got[len(got) -1].Pos())
+	if got[0].End() < got[len(got)-1].Pos() {
+		t.Errorf("Rule's end (%d) is smaller than directive that inside of rule's start (%v)\n", got[0].End(), got[len(got)-1].Pos())
 	}
 }
diff --git a/apex/apex.go b/apex/apex.go
index d7dc6d7..6bb2a1a 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -29,6 +29,7 @@
 	"android/soong/android"
 	"android/soong/bpf"
 	"android/soong/cc"
+	"android/soong/dexpreopt"
 	prebuilt_etc "android/soong/etc"
 	"android/soong/filesystem"
 	"android/soong/java"
@@ -54,7 +55,7 @@
 }
 
 func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
-	ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).Parallel()
+	ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).Parallel().UsesReverseDependencies()
 }
 
 func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
@@ -66,7 +67,7 @@
 	// it should create a platform variant.
 	ctx.BottomUp("mark_platform_availability", markPlatformAvailability).Parallel()
 	ctx.Transition("apex", &apexTransitionMutator{})
-	ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel()
+	ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel().MutatesDependencies()
 	ctx.BottomUp("apex_dcla_deps", apexDCLADepsMutator).Parallel()
 }
 
@@ -291,7 +292,7 @@
 }
 
 // Merge combines another ApexNativeDependencies into this one
-func (a *ResolvedApexNativeDependencies) Merge(ctx android.BaseMutatorContext, b ApexNativeDependencies) {
+func (a *ResolvedApexNativeDependencies) Merge(ctx android.BaseModuleContext, b ApexNativeDependencies) {
 	a.Native_shared_libs = append(a.Native_shared_libs, b.Native_shared_libs.GetOrDefault(ctx, nil)...)
 	a.Jni_libs = append(a.Jni_libs, b.Jni_libs.GetOrDefault(ctx, nil)...)
 	a.Rust_dyn_libs = append(a.Rust_dyn_libs, b.Rust_dyn_libs...)
@@ -1919,6 +1920,32 @@
 	})
 }
 
+// enforcePartitionTagOnApexSystemServerJar checks that the partition tags of an apex system server jar  matches
+// the partition tags of the top-level apex.
+// e.g. if the top-level apex sets system_ext_specific to true, the javalib must set this property to true as well.
+// This check ensures that the dexpreopt artifacts of the apex system server jar is installed in the same partition
+// as the apex.
+func (a *apexBundle) enforcePartitionTagOnApexSystemServerJar(ctx android.ModuleContext) {
+	global := dexpreopt.GetGlobalConfig(ctx)
+	ctx.VisitDirectDepsWithTag(sscpfTag, func(child android.Module) {
+		info, ok := android.OtherModuleProvider(ctx, child, java.LibraryNameToPartitionInfoProvider)
+		if !ok {
+			ctx.ModuleErrorf("Could not find partition info of apex system server jars.")
+		}
+		apexPartition := ctx.Module().PartitionTag(ctx.DeviceConfig())
+		for javalib, javalibPartition := range info.LibraryNameToPartition {
+			if !global.AllApexSystemServerJars(ctx).ContainsJar(javalib) {
+				continue // not an apex system server jar
+			}
+			if apexPartition != javalibPartition {
+				ctx.ModuleErrorf(`
+%s is an apex systemserver jar, but its partition does not match the partition of its containing apex. Expected %s, Got %s`,
+					javalib, apexPartition, javalibPartition)
+			}
+		}
+	})
+}
+
 func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, child, parent android.Module) bool {
 	depTag := ctx.OtherModuleDependencyTag(child)
 	if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
@@ -2341,6 +2368,7 @@
 	a.required = append(a.required, a.VintfFragmentModuleNames(ctx)...)
 
 	a.setOutputFiles(ctx)
+	a.enforcePartitionTagOnApexSystemServerJar(ctx)
 }
 
 // Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index f405cb2..00dd446 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -18,6 +18,7 @@
 
 import (
 	"encoding/json"
+	"strings"
 
 	"github.com/google/blueprint"
 
@@ -58,9 +59,9 @@
 
 	// Diff two given lists while ignoring comments in the allowed deps file.
 	diffAllowedApexDepsInfoRule = pctx.AndroidStaticRule("diffAllowedApexDepsInfoRule", blueprint.RuleParams{
-		Description: "Diff ${allowed_deps} and ${new_allowed_deps}",
+		Description: "Diff ${allowed_deps_list} and ${new_allowed_deps}",
 		Command: `
-			if grep -v '^#' ${allowed_deps} | diff -B - ${new_allowed_deps}; then
+			if grep -v -h '^#' ${allowed_deps_list} | sort -u -f| diff -B -u - ${new_allowed_deps}; then
 			   touch ${out};
 			else
 				echo -e "\n******************************";
@@ -81,10 +82,15 @@
 				exit 1;
 			fi;
 		`,
-	}, "allowed_deps", "new_allowed_deps")
+	}, "allowed_deps_list", "new_allowed_deps")
 )
 
 func (s *apexDepsInfoSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	allowedDepsSources := []android.OptionalPath{android.ExistentPathForSource(ctx, "packages/modules/common/build/allowed_deps.txt")}
+	extraAllowedDepsPath := ctx.Config().ExtraAllowedDepsTxt()
+	if extraAllowedDepsPath != "" {
+		allowedDepsSources = append(allowedDepsSources, android.ExistentPathForSource(ctx, extraAllowedDepsPath))
+	}
 	updatableFlatLists := android.Paths{}
 	ctx.VisitAllModules(func(module android.Module) {
 		if binaryInfo, ok := module.(android.ApexBundleDepsInfoIntf); ok {
@@ -96,37 +102,42 @@
 			}
 		}
 	})
-
-	allowedDepsSource := android.ExistentPathForSource(ctx, "packages/modules/common/build/allowed_deps.txt")
 	newAllowedDeps := android.PathForOutput(ctx, "apex", "depsinfo", "new-allowed-deps.txt")
 	s.allowedApexDepsInfoCheckResult = android.PathForOutput(ctx, newAllowedDeps.Rel()+".check")
-
-	if !allowedDepsSource.Valid() {
+	hasOneValidDepsPath := false
+	for _, allowedDepsSource := range allowedDepsSources {
+		if allowedDepsSource.Valid() {
+			hasOneValidDepsPath = true
+			updatableFlatLists = append(updatableFlatLists, allowedDepsSource.Path())
+		}
+	}
+	allowedDepsStrList := make([]string, len(allowedDepsSources))
+	for _, value := range allowedDepsSources {
+		allowedDepsStrList = append(allowedDepsStrList, value.String())
+	}
+	allowedDepsListString := strings.Join(allowedDepsStrList, " ")
+	if !hasOneValidDepsPath {
 		// Unbundled projects may not have packages/modules/common/ checked out; ignore those.
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   android.Touch,
 			Output: s.allowedApexDepsInfoCheckResult,
 		})
 	} else {
-		allowedDeps := allowedDepsSource.Path()
-
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   generateApexDepsInfoFilesRule,
-			Inputs: append(updatableFlatLists, allowedDeps),
+			Inputs: updatableFlatLists,
 			Output: newAllowedDeps,
 		})
-
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   diffAllowedApexDepsInfoRule,
 			Input:  newAllowedDeps,
 			Output: s.allowedApexDepsInfoCheckResult,
 			Args: map[string]string{
-				"allowed_deps":     allowedDeps.String(),
-				"new_allowed_deps": newAllowedDeps.String(),
+				"allowed_deps_list": allowedDepsListString,
+				"new_allowed_deps":  newAllowedDeps.String(),
 			},
 		})
 	}
-
 	ctx.Phony("apex-allowed-deps-check", s.allowedApexDepsInfoCheckResult)
 }
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index da6214d..1d2f3fb 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -2185,6 +2185,151 @@
 		flatlist, "yourlib(minSdkVersion:29)")
 }
 
+func TestTrackCustomAllowedDepsInvalidDefaultTxt(t *testing.T) {
+	ctx := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			updatable: true,
+			native_shared_libs: [
+				"mylib",
+				"yourlib",
+			],
+			min_sdk_version: "29",
+		}
+
+		apex {
+			name: "myapex2",
+			key: "myapex.key",
+			updatable: false,
+			native_shared_libs: ["yourlib"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["libbar"],
+			min_sdk_version: "29",
+			apex_available: ["myapex"],
+		}
+
+		cc_library {
+			name: "libbar",
+			stubs: { versions: ["29", "30"] },
+		}
+
+		cc_library {
+			name: "yourlib",
+			srcs: ["mylib.cpp"],
+			min_sdk_version: "29",
+			apex_available: ["myapex", "myapex2", "//apex_available:platform"],
+		}
+	`, withFiles(android.MockFS{
+		"packages/modules/common/build/custom_allowed_deps.txt": nil,
+	}),
+		android.FixtureModifyProductVariables(
+			func(variables android.FixtureProductVariables) {
+				variables.ExtraAllowedDepsTxt = proptools.StringPtr("packages/modules/common/build/custom_allowed_deps.txt")
+			},
+		))
+
+	depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton")
+	inputs := depsinfo.Rule("generateApexDepsInfoFilesRule").BuildParams.Inputs.Strings()
+	android.AssertStringListContains(t, "updatable myapex should generate depsinfo file", inputs,
+		"out/soong/.intermediates/myapex/android_common_myapex/depsinfo/flatlist.txt")
+	android.AssertStringListDoesNotContain(t, "non-updatable myapex2 should not generate depsinfo file", inputs,
+		"out/soong/.intermediates/myapex2/android_common_myapex2/depsinfo/flatlist.txt")
+
+	myapex := ctx.ModuleForTests("myapex", "android_common_myapex")
+	flatlist := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
+		myapex.Output("depsinfo/flatlist.txt")), "\n")
+	android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep",
+		flatlist, "libbar(minSdkVersion:(no version)) (external)")
+	android.AssertStringListDoesNotContain(t, "do not track if not available for platform",
+		flatlist, "mylib:(minSdkVersion:29)")
+	android.AssertStringListContains(t, "track platform-available lib",
+		flatlist, "yourlib(minSdkVersion:29)")
+}
+
+func TestTrackCustomAllowedDepsWithDefaultTxt(t *testing.T) {
+	ctx := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			updatable: true,
+			native_shared_libs: [
+				"mylib",
+				"yourlib",
+			],
+			min_sdk_version: "29",
+		}
+
+		apex {
+			name: "myapex2",
+			key: "myapex.key",
+			updatable: false,
+			native_shared_libs: ["yourlib"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["libbar"],
+			min_sdk_version: "29",
+			apex_available: ["myapex"],
+		}
+
+		cc_library {
+			name: "libbar",
+			stubs: { versions: ["29", "30"] },
+		}
+
+		cc_library {
+			name: "yourlib",
+			srcs: ["mylib.cpp"],
+			min_sdk_version: "29",
+			apex_available: ["myapex", "myapex2", "//apex_available:platform"],
+		}
+	`, withFiles(android.MockFS{
+		"packages/modules/common/build/custom_allowed_deps.txt": nil,
+		"packages/modules/common/build/allowed_deps.txt":        nil,
+	}),
+		android.FixtureModifyProductVariables(
+			func(variables android.FixtureProductVariables) {
+				variables.ExtraAllowedDepsTxt = proptools.StringPtr("packages/modules/common/build/custom_allowed_deps.txt")
+			},
+		))
+
+	depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton")
+	inputs := depsinfo.Rule("generateApexDepsInfoFilesRule").BuildParams.Inputs.Strings()
+	android.AssertStringListContains(t, "updatable myapex should generate depsinfo file", inputs,
+		"out/soong/.intermediates/myapex/android_common_myapex/depsinfo/flatlist.txt")
+	android.AssertStringListDoesNotContain(t, "non-updatable myapex2 should not generate depsinfo file", inputs,
+		"out/soong/.intermediates/myapex2/android_common_myapex2/depsinfo/flatlist.txt")
+
+	myapex := ctx.ModuleForTests("myapex", "android_common_myapex")
+	flatlist := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
+		myapex.Output("depsinfo/flatlist.txt")), "\n")
+	android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep",
+		flatlist, "libbar(minSdkVersion:(no version)) (external)")
+	android.AssertStringListDoesNotContain(t, "do not track if not available for platform",
+		flatlist, "mylib:(minSdkVersion:29)")
+	android.AssertStringListContains(t, "track platform-available lib",
+		flatlist, "yourlib(minSdkVersion:29)")
+}
+
 func TestTrackAllowedDeps_SkipWithoutAllowedDepsTxt(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -11670,3 +11815,48 @@
 		}
        `)
 }
+
+// If an apex sets system_ext_specific: true, its systemserverclasspath libraries must set this property as well.
+func TestApexSSCPJarMustBeInSamePartitionAsApex(t *testing.T) {
+	testApexError(t, `foo is an apex systemserver jar, but its partition does not match the partition of its containing apex`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			systemserverclasspath_fragments: [
+				"mysystemserverclasspathfragment",
+			],
+			min_sdk_version: "29",
+			updatable: true,
+			system_ext_specific: true,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_library {
+			name: "foo",
+			srcs: ["b.java"],
+			min_sdk_version: "29",
+			installable: true,
+			apex_available: [
+				"myapex",
+			],
+			sdk_version: "current",
+		}
+
+		systemserverclasspath_fragment {
+			name: "mysystemserverclasspathfragment",
+			contents: [
+				"foo",
+			],
+			apex_available: [
+				"myapex",
+			],
+		}
+	`,
+		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
+	)
+}
diff --git a/cc/Android.bp b/cc/Android.bp
index 3688c8a..88a793c 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -102,6 +102,7 @@
         "orderfile_test.go",
         "prebuilt_test.go",
         "proto_test.go",
+        "sabi_test.go",
         "sanitize_test.go",
         "sdk_test.go",
         "test_data_test.go",
diff --git a/cc/cc.go b/cc/cc.go
index a8ff474..1b7624d 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -76,9 +76,9 @@
 		ctx.BottomUp("double_loadable", checkDoubleLoadableLibraries).Parallel()
 	})
 
-	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
+	ctx.PostApexMutators(func(ctx android.RegisterMutatorsContext) {
 		// sabi mutator needs to be run after apex mutator finishes.
-		ctx.TopDown("sabi_deps", sabiDepsMutator)
+		ctx.Transition("sabi", &sabiTransitionMutator{})
 	})
 
 	ctx.RegisterParallelSingletonType("kythe_extract_all", kytheExtractAllFactory)
diff --git a/cc/compiler.go b/cc/compiler.go
index a6f623f..022b712 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -228,9 +228,6 @@
 		Static *bool `android:"arch_variant"`
 	} `android:"arch_variant"`
 
-	// Stores the original list of source files before being cleared by library reuse
-	OriginalSrcs proptools.Configurable[[]string] `blueprint:"mutated"`
-
 	// Build and link with OpenMP
 	Openmp *bool `android:"arch_variant"`
 }
@@ -363,10 +360,20 @@
 	tc := ctx.toolchain()
 	modulePath := ctx.ModuleDir()
 
-	srcs := compiler.Properties.Srcs.GetOrDefault(ctx, nil)
-	exclude_srcs := compiler.Properties.Exclude_srcs.GetOrDefault(ctx, nil)
-	compiler.srcsBeforeGen = android.PathsForModuleSrcExcludes(ctx, srcs, exclude_srcs)
-	compiler.srcsBeforeGen = append(compiler.srcsBeforeGen, deps.GeneratedSources...)
+	reuseObjs := false
+	if len(ctx.GetDirectDepsWithTag(reuseObjTag)) > 0 {
+		reuseObjs = true
+	}
+
+	// If a reuseObjTag dependency exists then this module is reusing the objects (generally the shared variant
+	// reusing objects from the static variant), and doesn't need to compile any sources of its own.
+	var srcs []string
+	if !reuseObjs {
+		srcs = compiler.Properties.Srcs.GetOrDefault(ctx, nil)
+		exclude_srcs := compiler.Properties.Exclude_srcs.GetOrDefault(ctx, nil)
+		compiler.srcsBeforeGen = android.PathsForModuleSrcExcludes(ctx, srcs, exclude_srcs)
+		compiler.srcsBeforeGen = append(compiler.srcsBeforeGen, deps.GeneratedSources...)
+	}
 
 	cflags := compiler.Properties.Cflags.GetOrDefault(ctx, nil)
 	cppflags := compiler.Properties.Cppflags.GetOrDefault(ctx, nil)
@@ -721,11 +728,6 @@
 			return true
 		}
 	}
-	for _, src := range compiler.Properties.OriginalSrcs.GetOrDefault(ctx, nil) {
-		if filepath.Ext(src) == ext {
-			return true
-		}
-	}
 
 	return false
 }
diff --git a/cc/library.go b/cc/library.go
index 3833b98..988a7fa 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -548,8 +548,7 @@
 	return flags
 }
 
-func (library *libraryDecorator) getHeaderAbiCheckerProperties(ctx android.BaseModuleContext) headerAbiCheckerProperties {
-	m := ctx.Module().(*Module)
+func (library *libraryDecorator) getHeaderAbiCheckerProperties(m *Module) headerAbiCheckerProperties {
 	variantProps := &library.Properties.Target.Platform.Header_abi_checker
 	if m.InVendor() {
 		variantProps = &library.Properties.Target.Vendor.Header_abi_checker
@@ -559,7 +558,7 @@
 	props := library.Properties.Header_abi_checker
 	err := proptools.AppendProperties(&props, variantProps, nil)
 	if err != nil {
-		ctx.ModuleErrorf("Cannot merge headerAbiCheckerProperties: %s", err.Error())
+		panic(fmt.Errorf("Cannot merge headerAbiCheckerProperties: %s", err.Error()))
 	}
 	return props
 }
@@ -718,7 +717,7 @@
 	setShared()
 
 	// Gets the ABI properties for vendor, product, or platform variant
-	getHeaderAbiCheckerProperties(ctx android.BaseModuleContext) headerAbiCheckerProperties
+	getHeaderAbiCheckerProperties(m *Module) headerAbiCheckerProperties
 
 	// Write LOCAL_ADDITIONAL_DEPENDENCIES for ABI diff
 	androidMkWriteAdditionalDependenciesForSourceAbiDiff(w io.Writer)
@@ -1365,7 +1364,7 @@
 	sourceVersion, errorMessage string) {
 
 	extraFlags := []string{"-target-version", sourceVersion}
-	headerAbiChecker := library.getHeaderAbiCheckerProperties(ctx)
+	headerAbiChecker := library.getHeaderAbiCheckerProperties(ctx.Module().(*Module))
 	if Bool(headerAbiChecker.Check_all_apis) {
 		extraFlags = append(extraFlags, "-check-all-apis")
 	} else {
@@ -1437,7 +1436,7 @@
 func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, deps PathDeps, objs Objects, fileName string, soFile android.Path) {
 	if library.sabi.shouldCreateSourceAbiDump() {
 		exportedIncludeDirs := library.exportedIncludeDirsForAbiCheck(ctx)
-		headerAbiChecker := library.getHeaderAbiCheckerProperties(ctx)
+		headerAbiChecker := library.getHeaderAbiCheckerProperties(ctx.Module().(*Module))
 		currSdkVersion := currRefAbiDumpSdkVersion(ctx)
 		currVendorVersion := ctx.Config().VendorApiLevel()
 
@@ -1451,7 +1450,7 @@
 			[]string{} /* includeSymbolTags */, currSdkVersion, false /* isLlndk */)
 
 		var llndkDump, apexVariantDump android.Path
-		tags := classifySourceAbiDump(ctx)
+		tags := classifySourceAbiDump(ctx.Module().(*Module))
 		optInTags := []lsdumpTag{}
 		for _, tag := range tags {
 			if tag == llndkLsdumpTag && currVendorVersion != "" {
@@ -1868,7 +1867,7 @@
 }
 
 func (library *libraryDecorator) symbolFileForAbiCheck(ctx ModuleContext) *string {
-	if props := library.getHeaderAbiCheckerProperties(ctx); props.Symbol_file != nil {
+	if props := library.getHeaderAbiCheckerProperties(ctx.Module().(*Module)); props.Symbol_file != nil {
 		return props.Symbol_file
 	}
 	if library.hasStubsVariants() && library.Properties.Stubs.Symbol_file != nil {
@@ -2071,12 +2070,7 @@
 			sharedCompiler.StaticProperties.Static.System_shared_libs == nil &&
 			sharedCompiler.SharedProperties.Shared.System_shared_libs == nil {
 
-			// TODO: namespaces?
 			ctx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, reuseObjTag, ctx.ModuleName())
-			sharedCompiler.baseCompiler.Properties.OriginalSrcs =
-				sharedCompiler.baseCompiler.Properties.Srcs
-			sharedCompiler.baseCompiler.Properties.Srcs = proptools.NewConfigurable[[]string](nil, nil)
-			sharedCompiler.baseCompiler.Properties.Generated_sources = nil
 		}
 
 		// This dep is just to reference static variant from shared variant
diff --git a/cc/sabi.go b/cc/sabi.go
index 64eab41..2caf0d4 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -84,8 +84,8 @@
 
 type SAbiProperties struct {
 	// Whether ABI dump should be created for this module.
-	// Set by `sabiDepsMutator` if this module is a shared library that needs ABI check, or a static
-	// library that is depended on by an ABI checked library.
+	// Set by `sabiTransitionMutator` if this module is a shared library that needs ABI check,
+	// or a static library that is depended on by an ABI checked library.
 	ShouldCreateSourceAbiDump bool `blueprint:"mutated"`
 
 	// Include directories that may contain ABI information exported by a library.
@@ -121,10 +121,9 @@
 }
 
 // Returns a slice of strings that represent the ABI dumps generated for this module.
-func classifySourceAbiDump(ctx android.BaseModuleContext) []lsdumpTag {
+func classifySourceAbiDump(m *Module) []lsdumpTag {
 	result := []lsdumpTag{}
-	m := ctx.Module().(*Module)
-	headerAbiChecker := m.library.getHeaderAbiCheckerProperties(ctx)
+	headerAbiChecker := m.library.getHeaderAbiCheckerProperties(m)
 	if headerAbiChecker.explicitlyDisabled() {
 		return result
 	}
@@ -149,24 +148,37 @@
 	return result
 }
 
-// Called from sabiDepsMutator to check whether ABI dumps should be created for this module.
+type shouldCreateAbiDumpContext interface {
+	android.ModuleProviderContext
+	Module() android.Module
+	Config() android.Config
+}
+
+var _ shouldCreateAbiDumpContext = android.ModuleContext(nil)
+var _ shouldCreateAbiDumpContext = android.OutgoingTransitionContext(nil)
+
+// Called from sabiTransitionMutator to check whether ABI dumps should be created for this module.
 // ctx should be wrapping a native library type module.
-func shouldCreateSourceAbiDumpForLibrary(ctx android.BaseModuleContext) bool {
-	// Only generate ABI dump for device modules.
-	if !ctx.Device() {
+func shouldCreateSourceAbiDumpForLibrary(ctx shouldCreateAbiDumpContext) bool {
+	m, ok := ctx.Module().(*Module)
+	if !ok {
 		return false
 	}
 
-	m := ctx.Module().(*Module)
+	// Only generate ABI dump for device modules.
+	if !m.Device() {
+		return false
+	}
 
 	// Only create ABI dump for native library module types.
 	if m.library == nil {
 		return false
 	}
 
-	// Create ABI dump for static libraries only if they are dependencies of ABI checked libraries.
+	// Don't create ABI dump for static libraries
+	// The sabi variant will be propagated to dependencies of ABI checked libraries.
 	if m.library.static() {
-		return m.sabi.shouldCreateSourceAbiDump()
+		return false
 	}
 
 	// Module is shared library type.
@@ -215,31 +227,64 @@
 			return false
 		}
 	}
-	return len(classifySourceAbiDump(ctx)) > 0
+	return len(classifySourceAbiDump(m)) > 0
 }
 
 // Mark the direct and transitive dependencies of libraries that need ABI check, so that ABI dumps
 // of their dependencies would be generated.
-func sabiDepsMutator(mctx android.TopDownMutatorContext) {
+type sabiTransitionMutator struct{}
+
+func (s *sabiTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+	return []string{""}
+}
+
+func (s *sabiTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
 	// Escape hatch to not check any ABI dump.
-	if mctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
-		return
+	if ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
+		return ""
 	}
+
 	// Only create ABI dump for native shared libraries and their static library dependencies.
-	if m, ok := mctx.Module().(*Module); ok && m.sabi != nil {
-		if shouldCreateSourceAbiDumpForLibrary(mctx) {
-			// Mark this module so that .sdump / .lsdump for this library can be generated.
+	if m, ok := ctx.Module().(*Module); ok && m.sabi != nil {
+		if shouldCreateSourceAbiDumpForLibrary(ctx) {
+			if IsStaticDepTag(ctx.DepTag()) || ctx.DepTag() == reuseObjTag {
+				return "sabi"
+			}
+		} else if sourceVariation == "sabi" {
+			if IsWholeStaticLib(ctx.DepTag()) || ctx.DepTag() == reuseObjTag {
+				return "sabi"
+			}
+		}
+	}
+
+	return ""
+}
+
+func (s *sabiTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	if incomingVariation == "" {
+		return ""
+	}
+
+	if incomingVariation == "sabi" {
+		if m, ok := ctx.Module().(*Module); ok && m.sabi != nil {
+			return "sabi"
+		}
+	}
+
+	return ""
+}
+
+func (s *sabiTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	if m, ok := ctx.Module().(*Module); ok && m.sabi != nil {
+		if variation == "sabi" {
 			m.sabi.Properties.ShouldCreateSourceAbiDump = true
-			// Mark all of its static library dependencies.
-			mctx.VisitDirectDeps(func(child android.Module) {
-				depTag := mctx.OtherModuleDependencyTag(child)
-				if IsStaticDepTag(depTag) || depTag == reuseObjTag {
-					if c, ok := child.(*Module); ok && c.sabi != nil {
-						// Mark this module so that .sdump for this static library can be generated.
-						c.sabi.Properties.ShouldCreateSourceAbiDump = true
-					}
-				}
-			})
+			m.HideFromMake()
+			m.Properties.PreventInstall = true
+		} else if shouldCreateSourceAbiDumpForLibrary(ctx) {
+			// Escape hatch to not check any ABI dump.
+			if !ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
+				m.sabi.Properties.ShouldCreateSourceAbiDump = true
+			}
 		}
 	}
 }
diff --git a/cc/sabi_test.go b/cc/sabi_test.go
new file mode 100644
index 0000000..6b8cc17
--- /dev/null
+++ b/cc/sabi_test.go
@@ -0,0 +1,66 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+	"android/soong/android"
+	"testing"
+)
+
+func TestSabi(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "libsabi",
+			srcs: ["sabi.cpp"],
+			static_libs: ["libdirect"],
+			header_abi_checker: {
+				enabled: true,
+				symbol_file: "libsabi.map.txt",
+                ref_dump_dirs: ["abi-dumps"],
+			},
+		}
+
+		cc_library {
+			name: "libdirect",
+			srcs: ["direct.cpp"],
+			whole_static_libs: ["libtransitive"],
+		}
+
+		cc_library {
+			name: "libtransitive",
+			srcs: ["transitive.cpp"],
+		}
+	`
+
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithCcDefaultModules,
+	).RunTestWithBp(t, bp)
+
+	libsabiStatic := result.ModuleForTests("libsabi", "android_arm64_armv8-a_static_sabi")
+	sabiObjSDump := libsabiStatic.Output("obj/sabi.sdump")
+
+	libDirect := result.ModuleForTests("libdirect", "android_arm64_armv8-a_static_sabi")
+	directObjSDump := libDirect.Output("obj/direct.sdump")
+
+	libTransitive := result.ModuleForTests("libtransitive", "android_arm64_armv8-a_static_sabi")
+	transitiveObjSDump := libTransitive.Output("obj/transitive.sdump")
+
+	libsabiShared := result.ModuleForTests("libsabi", "android_arm64_armv8-a_shared")
+	sabiLink := libsabiShared.Rule("sAbiLink")
+
+	android.AssertStringListContains(t, "sabi link inputs", sabiLink.Inputs.Strings(), sabiObjSDump.Output.String())
+	android.AssertStringListContains(t, "sabi link inputs", sabiLink.Inputs.Strings(), directObjSDump.Output.String())
+	android.AssertStringListContains(t, "sabi link inputs", sabiLink.Inputs.Strings(), transitiveObjSDump.Output.String())
+}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 7f52ce1..85fdb02 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -176,7 +176,7 @@
 	switch t {
 	case cfi, Hwasan, Asan, tsan, Fuzzer, scs, Memtag_stack:
 		sanitizer := &sanitizerSplitMutator{t}
-		ctx.BottomUp(t.variationName()+"_markapexes", sanitizer.markSanitizableApexesMutator)
+		ctx.BottomUp(t.variationName()+"_markapexes", sanitizer.markSanitizableApexesMutator).Parallel()
 		ctx.Transition(t.variationName(), sanitizer)
 	case Memtag_heap, Memtag_globals, intOverflow:
 		// do nothing
diff --git a/cmd/release_config/release_config_lib/release_config.go b/cmd/release_config/release_config_lib/release_config.go
index adf0e62..ee71336 100644
--- a/cmd/release_config/release_config_lib/release_config.go
+++ b/cmd/release_config/release_config_lib/release_config.go
@@ -280,11 +280,28 @@
 
 	directories := []string{}
 	valueDirectories := []string{}
+	// These path prefixes are exclusive for a release config.
+	// "A release config shall exist in at most one of these."
+	// If we find a benefit to generalizing this, we can do so at that time.
+	exclusiveDirPrefixes := []string{
+		"build/release",
+		"vendor/google_shared/build/release",
+	}
+	var exclusiveDir string
 	for idx, confDir := range configs.configDirs {
 		if _, ok := myDirsMap[idx]; ok {
 			directories = append(directories, confDir)
 		}
 		if _, ok := myValueDirsMap[idx]; ok {
+			for _, dir := range exclusiveDirPrefixes {
+				if strings.HasPrefix(confDir, dir) {
+					if exclusiveDir != "" && !strings.HasPrefix(exclusiveDir, dir) {
+						return fmt.Errorf("%s is declared in both %s and %s",
+							config.Name, exclusiveDir, confDir)
+					}
+					exclusiveDir = confDir
+				}
+			}
 			valueDirectories = append(valueDirectories, confDir)
 		}
 	}
diff --git a/compliance/notice_test.go b/compliance/notice_test.go
index 6187e53..e8578ec 100644
--- a/compliance/notice_test.go
+++ b/compliance/notice_test.go
@@ -35,4 +35,4 @@
 
 	m := result.Module("notice_xml_system", "android_arm64_armv8-a").(*NoticeXmlModule)
 	android.AssertStringEquals(t, "output file", "NOTICE.xml.gz", m.outputFile.Base())
-}
\ No newline at end of file
+}
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index fe6317c..84d4f10 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -191,6 +191,10 @@
 	ForceCreateAppImage bool
 
 	PresignedPrebuilt bool
+
+	// ApexPartition is the partition in which the dexpreopt files of apex system server jars (if any) are installed.
+	// This is a noop unless the module is apex system server jar.
+	ApexPartition string
 }
 
 type globalSoongConfigSingleton struct{}
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 5616483..7a39fa1 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -219,9 +219,9 @@
 }
 
 // Returns the location to the odex file for the dex file at `path`.
-func ToOdexPath(path string, arch android.ArchType) string {
+func ToOdexPath(path string, arch android.ArchType, partition string) string {
 	if strings.HasPrefix(path, "/apex/") {
-		return filepath.Join("/system/framework/oat", arch.String(),
+		return filepath.Join(partition, "framework/oat", arch.String(),
 			strings.ReplaceAll(path[1:], "/", "@")+"@classes.odex")
 	}
 
@@ -245,7 +245,7 @@
 
 	odexPath := module.BuildPath.InSameDir(ctx, "oat", arch.String(), pathtools.ReplaceExtension(base, "odex"))
 	odexSymbolsPath := odexPath.ReplaceExtension(ctx, "symbols.odex")
-	odexInstallPath := ToOdexPath(module.DexLocation, arch)
+	odexInstallPath := ToOdexPath(module.DexLocation, arch, module.ApexPartition)
 	if odexOnSystemOther(module, global) {
 		odexInstallPath = filepath.Join(SystemOtherPartition, odexInstallPath)
 	}
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 6f7d3bb..7b0f51f 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -42,12 +42,14 @@
 }
 
 func testApexModuleConfig(ctx android.PathContext, name, apexName string) *ModuleConfig {
-	return createTestModuleConfig(
+	ret := createTestModuleConfig(
 		name,
 		fmt.Sprintf("/apex/%s/javalib/%s.jar", apexName, name),
 		android.PathForOutput(ctx, fmt.Sprintf("%s/dexpreopt/%s.jar", name, name)),
 		android.PathForOutput(ctx, fmt.Sprintf("%s/aligned/%s.jar", name, name)),
 		android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name)))
+	ret.ApexPartition = "/system"
+	return ret
 }
 
 func testPlatformSystemServerModuleConfig(ctx android.PathContext, name string) *ModuleConfig {
@@ -221,6 +223,49 @@
 	DexpreoptRunningInSoong = oldDexpreoptRunningInSoong
 }
 
+// Same as `TestDexPreoptApexSystemServerJars`, but the apex jar is in /system_ext
+func TestDexPreoptApexSystemServerJarsSystemExt(t *testing.T) {
+	// modify the global variable for test
+	var oldDexpreoptRunningInSoong = DexpreoptRunningInSoong
+	DexpreoptRunningInSoong = true
+
+	// test begin
+	config := android.TestConfig("out", nil, "", nil)
+	ctx := android.BuilderContextForTesting(config)
+	globalSoong := globalSoongConfigForTests(ctx)
+	global := GlobalConfigForTests(ctx)
+	module := testApexModuleConfig(ctx, "service-A", "com.android.apex1")
+	module.ApexPartition = "/system_ext"
+	productPackages := android.PathForTesting("product_packages.txt")
+
+	global.ApexSystemServerJars = android.CreateTestConfiguredJarList(
+		[]string{"com.android.apex1:service-A"})
+
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	wantInstalls := android.RuleBuilderInstalls{
+		{android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.odex"), "/system_ext/framework/oat/arm/apex@com.android.apex1@javalib@service-A.jar@classes.odex"},
+		{android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.vdex"), "/system_ext/framework/oat/arm/apex@com.android.apex1@javalib@service-A.jar@classes.vdex"},
+	}
+
+	android.AssertStringEquals(t, "installs", wantInstalls.String(), rule.Installs().String())
+
+	android.AssertStringListContains(t, "apex sscp jar copy", rule.Outputs().Strings(), "out/soong/system_server_dexjars/service-A.jar")
+
+	// rule with apex sscp cp as false
+	rule, err = GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+	android.AssertStringListDoesNotContain(t, "apex sscp jar copy", rule.Outputs().Strings(), "out/soong/system_server_dexjars/service-A.jar")
+
+	// cleanup the global variable for test
+	DexpreoptRunningInSoong = oldDexpreoptRunningInSoong
+}
+
 func TestDexPreoptStandaloneSystemServerJars(t *testing.T) {
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
diff --git a/etc/Android.bp b/etc/Android.bp
index 580c54f..8e043b8 100644
--- a/etc/Android.bp
+++ b/etc/Android.bp
@@ -11,6 +11,7 @@
         "soong-android",
     ],
     srcs: [
+        "adb_keys.go",
         "install_symlink.go",
         "otacerts_zip.go",
         "prebuilt_etc.go",
diff --git a/etc/adb_keys.go b/etc/adb_keys.go
new file mode 100644
index 0000000..1bce2f1
--- /dev/null
+++ b/etc/adb_keys.go
@@ -0,0 +1,66 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package etc
+
+import (
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterModuleType("adb_keys", AdbKeysModuleFactory)
+}
+
+type AdbKeysModule struct {
+	android.ModuleBase
+	outputPath  android.OutputPath
+	installPath android.InstallPath
+}
+
+func AdbKeysModuleFactory() android.Module {
+	module := &AdbKeysModule{}
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
+
+func (m *AdbKeysModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	productVariables := ctx.Config().ProductVariables()
+	if !(android.Bool(productVariables.Debuggable) && len(android.String(productVariables.AdbKeys)) > 0) {
+		m.Disable()
+		m.SkipInstall()
+		return
+	}
+
+	m.outputPath = android.PathForModuleOut(ctx, "adb_keys").OutputPath
+	input := android.ExistentPathForSource(ctx, android.String(productVariables.AdbKeys))
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.Cp,
+		Output: m.outputPath,
+		Input:  input.Path(),
+	})
+	m.installPath = android.PathForModuleInPartitionInstall(ctx, ctx.DeviceConfig().ProductPath(), "etc/security")
+	ctx.InstallFile(m.installPath, "adb_keys", m.outputPath)
+}
+
+func (m *AdbKeysModule) AndroidMkEntries() []android.AndroidMkEntries {
+	if m.IsSkipInstall() {
+		return []android.AndroidMkEntries{}
+	}
+
+	return []android.AndroidMkEntries{
+		{
+			Class:      "ETC",
+			OutputFile: android.OptionalPathForPath(m.outputPath),
+		}}
+}
diff --git a/etc/install_symlink.go b/etc/install_symlink.go
index 2182b86..aa33445 100644
--- a/etc/install_symlink.go
+++ b/etc/install_symlink.go
@@ -26,6 +26,7 @@
 
 func RegisterInstallSymlinkBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("install_symlink", InstallSymlinkFactory)
+	ctx.RegisterModuleType("install_symlink_host", InstallSymlinkHostFactory)
 }
 
 // install_symlink can be used to install an symlink with an arbitrary target to an arbitrary path
@@ -37,6 +38,14 @@
 	return module
 }
 
+// install_symlink can be used to install an symlink to an arbitrary path on the host.
+func InstallSymlinkHostFactory() android.Module {
+	module := &InstallSymlink{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidMultiTargetsArchModule(module, android.HostSupported, android.MultilibCommon)
+	return module
+}
+
 type InstallSymlinkProperties struct {
 	// Where to install this symlink, relative to the partition it's installed on.
 	// Which partition it's installed on can be controlled by the vendor, system_ext, ramdisk, etc.
diff --git a/etc/install_symlink_test.go b/etc/install_symlink_test.go
index d7165e5..c97d97c 100644
--- a/etc/install_symlink_test.go
+++ b/etc/install_symlink_test.go
@@ -133,3 +133,39 @@
 		}
 	`)
 }
+
+var prepareForInstallSymlinkHostTest = android.GroupFixturePreparers(
+	android.PrepareForTestWithAndroidBuildComponents,
+	android.FixtureRegisterWithContext(RegisterInstallSymlinkBuildComponents),
+)
+
+func TestInstallSymlinkHostBasic(t *testing.T) {
+	result := prepareForInstallSymlinkHostTest.RunTestWithBp(t, `
+		install_symlink_host {
+			name: "foo",
+			installed_location: "bin/foo",
+			symlink_target: "aa/bb/cc",
+		}
+	`)
+
+	buildOS := result.Config.BuildOS.String()
+	foo := result.ModuleForTests("foo", buildOS+"_common").Module()
+
+	androidMkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, foo)
+	if len(androidMkEntries) != 1 {
+		t.Fatalf("expected 1 androidmkentry, got %d", len(androidMkEntries))
+	}
+
+	symlinks := androidMkEntries[0].EntryMap["LOCAL_SOONG_INSTALL_SYMLINKS"]
+	if len(symlinks) != 1 {
+		t.Fatalf("Expected 1 symlink, got %d", len(symlinks))
+	}
+
+	if !strings.HasSuffix(symlinks[0], "bin/foo") {
+		t.Fatalf("Expected symlink install path to end in bin/foo, got: %s", symlinks[0])
+	}
+
+	if !strings.Contains(symlinks[0], "host") {
+		t.Fatalf("Expected symlink install path to contain `host`, got: %s", symlinks[0])
+	}
+}
diff --git a/filesystem/aconfig_files.go b/filesystem/aconfig_files.go
index 8af2ffa..608fccd 100644
--- a/filesystem/aconfig_files.go
+++ b/filesystem/aconfig_files.go
@@ -76,10 +76,13 @@
 		cmd.ImplicitOutput(outputPath)
 		f.appendToEntry(ctx, outputPath)
 	}
-	generatePartitionAconfigStorageFile("package_map", "package.map")
-	generatePartitionAconfigStorageFile("flag_map", "flag.map")
-	generatePartitionAconfigStorageFile("flag_val", "flag.val")
-	generatePartitionAconfigStorageFile("flag_info", "flag.info")
+
+	if ctx.Config().ReleaseCreateAconfigStorageFile() {
+		generatePartitionAconfigStorageFile("package_map", "package.map")
+		generatePartitionAconfigStorageFile("flag_map", "flag.map")
+		generatePartitionAconfigStorageFile("flag_val", "flag.val")
+		generatePartitionAconfigStorageFile("flag_info", "flag.info")
+	}
 
 	android.WriteExecutableFileRuleVerbatim(ctx, aconfigFlagsBuilderPath, sb.String())
 }
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index a26fac7..09d8fba 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -35,7 +35,7 @@
 }
 
 func registerBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("android_filesystem", filesystemFactory)
+	ctx.RegisterModuleType("android_filesystem", FilesystemFactory)
 	ctx.RegisterModuleType("android_filesystem_defaults", filesystemDefaultsFactory)
 	ctx.RegisterModuleType("android_system_image", SystemImageFactory)
 	ctx.RegisterModuleType("avb_add_hash_footer", avbAddHashFooterFactory)
@@ -137,6 +137,12 @@
 	Gen_aconfig_flags_pb *bool
 
 	Fsverity fsverityProperties
+
+	// If this property is set to true, the filesystem will call ctx.UncheckedModule(), causing
+	// it to not be built on checkbuilds. Used for the automatic migration from make to soong
+	// build modules, where we want to emit some not-yet-working filesystems and we don't want them
+	// to be built.
+	Unchecked_module *bool `blueprint:"mutated"`
 }
 
 // android_filesystem packages a set of modules and their transitive dependencies into a filesystem
@@ -144,7 +150,7 @@
 // modules in the filesystem image are built for the target device (i.e. Android, not Linux host).
 // The modules are placed in the filesystem image just like they are installed to the ordinary
 // partitions like system.img. For example, cc_library modules are placed under ./lib[64] directory.
-func filesystemFactory() android.Module {
+func FilesystemFactory() android.Module {
 	module := &filesystem{}
 	module.filterPackagingSpec = module.filterInstallablePackagingSpec
 	initFilesystemModule(module, module)
@@ -177,6 +183,13 @@
 	unknown
 )
 
+type FilesystemInfo struct {
+	// A text file containing the list of paths installed on the partition.
+	FileListFile android.Path
+}
+
+var FilesystemProvider = blueprint.NewProvider[FilesystemInfo]()
+
 func (f *filesystem) fsType(ctx android.ModuleContext) fsType {
 	typeStr := proptools.StringDefault(f.properties.Type, "ext4")
 	switch typeStr {
@@ -227,6 +240,14 @@
 
 	f.fileListFile = android.PathForModuleOut(ctx, "fileList").OutputPath
 	android.WriteFileRule(ctx, f.fileListFile, f.installedFilesList())
+
+	android.SetProvider(ctx, FilesystemProvider, FilesystemInfo{
+		FileListFile: f.fileListFile,
+	})
+
+	if proptools.Bool(f.properties.Unchecked_module) {
+		ctx.UncheckedModule()
+	}
 }
 
 func (f *filesystem) appendToEntry(ctx android.ModuleContext, installedFile android.OutputPath) {
diff --git a/fsgen/Android.bp b/fsgen/Android.bp
index aa8881f..9fa9557 100644
--- a/fsgen/Android.bp
+++ b/fsgen/Android.bp
@@ -19,3 +19,7 @@
     ],
     pluginFor: ["soong_build"],
 }
+
+soong_filesystem_creator {
+    name: "soong_filesystem_creator",
+}
diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
index ca948f4..a9f4256 100644
--- a/fsgen/filesystem_creator.go
+++ b/fsgen/filesystem_creator.go
@@ -17,12 +17,16 @@
 import (
 	"android/soong/android"
 	"android/soong/filesystem"
+	"crypto/sha256"
 	"fmt"
 	"strconv"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
+var pctx = android.NewPackageContext("android/soong/fsgen")
+
 func init() {
 	registerBuildComponents(android.InitRegistrationContext)
 }
@@ -31,14 +35,22 @@
 	ctx.RegisterModuleType("soong_filesystem_creator", filesystemCreatorFactory)
 }
 
+type filesystemCreatorProps struct {
+	Generated_partition_types   []string `blueprint:"mutated"`
+	Unsupported_partition_types []string `blueprint:"mutated"`
+}
+
 type filesystemCreator struct {
 	android.ModuleBase
+
+	properties filesystemCreatorProps
 }
 
 func filesystemCreatorFactory() android.Module {
 	module := &filesystemCreator{}
 
 	android.InitAndroidModule(module)
+	module.AddProperties(&module.properties)
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
 		module.createInternalModules(ctx)
 	})
@@ -47,36 +59,62 @@
 }
 
 func (f *filesystemCreator) createInternalModules(ctx android.LoadHookContext) {
-	f.createSystemImage(ctx)
+	for _, partitionType := range []string{"system"} {
+		if f.createPartition(ctx, partitionType) {
+			f.properties.Generated_partition_types = append(f.properties.Generated_partition_types, partitionType)
+		} else {
+			f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, partitionType)
+		}
+	}
 }
 
-func (f *filesystemCreator) createSystemImage(ctx android.LoadHookContext) {
+func (f *filesystemCreator) generatedModuleNameForPartition(cfg android.Config, partitionType string) string {
+	prefix := "soong"
+	if cfg.HasDeviceProduct() {
+		prefix = cfg.DeviceProduct()
+	}
+	return fmt.Sprintf("%s_generated_%s_image", prefix, partitionType)
+}
+
+// Creates a soong module to build the given partition. Returns false if we can't support building
+// it.
+func (f *filesystemCreator) createPartition(ctx android.LoadHookContext, partitionType string) bool {
 	baseProps := &struct {
 		Name *string
 	}{
-		Name: proptools.StringPtr(fmt.Sprintf("%s_generated_system_image", ctx.Config().DeviceProduct())),
+		Name: proptools.StringPtr(f.generatedModuleNameForPartition(ctx.Config(), partitionType)),
 	}
 
-	fsProps := &(filesystem.FilesystemProperties{})
+	fsProps := &filesystem.FilesystemProperties{}
+
+	// Don't build this module on checkbuilds, the soong-built partitions are still in-progress
+	// and sometimes don't build.
+	fsProps.Unchecked_module = proptools.BoolPtr(true)
+
 	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
-	systemPartitionVars := partitionVars.PartitionQualifiedVariables["system"]
+	specificPartitionVars := partitionVars.PartitionQualifiedVariables[partitionType]
 
 	// BOARD_AVB_ENABLE
 	fsProps.Use_avb = proptools.BoolPtr(partitionVars.BoardAvbEnable)
 	// BOARD_AVB_KEY_PATH
-	fsProps.Avb_private_key = proptools.StringPtr(systemPartitionVars.BoardAvbKeyPath)
+	fsProps.Avb_private_key = proptools.StringPtr(specificPartitionVars.BoardAvbKeyPath)
 	// BOARD_AVB_ALGORITHM
-	fsProps.Avb_algorithm = proptools.StringPtr(systemPartitionVars.BoardAvbAlgorithm)
+	fsProps.Avb_algorithm = proptools.StringPtr(specificPartitionVars.BoardAvbAlgorithm)
 	// BOARD_AVB_SYSTEM_ROLLBACK_INDEX
-	if rollbackIndex, err := strconv.ParseInt(systemPartitionVars.BoardAvbRollbackIndex, 10, 64); err == nil {
+	if rollbackIndex, err := strconv.ParseInt(specificPartitionVars.BoardAvbRollbackIndex, 10, 64); err == nil {
 		fsProps.Rollback_index = proptools.Int64Ptr(rollbackIndex)
 	}
 
-	fsProps.Partition_name = proptools.StringPtr("system")
+	fsProps.Partition_name = proptools.StringPtr(partitionType)
 	// BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE
-	fsProps.Type = proptools.StringPtr(systemPartitionVars.BoardFileSystemType)
+	fsProps.Type = proptools.StringPtr(specificPartitionVars.BoardFileSystemType)
+	if *fsProps.Type != "ext4" {
+		// Currently the android_filesystem module type only supports ext4:
+		// https://cs.android.com/android/platform/superproject/main/+/main:build/soong/filesystem/filesystem.go;l=416;drc=98047cfd07944b297a12d173453bc984806760d2
+		return false
+	}
 
-	fsProps.Base_dir = proptools.StringPtr("system")
+	fsProps.Base_dir = proptools.StringPtr(partitionType)
 
 	fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
 
@@ -103,9 +141,77 @@
 	// - filesystemProperties.Build_logtags
 	// - filesystemProperties.Fsverity.Libs
 	// - systemImageProperties.Linker_config_src
-	ctx.CreateModule(filesystem.SystemImageFactory, baseProps, fsProps)
+	var module android.Module
+	if partitionType == "system" {
+		module = ctx.CreateModule(filesystem.SystemImageFactory, baseProps, fsProps)
+	} else {
+		module = ctx.CreateModule(filesystem.FilesystemFactory, baseProps, fsProps)
+	}
+	module.HideFromMake()
+	return true
+}
+
+func (f *filesystemCreator) createDiffTest(ctx android.ModuleContext, partitionType string) android.Path {
+	partitionModuleName := f.generatedModuleNameForPartition(ctx.Config(), partitionType)
+	systemImage := ctx.GetDirectDepWithTag(partitionModuleName, generatedFilesystemDepTag)
+	filesystemInfo, ok := android.OtherModuleProvider(ctx, systemImage, filesystem.FilesystemProvider)
+	if !ok {
+		ctx.ModuleErrorf("Expected module %s to provide FileysystemInfo", partitionModuleName)
+	}
+	makeFileList := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/file_list.txt", ctx.Config().DeviceName(), partitionType))
+	// For now, don't allowlist anything. The test will fail, but that's fine in the current
+	// early stages where we're just figuring out what we need
+	emptyAllowlistFile := android.PathForModuleOut(ctx, fmt.Sprintf("allowlist_%s.txt", partitionModuleName))
+	android.WriteFileRule(ctx, emptyAllowlistFile, "")
+	diffTestResultFile := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.txt", partitionModuleName))
+
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().BuiltTool("file_list_diff").
+		Input(makeFileList).
+		Input(filesystemInfo.FileListFile).
+		Text(partitionModuleName).
+		FlagWithInput("--allowlists ", emptyAllowlistFile)
+	builder.Command().Text("touch").Output(diffTestResultFile)
+	builder.Build(partitionModuleName+" diff test", partitionModuleName+" diff test")
+	return diffTestResultFile
+}
+
+func createFailingCommand(ctx android.ModuleContext, message string) android.Path {
+	hasher := sha256.New()
+	hasher.Write([]byte(message))
+	filename := fmt.Sprintf("failing_command_%x.txt", hasher.Sum(nil))
+	file := android.PathForModuleOut(ctx, filename)
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().Textf("echo %s", proptools.NinjaAndShellEscape(message))
+	builder.Command().Text("exit 1 #").Output(file)
+	builder.Build("failing command "+filename, "failing command "+filename)
+	return file
+}
+
+type systemImageDepTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var generatedFilesystemDepTag systemImageDepTagType
+
+func (f *filesystemCreator) DepsMutator(ctx android.BottomUpMutatorContext) {
+	for _, partitionType := range f.properties.Generated_partition_types {
+		ctx.AddDependency(ctx.Module(), generatedFilesystemDepTag, f.generatedModuleNameForPartition(ctx.Config(), partitionType))
+	}
 }
 
 func (f *filesystemCreator) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if ctx.ModuleDir() != "build/soong/fsgen" {
+		ctx.ModuleErrorf("There can only be one soong_filesystem_creator in build/soong/fsgen")
+	}
+	f.HideFromMake()
 
+	var diffTestFiles []android.Path
+	for _, partitionType := range f.properties.Generated_partition_types {
+		diffTestFiles = append(diffTestFiles, f.createDiffTest(ctx, partitionType))
+	}
+	for _, partitionType := range f.properties.Unsupported_partition_types {
+		diffTestFiles = append(diffTestFiles, createFailingCommand(ctx, fmt.Sprintf("Couldn't build %s partition", partitionType)))
+	}
+	ctx.Phony("soong_generated_filesystem_tests", diffTestFiles...)
 }
diff --git a/fsgen/filesystem_creator_test.go b/fsgen/filesystem_creator_test.go
index 6bb0e77..554b66b 100644
--- a/fsgen/filesystem_creator_test.go
+++ b/fsgen/filesystem_creator_test.go
@@ -44,12 +44,13 @@
 		}),
 		android.FixtureMergeMockFs(android.MockFS{
 			"external/avb/test/data/testkey_rsa4096.pem": nil,
+			"build/soong/fsgen/Android.bp": []byte(`
+			soong_filesystem_creator {
+				name: "foo",
+			}
+			`),
 		}),
-	).RunTestWithBp(t, `
-	soong_filesystem_creator {
-		name: "foo",
-	}
-	`)
+	).RunTest(t)
 
 	fooSystem := result.ModuleForTests("test_product_generated_system_image", "android_common").Module().(interface {
 		FsProps() filesystem.FilesystemProperties
diff --git a/genrule/genrule.go b/genrule/genrule.go
index e5222a4..18ec0a4 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -139,8 +139,7 @@
 	Export_include_dirs []string
 
 	// list of input files
-	Srcs         proptools.Configurable[[]string] `android:"path,arch_variant"`
-	ResolvedSrcs []string                         `blueprint:"mutated"`
+	Srcs proptools.Configurable[[]string] `android:"path,arch_variant"`
 
 	// input files to exclude
 	Exclude_srcs []string `android:"path,arch_variant"`
@@ -426,8 +425,8 @@
 		}
 		return srcFiles
 	}
-	g.properties.ResolvedSrcs = g.properties.Srcs.GetOrDefault(ctx, nil)
-	srcFiles := addLabelsForInputs("srcs", g.properties.ResolvedSrcs, g.properties.Exclude_srcs)
+	srcs := g.properties.Srcs.GetOrDefault(ctx, nil)
+	srcFiles := addLabelsForInputs("srcs", srcs, g.properties.Exclude_srcs)
 	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcFiles.Strings()})
 
 	var copyFrom android.Paths
@@ -659,7 +658,7 @@
 // Collect information for opening IDE project files in java/jdeps.go.
 func (g *Module) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
 	dpInfo.Srcs = append(dpInfo.Srcs, g.Srcs().Strings()...)
-	for _, src := range g.properties.ResolvedSrcs {
+	for _, src := range g.properties.Srcs.GetOrDefault(ctx, nil) {
 		if strings.HasPrefix(src, ":") {
 			src = strings.Trim(src, ":")
 			dpInfo.Deps = append(dpInfo.Deps, src)
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 9278f15..f190750 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -24,6 +24,7 @@
 
 	"android/soong/android"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -694,8 +695,12 @@
 	expectedCmd := "cp in1 __SBOX_SANDBOX_DIR__/out/out"
 	android.AssertStringEquals(t, "cmd", expectedCmd, gen.rawCommands[0])
 
+	srcsFileProvider, ok := android.OtherModuleProvider(result.TestContext, gen, blueprint.SrcsFileProviderKey)
+	if !ok {
+		t.Fatal("Expected genrule to have a SrcsFileProviderData, but did not")
+	}
 	expectedSrcs := []string{"in1"}
-	android.AssertDeepEquals(t, "srcs", expectedSrcs, gen.properties.ResolvedSrcs)
+	android.AssertDeepEquals(t, "srcs", expectedSrcs, srcsFileProvider.SrcPaths)
 }
 
 func TestGenruleAllowMissingDependencies(t *testing.T) {
diff --git a/golang/golang.go b/golang/golang.go
index 618a085..6ee924f 100644
--- a/golang/golang.go
+++ b/golang/golang.go
@@ -22,6 +22,7 @@
 
 import (
 	"android/soong/android"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
 )
@@ -46,7 +47,7 @@
 func goPackageModuleFactory() android.Module {
 	module := &GoPackage{}
 	module.AddProperties(module.Properties()...)
-	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+	android.InitAndroidArchModule(module, android.HostSupportedNoCross, android.MultilibFirst)
 	return module
 }
 
diff --git a/golang/golang_test.go b/golang/golang_test.go
index 9c5b809..0a4baed 100644
--- a/golang/golang_test.go
+++ b/golang/golang_test.go
@@ -40,7 +40,7 @@
 		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
 			RegisterGoModuleTypes(ctx)
 			ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-				ctx.BottomUpBlueprint("bootstrap_deps", bootstrap.BootstrapDeps)
+				ctx.BottomUpBlueprint("bootstrap_deps", bootstrap.BootstrapDeps).UsesReverseDependencies()
 			})
 		}),
 	).RunTestWithBp(t, bp)
diff --git a/java/aar.go b/java/aar.go
index 7d73b03..41cc24a 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -483,9 +483,9 @@
 	}
 
 	linkFlags = append(linkFlags, "--no-static-lib-packages")
-	if a.isLibrary && a.useResourceProcessorBusyBox(ctx) {
-		// When building an android_library using ResourceProcessorBusyBox pass --merge-only to skip resource
-		// references validation until the final app link step when all static libraries are present.
+	if a.isLibrary {
+		// Pass --merge-only to skip resource references validation until the final
+		// app link step when when all static libraries are present.
 		linkFlags = append(linkFlags, "--merge-only")
 	}
 
diff --git a/java/androidmk.go b/java/androidmk.go
index 0539d25..2dff6cd 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -313,6 +313,7 @@
 			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 					entries.SetBool("LOCAL_STRIP_MODULE", false)
+					entries.AddStrings("LOCAL_REQUIRED_MODULES", binary.androidMkNamesOfJniLibs...)
 				},
 			},
 			ExtraFooters: []android.AndroidMkExtraFootersFunc{
diff --git a/java/app.go b/java/app.go
index 1358faf..dd99675 100644
--- a/java/app.go
+++ b/java/app.go
@@ -172,7 +172,7 @@
 	RotationMinSdkVersion *string
 
 	// the package name of this app. The package name in the manifest file is used if one was not given.
-	Package_name *string
+	Package_name proptools.Configurable[string]
 
 	// the logging parent of this app.
 	Logging_parent *string
@@ -386,7 +386,8 @@
 	checkMinSdkVersionMts(ctx, a.MinSdkVersion(ctx))
 	applicationId := a.appTestHelperAppProperties.Manifest_values.ApplicationId
 	if applicationId != nil {
-		if a.overridableAppProperties.Package_name != nil {
+		packageName := a.overridableAppProperties.Package_name.Get(ctx)
+		if packageName.IsPresent() {
 			ctx.PropertyErrorf("manifest_values.applicationId", "property is not supported when property package_name is set.")
 		}
 		a.aapt.manifestValues.applicationId = *applicationId
@@ -586,10 +587,11 @@
 	}
 
 	manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
-	if overridden || a.overridableAppProperties.Package_name != nil {
+	packageNameProp := a.overridableAppProperties.Package_name.Get(ctx)
+	if overridden || packageNameProp.IsPresent() {
 		// The product override variable has a priority over the package_name property.
 		if !overridden {
-			manifestPackageName = *a.overridableAppProperties.Package_name
+			manifestPackageName = packageNameProp.Get()
 		}
 		aaptLinkFlags = append(aaptLinkFlags, generateAaptRenamePackageFlags(manifestPackageName, a.renameResourcesPackage())...)
 		a.overriddenManifestPackageName = manifestPackageName
@@ -829,11 +831,12 @@
 		return android.PathForModuleSrc(ctx, *a.appProperties.Privapp_allowlist)
 	}
 
-	if a.overridableAppProperties.Package_name == nil {
+	packageNameProp := a.overridableAppProperties.Package_name.Get(ctx)
+	if packageNameProp.IsEmpty() {
 		ctx.PropertyErrorf("privapp_allowlist", "package_name must be set to use privapp_allowlist")
 	}
 
-	packageName := *a.overridableAppProperties.Package_name
+	packageName := packageNameProp.Get()
 	fileName := "privapp_allowlist_" + packageName + ".xml"
 	outPath := android.PathForModuleOut(ctx, fileName).OutputPath
 	ctx.Build(pctx, android.BuildParams{
@@ -1418,7 +1421,8 @@
 	}
 	applicationId := a.appTestProperties.Manifest_values.ApplicationId
 	if applicationId != nil {
-		if a.overridableAppProperties.Package_name != nil {
+		packageNameProp := a.overridableAppProperties.Package_name.Get(ctx)
+		if packageNameProp.IsPresent() {
 			ctx.PropertyErrorf("manifest_values.applicationId", "property is not supported when property package_name is set.")
 		}
 		a.aapt.manifestValues.applicationId = *applicationId
@@ -1469,10 +1473,11 @@
 		command.FlagWithArg("--test-file-name ", a.installApkName+".apk")
 	}
 
-	if a.overridableAppProperties.Package_name != nil {
+	packageNameProp := a.overridableAppProperties.Package_name.Get(ctx)
+	if packageNameProp.IsPresent() {
 		fixNeeded = true
 		command.FlagWithInput("--manifest ", a.manifestPath).
-			FlagWithArg("--package-name ", *a.overridableAppProperties.Package_name)
+			FlagWithArg("--package-name ", packageNameProp.Get())
 	}
 
 	if a.appTestProperties.Mainline_package_name != nil {
diff --git a/java/base.go b/java/base.go
index fc21c44..a9399cb 100644
--- a/java/base.go
+++ b/java/base.go
@@ -1628,7 +1628,7 @@
 
 	if j.ravenizer.enabled {
 		ravenizerInput := outputFile
-		ravenizerOutput := android.PathForModuleOut(ctx, "ravenizer", jarName)
+		ravenizerOutput := android.PathForModuleOut(ctx, "ravenizer", "", jarName)
 		ravenizerArgs := ""
 		if proptools.Bool(j.properties.Ravenizer.Strip_mockito) {
 			ravenizerArgs = "--strip-mockito"
@@ -1637,6 +1637,14 @@
 		outputFile = ravenizerOutput
 		localImplementationJars = android.Paths{ravenizerOutput}
 		completeStaticLibsImplementationJars = android.NewDepSet(android.PREORDER, localImplementationJars, nil)
+		if combinedResourceJar != nil {
+			ravenizerInput = combinedResourceJar
+			ravenizerOutput = android.PathForModuleOut(ctx, "ravenizer", "resources", jarName)
+			TransformRavenizer(ctx, ravenizerOutput, ravenizerInput, ravenizerArgs)
+			combinedResourceJar = ravenizerOutput
+			localResourceJars = android.Paths{ravenizerOutput}
+			completeStaticLibsResourceJars = android.NewDepSet(android.PREORDER, localResourceJars, nil)
+		}
 	}
 
 	if j.shouldApiMapper() {
@@ -2164,16 +2172,14 @@
 
 // Collect information for opening IDE project files in java/jdeps.go.
 func (j *Module) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
-	// jarjar rules will repackage the sources. To prevent misleading results, IdeInfo should contain the
-	// repackaged jar instead of the input sources.
 	if j.expandJarjarRules != nil {
 		dpInfo.Jarjar_rules = append(dpInfo.Jarjar_rules, j.expandJarjarRules.String())
+		// Add the header jar so that the rdeps can be resolved to the repackaged classes.
 		dpInfo.Jars = append(dpInfo.Jars, j.headerJarFile.String())
-	} else {
-		dpInfo.Srcs = append(dpInfo.Srcs, j.expandIDEInfoCompiledSrcs...)
-		dpInfo.SrcJars = append(dpInfo.SrcJars, j.compiledSrcJars.Strings()...)
-		dpInfo.SrcJars = append(dpInfo.SrcJars, j.annoSrcJars.Strings()...)
 	}
+	dpInfo.Srcs = append(dpInfo.Srcs, j.expandIDEInfoCompiledSrcs...)
+	dpInfo.SrcJars = append(dpInfo.SrcJars, j.compiledSrcJars.Strings()...)
+	dpInfo.SrcJars = append(dpInfo.SrcJars, j.annoSrcJars.Strings()...)
 	dpInfo.Deps = append(dpInfo.Deps, j.CompilerDeps()...)
 	dpInfo.Aidl_include_dirs = append(dpInfo.Aidl_include_dirs, j.deviceProperties.Aidl.Include_dirs...)
 	dpInfo.Static_libs = append(dpInfo.Static_libs, j.staticLibs(ctx)...)
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index 1cca7ad..da86540 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -41,10 +41,50 @@
     is_stubs_module: true,
 }
 
+soong_config_module_type {
+    name: "core_current_stubs_soong_config_defaults",
+    module_type: "java_defaults",
+    config_namespace: "ANDROID",
+    bool_variables: [
+        "release_hidden_api_exportable_stubs",
+    ],
+    properties: [
+        "dist.targets",
+        "dist.dest",
+    ],
+}
+
+core_current_stubs_soong_config_defaults {
+    name: "core_current_stubs_everything_soong_config_defaults",
+    soong_config_variables: {
+        release_hidden_api_exportable_stubs: {
+            conditions_default: {
+                dist: {
+                    targets: dist_targets,
+                    dest: "core.current.stubs.jar",
+                },
+            },
+        },
+    },
+}
+
+core_current_stubs_soong_config_defaults {
+    name: "core_current_stubs_exportable_soong_config_defaults",
+    soong_config_variables: {
+        release_hidden_api_exportable_stubs: {
+            dist: {
+                targets: dist_targets,
+                dest: "core.current.stubs.jar",
+            },
+        },
+    },
+}
+
 java_library {
     name: "core.current.stubs",
     defaults: [
         "core.current.stubs.defaults",
+        "core_current_stubs_everything_soong_config_defaults",
     ],
     static_libs: [
         "art.module.public.api.stubs",
@@ -76,16 +116,13 @@
     name: "core.current.stubs.exportable",
     defaults: [
         "core.current.stubs.defaults",
+        "core_current_stubs_exportable_soong_config_defaults",
     ],
     static_libs: [
         "art.module.public.api.stubs.exportable",
         "conscrypt.module.public.api.stubs.exportable",
         "i18n.module.public.api.stubs.exportable",
     ],
-    dist: {
-        targets: dist_targets,
-        dest: "core.current.stubs.jar",
-    },
 }
 
 // Distributed with the SDK for turning into system modules to compile apps
diff --git a/java/dex.go b/java/dex.go
index e16b052..a3f699b 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -133,7 +133,7 @@
 			`$d8Template${config.D8Cmd} ${config.D8Flags} $d8Flags --output $outDir --no-dex-input-jar $in && ` +
 			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in && ` +
-			`rm -f "$outDir/classes*.dex" "$outDir/classes.dex.jar"`,
+			`rm -f "$outDir"/classes*.dex "$outDir/classes.dex.jar"`,
 		CommandDeps: []string{
 			"${config.D8Cmd}",
 			"${config.SoongZipCmd}",
@@ -172,7 +172,7 @@
 			`rm -rf ${outUsageDir} && ` +
 			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in && ` +
-			`rm -f "$outDir/classes*.dex" "$outDir/classes.dex.jar"`,
+			`rm -f "$outDir"/classes*.dex "$outDir/classes.dex.jar"`,
 		Depfile: "${out}.d",
 		Deps:    blueprint.DepsGCC,
 		CommandDeps: []string{
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 63a8634..637da36 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -494,6 +494,12 @@
 		PresignedPrebuilt: d.isPresignedPrebuilt,
 	}
 
+	if ctx.Config().InstallApexSystemServerDexpreoptSamePartition() {
+		dexpreoptConfig.ApexPartition = android.PathForModuleInstall(ctx).Partition()
+	} else {
+		dexpreoptConfig.ApexPartition = "system"
+	}
+
 	d.configPath = android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, "dexpreopt.config")
 	dexpreopt.WriteModuleConfig(ctx, dexpreoptConfig, d.configPath)
 	ctx.CheckbuildFile(d.configPath)
diff --git a/java/dexpreopt_check.go b/java/dexpreopt_check.go
index 33be603..c971565 100644
--- a/java/dexpreopt_check.go
+++ b/java/dexpreopt_check.go
@@ -17,6 +17,8 @@
 import (
 	"strings"
 
+	"github.com/google/blueprint"
+
 	"android/soong/android"
 	"android/soong/dexpreopt"
 
@@ -43,16 +45,12 @@
 type dexpreoptSystemserverCheck struct {
 	android.SingletonModuleBase
 
-	// Mapping from the module name to the install paths to the compilation artifacts.
-	artifactsByModuleName map[string][]string
-
 	// The install paths to the compilation artifacts.
 	artifacts []string
 }
 
 func dexpreoptSystemserverCheckFactory() android.SingletonModule {
 	m := &dexpreoptSystemserverCheck{}
-	m.artifactsByModuleName = make(map[string][]string)
 	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
 	return m
 }
@@ -62,7 +60,25 @@
 		ctx, "", strings.TrimPrefix(location, "/"))
 }
 
-func (m *dexpreoptSystemserverCheck) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+type systemServerDependencyTag struct {
+	blueprint.BaseDependencyTag
+}
+
+// systemServerJarDepTag willl be used for validation. Skip visiblility.
+func (b systemServerDependencyTag) ExcludeFromVisibilityEnforcement() {
+}
+
+var (
+	// dep tag for platform and apex system server jars
+	systemServerJarDepTag = systemServerDependencyTag{}
+)
+
+var _ android.ExcludeFromVisibilityEnforcementTag = systemServerJarDepTag
+
+// Add a depenendency on the system server jars. The dexpreopt files of those will be emitted to make.
+// The kati packaging system will verify that those files appear in installed files.
+// Adding the dependency allows the singleton module to determine whether an apex system server jar is system_ext specific.
+func (m *dexpreoptSystemserverCheck) DepsMutator(ctx android.BottomUpMutatorContext) {
 	global := dexpreopt.GetGlobalConfig(ctx)
 	targets := ctx.Config().Targets[android.Android]
 
@@ -72,23 +88,27 @@
 		return
 	}
 
-	systemServerJars := global.AllSystemServerJars(ctx)
-	for _, jar := range systemServerJars.CopyOfJars() {
-		dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, jar)
-		odexLocation := dexpreopt.ToOdexPath(dexLocation, targets[0].Arch.ArchType)
+	ctx.AddDependency(ctx.Module(), systemServerJarDepTag, global.AllSystemServerJars(ctx).CopyOfJars()...)
+}
+
+func (m *dexpreoptSystemserverCheck) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	global := dexpreopt.GetGlobalConfig(ctx)
+	targets := ctx.Config().Targets[android.Android]
+
+	ctx.VisitDirectDepsWithTag(systemServerJarDepTag, func(systemServerJar android.Module) {
+		partition := "system"
+		if systemServerJar.InstallInSystemExt() && ctx.Config().InstallApexSystemServerDexpreoptSamePartition() {
+			partition = ctx.DeviceConfig().SystemExtPath() // system_ext
+		}
+		dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, systemServerJar.Name())
+		odexLocation := dexpreopt.ToOdexPath(dexLocation, targets[0].Arch.ArchType, partition)
 		odexPath := getInstallPath(ctx, odexLocation)
 		vdexPath := getInstallPath(ctx, pathtools.ReplaceExtension(odexLocation, "vdex"))
-		m.artifactsByModuleName[jar] = []string{odexPath.String(), vdexPath.String()}
-	}
+		m.artifacts = append(m.artifacts, odexPath.String(), vdexPath.String())
+	})
 }
 
 func (m *dexpreoptSystemserverCheck) GenerateSingletonBuildActions(ctx android.SingletonContext) {
-	// Only keep modules defined in Soong.
-	ctx.VisitAllModules(func(module android.Module) {
-		if artifacts, ok := m.artifactsByModuleName[module.Name()]; ok {
-			m.artifacts = append(m.artifacts, artifacts...)
-		}
-	})
 }
 
 func (m *dexpreoptSystemserverCheck) MakeVars(ctx android.MakeVarsContext) {
diff --git a/java/java.go b/java/java.go
index 661422b..018850f 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1795,8 +1795,7 @@
 	// Name of the class containing main to be inserted into the manifest as Main-Class.
 	Main_class *string
 
-	// Names of modules containing JNI libraries that should be installed alongside the host
-	// variant of the binary.
+	// Names of modules containing JNI libraries that should be installed alongside the binary.
 	Jni_libs []string `android:"arch_variant"`
 }
 
@@ -1809,6 +1808,8 @@
 
 	wrapperFile android.Path
 	binaryFile  android.InstallPath
+
+	androidMkNamesOfJniLibs []string
 }
 
 func (j *Binary) HostToolPath() android.OptionalPath {
@@ -1880,6 +1881,21 @@
 			ctx.ModuleName()+ext, j.wrapperFile)
 
 		setOutputFiles(ctx, j.Library.Module)
+
+		// Set the jniLibs of this binary.
+		// These will be added to `LOCAL_REQUIRED_MODULES`, and the kati packaging system will
+		// install these alongside the java binary.
+		ctx.VisitDirectDepsWithTag(jniInstallTag, func(jni android.Module) {
+			// Use the BaseModuleName of the dependency (without any prebuilt_ prefix)
+			bmn, _ := jni.(interface{ BaseModuleName() string })
+			j.androidMkNamesOfJniLibs = append(j.androidMkNamesOfJniLibs, bmn.BaseModuleName()+":"+jni.Target().Arch.ArchType.Bitness())
+		})
+		// Check that native libraries are not listed in `required`. Prompt users to use `jni_libs` instead.
+		ctx.VisitDirectDepsWithTag(android.RequiredDepTag, func(dep android.Module) {
+			if _, hasSharedLibraryInfo := android.OtherModuleProvider(ctx, dep, cc.SharedLibraryInfoProvider); hasSharedLibraryInfo {
+				ctx.ModuleErrorf("cc_library %s is no longer supported in `required` of java_binary modules. Please use jni_libs instead.", dep.Name())
+			}
+		})
 	}
 }
 
@@ -1888,11 +1904,9 @@
 		j.deps(ctx)
 	}
 	// These dependencies ensure the installation rules will install the jar file when the
-	// wrapper is installed, and the jni libraries on host when the wrapper is installed.
-	if ctx.Arch().ArchType != android.Common && ctx.Os().Class == android.Host {
-		ctx.AddVariationDependencies(nil, jniInstallTag, j.binaryProperties.Jni_libs...)
-	}
+	// wrapper is installed, and the jni libraries when the wrapper is installed.
 	if ctx.Arch().ArchType != android.Common {
+		ctx.AddVariationDependencies(nil, jniInstallTag, j.binaryProperties.Jni_libs...)
 		ctx.AddVariationDependencies(
 			[]blueprint.Variation{{Mutator: "arch", Variation: android.CommonArch.String()}},
 			binaryInstallTag, ctx.ModuleName())
diff --git a/java/java_test.go b/java/java_test.go
index db154ce..24dabdb1 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -3102,7 +3102,7 @@
 	}
 }
 
-// Test that a dependency edge is created to the "first" variant of a native library listed in `required` of java_binary
+// Test that a dependency edge is created to the matching variant of a native library listed in `jni_libs` of java_binary
 func TestNativeRequiredDepOfJavaBinary(t *testing.T) {
 	findDepsOfModule := func(ctx *android.TestContext, module android.Module, depName string) []blueprint.Module {
 		var ret []blueprint.Module
@@ -3118,7 +3118,7 @@
 java_binary {
 	name: "myjavabin",
 	main_class: "com.android.MyJava",
-	required: ["mynativelib"],
+	jni_libs: ["mynativelib"],
 }
 cc_library_shared {
 	name: "mynativelib",
diff --git a/java/jdeps_test.go b/java/jdeps_test.go
index d282f19..7a0fb10 100644
--- a/java/jdeps_test.go
+++ b/java/jdeps_test.go
@@ -109,7 +109,7 @@
 	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
 	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 
-	android.AssertBoolEquals(t, "IdeInfo.Srcs of repackaged library should be empty", true, len(dpInfo.Srcs) == 0)
+	android.AssertStringEquals(t, "IdeInfo.Srcs of repackaged library should not be empty", "foo.java", dpInfo.Srcs[0])
 	android.AssertStringEquals(t, "IdeInfo.Jar_rules of repackaged library should not be empty", "jarjar_rules.txt", dpInfo.Jarjar_rules[0])
 	if !android.SubstringInList(dpInfo.Jars, "soong/.intermediates/javalib/android_common/jarjar/turbine/javalib.jar") {
 		t.Errorf("IdeInfo.Jars of repackaged library should contain the output of jarjar-ing. All outputs: %v\n", dpInfo.Jars)
diff --git a/java/sdk_library.go b/java/sdk_library.go
index f308772..dfbde0e 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -2481,19 +2481,3 @@
 		propertySet.AddProperty("doctag_files", dests)
 	}
 }
-
-// TODO(b/358613520): This can be removed when modules are no longer allowed to depend on the top-level library.
-func (s *SdkLibrary) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
-	s.Library.IDEInfo(ctx, dpInfo)
-	if s.implLibraryModule != nil {
-		dpInfo.Deps = append(dpInfo.Deps, s.implLibraryModule.Name())
-	} else {
-		// This java_sdk_library does not have an implementation (it sets `api_only` to true).
-		// Examples of this are `art.module.intra.core.api` (IntraCore api surface).
-		// Return the "public" stubs for these.
-		stubPaths := s.findClosestScopePath(apiScopePublic)
-		if len(stubPaths.stubsHeaderPath) > 0 {
-			dpInfo.Jars = append(dpInfo.Jars, stubPaths.stubsHeaderPath[0].String())
-		}
-	}
-}
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index 924abd4..aad1060 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -127,6 +127,26 @@
 	configuredJars = configuredJars.AppendList(&standaloneConfiguredJars)
 	classpathJars = append(classpathJars, standaloneClasspathJars...)
 	s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
+	s.setPartitionInfoOfLibraries(ctx)
+}
+
+// Map of java library name to their install partition.
+type LibraryNameToPartitionInfo struct {
+	LibraryNameToPartition map[string]string
+}
+
+// LibraryNameToPartitionInfoProvider will be used by the top-level apex to enforce that dexpreopt files
+// of apex system server jars are installed in the same partition as the top-level apex.
+var LibraryNameToPartitionInfoProvider = blueprint.NewProvider[LibraryNameToPartitionInfo]()
+
+func (s *SystemServerClasspathModule) setPartitionInfoOfLibraries(ctx android.ModuleContext) {
+	libraryNameToPartition := map[string]string{}
+	ctx.VisitDirectDepsWithTag(systemServerClasspathFragmentContentDepTag, func(m android.Module) {
+		libraryNameToPartition[m.Name()] = m.PartitionTag(ctx.DeviceConfig())
+	})
+	android.SetProvider(ctx, LibraryNameToPartitionInfoProvider, LibraryNameToPartitionInfo{
+		LibraryNameToPartition: libraryNameToPartition,
+	})
 }
 
 func (s *SystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
diff --git a/rust/compiler.go b/rust/compiler.go
index 5bce16b..fd86917 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -154,7 +154,7 @@
 
 	// list of rust automatic crate dependencies.
 	// Rustlibs linkage is rlib for host targets and dylib for device targets.
-	Rustlibs []string `android:"arch_variant"`
+	Rustlibs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// list of rust proc_macro crate dependencies
 	Proc_macros []string `android:"arch_variant"`
@@ -497,7 +497,7 @@
 
 func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps {
 	deps.Rlibs = append(deps.Rlibs, compiler.Properties.Rlibs...)
-	deps.Rustlibs = append(deps.Rustlibs, compiler.Properties.Rustlibs...)
+	deps.Rustlibs = append(deps.Rustlibs, compiler.Properties.Rustlibs.GetOrDefault(ctx, nil)...)
 	deps.ProcMacros = append(deps.ProcMacros, compiler.Properties.Proc_macros...)
 	deps.StaticLibs = append(deps.StaticLibs, compiler.Properties.Static_libs...)
 	deps.WholeStaticLibs = append(deps.WholeStaticLibs, compiler.Properties.Whole_static_libs...)
diff --git a/rust/rust.go b/rust/rust.go
index 5602edc..a044a99 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -1756,6 +1756,16 @@
 
 var _ android.ApexModule = (*Module)(nil)
 
+// If a module is marked for exclusion from apexes, don't provide apex variants.
+// TODO(b/362509506): remove this once stubs are properly supported by rust_ffi targets.
+func (m *Module) CanHaveApexVariants() bool {
+	if m.ApexExclude() {
+		return false
+	} else {
+		return m.ApexModuleBase.CanHaveApexVariants()
+	}
+}
+
 func (mod *Module) MinSdkVersion() string {
 	return String(mod.Properties.Min_sdk_version)
 }
diff --git a/ui/build/config.go b/ui/build/config.go
index bd20442..75edfcd 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -1754,12 +1754,10 @@
 }
 
 func (c *configImpl) PrebuiltBuildTool(name string) string {
-	if v, ok := c.environ.Get("SANITIZE_HOST"); ok {
-		if sanitize := strings.Fields(v); inList("address", sanitize) {
-			asan := filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "asan/bin", name)
-			if _, err := os.Stat(asan); err == nil {
-				return asan
-			}
+	if c.environ.IsEnvTrue("SANITIZE_BUILD_TOOL_PREBUILTS") {
+		asan := filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "asan/bin", name)
+		if _, err := os.Stat(asan); err == nil {
+			return asan
 		}
 	}
 	return filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "bin", name)