Merge "Convert cc modules to use AndroidMkInfoProvider." into main
diff --git a/Android.bp b/Android.bp
index 34f9bf0..bd4b40f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -161,10 +161,11 @@
     name: "system-build.prop",
     stem: "build.prop",
     product_config: ":product_config",
-    // Currently, only microdroid and cf system image can refer to system-build.prop
+    // Currently, only microdroid, Ravenwood, and cf system image can refer to system-build.prop
     visibility: [
         "//build/make/target/product/generic",
         "//packages/modules/Virtualization/build/microdroid",
+        "//frameworks/base/ravenwood",
     ],
 }
 
diff --git a/aconfig/codegen/aconfig_declarations_group.go b/aconfig/codegen/aconfig_declarations_group.go
index 13daf47..6811d06 100644
--- a/aconfig/codegen/aconfig_declarations_group.go
+++ b/aconfig/codegen/aconfig_declarations_group.go
@@ -59,7 +59,7 @@
 func AconfigDeclarationsGroupFactory() android.Module {
 	module := &AconfigDeclarationsGroup{}
 	module.AddProperties(&module.properties)
-	android.InitAndroidModule(module)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
 	return module
 }
diff --git a/aconfig/codegen/aconfig_declarations_group_test.go b/aconfig/codegen/aconfig_declarations_group_test.go
index c69d21f..ef954ce 100644
--- a/aconfig/codegen/aconfig_declarations_group_test.go
+++ b/aconfig/codegen/aconfig_declarations_group_test.go
@@ -67,7 +67,7 @@
 	`)
 
 	// Check if aconfig_declarations_group module depends on the aconfig_library modules
-	java.CheckModuleDependencies(t, result.TestContext, "my_group", "", []string{
+	java.CheckModuleDependencies(t, result.TestContext, "my_group", "android_common", []string{
 		`bar-java`,
 		`foo-java`,
 	})
diff --git a/android/Android.bp b/android/Android.bp
index eb8c64d..3b54326 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -52,6 +52,7 @@
         "defs.go",
         "depset_generic.go",
         "deptag.go",
+        "dirgroup.go",
         "early_module_context.go",
         "expand.go",
         "filegroup.go",
@@ -89,7 +90,6 @@
         "prebuilt.go",
         "prebuilt_build_tool.go",
         "product_config.go",
-        "product_config_to_bp.go",
         "proto.go",
         "provider.go",
         "raw_files.go",
diff --git a/android/defaults.go b/android/defaults.go
index 8fe2879..510ebe0 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -273,8 +273,8 @@
 }
 
 func RegisterDefaultsPreArchMutators(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("defaults_deps", defaultsDepsMutator).Parallel()
-	ctx.BottomUp("defaults", defaultsMutator).Parallel().UsesCreateModule()
+	ctx.BottomUp("defaults_deps", defaultsDepsMutator)
+	ctx.BottomUp("defaults", defaultsMutator).UsesCreateModule()
 }
 
 func defaultsDepsMutator(ctx BottomUpMutatorContext) {
diff --git a/android/dirgroup.go b/android/dirgroup.go
new file mode 100644
index 0000000..20c4d13
--- /dev/null
+++ b/android/dirgroup.go
@@ -0,0 +1,63 @@
+// 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 android
+
+import (
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+func init() {
+	RegisterDirgroupBuildComponents(InitRegistrationContext)
+}
+
+func RegisterDirgroupBuildComponents(ctx RegistrationContext) {
+	ctx.RegisterModuleType("dirgroup", DirGroupFactory)
+}
+
+type dirGroupProperties struct {
+	// dirs lists directories that will be included in this dirgroup
+	Dirs proptools.Configurable[[]string] `android:"path"`
+}
+
+type dirGroup struct {
+	ModuleBase
+	DefaultableModuleBase
+	properties dirGroupProperties
+}
+
+type DirInfo struct {
+	// TODO(b/358302178): Use DirectoryPaths instead of Paths
+	Dirs Paths
+}
+
+var DirProvider = blueprint.NewProvider[DirInfo]()
+
+// dirgroup contains a list of dirs that are referenced by other modules
+// properties using the syntax ":<name>". dirgroup are also be used to export
+// dirs across package boundaries. Currently the only allowed usage is genrule's
+// dir_srcs property.
+func DirGroupFactory() Module {
+	module := &dirGroup{}
+	module.AddProperties(&module.properties)
+	InitAndroidModule(module)
+	InitDefaultableModule(module)
+	return module
+}
+
+func (fg *dirGroup) GenerateAndroidBuildActions(ctx ModuleContext) {
+	dirs := DirectoryPathsForModuleSrc(ctx, fg.properties.Dirs.GetOrDefault(ctx, nil))
+	SetProvider(ctx, DirProvider, DirInfo{Dirs: dirs})
+}
diff --git a/android/licenses.go b/android/licenses.go
index be1eede..949d678 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -106,19 +106,19 @@
 //
 // This goes before defaults expansion so the defaults can pick up the package default.
 func RegisterLicensesPackageMapper(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("licensesPackageMapper", licensesPackageMapper).Parallel()
+	ctx.BottomUp("licensesPackageMapper", licensesPackageMapper)
 }
 
 // Registers the function that gathers the license dependencies for each module.
 //
 // This goes after defaults expansion so that it can pick up default licenses and before visibility enforcement.
 func RegisterLicensesPropertyGatherer(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("licensesPropertyGatherer", licensesPropertyGatherer).Parallel()
+	ctx.BottomUp("licensesPropertyGatherer", licensesPropertyGatherer)
 }
 
 // Registers the function that verifies the licenses and license_kinds dependency types for each module.
 func RegisterLicensesDependencyChecker(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("licensesPropertyChecker", licensesDependencyChecker).Parallel()
+	ctx.BottomUp("licensesPropertyChecker", licensesDependencyChecker)
 }
 
 // Maps each package to its default applicable licenses.
diff --git a/android/module.go b/android/module.go
index 1979536..ea59b3b 100644
--- a/android/module.go
+++ b/android/module.go
@@ -113,6 +113,10 @@
 	VintfFragmentModuleNames(ctx ConfigurableEvaluatorContext) []string
 
 	ConfigurableEvaluator(ctx ConfigurableEvaluatorContext) proptools.ConfigurableEvaluator
+
+	// The usage of this method is experimental and should not be used outside of fsgen package.
+	// This will be removed once product packaging migration to Soong is complete.
+	DecodeMultilib(ctx ConfigContext) (string, string)
 }
 
 // Qualified id for a module
@@ -1073,6 +1077,11 @@
 
 	modPartition := mod.PartitionTag(deviceConfig)
 	for _, vintf := range vintfModules {
+		if vintf == nil {
+			// TODO(b/372091092): Remove this. Having it gives us missing dependency errors instead
+			// of nil pointer dereference errors, but we should resolve the missing dependencies.
+			continue
+		}
 		if vintfModule, ok := vintf.(*vintfFragmentModule); ok {
 			vintfPartition := vintfModule.PartitionTag(deviceConfig)
 			if modPartition != vintfPartition {
@@ -2288,6 +2297,10 @@
 	return proptools.Bool(m.commonProperties.Native_bridge_supported)
 }
 
+func (m *ModuleBase) DecodeMultilib(ctx ConfigContext) (string, string) {
+	return decodeMultilib(ctx, m)
+}
+
 type ConfigContext interface {
 	Config() Config
 }
diff --git a/android/module_proxy.go b/android/module_proxy.go
index bc5090e..0f552dd 100644
--- a/android/module_proxy.go
+++ b/android/module_proxy.go
@@ -201,3 +201,7 @@
 func (m ModuleProxy) ConfigurableEvaluator(ctx ConfigurableEvaluatorContext) proptools.ConfigurableEvaluator {
 	panic("method is not implemented on ModuleProxy")
 }
+
+func (m ModuleProxy) DecodeMultilib(ctx ConfigContext) (string, string) {
+	panic("method is not implemented on ModuleProxy")
+}
diff --git a/android/mutator.go b/android/mutator.go
index 0da3ec7..6bd2e60 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -211,10 +211,7 @@
 	// AddDependency adds a dependency to the given module.  It returns a slice of modules for each
 	// dependency (some entries may be nil).
 	//
-	// If the mutator is parallel (see MutatorHandle.Parallel), this method will pause until the
-	// new dependencies have had the current mutator called on them.  If the mutator is not
-	// parallel this method does not affect the ordering of the current mutator pass, but will
-	// be ordered correctly for all future mutator passes.
+	// This method will pause until the new dependencies have had the current mutator called on them.
 	AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) []blueprint.Module
 
 	// AddReverseDependency adds a dependency from the destination to the given module.
@@ -230,10 +227,7 @@
 	// each dependency (some entries may be nil).  A variant of the dependency must exist that matches
 	// all the non-local variations of the current module, plus the variations argument.
 	//
-	// If the mutator is parallel (see MutatorHandle.Parallel), this method will pause until the
-	// new dependencies have had the current mutator called on them.  If the mutator is not
-	// parallel this method does not affect the ordering of the current mutator pass, but will
-	// be ordered correctly for all future mutator passes.
+	// This method will pause until the new dependencies have had the current mutator called on them.
 	AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag, names ...string) []blueprint.Module
 
 	// AddReverseVariationDependency adds a dependency from the named module to the current
@@ -256,10 +250,7 @@
 	// Unlike AddVariationDependencies, the variations of the current module are ignored - the
 	// dependency only needs to match the supplied variations.
 	//
-	// If the mutator is parallel (see MutatorHandle.Parallel), this method will pause until the
-	// new dependencies have had the current mutator called on them.  If the mutator is not
-	// parallel this method does not affect the ordering of the current mutator pass, but will
-	// be ordered correctly for all future mutator passes.
+	// This method will pause until the new dependencies have had the current mutator called on them.
 	AddFarVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string) []blueprint.Module
 
 	// ReplaceDependencies finds all the variants of the module with the specified name, then
@@ -628,9 +619,6 @@
 	}
 
 	// Forward booleans set on the MutatorHandle to the blueprint.MutatorHandle.
-	if mutator.parallel {
-		handle.Parallel()
-	}
 	if mutator.usesRename {
 		handle.UsesRename()
 	}
@@ -655,6 +643,7 @@
 	// 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.
+	// Deprecated: all Mutators are parallel by default.
 	Parallel() MutatorHandle
 
 	// UsesRename marks the mutator as using the BottomUpMutatorContext.Rename method, which prevents
@@ -683,7 +672,6 @@
 }
 
 func (mutator *mutator) Parallel() MutatorHandle {
-	mutator.parallel = true
 	return mutator
 }
 
@@ -718,7 +706,7 @@
 }
 
 func RegisterComponentsMutator(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("component-deps", componentDepsMutator).Parallel()
+	ctx.BottomUp("component-deps", componentDepsMutator)
 }
 
 // A special mutator that runs just prior to the deps mutator to allow the dependencies
@@ -736,7 +724,7 @@
 }
 
 func registerDepsMutator(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("deps", depsMutator).Parallel().UsesReverseDependencies()
+	ctx.BottomUp("deps", depsMutator).UsesReverseDependencies()
 }
 
 // android.topDownMutatorContext either has to embed blueprint.TopDownMutatorContext, in which case every method that
diff --git a/android/mutator_test.go b/android/mutator_test.go
index 33fca9e..1d5f890 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -17,6 +17,8 @@
 import (
 	"fmt"
 	"strings"
+	"sync"
+	"sync/atomic"
 	"testing"
 
 	"github.com/google/blueprint"
@@ -220,7 +222,7 @@
 		}
 	`
 
-	finalGot := map[string]int{}
+	finalGot := sync.Map{}
 
 	GroupFixturePreparers(
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
@@ -251,9 +253,11 @@
 					}
 				})
 				ctx.BottomUp("final", func(ctx BottomUpMutatorContext) {
-					finalGot[ctx.Module().String()] += 1
+					counter, _ := finalGot.LoadOrStore(ctx.Module().String(), &atomic.Int64{})
+					counter.(*atomic.Int64).Add(1)
 					ctx.VisitDirectDeps(func(mod Module) {
-						finalGot[fmt.Sprintf("%s -> %s", ctx.Module().String(), mod)] += 1
+						counter, _ := finalGot.LoadOrStore(fmt.Sprintf("%s -> %s", ctx.Module().String(), mod), &atomic.Int64{})
+						counter.(*atomic.Int64).Add(1)
 					})
 				})
 			})
@@ -276,7 +280,13 @@
 		"foo{variant:b} -> common_dep_2{variant:a}": 1,
 	}
 
-	AssertDeepEquals(t, "final", finalWant, finalGot)
+	finalGotMap := make(map[string]int)
+	finalGot.Range(func(k, v any) bool {
+		finalGotMap[k.(string)] = int(v.(*atomic.Int64).Load())
+		return true
+	})
+
+	AssertDeepEquals(t, "final", finalWant, finalGotMap)
 }
 
 func TestTransitionMutatorInFinalDeps(t *testing.T) {
diff --git a/android/namespace.go b/android/namespace.go
index 866d125..8b3ebc4 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -457,7 +457,7 @@
 }
 
 func RegisterNamespaceMutator(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("namespace_deps", namespaceMutator).Parallel().MutatesGlobalState()
+	ctx.BottomUp("namespace_deps", namespaceMutator).MutatesGlobalState()
 }
 
 func namespaceMutator(ctx BottomUpMutatorContext) {
diff --git a/android/neverallow.go b/android/neverallow.go
index e135f57..439fe2d 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -44,7 +44,7 @@
 // - it has none of the "Without" properties matched (same rules as above)
 
 func registerNeverallowMutator(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("neverallow", neverallowMutator).Parallel()
+	ctx.BottomUp("neverallow", neverallowMutator)
 }
 
 var neverallows = []Rule{}
@@ -60,6 +60,8 @@
 	AddNeverAllowRules(createCcStubsRule())
 	AddNeverAllowRules(createProhibitHeaderOnlyRule())
 	AddNeverAllowRules(createLimitNdkExportRule()...)
+	AddNeverAllowRules(createLimitDirgroupRule()...)
+	AddNeverAllowRules(createFilesystemIsAutoGeneratedRule())
 }
 
 // Add a NeverAllow rule to the set of rules to apply.
@@ -275,6 +277,31 @@
 	}
 }
 
+func createLimitDirgroupRule() []Rule {
+	reason := "dirgroup module and dir_srcs property of genrule is allowed only to Trusty build rule."
+	return []Rule{
+		NeverAllow().
+			ModuleType("dirgroup").
+			WithMatcher("visibility", NotInList([]string{"//trusty/vendor/google/aosp/scripts"})).Because(reason),
+		NeverAllow().
+			ModuleType("dirgroup").
+			Without("visibility", "//trusty/vendor/google/aosp/scripts").Because(reason),
+		NeverAllow().
+			ModuleType("genrule").
+			Without("name", "lk.elf.arm64").
+			Without("name", "lk.elf.x86_64").
+			WithMatcher("dir_srcs", isSetMatcherInstance).Because(reason),
+	}
+}
+
+func createFilesystemIsAutoGeneratedRule() Rule {
+	return NeverAllow().
+		NotIn("build/soong/fsgen").
+		ModuleType("filesystem", "android_system_image").
+		WithMatcher("is_auto_generated", isSetMatcherInstance).
+		Because("is_auto_generated property is only allowed for filesystem modules in build/soong/fsgen directory")
+}
+
 func neverallowMutator(ctx BottomUpMutatorContext) {
 	m, ok := ctx.Module().(Module)
 	if !ok {
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 192c924..caec8c7 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -359,6 +359,21 @@
 			`headers_only can only be used for generating framework-minus-apex headers for non-updatable modules`,
 		},
 	},
+	// Test for the rule restricting use of is_auto_generated
+	{
+		name: `"is_auto_generated" outside allowed directory`,
+		fs: map[string][]byte{
+			"a/b/Android.bp": []byte(`
+				filesystem {
+					name: "baaz",
+					is_auto_generated: true,
+				}
+			`),
+		},
+		expectedErrors: []string{
+			`is_auto_generated property is only allowed for filesystem modules in build/soong/fsgen directory`,
+		},
+	},
 }
 
 var prepareForNeverAllowTest = GroupFixturePreparers(
@@ -367,6 +382,7 @@
 		ctx.RegisterModuleType("java_library", newMockJavaLibraryModule)
 		ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule)
 		ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule)
+		ctx.RegisterModuleType("filesystem", newMockFilesystemModule)
 	}),
 )
 
diff --git a/android/override_module.go b/android/override_module.go
index d844da6..50ddc9b 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -234,18 +234,18 @@
 // 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().MutatesDependencies() // modifies deps via addOverride
+	ctx.BottomUp("override_deps", overrideModuleDepsMutator).MutatesDependencies() // modifies deps via addOverride
 	ctx.Transition("override", &overrideTransitionMutator{})
-	ctx.BottomUp("override_apply", overrideApplyMutator).Parallel().MutatesDependencies()
+	ctx.BottomUp("override_apply", overrideApplyMutator).MutatesDependencies()
 	// overridableModuleDepsMutator calls OverridablePropertiesDepsMutator so that overridable modules can
 	// add deps from overridable properties.
-	ctx.BottomUp("overridable_deps", overridableModuleDepsMutator).Parallel()
+	ctx.BottomUp("overridable_deps", overridableModuleDepsMutator)
 	// Because overridableModuleDepsMutator is run after PrebuiltPostDepsMutator,
 	// 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().UsesReplaceDependencies()
-	ctx.BottomUp("replace_deps_on_override", replaceDepsOnOverridingModuleMutator).Parallel().UsesReplaceDependencies()
+	ctx.BottomUp("replace_deps_on_prebuilts_for_overridable_deps_again", PrebuiltPostDepsMutator).UsesReplaceDependencies()
+	ctx.BottomUp("replace_deps_on_override", replaceDepsOnOverridingModuleMutator).UsesReplaceDependencies()
 }
 
 type overrideBaseDependencyTag struct {
diff --git a/android/path_properties.go b/android/path_properties.go
index 6210aee..d80a8ed 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -27,7 +27,7 @@
 // to the output file of the referenced module.
 
 func registerPathDepsMutator(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("pathdeps", pathDepsMutator).Parallel()
+	ctx.BottomUp("pathdeps", pathDepsMutator)
 }
 
 // The pathDepsMutator automatically adds dependencies on any module that is listed with the
diff --git a/android/paths.go b/android/paths.go
index ec05831..371aed8 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -550,6 +550,58 @@
 	return ret
 }
 
+// DirectoryPathsForModuleSrcExcludes returns a Paths{} containing the resolved references in
+// directory paths. Elements of paths are resolved as:
+//   - filepath, relative to local module directory, resolves as a filepath relative to the local
+//     source directory
+//   - other modules using the ":name" syntax. These modules must implement DirProvider.
+//
+// TODO(b/358302178): Implement DirectoryPath and change the return type.
+func DirectoryPathsForModuleSrc(ctx ModuleMissingDepsPathContext, paths []string) Paths {
+	var ret Paths
+
+	for _, path := range paths {
+		if m, t := SrcIsModuleWithTag(path); m != "" {
+			module := GetModuleFromPathDep(ctx, m, t)
+			if module == nil {
+				ctx.ModuleErrorf(`missing dependency on %q, is the property annotated with android:"path"?`, m)
+				continue
+			}
+			if t != "" {
+				ctx.ModuleErrorf("DirProvider dependency %q does not support the tag %q", module, t)
+				continue
+			}
+			mctx, ok := ctx.(OtherModuleProviderContext)
+			if !ok {
+				panic(fmt.Errorf("%s is not an OtherModuleProviderContext", ctx))
+			}
+			if dirProvider, ok := OtherModuleProvider(mctx, module, DirProvider); ok {
+				ret = append(ret, dirProvider.Dirs...)
+			} else {
+				ReportPathErrorf(ctx, "module %q does not implement DirProvider", module)
+			}
+		} else {
+			p := pathForModuleSrc(ctx, path)
+			if isDir, err := ctx.Config().fs.IsDir(p.String()); err != nil {
+				ReportPathErrorf(ctx, "%s: %s", p, err.Error())
+			} else if !isDir {
+				ReportPathErrorf(ctx, "module directory path %q is not a directory", p)
+			} else {
+				ret = append(ret, p)
+			}
+		}
+	}
+
+	seen := make(map[Path]bool, len(ret))
+	for _, path := range ret {
+		if seen[path] {
+			ReportPathErrorf(ctx, "duplicated path %q", path)
+		}
+		seen[path] = true
+	}
+	return ret
+}
+
 // OutputPaths is a slice of OutputPath objects, with helpers to operate on the collection.
 type OutputPaths []OutputPath
 
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 0b0c517..5d75b62 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -272,6 +272,25 @@
 	InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, "srcs")
 }
 
+// InitConfigurablePrebuiltModuleString is the same as InitPrebuiltModule, but uses a
+// Configurable string property instead of a regular list of strings. It only produces a single
+// source file.
+func InitConfigurablePrebuiltModuleString(module PrebuiltInterface, srcs *proptools.Configurable[string], propertyName string) {
+	if srcs == nil {
+		panic(fmt.Errorf("%s must not be nil", propertyName))
+	}
+
+	srcsSupplier := func(ctx BaseModuleContext, _ Module) []string {
+		src := srcs.GetOrDefault(ctx, "")
+		if src == "" {
+			return nil
+		}
+		return []string{src}
+	}
+
+	InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, propertyName)
+}
+
 func InitSingleSourcePrebuiltModule(module PrebuiltInterface, srcProps interface{}, srcField string) {
 	srcPropsValue := reflect.ValueOf(srcProps).Elem()
 	srcStructField, _ := srcPropsValue.Type().FieldByName(srcField)
@@ -400,13 +419,13 @@
 }
 
 func RegisterPrebuiltsPreArchMutators(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("prebuilt_rename", PrebuiltRenameMutator).Parallel().UsesRename()
+	ctx.BottomUp("prebuilt_rename", PrebuiltRenameMutator).UsesRename()
 }
 
 func RegisterPrebuiltsPostDepsMutators(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("prebuilt_source", PrebuiltSourceDepsMutator).Parallel().UsesReverseDependencies()
-	ctx.BottomUp("prebuilt_select", PrebuiltSelectModuleMutator).Parallel()
-	ctx.BottomUp("prebuilt_postdeps", PrebuiltPostDepsMutator).Parallel().UsesReplaceDependencies()
+	ctx.BottomUp("prebuilt_source", PrebuiltSourceDepsMutator).UsesReverseDependencies()
+	ctx.BottomUp("prebuilt_select", PrebuiltSelectModuleMutator)
+	ctx.BottomUp("prebuilt_postdeps", PrebuiltPostDepsMutator).UsesReplaceDependencies()
 }
 
 // Returns the name of the source module corresponding to a prebuilt module
diff --git a/android/product_config_to_bp.go b/android/product_config_to_bp.go
deleted file mode 100644
index 680328f..0000000
--- a/android/product_config_to_bp.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2024 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package android
-
-func init() {
-	ctx := InitRegistrationContext
-	ctx.RegisterParallelSingletonType("product_config_to_bp_singleton", productConfigToBpSingletonFactory)
-}
-
-type productConfigToBpSingleton struct{}
-
-func (s *productConfigToBpSingleton) GenerateBuildActions(ctx SingletonContext) {
-	// TODO: update content from make-based product config
-	var content string
-	generatedBp := PathForOutput(ctx, "soong_generated_product_config.bp")
-	WriteFileRule(ctx, generatedBp, content)
-	ctx.Phony("product_config_to_bp", generatedBp)
-}
-
-// productConfigToBpSingleton generates a bp file from make-based product config
-func productConfigToBpSingletonFactory() Singleton {
-	return &productConfigToBpSingleton{}
-}
diff --git a/android/register.go b/android/register.go
index 94d875d..bb1ead7 100644
--- a/android/register.go
+++ b/android/register.go
@@ -92,7 +92,6 @@
 	topDownMutator    blueprint.TopDownMutator
 	transitionMutator blueprint.TransitionMutator
 
-	parallel                bool
 	usesRename              bool
 	usesReverseDependencies bool
 	usesReplaceDependencies bool
diff --git a/android/variable.go b/android/variable.go
index 4210f67..73c0d0e 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -29,7 +29,7 @@
 
 func registerVariableBuildComponents(ctx RegistrationContext) {
 	ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-		ctx.BottomUp("variable", VariableMutator).Parallel()
+		ctx.BottomUp("variable", VariableMutator)
 	})
 }
 
diff --git a/android/vintf_fragment.go b/android/vintf_fragment.go
index 329eac9..42eaaf0 100644
--- a/android/vintf_fragment.go
+++ b/android/vintf_fragment.go
@@ -44,7 +44,7 @@
 	m.AddProperties(
 		&m.properties,
 	)
-	InitAndroidArchModule(m, DeviceSupported, MultilibFirst)
+	InitAndroidArchModule(m, DeviceSupported, MultilibCommon)
 
 	return m
 }
diff --git a/android/vintf_fragment_test.go b/android/vintf_fragment_test.go
index 8be534c..cd90b98 100644
--- a/android/vintf_fragment_test.go
+++ b/android/vintf_fragment_test.go
@@ -29,7 +29,7 @@
 
 	testResult := PrepareForTestWithAndroidBuildComponents.RunTestWithBp(t, bp)
 
-	vintfFragmentBuild := testResult.TestContext.ModuleForTests("test_vintf_fragment", "android_arm64_armv8-a").Rule("assemble_vintf")
+	vintfFragmentBuild := testResult.TestContext.ModuleForTests("test_vintf_fragment", "android_common").Rule("assemble_vintf")
 	if !strings.Contains(vintfFragmentBuild.RuleParams.Command, "assemble_vintf") {
 		t.Errorf("Vintf_manifest build command does not process with assemble_vintf : " + vintfFragmentBuild.RuleParams.Command)
 	}
diff --git a/android/visibility.go b/android/visibility.go
index 61f2200..cee465e 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -268,7 +268,7 @@
 // The rule checker needs to be registered before defaults expansion to correctly check that
 // //visibility:xxx isn't combined with other packages in the same list in any one module.
 func RegisterVisibilityRuleChecker(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("visibilityRuleChecker", visibilityRuleChecker).Parallel()
+	ctx.BottomUp("visibilityRuleChecker", visibilityRuleChecker)
 }
 
 // Registers the function that gathers the visibility rules for each module.
@@ -278,12 +278,12 @@
 // the complete visibility lists from flat lists and after the package info is gathered to ensure
 // that default_visibility is available.
 func RegisterVisibilityRuleGatherer(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("visibilityRuleGatherer", visibilityRuleGatherer).Parallel()
+	ctx.BottomUp("visibilityRuleGatherer", visibilityRuleGatherer)
 }
 
 // This must be registered after the deps have been resolved.
 func RegisterVisibilityRuleEnforcer(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("visibilityRuleEnforcer", visibilityRuleEnforcer).Parallel()
+	ctx.BottomUp("visibilityRuleEnforcer", visibilityRuleEnforcer)
 }
 
 // Checks the per-module visibility rule lists before defaults expansion.
diff --git a/android/visibility_test.go b/android/visibility_test.go
index 1a2eeca..277be0f 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -2098,8 +2098,9 @@
 }
 
 type mockFilesystemModuleProperties struct {
-	Partition_type *string
-	Deps           []string
+	Partition_type    *string
+	Deps              []string
+	Is_auto_generated *bool
 }
 
 type mockFilesystemModule struct {
diff --git a/apex/apex.go b/apex/apex.go
index 6bb2a1a..30b16ee 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -55,20 +55,20 @@
 }
 
 func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
-	ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).Parallel().UsesReverseDependencies()
+	ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).UsesReverseDependencies()
 }
 
 func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
-	ctx.TopDown("apex_info", apexInfoMutator).Parallel()
-	ctx.BottomUp("apex_unique", apexUniqueVariationsMutator).Parallel()
-	ctx.BottomUp("apex_test_for_deps", apexTestForDepsMutator).Parallel()
-	ctx.BottomUp("apex_test_for", apexTestForMutator).Parallel()
+	ctx.TopDown("apex_info", apexInfoMutator)
+	ctx.BottomUp("apex_unique", apexUniqueVariationsMutator)
+	ctx.BottomUp("apex_test_for_deps", apexTestForDepsMutator)
+	ctx.BottomUp("apex_test_for", apexTestForMutator)
 	// Run mark_platform_availability before the apexMutator as the apexMutator needs to know whether
 	// it should create a platform variant.
-	ctx.BottomUp("mark_platform_availability", markPlatformAvailability).Parallel()
+	ctx.BottomUp("mark_platform_availability", markPlatformAvailability)
 	ctx.Transition("apex", &apexTransitionMutator{})
-	ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel().MutatesDependencies()
-	ctx.BottomUp("apex_dcla_deps", apexDCLADepsMutator).Parallel()
+	ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).MutatesDependencies()
+	ctx.BottomUp("apex_dcla_deps", apexDCLADepsMutator)
 }
 
 type apexBundleProperties struct {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index b3851e6..042b43d 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -82,14 +82,6 @@
 	return files.AddToFixture()
 }
 
-func withTargets(targets map[android.OsType][]android.Target) android.FixturePreparer {
-	return android.FixtureModifyConfig(func(config android.Config) {
-		for k, v := range targets {
-			config.Targets[k] = v
-		}
-	})
-}
-
 // withNativeBridgeTargets sets configuration with targets including:
 // - X86_64 (primary)
 // - X86 (secondary)
@@ -4051,11 +4043,20 @@
 			"libvndk27binder32.so": nil,
 		}),
 		withBinder32bit,
-		withTargets(map[android.OsType][]android.Target{
-			android.Android: {
-				{Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}},
-					NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
-			},
+		android.FixtureModifyConfig(func(config android.Config) {
+			target := android.Target{
+				Os: android.Android,
+				Arch: android.Arch{
+					ArchType:    android.Arm,
+					ArchVariant: "armv7-a-neon",
+					Abi:         []string{"armeabi-v7a"},
+				},
+				NativeBridge:             android.NativeBridgeDisabled,
+				NativeBridgeHostArchName: "",
+				NativeBridgeRelativePath: "",
+			}
+			config.Targets[android.Android] = []android.Target{target}
+			config.AndroidFirstDeviceTarget = target
 		}),
 	)
 
@@ -11863,3 +11864,42 @@
 		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
 	)
 }
+
+// partitions should not package the artifacts that are included inside the apex.
+func TestFilesystemWithApexDeps(t *testing.T) {
+	t.Parallel()
+	result := testApex(t, `
+		android_filesystem {
+			name: "myfilesystem",
+			deps: ["myapex"],
+		}
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			binaries: ["binfoo"],
+			native_shared_libs: ["libfoo"],
+			apps: ["appfoo"],
+			updatable: false,
+		}
+		apex_key {
+			name: "myapex.key",
+		}
+		cc_binary {
+			name: "binfoo",
+			apex_available: ["myapex"],
+		}
+		cc_library {
+			name: "libfoo",
+			apex_available: ["myapex"],
+		}
+		android_app {
+			name: "appfoo",
+			sdk_version: "current",
+			apex_available: ["myapex"],
+		}
+	`, filesystem.PrepareForTestWithFilesystemBuildComponents)
+
+	partition := result.ModuleForTests("myfilesystem", "android_common")
+	fileList := android.ContentFromFileRuleForTests(t, result, partition.Output("fileList"))
+	android.AssertDeepEquals(t, "filesystem with apex", "apex/myapex.apex\n", fileList)
+}
diff --git a/apex/builder.go b/apex/builder.go
index 371d7d5..20b4dbe 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -253,10 +253,10 @@
 
 	apexHostVerifierRule = pctx.StaticRule("apexHostVerifierRule", blueprint.RuleParams{
 		Command: `${host_apex_verifier} --deapexer=${deapexer} --debugfs=${debugfs_static} ` +
-			`--fsckerofs=${fsck_erofs} --apex=${in} && touch ${out}`,
+			`--fsckerofs=${fsck_erofs} --apex=${in} --partition_tag=${partition_tag} && touch ${out}`,
 		CommandDeps: []string{"${host_apex_verifier}", "${deapexer}", "${debugfs_static}", "${fsck_erofs}"},
 		Description: "run host_apex_verifier",
-	})
+	}, "partition_tag")
 
 	assembleVintfRule = pctx.StaticRule("assembleVintfRule", blueprint.RuleParams{
 		Command:     `rm -f $out && VINTF_IGNORE_TARGET_FCM_VERSION=true ${assemble_vintf} -i $in -o $out`,
@@ -621,7 +621,8 @@
 				}
 			} else {
 				if installSymbolFiles {
-					installedPath = ctx.InstallFile(apexDir.Join(ctx, fi.installDir), fi.stem(), fi.builtFile)
+					// store installedPath. symlinks might be created if required.
+					installedPath = apexDir.Join(ctx, fi.installDir, fi.stem())
 				}
 			}
 
@@ -976,7 +977,7 @@
 			runApexElfCheckerUnwanted(ctx, unsignedOutputFile.OutputPath, a.properties.Unwanted_transitive_deps))
 	}
 	if !a.testApex && android.InList(a.payloadFsType, []fsType{ext4, erofs}) {
-		validations = append(validations, runApexHostVerifier(ctx, unsignedOutputFile.OutputPath))
+		validations = append(validations, runApexHostVerifier(ctx, a, unsignedOutputFile.OutputPath))
 	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        rule,
@@ -1287,12 +1288,15 @@
 	return timestamp
 }
 
-func runApexHostVerifier(ctx android.ModuleContext, apexFile android.OutputPath) android.Path {
+func runApexHostVerifier(ctx android.ModuleContext, a *apexBundle, apexFile android.OutputPath) android.Path {
 	timestamp := android.PathForModuleOut(ctx, "host_apex_verifier.timestamp")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   apexHostVerifierRule,
 		Input:  apexFile,
 		Output: timestamp,
+		Args: map[string]string{
+			"partition_tag": a.PartitionTag(ctx.DeviceConfig()),
+		},
 	})
 	return timestamp
 }
diff --git a/apex/vndk.go b/apex/vndk.go
index 5e630c0..d88808b 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -95,7 +95,11 @@
 				// level for the primary architecture.
 				a.Disable()
 			} else {
-				mctx.AddDependency(mctx.Module(), prebuiltTag, cc.VndkLibrariesTxtModules(vndkVersion, mctx)...)
+				mctx.AddVariationDependencies(
+					mctx.Config().AndroidFirstDeviceTarget.Variations(),
+					prebuiltTag,
+					cc.VndkLibrariesTxtModules(vndkVersion, mctx)...,
+				)
 			}
 		}
 	}
diff --git a/cc/Android.bp b/cc/Android.bp
index 88a793c..a5ad9ce 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -27,6 +27,7 @@
         "builder.go",
         "cc.go",
         "ccdeps.go",
+        "cc_preprocess_no_configuration.go",
         "check.go",
         "coverage.go",
         "gen.go",
@@ -88,6 +89,7 @@
     testSrcs: [
         "afdo_test.go",
         "binary_test.go",
+        "cc_preprocess_no_configuration_test.go",
         "cc_test.go",
         "cc_test_only_property_test.go",
         "cmake_snapshot_test.go",
diff --git a/cc/cc.go b/cc/cc.go
index 9aff7df..80ae6a0 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -48,10 +48,10 @@
 
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.Transition("sdk", &sdkTransitionMutator{})
-		ctx.BottomUp("llndk", llndkMutator).Parallel()
+		ctx.BottomUp("llndk", llndkMutator)
 		ctx.Transition("link", &linkageTransitionMutator{})
 		ctx.Transition("version", &versionTransitionMutator{})
-		ctx.BottomUp("begin", BeginMutator).Parallel()
+		ctx.BottomUp("begin", BeginMutator)
 	})
 
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
@@ -59,10 +59,10 @@
 			san.registerMutators(ctx)
 		}
 
-		ctx.BottomUp("sanitize_runtime_deps", sanitizerRuntimeDepsMutator).Parallel()
-		ctx.BottomUp("sanitize_runtime", sanitizerRuntimeMutator).Parallel()
+		ctx.BottomUp("sanitize_runtime_deps", sanitizerRuntimeDepsMutator)
+		ctx.BottomUp("sanitize_runtime", sanitizerRuntimeMutator)
 
-		ctx.BottomUp("fuzz_deps", fuzzMutatorDeps)
+		ctx.Transition("fuzz", &fuzzTransitionMutator{})
 
 		ctx.Transition("coverage", &coverageTransitionMutator{})
 
@@ -72,8 +72,8 @@
 
 		ctx.Transition("lto", &ltoTransitionMutator{})
 
-		ctx.BottomUp("check_linktype", checkLinkTypeMutator).Parallel()
-		ctx.BottomUp("double_loadable", checkDoubleLoadableLibraries).Parallel()
+		ctx.BottomUp("check_linktype", checkLinkTypeMutator)
+		ctx.BottomUp("double_loadable", checkDoubleLoadableLibraries)
 	})
 
 	ctx.PostApexMutators(func(ctx android.RegisterMutatorsContext) {
diff --git a/cc/cc_preprocess_no_configuration.go b/cc/cc_preprocess_no_configuration.go
new file mode 100644
index 0000000..3d1d0a5
--- /dev/null
+++ b/cc/cc_preprocess_no_configuration.go
@@ -0,0 +1,108 @@
+// 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"
+	"strings"
+)
+
+func init() {
+	RegisterCCPreprocessNoConfiguration(android.InitRegistrationContext)
+}
+
+func RegisterCCPreprocessNoConfiguration(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("cc_preprocess_no_configuration", ccPreprocessNoConfigurationFactory)
+}
+
+// cc_preprocess_no_configuration modules run the c preprocessor on a single input source file.
+// They also have "no configuration", meaning they don't have an arch or os associated with them,
+// they should be thought of as pure textual transformations of the input file. In some cases this
+// is good, in others you might want to do different transformations depending on what arch the
+// result will be compiled in, in which case you can use cc_object instead of this module.
+func ccPreprocessNoConfigurationFactory() android.Module {
+	m := &ccPreprocessNoConfiguration{}
+	m.AddProperties(&m.properties)
+	android.InitAndroidModule(m)
+	return m
+}
+
+type ccPreprocessNoConfigurationProps struct {
+	// Called Srcs for consistency with the other cc module types, but only accepts 1 input source
+	// file.
+	Srcs []string `android:"path"`
+	// The flags to pass to the c compiler. Must include -E in order to enable preprocessing-only
+	// mode.
+	Cflags []string `android:"path"`
+}
+
+type ccPreprocessNoConfiguration struct {
+	android.ModuleBase
+	properties ccPreprocessNoConfigurationProps
+}
+
+func (m *ccPreprocessNoConfiguration) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	srcs := android.PathsForModuleSrc(ctx, m.properties.Srcs)
+	if len(srcs) != 1 {
+		ctx.PropertyErrorf("Srcs", "cc_preprocess_no_configuration only accepts 1 source file, found: %v", srcs.Strings())
+		return
+	}
+	src := srcs[0]
+
+	hasE := false
+	for _, cflag := range m.properties.Cflags {
+		if cflag == "-E" {
+			hasE = true
+			break
+		} else if cflag == "-P" || strings.HasPrefix(cflag, "-D") {
+			// do nothing, allow it
+		} else {
+			ctx.PropertyErrorf("Cflags", "cc_preprocess_no_configuration only allows -D and -P flags, found: %q", cflag)
+			return
+		}
+	}
+	if !hasE {
+		ctx.PropertyErrorf("Cflags", "cc_preprocess_no_configuration must have a -E cflag")
+		return
+	}
+
+	var ccCmd string
+	switch src.Ext() {
+	case ".c":
+		ccCmd = "clang"
+	case ".cpp", ".cc", ".cxx", ".mm":
+		ccCmd = "clang++"
+	default:
+		ctx.PropertyErrorf("srcs", "File %s has unknown extension. Supported extensions: .c, .cpp, .cc, .cxx, .mm", src)
+		return
+	}
+
+	ccCmd = "${config.ClangBin}/" + ccCmd
+
+	outFile := android.PathForModuleOut(ctx, src.Base())
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        cc,
+		Description: ccCmd + " " + src.Rel(),
+		Output:      outFile,
+		Input:       src,
+		Args: map[string]string{
+			"cFlags": strings.Join(m.properties.Cflags, " "),
+			"ccCmd":  ccCmd,
+		},
+	})
+
+	ctx.SetOutputFiles([]android.Path{outFile}, "")
+}
diff --git a/cc/cc_preprocess_no_configuration_test.go b/cc/cc_preprocess_no_configuration_test.go
new file mode 100644
index 0000000..43e726d
--- /dev/null
+++ b/cc/cc_preprocess_no_configuration_test.go
@@ -0,0 +1,40 @@
+// Copyright 2019 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 TestCcPreprocessNoConfiguration(t *testing.T) {
+	fixture := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		android.FixtureRegisterWithContext(RegisterCCPreprocessNoConfiguration),
+	)
+
+	result := fixture.RunTestWithBp(t, `
+cc_preprocess_no_configuration {
+	name: "foo",
+	srcs: ["main.cc"],
+	cflags: ["-E", "-DANDROID"],
+}
+`)
+
+	foo := result.ModuleForTests("foo", "")
+	actual := foo.Rule("cc").Args["cFlags"]
+	expected := "-E -DANDROID"
+	android.AssertStringEquals(t, "cflags should be correct", expected, actual)
+}
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 8b930e9..d7ad6bc 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -1903,11 +1903,11 @@
 
 	moduleName = "afl_fuzz_static_lib"
 	checkPcGuardFlag(moduleName, variant+"_static", false)
-	checkPcGuardFlag(moduleName, variant+"_static_fuzzer", true)
+	checkPcGuardFlag(moduleName, variant+"_static_fuzzer_afl", true)
 
 	moduleName = "second_static_lib"
 	checkPcGuardFlag(moduleName, variant+"_static", false)
-	checkPcGuardFlag(moduleName, variant+"_static_fuzzer", true)
+	checkPcGuardFlag(moduleName, variant+"_static_fuzzer_afl", true)
 
 	ctx.ModuleForTests("afl_fuzz_shared_lib",
 		"android_arm64_armv8-a_shared").Rule("cc")
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 3f21bc6..0aa9d4b 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -57,38 +57,76 @@
 	return []interface{}{&fuzzer.Properties}
 }
 
-func fuzzMutatorDeps(mctx android.BottomUpMutatorContext) {
-	currentModule, ok := mctx.Module().(*Module)
+// fuzzTransitionMutator creates variants to propagate the FuzzFramework value down to dependencies.
+type fuzzTransitionMutator struct{}
+
+func (f *fuzzTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+	return []string{""}
+}
+
+func (f *fuzzTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	m, ok := ctx.Module().(*Module)
+	if !ok {
+		return ""
+	}
+
+	if m.fuzzer == nil {
+		return ""
+	}
+
+	if m.sanitize == nil {
+		return ""
+	}
+
+	isFuzzerPointer := m.sanitize.getSanitizerBoolPtr(Fuzzer)
+	if isFuzzerPointer == nil || !*isFuzzerPointer {
+		return ""
+	}
+
+	if m.fuzzer.Properties.FuzzFramework != "" {
+		return m.fuzzer.Properties.FuzzFramework.Variant()
+	}
+
+	return sourceVariation
+}
+
+func (f *fuzzTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	m, ok := ctx.Module().(*Module)
+	if !ok {
+		return ""
+	}
+
+	if m.fuzzer == nil {
+		return ""
+	}
+
+	if m.sanitize == nil {
+		return ""
+	}
+
+	isFuzzerPointer := m.sanitize.getSanitizerBoolPtr(Fuzzer)
+	if isFuzzerPointer == nil || !*isFuzzerPointer {
+		return ""
+	}
+
+	return incomingVariation
+}
+
+func (f *fuzzTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	m, ok := ctx.Module().(*Module)
 	if !ok {
 		return
 	}
 
-	if currentModule.fuzzer == nil {
+	if m.fuzzer == nil {
 		return
 	}
 
-	mctx.WalkDeps(func(child android.Module, parent android.Module) bool {
-		c, ok := child.(*Module)
-		if !ok {
-			return false
-		}
-
-		if c.sanitize == nil {
-			return false
-		}
-
-		isFuzzerPointer := c.sanitize.getSanitizerBoolPtr(Fuzzer)
-		if isFuzzerPointer == nil || !*isFuzzerPointer {
-			return false
-		}
-
-		if c.fuzzer == nil {
-			return false
-		}
-
-		c.fuzzer.Properties.FuzzFramework = currentModule.fuzzer.Properties.FuzzFramework
-		return true
-	})
+	if variation != "" {
+		m.fuzzer.Properties.FuzzFramework = fuzz.FrameworkFromVariant(variation)
+		m.SetHideFromMake()
+		m.SetPreventInstall()
+	}
 }
 
 // cc_fuzz creates a host/device fuzzer binary. Host binaries can be found at
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 18851d1..124dda4 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).Parallel()
+		ctx.BottomUp(t.variationName()+"_markapexes", sanitizer.markSanitizableApexesMutator)
 		ctx.Transition(t.variationName(), sanitizer)
 	case Memtag_heap, Memtag_globals, intOverflow:
 		// do nothing
diff --git a/cmd/release_config/build_flag/main.go b/cmd/release_config/build_flag/main.go
index 5d183ee..46efce7 100644
--- a/cmd/release_config/build_flag/main.go
+++ b/cmd/release_config/build_flag/main.go
@@ -329,7 +329,7 @@
 		return err
 	}
 	updatedFiles = append(updatedFiles, flagPath)
-	fmt.Printf("Added/Updated: %s\n", strings.Join(updatedFiles, " "))
+	fmt.Printf("\033[1mAdded/Updated: %s\033[0m\n", strings.Join(updatedFiles, " "))
 	return nil
 }
 
diff --git a/etc/adb_keys.go b/etc/adb_keys.go
index 1bce2f1..a2df41c 100644
--- a/etc/adb_keys.go
+++ b/etc/adb_keys.go
@@ -37,7 +37,6 @@
 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
 	}
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index fc6d1f7..fbe24d1 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -78,9 +78,16 @@
 	Src proptools.Configurable[string] `android:"path,arch_variant,replace_instead_of_append"`
 
 	// Source files of this prebuilt. Can reference a genrule type module with the ":module" syntax.
-	// Mutually exclusive with src. When used, filename_from_src is set to true.
+	// Mutually exclusive with src. When used, filename_from_src is set to true unless dsts is also
+	// set. May use globs in filenames.
 	Srcs proptools.Configurable[[]string] `android:"path,arch_variant"`
 
+	// Destination files of this prebuilt. Requires srcs to be used and causes srcs not to implicitly
+	// set filename_from_src. This can be used to install each source file to a different directory
+	// and/or change filenames when files are installed. Must be exactly one entry per source file,
+	// which means care must be taken if srcs has globs.
+	Dsts proptools.Configurable[[]string] `android:"path,arch_variant"`
+
 	// Optional name for the installed file. If unspecified, name of the module is used as the file
 	// name. Only available when using a single source (src).
 	Filename *string `android:"arch_variant"`
@@ -166,7 +173,7 @@
 	// The base install location when soc_specific property is set to true, e.g. "firmware" for
 	// prebuilt_firmware.
 	socInstallDirBase      string
-	installDirPath         android.InstallPath
+	installDirPaths        []android.InstallPath
 	additionalDependencies *android.Paths
 
 	usedSrcsProperty bool
@@ -279,7 +286,10 @@
 }
 
 func (p *PrebuiltEtc) InstallDirPath() android.InstallPath {
-	return p.installDirPath
+	if len(p.installDirPaths) != 1 {
+		panic(fmt.Errorf("InstallDirPath not available on multi-source prebuilt %q", p.Name()))
+	}
+	return p.installDirPaths[0]
 }
 
 // This allows other derivative modules (e.g. prebuilt_etc_xml) to perform
@@ -338,12 +348,16 @@
 	if srcProperty.IsPresent() && len(srcsProperty) > 0 {
 		ctx.PropertyErrorf("src", "src is set. Cannot set srcs")
 	}
+	dstsProperty := p.properties.Dsts.GetOrDefault(ctx, nil)
+	if len(dstsProperty) > 0 && len(srcsProperty) == 0 {
+		ctx.PropertyErrorf("dsts", "dsts is set. Must use srcs")
+	}
 
 	// Check that `sub_dir` and `relative_install_path` are not set at the same time.
 	if p.subdirProperties.Sub_dir != nil && p.subdirProperties.Relative_install_path != nil {
 		ctx.PropertyErrorf("sub_dir", "relative_install_path is set. Cannot set sub_dir")
 	}
-	p.installDirPath = android.PathForModuleInstall(ctx, p.installBaseDir(ctx), p.SubDir())
+	baseInstallDirPath := android.PathForModuleInstall(ctx, p.installBaseDir(ctx), p.SubDir())
 
 	filename := proptools.String(p.properties.Filename)
 	filenameFromSrc := proptools.Bool(p.properties.Filename_from_src)
@@ -379,10 +393,11 @@
 			filename:       filename,
 			sourceFilePath: p.sourceFilePaths[0],
 			outputFilePath: p.outputFilePaths[0],
-			installDirPath: p.installDirPath,
+			installDirPath: baseInstallDirPath,
 			symlinks:       p.properties.Symlinks,
 		}
 		installs = append(installs, ip)
+		p.installDirPaths = append(p.installDirPaths, baseInstallDirPath)
 	} else if len(srcsProperty) > 0 {
 		p.usedSrcsProperty = true
 		if filename != "" {
@@ -392,20 +407,39 @@
 			ctx.PropertyErrorf("symlinks", "symlinks cannot be set when using srcs")
 		}
 		if p.properties.Filename_from_src != nil {
-			ctx.PropertyErrorf("filename_from_src", "filename_from_src is implicitly set to true when using srcs")
+			if len(dstsProperty) > 0 {
+				ctx.PropertyErrorf("filename_from_src", "dsts is set. Cannot set filename_from_src")
+			} else {
+				ctx.PropertyErrorf("filename_from_src", "filename_from_src is implicitly set to true when using srcs")
+			}
 		}
 		p.sourceFilePaths = android.PathsForModuleSrc(ctx, srcsProperty)
-		for _, src := range p.sourceFilePaths {
-			filename := src.Base()
+		if len(dstsProperty) > 0 && len(p.sourceFilePaths) != len(dstsProperty) {
+			ctx.PropertyErrorf("dsts", "Must have one entry in dsts per source file")
+		}
+		for i, src := range p.sourceFilePaths {
+			var filename string
+			var installDirPath android.InstallPath
+
+			if len(dstsProperty) > 0 {
+				var dstdir string
+
+				dstdir, filename = filepath.Split(dstsProperty[i])
+				installDirPath = baseInstallDirPath.Join(ctx, dstdir)
+			} else {
+				filename = src.Base()
+				installDirPath = baseInstallDirPath
+			}
 			output := android.PathForModuleOut(ctx, filename).OutputPath
 			ip := installProperties{
 				filename:       filename,
 				sourceFilePath: src,
 				outputFilePath: output,
-				installDirPath: p.installDirPath,
+				installDirPath: installDirPath,
 			}
 			p.outputFilePaths = append(p.outputFilePaths, output)
 			installs = append(installs, ip)
+			p.installDirPaths = append(p.installDirPaths, installDirPath)
 		}
 	} else if ctx.Config().AllowMissingDependencies() {
 		// If no srcs was set and AllowMissingDependencies is enabled then
@@ -421,9 +455,10 @@
 			filename:       filename,
 			sourceFilePath: p.sourceFilePaths[0],
 			outputFilePath: p.outputFilePaths[0],
-			installDirPath: p.installDirPath,
+			installDirPath: baseInstallDirPath,
 		}
 		installs = append(installs, ip)
+		p.installDirPaths = append(p.installDirPaths, baseInstallDirPath)
 	} else {
 		ctx.PropertyErrorf("src", "missing prebuilt source file")
 		return
@@ -493,7 +528,7 @@
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_MODULE_TAGS", "optional")
-				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.String())
+				entries.SetString("LOCAL_MODULE_PATH", p.installDirPaths[0].String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePaths[0].Base())
 				if len(p.properties.Symlinks) > 0 {
 					entries.AddStrings("LOCAL_MODULE_SYMLINKS", p.properties.Symlinks...)
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index e739afe..75c6d12 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -119,6 +119,113 @@
 	android.AssertStringEquals(t, "output file path", "foo.conf", p.outputFilePaths[2].Base())
 }
 
+func TestPrebuiltEtcDsts(t *testing.T) {
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
+		prebuilt_etc {
+			name: "foo",
+			srcs: ["foo.conf", "bar.conf"],
+			dsts: ["foodir/foo.conf", "bardir/extradir/different.name"],
+		}
+	`)
+
+	p := result.Module("foo", "android_arm64_armv8-a").(*PrebuiltEtc)
+	android.AssertStringEquals(t, "output file path", "foo.conf", p.outputFilePaths[0].Base())
+	android.AssertStringEquals(t, "output file path", "different.name", p.outputFilePaths[1].Base())
+
+	expectedPaths := [...]string{
+		"out/soong/target/product/test_device/system/etc/foodir",
+		"out/soong/target/product/test_device/system/etc/bardir/extradir",
+	}
+	android.AssertPathRelativeToTopEquals(t, "install dir", expectedPaths[0], p.installDirPaths[0])
+	android.AssertPathRelativeToTopEquals(t, "install dir", expectedPaths[1], p.installDirPaths[1])
+}
+
+func TestPrebuiltEtcDstsPlusRelativeInstallPath(t *testing.T) {
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
+		prebuilt_etc {
+			name: "foo",
+			srcs: ["foo.conf", "bar.conf"],
+			dsts: ["foodir/foo.conf", "bardir/extradir/different.name"],
+			relative_install_path: "somewhere",
+		}
+	`)
+
+	p := result.Module("foo", "android_arm64_armv8-a").(*PrebuiltEtc)
+	android.AssertStringEquals(t, "output file path", "foo.conf", p.outputFilePaths[0].Base())
+	android.AssertStringEquals(t, "output file path", "different.name", p.outputFilePaths[1].Base())
+
+	expectedPaths := [...]string{
+		"out/soong/target/product/test_device/system/etc/somewhere/foodir",
+		"out/soong/target/product/test_device/system/etc/somewhere/bardir/extradir",
+	}
+	android.AssertPathRelativeToTopEquals(t, "install dir", expectedPaths[0], p.installDirPaths[0])
+	android.AssertPathRelativeToTopEquals(t, "install dir", expectedPaths[1], p.installDirPaths[1])
+}
+
+func TestPrebuiltEtcDstsSrcGlob(t *testing.T) {
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
+		prebuilt_etc {
+			name: "foo",
+			srcs: ["*.conf"],
+			dsts: ["a.conf", "b.conf", "c.conf"],
+		}
+	`)
+
+	p := result.Module("foo", "android_arm64_armv8-a").(*PrebuiltEtc)
+	android.AssertStringEquals(t, "output file path", "a.conf", p.outputFilePaths[0].Base())
+	android.AssertStringEquals(t, "output file path", "b.conf", p.outputFilePaths[1].Base())
+	android.AssertStringEquals(t, "output file path", "c.conf", p.outputFilePaths[2].Base())
+}
+
+func TestPrebuiltEtcDstsSrcGlobDstsTooShort(t *testing.T) {
+	prepareForPrebuiltEtcTest.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("Must have one entry in dsts per source file")).
+		RunTestWithBp(t, `
+			prebuilt_etc {
+				name: "foo",
+				srcs: ["*.conf"],
+				dsts: ["a.conf", "b.conf"],
+			}
+		`)
+}
+
+func TestPrebuiltEtcDstsSrcGlobDstsTooLong(t *testing.T) {
+	prepareForPrebuiltEtcTest.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("Must have one entry in dsts per source file")).
+		RunTestWithBp(t, `
+			prebuilt_etc {
+				name: "foo",
+				srcs: ["*.conf"],
+				dsts: ["a.conf", "b.conf", "c.conf", "d.conf"],
+			}
+		`)
+}
+
+func TestPrebuiltEtcCannotDstsWithSrc(t *testing.T) {
+	prepareForPrebuiltEtcTest.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("dsts is set. Must use srcs")).
+		RunTestWithBp(t, `
+			prebuilt_etc {
+				name: "foo.conf",
+				src: "foo.conf",
+				dsts: ["a.conf"],
+			}
+		`)
+}
+
+func TestPrebuiltEtcCannotDstsWithFilenameFromSrc(t *testing.T) {
+	prepareForPrebuiltEtcTest.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("dsts is set. Cannot set filename_from_src")).
+		RunTestWithBp(t, `
+			prebuilt_etc {
+				name: "foo.conf",
+				srcs: ["foo.conf"],
+				dsts: ["a.conf"],
+				filename_from_src: true,
+			}
+		`)
+}
+
 func TestPrebuiltEtcAndroidMk(t *testing.T) {
 	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_etc {
@@ -165,7 +272,7 @@
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/system/etc/bar"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltEtcCannotSetRelativeInstallPathAndSubDir(t *testing.T) {
@@ -231,7 +338,7 @@
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/system"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltRootInstallDirPathValidate(t *testing.T) {
@@ -256,7 +363,7 @@
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/root/avb"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltAvdInstallDirPathValidate(t *testing.T) {
@@ -280,7 +387,7 @@
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/system/usr/share/bar"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltUserShareHostInstallDirPath(t *testing.T) {
@@ -295,7 +402,7 @@
 	buildOS := result.Config.BuildOS.String()
 	p := result.Module("foo.conf", buildOS+"_common").(*PrebuiltEtc)
 	expected := filepath.Join("out/soong/host", result.Config.PrebuiltOS(), "usr", "share", "bar")
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltPrebuiltUserHyphenDataInstallDirPath(t *testing.T) {
@@ -309,7 +416,7 @@
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/system/usr/hyphen-data/bar"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltPrebuiltUserKeyLayoutInstallDirPath(t *testing.T) {
@@ -323,7 +430,7 @@
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/system/usr/keylayout/bar"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltPrebuiltUserKeyCharsInstallDirPath(t *testing.T) {
@@ -337,7 +444,7 @@
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/system/usr/keychars/bar"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltPrebuiltUserIdcInstallDirPath(t *testing.T) {
@@ -351,7 +458,7 @@
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/system/usr/idc/bar"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltFontInstallDirPath(t *testing.T) {
@@ -364,7 +471,7 @@
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/system/fonts"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltOverlayInstallDirPath(t *testing.T) {
@@ -377,7 +484,7 @@
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/system/overlay"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltFirmwareDirPath(t *testing.T) {
@@ -409,7 +516,7 @@
 		t.Run(tt.description, func(t *testing.T) {
 			result := prepareForPrebuiltEtcTest.RunTestWithBp(t, tt.config)
 			p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
-			android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPath)
+			android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPaths[0])
 		})
 	}
 }
@@ -443,7 +550,7 @@
 		t.Run(tt.description, func(t *testing.T) {
 			result := prepareForPrebuiltEtcTest.RunTestWithBp(t, tt.config)
 			p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
-			android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPath)
+			android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPaths[0])
 		})
 	}
 }
@@ -477,7 +584,7 @@
 		t.Run(tt.description, func(t *testing.T) {
 			result := prepareForPrebuiltEtcTest.RunTestWithBp(t, tt.config)
 			p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
-			android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPath)
+			android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPaths[0])
 		})
 	}
 }
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 9b3eae4..4bdd0a4 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -92,7 +92,7 @@
 	// Name of the partition stored in vbmeta desc. Defaults to the name of this module.
 	Partition_name *string
 
-	// Type of the filesystem. Currently, ext4, cpio, and compressed_cpio are supported. Default
+	// Type of the filesystem. Currently, ext4, erofs, cpio, and compressed_cpio are supported. Default
 	// is ext4.
 	Type *string
 
@@ -143,6 +143,24 @@
 	// 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"`
+
+	Erofs ErofsProperties
+
+	// Determines if the module is auto-generated from Soong or not. If the module is
+	// auto-generated, its deps are exempted from visibility enforcement.
+	Is_auto_generated *bool
+}
+
+// Additional properties required to generate erofs FS partitions.
+type ErofsProperties struct {
+	// Compressor and Compression level passed to mkfs.erofs. e.g. (lz4hc,9)
+	// Please see external/erofs-utils/README for complete documentation.
+	Compressor *string
+
+	// Used as --compress-hints for mkfs.erofs
+	Compress_hints *string `android:"path"`
+
+	Sparse *bool
 }
 
 // android_filesystem packages a set of modules and their transitive dependencies into a filesystem
@@ -165,24 +183,45 @@
 	android.InitDefaultableModule(module)
 }
 
-var dependencyTag = struct {
+type depTag struct {
 	blueprint.BaseDependencyTag
 	android.PackagingItemAlwaysDepTag
-}{}
+}
+
+var dependencyTag = depTag{}
+
+type depTagWithVisibilityEnforcementBypass struct {
+	depTag
+}
+
+var _ android.ExcludeFromVisibilityEnforcementTag = (*depTagWithVisibilityEnforcementBypass)(nil)
+
+func (t depTagWithVisibilityEnforcementBypass) ExcludeFromVisibilityEnforcement() {}
+
+var dependencyTagWithVisibilityEnforcementBypass = depTagWithVisibilityEnforcementBypass{}
 
 func (f *filesystem) DepsMutator(ctx android.BottomUpMutatorContext) {
-	f.AddDeps(ctx, dependencyTag)
+	if proptools.Bool(f.properties.Is_auto_generated) {
+		f.AddDeps(ctx, dependencyTagWithVisibilityEnforcementBypass)
+	} else {
+		f.AddDeps(ctx, dependencyTag)
+	}
 }
 
 type fsType int
 
 const (
 	ext4Type fsType = iota
+	erofsType
 	compressedCpioType
 	cpioType // uncompressed
 	unknown
 )
 
+func (fs fsType) IsUnknown() bool {
+	return fs == unknown
+}
+
 type FilesystemInfo struct {
 	// A text file containing the list of paths installed on the partition.
 	FileListFile android.Path
@@ -190,21 +229,30 @@
 
 var FilesystemProvider = blueprint.NewProvider[FilesystemInfo]()
 
-func (f *filesystem) fsType(ctx android.ModuleContext) fsType {
-	typeStr := proptools.StringDefault(f.properties.Type, "ext4")
+func GetFsTypeFromString(ctx android.EarlyModuleContext, typeStr string) fsType {
 	switch typeStr {
 	case "ext4":
 		return ext4Type
+	case "erofs":
+		return erofsType
 	case "compressed_cpio":
 		return compressedCpioType
 	case "cpio":
 		return cpioType
 	default:
-		ctx.PropertyErrorf("type", "%q not supported", typeStr)
 		return unknown
 	}
 }
 
+func (f *filesystem) fsType(ctx android.ModuleContext) fsType {
+	typeStr := proptools.StringDefault(f.properties.Type, "ext4")
+	fsType := GetFsTypeFromString(ctx, typeStr)
+	if fsType == unknown {
+		ctx.PropertyErrorf("type", "%q not supported", typeStr)
+	}
+	return fsType
+}
+
 func (f *filesystem) installFileName() string {
 	return f.BaseModuleName() + ".img"
 }
@@ -224,7 +272,7 @@
 func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	validatePartitionType(ctx, f)
 	switch f.fsType(ctx) {
-	case ext4Type:
+	case ext4Type, erofsType:
 		f.output = f.buildImageUsingBuildImage(ctx)
 	case compressedCpioType:
 		f.output = f.buildCpioImage(ctx, true)
@@ -437,6 +485,8 @@
 		// TODO(372522486): add more types like f2fs, erofs, etc.
 		case ext4Type:
 			return "ext4"
+		case erofsType:
+			return "erofs"
 		}
 		panic(fmt.Errorf("unsupported fs type %v", t))
 	}
@@ -486,6 +536,24 @@
 		addStr("uuid", uuid)
 		addStr("hash_seed", uuid)
 	}
+	// Add erofs properties
+	if f.fsType(ctx) == erofsType {
+		if compressor := f.properties.Erofs.Compressor; compressor != nil {
+			addStr("erofs_default_compressor", proptools.String(compressor))
+		}
+		if compressHints := f.properties.Erofs.Compress_hints; compressHints != nil {
+			addPath("erofs_default_compress_hints", android.PathForModuleSrc(ctx, *compressHints))
+		}
+		if proptools.BoolDefault(f.properties.Erofs.Sparse, true) {
+			// https://source.corp.google.com/h/googleplex-android/platform/build/+/88b1c67239ca545b11580237242774b411f2fed9:core/Makefile;l=2292;bpv=1;bpt=0;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b
+			addStr("erofs_sparse_flag", "-s")
+		}
+	} else if f.properties.Erofs.Compressor != nil || f.properties.Erofs.Compress_hints != nil || f.properties.Erofs.Sparse != nil {
+		// Raise an exception if the propfile contains erofs properties, but the fstype is not erofs
+		fs := fsTypeStr(f.fsType(ctx))
+		ctx.PropertyErrorf("erofs", "erofs is non-empty, but FS type is %s\n. Please delete erofs properties if this partition should use %s\n", fs, fs)
+	}
+
 	propFile = android.PathForModuleOut(ctx, "prop").OutputPath
 	android.WriteFileRuleVerbatim(ctx, propFile, propFileString.String())
 	return propFile, deps
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index 8c0d111..057dcaa 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -559,3 +559,28 @@
 		}
 	}
 }
+
+func TestErofsPartition(t *testing.T) {
+	result := fixture.RunTestWithBp(t, `
+		android_filesystem {
+			name: "erofs_partition",
+			type: "erofs",
+			erofs: {
+				compressor: "lz4hc,9",
+				compress_hints: "compress_hints.txt",
+			},
+			deps: ["binfoo"],
+		}
+
+		cc_binary {
+			name: "binfoo",
+		}
+	`)
+
+	partition := result.ModuleForTests("erofs_partition", "android_common")
+	buildImageConfig := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("prop"))
+	android.AssertStringDoesContain(t, "erofs fs type", buildImageConfig, "fs_type=erofs")
+	android.AssertStringDoesContain(t, "erofs fs type compress algorithm", buildImageConfig, "erofs_default_compressor=lz4hc,9")
+	android.AssertStringDoesContain(t, "erofs fs type compress hint", buildImageConfig, "erofs_default_compress_hints=compress_hints.txt")
+	android.AssertStringDoesContain(t, "erofs fs type sparse", buildImageConfig, "erofs_sparse_flag=-s")
+}
diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
index d75a4a2..f446c2b 100644
--- a/fsgen/filesystem_creator.go
+++ b/fsgen/filesystem_creator.go
@@ -15,13 +15,18 @@
 package fsgen
 
 import (
-	"android/soong/android"
-	"android/soong/filesystem"
 	"crypto/sha256"
 	"fmt"
+	"slices"
 	"strconv"
+	"strings"
+	"sync"
+
+	"android/soong/android"
+	"android/soong/filesystem"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/parser"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -33,6 +38,229 @@
 
 func registerBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("soong_filesystem_creator", filesystemCreatorFactory)
+	ctx.PreDepsMutators(RegisterCollectFileSystemDepsMutators)
+}
+
+func RegisterCollectFileSystemDepsMutators(ctx android.RegisterMutatorsContext) {
+	ctx.BottomUp("fs_collect_deps", collectDepsMutator).MutatesGlobalState()
+	ctx.BottomUp("fs_set_deps", setDepsMutator)
+}
+
+var fsGenStateOnceKey = android.NewOnceKey("FsGenState")
+
+// Map of partition module name to its partition that may be generated by Soong.
+// Note that it is not guaranteed that all modules returned by this function are successfully
+// created.
+func getAllSoongGeneratedPartitionNames(config android.Config, partitions []string) map[string]string {
+	ret := map[string]string{}
+	for _, partition := range partitions {
+		ret[generatedModuleNameForPartition(config, partition)] = partition
+	}
+	return ret
+}
+
+type depCandidateProps struct {
+	Namespace string
+	Multilib  string
+	Arch      []android.ArchType
+}
+
+// Map of module name to depCandidateProps
+type multilibDeps *map[string]*depCandidateProps
+
+// Information necessary to generate the filesystem modules, including details about their
+// dependencies
+type FsGenState struct {
+	// List of modules in `PRODUCT_PACKAGES` and `PRODUCT_PACKAGES_DEBUG`
+	depCandidates []string
+	// Map of names of partition to the information of modules to be added as deps
+	fsDeps map[string]multilibDeps
+	// List of name of partitions to be generated by the filesystem_creator module
+	soongGeneratedPartitions []string
+	// Mutex to protect the fsDeps
+	fsDepsMutex sync.Mutex
+}
+
+func newMultilibDeps() multilibDeps {
+	return &map[string]*depCandidateProps{}
+}
+
+func defaultDepCandidateProps(config android.Config) *depCandidateProps {
+	return &depCandidateProps{
+		Namespace: ".",
+		Arch:      []android.ArchType{config.BuildArch},
+	}
+}
+
+func createFsGenState(ctx android.LoadHookContext) *FsGenState {
+	return ctx.Config().Once(fsGenStateOnceKey, func() interface{} {
+		partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+		candidates := android.FirstUniqueStrings(android.Concat(partitionVars.ProductPackages, partitionVars.ProductPackagesDebug))
+
+		generatedPartitions := []string{"system"}
+		if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
+			generatedPartitions = append(generatedPartitions, "system_ext")
+		}
+
+		return &FsGenState{
+			depCandidates: candidates,
+			fsDeps: map[string]multilibDeps{
+				// These additional deps are added according to the cuttlefish system image bp.
+				"system": &map[string]*depCandidateProps{
+					"com.android.apex.cts.shim.v1_prebuilt":     defaultDepCandidateProps(ctx.Config()),
+					"dex_bootjars":                              defaultDepCandidateProps(ctx.Config()),
+					"framework_compatibility_matrix.device.xml": defaultDepCandidateProps(ctx.Config()),
+					"idc_data":                     defaultDepCandidateProps(ctx.Config()),
+					"init.environ.rc-soong":        defaultDepCandidateProps(ctx.Config()),
+					"keychars_data":                defaultDepCandidateProps(ctx.Config()),
+					"keylayout_data":               defaultDepCandidateProps(ctx.Config()),
+					"libclang_rt.asan":             defaultDepCandidateProps(ctx.Config()),
+					"libcompiler_rt":               defaultDepCandidateProps(ctx.Config()),
+					"libdmabufheap":                defaultDepCandidateProps(ctx.Config()),
+					"libgsi":                       defaultDepCandidateProps(ctx.Config()),
+					"llndk.libraries.txt":          defaultDepCandidateProps(ctx.Config()),
+					"logpersist.start":             defaultDepCandidateProps(ctx.Config()),
+					"preloaded-classes":            defaultDepCandidateProps(ctx.Config()),
+					"public.libraries.android.txt": defaultDepCandidateProps(ctx.Config()),
+					"update_engine_sideload":       defaultDepCandidateProps(ctx.Config()),
+				},
+				"vendor":     newMultilibDeps(),
+				"odm":        newMultilibDeps(),
+				"product":    newMultilibDeps(),
+				"system_ext": newMultilibDeps(),
+			},
+			soongGeneratedPartitions: generatedPartitions,
+			fsDepsMutex:              sync.Mutex{},
+		}
+	}).(*FsGenState)
+}
+
+func checkDepModuleInMultipleNamespaces(mctx android.BottomUpMutatorContext, foundDeps map[string]*depCandidateProps, module string, partitionName string) {
+	otherNamespace := mctx.Namespace().Path
+	if val, found := foundDeps[module]; found && otherNamespace != "." && !android.InList(val.Namespace, []string{".", otherNamespace}) {
+		mctx.ModuleErrorf("found in multiple namespaces(%s and %s) when including in %s partition", val.Namespace, otherNamespace, partitionName)
+	}
+}
+
+func appendDepIfAppropriate(mctx android.BottomUpMutatorContext, deps *map[string]*depCandidateProps, installPartition string) {
+	checkDepModuleInMultipleNamespaces(mctx, *deps, mctx.Module().Name(), installPartition)
+	if _, ok := (*deps)[mctx.Module().Name()]; ok {
+		// Prefer the namespace-specific module over the platform module
+		if mctx.Namespace().Path != "." {
+			(*deps)[mctx.Module().Name()].Namespace = mctx.Namespace().Path
+		}
+		(*deps)[mctx.Module().Name()].Arch = append((*deps)[mctx.Module().Name()].Arch, mctx.Module().Target().Arch.ArchType)
+	} else {
+		multilib, _ := mctx.Module().DecodeMultilib(mctx)
+		(*deps)[mctx.Module().Name()] = &depCandidateProps{
+			Namespace: mctx.Namespace().Path,
+			Multilib:  multilib,
+			Arch:      []android.ArchType{mctx.Module().Target().Arch.ArchType},
+		}
+	}
+}
+
+func collectDepsMutator(mctx android.BottomUpMutatorContext) {
+	fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
+
+	m := mctx.Module()
+	if slices.Contains(fsGenState.depCandidates, m.Name()) {
+		installPartition := m.PartitionTag(mctx.DeviceConfig())
+		fsGenState.fsDepsMutex.Lock()
+		// Only add the module as dependency when:
+		// - its enabled
+		// - its namespace is included in PRODUCT_SOONG_NAMESPACES
+		if m.Enabled(mctx) && m.ExportedToMake() {
+			appendDepIfAppropriate(mctx, fsGenState.fsDeps[installPartition], installPartition)
+		}
+		fsGenState.fsDepsMutex.Unlock()
+	}
+}
+
+type depsStruct struct {
+	Deps []string
+}
+
+type multilibDepsStruct struct {
+	Common   depsStruct
+	Lib32    depsStruct
+	Lib64    depsStruct
+	Both     depsStruct
+	Prefer32 depsStruct
+}
+
+type packagingPropsStruct struct {
+	Deps     []string
+	Multilib multilibDepsStruct
+}
+
+func fullyQualifiedModuleName(moduleName, namespace string) string {
+	if namespace == "." {
+		return moduleName
+	}
+	return fmt.Sprintf("//%s:%s", namespace, moduleName)
+}
+
+// Returns the sorted unique list of module names with namespace, if the module specifies one.
+func fullyQualifiedModuleNames(modules multilibDeps) (ret []string) {
+	for moduleName, moduleProp := range *modules {
+		ret = append(ret, fullyQualifiedModuleName(moduleName, moduleProp.Namespace))
+	}
+	return android.SortedUniqueStrings(ret)
+}
+
+func getBitness(archTypes []android.ArchType) (ret []string) {
+	for _, archType := range archTypes {
+		if archType.Multilib == "" {
+			ret = append(ret, android.COMMON_VARIANT)
+		} else {
+			ret = append(ret, archType.Bitness())
+		}
+	}
+	return ret
+}
+
+func setDepsMutator(mctx android.BottomUpMutatorContext) {
+	fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
+	fsDeps := fsGenState.fsDeps
+	soongGeneratedPartitionMap := getAllSoongGeneratedPartitionNames(mctx.Config(), fsGenState.soongGeneratedPartitions)
+	m := mctx.Module()
+	if partition, ok := soongGeneratedPartitionMap[m.Name()]; ok {
+		depsStruct := packagingPropsStruct{}
+		for depName, depProps := range *fsDeps[partition] {
+			bitness := getBitness(depProps.Arch)
+			fullyQualifiedDepName := fullyQualifiedModuleName(depName, depProps.Namespace)
+			if android.InList("32", bitness) && android.InList("64", bitness) {
+				// If both 32 and 64 bit variants are enabled for this module
+				switch depProps.Multilib {
+				case string(android.MultilibBoth):
+					depsStruct.Multilib.Both.Deps = append(depsStruct.Multilib.Both.Deps, fullyQualifiedDepName)
+				case string(android.MultilibCommon), string(android.MultilibFirst):
+					depsStruct.Deps = append(depsStruct.Deps, fullyQualifiedDepName)
+				case "32":
+					depsStruct.Multilib.Lib32.Deps = append(depsStruct.Multilib.Lib32.Deps, fullyQualifiedDepName)
+				case "64", "darwin_universal":
+					depsStruct.Multilib.Lib64.Deps = append(depsStruct.Multilib.Lib64.Deps, fullyQualifiedDepName)
+				case "prefer32", "first_prefer32":
+					depsStruct.Multilib.Prefer32.Deps = append(depsStruct.Multilib.Prefer32.Deps, fullyQualifiedDepName)
+				default:
+					depsStruct.Multilib.Both.Deps = append(depsStruct.Multilib.Both.Deps, fullyQualifiedDepName)
+				}
+			} else if android.InList("64", bitness) {
+				// If only 64 bit variant is enabled
+				depsStruct.Multilib.Lib64.Deps = append(depsStruct.Multilib.Lib64.Deps, fullyQualifiedDepName)
+			} else if android.InList("32", bitness) {
+				// If only 32 bit variant is enabled
+				depsStruct.Multilib.Lib32.Deps = append(depsStruct.Multilib.Lib32.Deps, fullyQualifiedDepName)
+			} else {
+				// If only common variant is enabled
+				depsStruct.Multilib.Common.Deps = append(depsStruct.Multilib.Common.Deps, fullyQualifiedDepName)
+			}
+		}
+		if err := proptools.AppendMatchingProperties(m.GetProperties(), &depsStruct, nil); err != nil {
+			mctx.ModuleErrorf(err.Error())
+		}
+	}
 }
 
 type filesystemCreatorProps struct {
@@ -49,9 +277,10 @@
 func filesystemCreatorFactory() android.Module {
 	module := &filesystemCreator{}
 
-	android.InitAndroidModule(module)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	module.AddProperties(&module.properties)
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+		createFsGenState(ctx)
 		module.createInternalModules(ctx)
 	})
 
@@ -59,17 +288,19 @@
 }
 
 func (f *filesystemCreator) createInternalModules(ctx android.LoadHookContext) {
-	for _, partitionType := range []string{"system"} {
+	soongGeneratedPartitions := &ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions
+	for _, partitionType := range *soongGeneratedPartitions {
 		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)
+			_, *soongGeneratedPartitions = android.RemoveFromList(partitionType, *soongGeneratedPartitions)
 		}
 	}
 	f.createDeviceModule(ctx)
 }
 
-func (f *filesystemCreator) generatedModuleName(cfg android.Config, suffix string) string {
+func generatedModuleName(cfg android.Config, suffix string) string {
 	prefix := "soong"
 	if cfg.HasDeviceProduct() {
 		prefix = cfg.DeviceProduct()
@@ -77,44 +308,89 @@
 	return fmt.Sprintf("%s_generated_%s", prefix, suffix)
 }
 
-func (f *filesystemCreator) generatedModuleNameForPartition(cfg android.Config, partitionType string) string {
-	return f.generatedModuleName(cfg, fmt.Sprintf("%s_image", partitionType))
+func generatedModuleNameForPartition(cfg android.Config, partitionType string) string {
+	return generatedModuleName(cfg, fmt.Sprintf("%s_image", partitionType))
 }
 
 func (f *filesystemCreator) createDeviceModule(ctx android.LoadHookContext) {
 	baseProps := &struct {
 		Name *string
 	}{
-		Name: proptools.StringPtr(f.generatedModuleName(ctx.Config(), "device")),
+		Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "device")),
 	}
 
-	// Currently, only the system partition module is created.
+	// Currently, only the system and system_ext partition module is created.
 	partitionProps := &filesystem.PartitionNameProperties{}
 	if android.InList("system", f.properties.Generated_partition_types) {
-		partitionProps.System_partition_name = proptools.StringPtr(f.generatedModuleNameForPartition(ctx.Config(), "system"))
+		partitionProps.System_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
+	}
+	if android.InList("system_ext", f.properties.Generated_partition_types) {
+		partitionProps.System_ext_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
 	}
 
 	ctx.CreateModule(filesystem.AndroidDeviceFactory, baseProps, partitionProps)
 }
 
+var (
+	// https://source.corp.google.com/h/googleplex-android/platform/build/+/639d79f5012a6542ab1f733b0697db45761ab0f3:core/packaging/flags.mk;l=21;drc=5ba8a8b77507f93aa48cc61c5ba3f31a4d0cbf37;bpv=1;bpt=0
+	partitionsWithAconfig = []string{"system", "product", "vendor"}
+)
+
 // 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(f.generatedModuleNameForPartition(ctx.Config(), partitionType)),
+	baseProps := generateBaseProps(proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), partitionType)))
+
+	fsProps, supported := generateFsProps(ctx, partitionType)
+	if !supported {
+		return false
 	}
 
+	var module android.Module
+	if partitionType == "system" {
+		module = ctx.CreateModule(filesystem.SystemImageFactory, baseProps, fsProps)
+	} else {
+		// Explicitly set the partition.
+		fsProps.Partition_type = proptools.StringPtr(partitionType)
+		module = ctx.CreateModule(filesystem.FilesystemFactory, baseProps, fsProps)
+	}
+	module.HideFromMake()
+	return true
+}
+
+type filesystemBaseProperty struct {
+	Name             *string
+	Compile_multilib *string
+}
+
+func generateBaseProps(namePtr *string) *filesystemBaseProperty {
+	return &filesystemBaseProperty{
+		Name:             namePtr,
+		Compile_multilib: proptools.StringPtr("both"),
+	}
+}
+
+func generateFsProps(ctx android.EarlyModuleContext, partitionType string) (*filesystem.FilesystemProperties, bool) {
 	fsProps := &filesystem.FilesystemProperties{}
 
+	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+	specificPartitionVars := partitionVars.PartitionQualifiedVariables[partitionType]
+
+	// BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE
+	fsType := specificPartitionVars.BoardFileSystemType
+	if fsType == "" {
+		fsType = "ext4" //default
+	}
+	fsProps.Type = proptools.StringPtr(fsType)
+	if filesystem.GetFsTypeFromString(ctx, *fsProps.Type).IsUnknown() {
+		// Currently the android_filesystem module type only supports a handful of FS types like ext4, erofs
+		return nil, false
+	}
+
 	// 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
-	specificPartitionVars := partitionVars.PartitionQualifiedVariables[partitionType]
-
 	// BOARD_AVB_ENABLE
 	fsProps.Use_avb = proptools.BoolPtr(partitionVars.BoardAvbEnable)
 	// BOARD_AVB_KEY_PATH
@@ -127,18 +403,12 @@
 	}
 
 	fsProps.Partition_name = proptools.StringPtr(partitionType)
-	// BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE
-	fsProps.Type = proptools.StringPtr(specificPartitionVars.BoardFileSystemType)
-	if *fsProps.Type != "ext4" {
-		// TODO(b/372522486): Support other FS types.
-		// 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(partitionType)
 
-	fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
+	fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(android.InList(partitionType, partitionsWithAconfig))
+
+	fsProps.Is_auto_generated = proptools.BoolPtr(true)
 
 	// Identical to that of the generic_system_image
 	fsProps.Fsverity.Inputs = []string{
@@ -163,18 +433,12 @@
 	// - filesystemProperties.Build_logtags
 	// - filesystemProperties.Fsverity.Libs
 	// - systemImageProperties.Linker_config_src
-	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
+
+	return fsProps, true
 }
 
 func (f *filesystemCreator) createDiffTest(ctx android.ModuleContext, partitionType string) android.Path {
-	partitionModuleName := f.generatedModuleNameForPartition(ctx.Config(), partitionType)
+	partitionModuleName := generatedModuleNameForPartition(ctx.Config(), partitionType)
 	systemImage := ctx.GetDirectDepWithTag(partitionModuleName, generatedFilesystemDepTag)
 	filesystemInfo, ok := android.OtherModuleProvider(ctx, systemImage, filesystem.FilesystemProvider)
 	if !ok {
@@ -218,7 +482,7 @@
 
 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))
+		ctx.AddDependency(ctx.Module(), generatedFilesystemDepTag, generatedModuleNameForPartition(ctx.Config(), partitionType))
 	}
 }
 
@@ -228,6 +492,11 @@
 	}
 	f.HideFromMake()
 
+	content := generateBpContent(ctx, "system")
+	generatedBp := android.PathForOutput(ctx, "soong_generated_product_config.bp")
+	android.WriteFileRule(ctx, generatedBp, content)
+	ctx.Phony("product_config_to_bp", generatedBp)
+
 	var diffTestFiles []android.Path
 	for _, partitionType := range f.properties.Generated_partition_types {
 		diffTestFiles = append(diffTestFiles, f.createDiffTest(ctx, partitionType))
@@ -237,3 +506,37 @@
 	}
 	ctx.Phony("soong_generated_filesystem_tests", diffTestFiles...)
 }
+
+func generateBpContent(ctx android.EarlyModuleContext, partitionType string) string {
+	// Currently only system partition is supported
+	if partitionType != "system" {
+		return ""
+	}
+
+	baseProps := generateBaseProps(proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), partitionType)))
+	fsProps, _ := generateFsProps(ctx, partitionType)
+
+	deps := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).fsDeps
+	depProps := &android.PackagingProperties{
+		Deps: android.NewSimpleConfigurable(fullyQualifiedModuleNames(deps[partitionType])),
+	}
+
+	result, err := proptools.RepackProperties([]interface{}{baseProps, fsProps, depProps})
+	if err != nil {
+		ctx.ModuleErrorf(err.Error())
+	}
+
+	file := &parser.File{
+		Defs: []parser.Definition{
+			&parser.Module{
+				Type: "module",
+				Map:  *result,
+			},
+		},
+	}
+	bytes, err := parser.Print(file)
+	if err != nil {
+		ctx.ModuleErrorf(err.Error())
+	}
+	return strings.TrimSpace(string(bytes))
+}
diff --git a/fsgen/filesystem_creator_test.go b/fsgen/filesystem_creator_test.go
index 554b66b..484cc38 100644
--- a/fsgen/filesystem_creator_test.go
+++ b/fsgen/filesystem_creator_test.go
@@ -17,6 +17,7 @@
 import (
 	"android/soong/android"
 	"android/soong/filesystem"
+	"android/soong/java"
 	"testing"
 
 	"github.com/google/blueprint/proptools"
@@ -28,6 +29,7 @@
 	result := android.GroupFixturePreparers(
 		android.PrepareForIntegrationTestWithAndroid,
 		android.PrepareForTestWithAndroidBuildComponents,
+		android.PrepareForTestWithAllowMissingDependencies,
 		filesystem.PrepareForTestWithFilesystemBuildComponents,
 		prepareForTestWithFsgenBuildComponents,
 		android.FixtureModifyConfig(func(config android.Config) {
@@ -86,3 +88,132 @@
 		proptools.String(fooSystem.FsProps().Type),
 	)
 }
+
+func TestFileSystemCreatorSetPartitionDeps(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		android.PrepareForTestWithAndroidBuildComponents,
+		android.PrepareForTestWithAllowMissingDependencies,
+		filesystem.PrepareForTestWithFilesystemBuildComponents,
+		prepareForTestWithFsgenBuildComponents,
+		java.PrepareForTestWithJavaBuildComponents,
+		java.PrepareForTestWithJavaDefaultModules,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.TestProductVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.ProductPackages = []string{"bar", "baz"}
+			config.TestProductVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.PartitionQualifiedVariables =
+				map[string]android.PartitionQualifiedVariablesType{
+					"system": {
+						BoardFileSystemType: "ext4",
+					},
+				}
+		}),
+		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, `
+	java_library {
+		name: "bar",
+		srcs: ["A.java"],
+	}
+	java_library {
+		name: "baz",
+		srcs: ["A.java"],
+		product_specific: true,
+	}
+	`)
+
+	android.AssertBoolEquals(
+		t,
+		"Generated system image expected to depend on system partition installed \"bar\"",
+		true,
+		java.CheckModuleHasDependency(t, result.TestContext, "test_product_generated_system_image", "android_common", "bar"),
+	)
+	android.AssertBoolEquals(
+		t,
+		"Generated system image expected to not depend on product partition installed \"baz\"",
+		false,
+		java.CheckModuleHasDependency(t, result.TestContext, "test_product_generated_system_image", "android_common", "baz"),
+	)
+}
+
+func TestFileSystemCreatorDepsWithNamespace(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		android.PrepareForTestWithAndroidBuildComponents,
+		android.PrepareForTestWithAllowMissingDependencies,
+		android.PrepareForTestWithNamespace,
+		android.PrepareForTestWithArchMutator,
+		filesystem.PrepareForTestWithFilesystemBuildComponents,
+		prepareForTestWithFsgenBuildComponents,
+		java.PrepareForTestWithJavaBuildComponents,
+		java.PrepareForTestWithJavaDefaultModules,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.TestProductVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.ProductPackages = []string{"bar"}
+			config.TestProductVariables.NamespacesToExport = []string{"a/b"}
+			config.TestProductVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.PartitionQualifiedVariables =
+				map[string]android.PartitionQualifiedVariablesType{
+					"system": {
+						BoardFileSystemType: "ext4",
+					},
+				}
+			config.Targets[android.Android] = []android.Target{
+				{Os: android.Android, Arch: android.Arch{ArchType: android.X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: "", HostCross: false},
+				{Os: android.Android, Arch: android.Arch{ArchType: android.X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: "", HostCross: false},
+				{Os: android.Android, Arch: android.Arch{ArchType: android.Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "x86_64", NativeBridgeRelativePath: "arm64", HostCross: false},
+				{Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "x86", NativeBridgeRelativePath: "arm", HostCross: false},
+			}
+		}),
+		android.FixtureMergeMockFs(android.MockFS{
+			"external/avb/test/data/testkey_rsa4096.pem": nil,
+			"build/soong/fsgen/Android.bp": []byte(`
+			soong_filesystem_creator {
+				name: "foo",
+			}
+			`),
+			"a/b/Android.bp": []byte(`
+			soong_namespace{
+			}
+			java_library {
+				name: "bar",
+				srcs: ["A.java"],
+				compile_multilib: "64",
+			}
+			`),
+			"c/d/Android.bp": []byte(`
+			soong_namespace{
+			}
+			java_library {
+				name: "bar",
+				srcs: ["A.java"],
+			}
+			`),
+		}),
+	).RunTest(t)
+
+	var packagingProps android.PackagingProperties
+	for _, prop := range result.ModuleForTests("test_product_generated_system_image", "android_common").Module().GetProperties() {
+		if packagingPropStruct, ok := prop.(*android.PackagingProperties); ok {
+			packagingProps = *packagingPropStruct
+		}
+	}
+	moduleDeps := packagingProps.Multilib.Lib64.Deps
+
+	eval := result.ModuleForTests("test_product_generated_system_image", "android_common").Module().ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
+	android.AssertStringListContains(
+		t,
+		"Generated system image expected to depend on \"bar\" defined in \"a/b\" namespace",
+		moduleDeps.GetOrDefault(eval, nil),
+		"//a/b:bar",
+	)
+	android.AssertStringListDoesNotContain(
+		t,
+		"Generated system image expected to not depend on \"bar\" defined in \"c/d\" namespace",
+		moduleDeps.GetOrDefault(eval, nil),
+		"//c/d:bar",
+	)
+}
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index a059837..42fd228 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -44,6 +44,32 @@
 	UnknownFramework Framework = "unknownframework"
 )
 
+func (f Framework) Variant() string {
+	switch f {
+	case AFL:
+		return "afl"
+	case LibFuzzer:
+		return "libfuzzer"
+	case Jazzer:
+		return "jazzer"
+	default:
+		panic(fmt.Errorf("unknown fuzzer %q when getting variant", f))
+	}
+}
+
+func FrameworkFromVariant(v string) Framework {
+	switch v {
+	case "afl":
+		return AFL
+	case "libfuzzer":
+		return LibFuzzer
+	case "jazzer":
+		return Jazzer
+	default:
+		panic(fmt.Errorf("unknown variant %q when getting fuzzer", v))
+	}
+}
+
 var BoolDefault = proptools.BoolDefault
 
 type FuzzModule struct {
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 8721f15..f2a761c 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -63,7 +63,7 @@
 	ctx.RegisterModuleType("genrule", GenRuleFactory)
 
 	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("genrule_tool_deps", toolDepsMutator).Parallel()
+		ctx.BottomUp("genrule_tool_deps", toolDepsMutator)
 	})
 }
 
@@ -219,6 +219,7 @@
 
 	// For nsjail tasks
 	useNsjail bool
+	dirSrcs   android.Paths
 }
 
 func (g *Module) GeneratedSourceFiles() android.Paths {
@@ -579,10 +580,12 @@
 		}
 
 		if task.useNsjail {
-			for _, input := range task.in {
-				// can fail if input is a file.
+			for _, input := range task.dirSrcs {
+				cmd.Implicit(input)
 				if paths, err := ctx.GlobWithDeps(filepath.Join(input.String(), "**/*"), nil); err == nil {
 					rule.NsjailImplicits(android.PathsForSource(ctx, paths))
+				} else {
+					ctx.PropertyErrorf("dir_srcs", "can't glob %q", input.String())
 				}
 			}
 		}
@@ -645,6 +648,12 @@
 	}
 
 	g.setOutputFiles(ctx)
+
+	if ctx.Os() == android.Windows {
+		// Make doesn't support windows:
+		// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/module_arch_supported.mk;l=66;drc=f264690860bb6ee7762784d6b7201aae057ba6f2
+		g.HideFromMake()
+	}
 }
 
 func (g *Module) setOutputFiles(ctx android.ModuleContext) {
@@ -852,6 +861,12 @@
 	taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask {
 		useNsjail := Bool(properties.Use_nsjail)
 
+		dirSrcs := android.DirectoryPathsForModuleSrc(ctx, properties.Dir_srcs)
+		if len(dirSrcs) > 0 && !useNsjail {
+			ctx.PropertyErrorf("dir_srcs", "can't use dir_srcs if use_nsjail is false")
+			return nil
+		}
+
 		outs := make(android.WritablePaths, len(properties.Out))
 		for i, out := range properties.Out {
 			outs[i] = android.PathForModuleGen(ctx, out)
@@ -862,6 +877,7 @@
 			genDir:    android.PathForModuleGen(ctx),
 			cmd:       rawCommand,
 			useNsjail: useNsjail,
+			dirSrcs:   dirSrcs,
 		}}
 	}
 
@@ -878,6 +894,10 @@
 type genRuleProperties struct {
 	Use_nsjail *bool
 
+	// List of input directories. Can be set only when use_nsjail is true. Currently, usage of
+	// dir_srcs is limited only to Trusty build.
+	Dir_srcs []string `android:"path"`
+
 	// names of the output files that will be generated
 	Out []string `android:"arch_variant"`
 }
diff --git a/go.mod b/go.mod
index aa43066..57b59c0 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module android/soong
 
-go 1.22
+go 1.23
 
 require (
 	github.com/google/blueprint v0.0.0
diff --git a/go.work b/go.work
index 46a135b..e538915 100644
--- a/go.work
+++ b/go.work
@@ -1,4 +1,4 @@
-go 1.22
+go 1.23
 
 use (
 	.
diff --git a/java/app_import.go b/java/app_import.go
index a54cf2f..f5d9f3e 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -61,6 +61,9 @@
 func RegisterAppImportBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("android_app_import", AndroidAppImportFactory)
 	ctx.RegisterModuleType("android_test_import", AndroidTestImportFactory)
+	ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("disable_prebuilts_without_apk", disablePrebuiltsWithoutApkMutator)
+	})
 }
 
 type AndroidAppImport struct {
@@ -90,7 +93,7 @@
 
 type AndroidAppImportProperties struct {
 	// A prebuilt apk to import
-	Apk *string `android:"path"`
+	Apk proptools.Configurable[string] `android:"path,replace_instead_of_append"`
 
 	// The name of a certificate in the default certificate directory or an android_app_certificate
 	// module name in the form ":module". Should be empty if presigned or default_dev_cert is set.
@@ -193,13 +196,6 @@
 			}
 		}
 	}
-
-	if String(a.properties.Apk) == "" {
-		// Disable this module since the apk property is still empty after processing all matching
-		// variants. This likely means there is no matching variant, and the default variant doesn't
-		// have an apk property value either.
-		a.Disable()
-	}
 }
 
 func MergePropertiesFromVariant(ctx android.EarlyModuleContext,
@@ -219,6 +215,30 @@
 	}
 }
 
+// disablePrebuiltsWithoutApkMutator is a pre-arch mutator that disables AndroidAppImport or
+// AndroidTestImport modules that don't have an apk set. We need this separate mutator instead
+// of doing it in processVariants because processVariants is a defaultable hook, and configurable
+// properties can only be evaluated after the defaults (and eventually, base configurabtion)
+// mutators.
+func disablePrebuiltsWithoutApkMutator(ctx android.BottomUpMutatorContext) {
+	switch a := ctx.Module().(type) {
+	case *AndroidAppImport:
+		if a.properties.Apk.GetOrDefault(ctx, "") == "" {
+			// Disable this module since the apk property is still empty after processing all
+			// matching variants. This likely means there is no matching variant, and the default
+			// variant doesn't have an apk property value either.
+			a.Disable()
+		}
+	case *AndroidTestImport:
+		if a.properties.Apk.GetOrDefault(ctx, "") == "" {
+			// Disable this module since the apk property is still empty after processing all
+			// matching variants. This likely means there is no matching variant, and the default
+			// variant doesn't have an apk property value either.
+			a.Disable()
+		}
+	}
+}
+
 func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) {
 	cert := android.SrcIsModule(String(a.properties.Certificate))
 	if cert != "" {
@@ -409,7 +429,7 @@
 
 	if apexInfo.IsForPlatform() {
 		a.installPath = ctx.InstallFile(installDir, apkFilename, a.outputFile)
-		artifactPath := android.PathForModuleSrc(ctx, *a.properties.Apk)
+		artifactPath := android.PathForModuleSrc(ctx, a.properties.Apk.GetOrDefault(ctx, ""))
 		a.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, artifactPath, a.installPath)
 	}
 
@@ -633,7 +653,7 @@
 	android.InitApexModule(module)
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
-	android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
+	android.InitConfigurablePrebuiltModuleString(module, &module.properties.Apk, "Apk")
 
 	module.usesLibrary.enforce = true
 
@@ -686,7 +706,7 @@
 	android.InitApexModule(module)
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
-	android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
+	android.InitConfigurablePrebuiltModuleString(module, &module.properties.Apk, "Apk")
 
 	return module
 }
diff --git a/java/bootclasspath.go b/java/bootclasspath.go
index 029f6f6..3413cf3 100644
--- a/java/bootclasspath.go
+++ b/java/bootclasspath.go
@@ -29,7 +29,7 @@
 
 func registerBootclasspathBuildComponents(ctx android.RegistrationContext) {
 	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("bootclasspath_deps", bootclasspathDepsMutator).Parallel()
+		ctx.BottomUp("bootclasspath_deps", bootclasspathDepsMutator)
 	})
 }
 
diff --git a/java/builder.go b/java/builder.go
index e5d5109..895ddb6 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -301,7 +301,7 @@
 
 	gatherReleasedFlaggedApisRule = pctx.AndroidStaticRule("gatherReleasedFlaggedApisRule",
 		blueprint.RuleParams{
-			Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}={state:bool}' ` +
+			Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}' ` +
 				`--out ${out} ` +
 				`${flags_path} ` +
 				`${filter_args} `,
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 5c69ff1..8c60d23 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -465,7 +465,7 @@
 	ctx.RegisterParallelSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory)
 	ctx.RegisterModuleType("art_boot_images", artBootImagesFactory)
 	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("dex_bootjars_deps", DexpreoptBootJarsMutator).Parallel()
+		ctx.BottomUp("dex_bootjars_deps", DexpreoptBootJarsMutator)
 	})
 }
 
@@ -1442,7 +1442,7 @@
 
 func (dbj *artBootImages) DepsMutator(ctx android.BottomUpMutatorContext) {
 	// Create a dependency on `dex_bootjars` to access the intermediate locations of host art boot image.
-	ctx.AddDependency(ctx.Module(), dexpreoptBootJarDepTag, "dex_bootjars")
+	ctx.AddVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), dexpreoptBootJarDepTag, "dex_bootjars")
 }
 
 func (d *artBootImages) GenerateAndroidBuildActions(ctx android.ModuleContext) {
diff --git a/java/java.go b/java/java.go
index d972887..288042b 100644
--- a/java/java.go
+++ b/java/java.go
@@ -70,9 +70,9 @@
 	// established, to not get the dependencies split into the wrong variants and
 	// to support the checks in dexpreoptDisabled().
 	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("dexpreopt_tool_deps", dexpreoptToolDepsMutator).Parallel()
+		ctx.BottomUp("dexpreopt_tool_deps", dexpreoptToolDepsMutator)
 		// needs access to ApexInfoProvider which is available after variant creation
-		ctx.BottomUp("jacoco_deps", jacocoDepsMutator).Parallel()
+		ctx.BottomUp("jacoco_deps", jacocoDepsMutator)
 	})
 
 	ctx.RegisterParallelSingletonType("kythe_java_extract", kytheExtractJavaFactory)
@@ -2454,7 +2454,7 @@
 	ret := []string{}
 	ret = append(ret, al.properties.Libs.GetOrDefault(ctx, nil)...)
 	ret = append(ret, al.properties.Static_libs.GetOrDefault(ctx, nil)...)
-	if al.properties.System_modules != nil {
+	if proptools.StringDefault(al.properties.System_modules, "none") != "none" {
 		ret = append(ret, proptools.String(al.properties.System_modules))
 	}
 	// Other non java_library dependencies like java_api_contribution are ignored for now.
diff --git a/java/jdeps_test.go b/java/jdeps_test.go
index 7a0fb10..1435000 100644
--- a/java/jdeps_test.go
+++ b/java/jdeps_test.go
@@ -134,3 +134,42 @@
 
 	android.AssertStringListContains(t, "IdeInfo.Deps should contain versioned sdk module", dpInfo.Deps, "sdk_public_29_android")
 }
+
+func TestDoNotAddNoneSystemModulesToDeps(t *testing.T) {
+	ctx := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		android.FixtureMergeEnv(
+			map[string]string{
+				"DISABLE_STUB_VALIDATION": "true",
+			},
+		),
+	).RunTestWithBp(t,
+		`
+		java_library {
+			name: "javalib",
+			srcs: ["foo.java"],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+
+		java_api_library {
+			name: "javalib.stubs",
+			stubs_type: "everything",
+			api_contributions: ["javalib-current.txt"],
+			api_surface: "public",
+			system_modules: "none",
+		}
+		java_api_contribution {
+			name: "javalib-current.txt",
+			api_file: "javalib-current.txt",
+			api_surface: "public",
+		}
+	`)
+	javalib := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	dpInfo, _ := android.OtherModuleProvider(ctx, javalib, android.IdeInfoProviderKey)
+	android.AssertStringListDoesNotContain(t, "IdeInfo.Deps should contain not contain `none`", dpInfo.Deps, "none")
+
+	javalib_stubs := ctx.ModuleForTests("javalib.stubs", "android_common").Module().(*ApiLibrary)
+	dpInfo, _ = android.OtherModuleProvider(ctx, javalib_stubs, android.IdeInfoProviderKey)
+	android.AssertStringListDoesNotContain(t, "IdeInfo.Deps should contain not contain `none`", dpInfo.Deps, "none")
+}
diff --git a/rust/rust.go b/rust/rust.go
index a044a99..b22ebf7 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -47,11 +47,11 @@
 func registerPreDepsMutators(ctx android.RegisterMutatorsContext) {
 	ctx.Transition("rust_libraries", &libraryTransitionMutator{})
 	ctx.Transition("rust_stdlinkage", &libstdTransitionMutator{})
-	ctx.BottomUp("rust_begin", BeginMutator).Parallel()
+	ctx.BottomUp("rust_begin", BeginMutator)
 }
 
 func registerPostDepsMutators(ctx android.RegisterMutatorsContext) {
-	ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel()
+	ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator)
 }
 
 type Flags struct {
diff --git a/scripts/keep-flagged-apis.sh b/scripts/keep-flagged-apis.sh
index 9c48fdb..48efb7a 100755
--- a/scripts/keep-flagged-apis.sh
+++ b/scripts/keep-flagged-apis.sh
@@ -25,21 +25,12 @@
 # Convert the list of feature flags in the input file to Metalava options
 # of the form `--revert-annotation !android.annotation.FlaggedApi("<flag>")`
 # to prevent the annotated APIs from being hidden, i.e. include the annotated
-# APIs in the SDK snapshots. This also preserves the line comments, they will
-# be ignored by Metalava but might be useful when debugging.
+# APIs in the SDK snapshots.
 while read -r line; do
-  key=$(echo "$line" | cut -d= -f1)
-  value=$(echo "$line" | cut -d= -f2)
-
-  # Skip if value is not true and line does not start with '#'
-  if [[ ( $value != "true" ) && ( $line =~ ^[^#] )]]; then
-    continue
-  fi
-
   # Escape and quote the key for sed
-  escaped_key=$(echo "$key" | sed "s/'/\\\'/g; s/ /\\ /g")
+  escaped_line=$(echo "$line" | sed "s/'/\\\'/g; s/ /\\ /g")
 
-  echo $line | sed "s|^[^#].*$|--revert-annotation '!$FLAGGED(\"$escaped_key\")'|"
+  echo "--revert-annotation '!$FLAGGED(\"$escaped_line\")'"
 done < "$FLAGS"
 
 # Revert all flagged APIs, unless listed above.
diff --git a/ui/build/androidmk_denylist.go b/ui/build/androidmk_denylist.go
index 2ec8972..a8044df 100644
--- a/ui/build/androidmk_denylist.go
+++ b/ui/build/androidmk_denylist.go
@@ -16,6 +16,8 @@
 
 import (
 	"strings"
+
+	"android/soong/android"
 )
 
 var androidmk_denylist []string = []string{
@@ -64,3 +66,29 @@
 		}
 	}
 }
+
+// The Android.mk files in these directories are for NDK build system.
+var external_ndk_androidmks []string = []string{
+	"external/fmtlib/",
+	"external/google-breakpad/",
+	"external/googletest/",
+	"external/libaom/",
+	"external/libusb/",
+	"external/libvpx/",
+	"external/libwebm/",
+	"external/libwebsockets/",
+	"external/vulkan-validation-layers/",
+	"external/walt/",
+	"external/webp/",
+}
+
+func ignoreNdkAndroidMks(androidMks []string) []string {
+	return android.FilterListPred(androidMks, func(s string) bool {
+		for _, d := range external_ndk_androidmks {
+			if strings.HasPrefix(s, d) {
+				return false
+			}
+		}
+		return true
+	})
+}
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 573df21..a899822 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -128,6 +128,7 @@
 
 	// Stop searching a subdirectory recursively after finding an Android.mk.
 	androidMks := f.FindFirstNamedAt(".", "Android.mk")
+	androidMks = ignoreNdkAndroidMks(androidMks)
 	blockAndroidMks(ctx, androidMks)
 	err := dumpListToFile(ctx, config, androidMks, filepath.Join(dumpDir, "Android.mk.list"))
 	if err != nil {