Merge "Add soong config variables of paths of *_dlkm partitions and whether they are built for a product." into main
diff --git a/android/dirgroup.go b/android/dirgroup.go
index 20c4d13..62fbaa5 100644
--- a/android/dirgroup.go
+++ b/android/dirgroup.go
@@ -39,8 +39,7 @@
 }
 
 type DirInfo struct {
-	// TODO(b/358302178): Use DirectoryPaths instead of Paths
-	Dirs Paths
+	Dirs DirectoryPaths
 }
 
 var DirProvider = blueprint.NewProvider[DirInfo]()
diff --git a/android/filegroup_test.go b/android/filegroup_test.go
index 14e9368..670037d 100644
--- a/android/filegroup_test.go
+++ b/android/filegroup_test.go
@@ -1,55 +1,9 @@
 package android
 
 import (
-	"path/filepath"
 	"testing"
 )
 
-func TestFileGroupWithPathProp(t *testing.T) {
-	// TODO(b/247782695), TODO(b/242847534) Fix mixed builds for filegroups
-	t.Skip("Re-enable once filegroups are corrected for mixed builds")
-	outBaseDir := "outputbase"
-	pathPrefix := outBaseDir + "/execroot/__main__"
-	expectedOutputfile := filepath.Join(pathPrefix, "a/b/c/d/test.aidl")
-
-	testCases := []struct {
-		bp  string
-		rel string
-	}{
-		{
-			bp: `
-	filegroup {
-		name: "baz",
-		srcs: ["a/b/c/d/test.aidl"],
-		path: "a/b",
-		bazel_module: { label: "//:baz" },
-	}
-`,
-			rel: "c/d/test.aidl",
-		},
-		{
-			bp: `
-	filegroup {
-		name: "baz",
-		srcs: ["a/b/c/d/test.aidl"],
-		bazel_module: { label: "//:baz" },
-	}
-`,
-			rel: "a/b/c/d/test.aidl",
-		},
-	}
-
-	for _, testCase := range testCases {
-		result := GroupFixturePreparers(
-			PrepareForTestWithFilegroup,
-		).RunTestWithBp(t, testCase.bp)
-
-		fg := result.Module("baz", "").(*fileGroup)
-		AssertStringEquals(t, "src relativeRoot", testCase.rel, fg.srcs[0].Rel())
-		AssertStringEquals(t, "src full path", expectedOutputfile, fg.srcs[0].String())
-	}
-}
-
 func TestFilegroupDefaults(t *testing.T) {
 	bp := FixtureAddTextFile("p/Android.bp", `
 		filegroup_defaults {
diff --git a/android/module_context.go b/android/module_context.go
index 9fa3fa3..1f5e706 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -19,6 +19,7 @@
 	"github.com/google/blueprint/depset"
 	"path"
 	"path/filepath"
+	"slices"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -562,9 +563,22 @@
 	m.aconfigFilePaths = paths
 }
 
+func (m *moduleContext) getOwnerAndOverrides() (string, []string) {
+	owner := m.ModuleName()
+	overrides := slices.Clone(m.Module().base().commonProperties.Overrides)
+	if b, ok := m.Module().(OverridableModule); ok {
+		if b.GetOverriddenBy() != "" {
+			// overriding variant of base module
+			overrides = append(overrides, m.ModuleName()) // com.android.foo
+			owner = m.Module().Name()                     // com.company.android.foo
+		}
+	}
+	return owner, overrides
+}
+
 func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, executable bool) PackagingSpec {
 	licenseFiles := m.Module().EffectiveLicenseFiles()
-	overrides := CopyOf(m.Module().base().commonProperties.Overrides)
+	owner, overrides := m.getOwnerAndOverrides()
 	spec := PackagingSpec{
 		relPathInPackage:      Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
 		srcPath:               srcPath,
@@ -576,7 +590,7 @@
 		aconfigPaths:          m.getAconfigPaths(),
 		archType:              m.target.Arch.ArchType,
 		overrides:             &overrides,
-		owner:                 m.ModuleName(),
+		owner:                 owner,
 	}
 	m.packagingSpecs = append(m.packagingSpecs, spec)
 	return spec
@@ -695,7 +709,7 @@
 		m.installFiles = append(m.installFiles, fullInstallPath)
 	}
 
-	overrides := CopyOf(m.Module().base().commonProperties.Overrides)
+	owner, overrides := m.getOwnerAndOverrides()
 	m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{
 		relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
 		srcPath:          nil,
@@ -706,7 +720,7 @@
 		aconfigPaths:     m.getAconfigPaths(),
 		archType:         m.target.Arch.ArchType,
 		overrides:        &overrides,
-		owner:            m.ModuleName(),
+		owner:            owner,
 	})
 
 	return fullInstallPath
@@ -742,7 +756,7 @@
 		m.installFiles = append(m.installFiles, fullInstallPath)
 	}
 
-	overrides := CopyOf(m.Module().base().commonProperties.Overrides)
+	owner, overrides := m.getOwnerAndOverrides()
 	m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{
 		relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
 		srcPath:          nil,
@@ -753,7 +767,7 @@
 		aconfigPaths:     m.getAconfigPaths(),
 		archType:         m.target.Arch.ArchType,
 		overrides:        &overrides,
-		owner:            m.ModuleName(),
+		owner:            owner,
 	})
 
 	return fullInstallPath
diff --git a/android/neverallow.go b/android/neverallow.go
index 7fb22bf..6251e2b 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -334,6 +334,8 @@
 			"prebuilt_res",
 			"prebuilt_wlc_upt",
 			"prebuilt_odm",
+			"prebuilt_vendor_dlkm",
+			"prebuilt_bt_firmware",
 		).
 		DefinedInBpFile().
 		Because("module type not allowed to be defined in bp file")
diff --git a/android/paths.go b/android/paths.go
index bf2c3a0..a7ee7ac 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -551,15 +551,35 @@
 	return ret
 }
 
+type directoryPath struct {
+	basePath
+}
+
+func (d *directoryPath) String() string {
+	return d.basePath.String()
+}
+
+func (d *directoryPath) base() basePath {
+	return d.basePath
+}
+
+// DirectoryPath represents a source path for directories. Incompatible with Path by design.
+type DirectoryPath interface {
+	String() string
+	base() basePath
+}
+
+var _ DirectoryPath = (*directoryPath)(nil)
+
+type DirectoryPaths []DirectoryPath
+
 // 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
+func DirectoryPathsForModuleSrc(ctx ModuleMissingDepsPathContext, paths []string) DirectoryPaths {
+	var ret DirectoryPaths
 
 	for _, path := range paths {
 		if m, t := SrcIsModuleWithTag(path); m != "" {
@@ -588,12 +608,12 @@
 			} else if !isDir {
 				ReportPathErrorf(ctx, "module directory path %q is not a directory", p)
 			} else {
-				ret = append(ret, p)
+				ret = append(ret, &directoryPath{basePath{path: p.path, rel: p.rel}})
 			}
 		}
 	}
 
-	seen := make(map[Path]bool, len(ret))
+	seen := make(map[DirectoryPath]bool, len(ret))
 	for _, path := range ret {
 		if seen[path] {
 			ReportPathErrorf(ctx, "duplicated path %q", path)
diff --git a/android/paths_test.go b/android/paths_test.go
index 941f0ca..5e618f9 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -1592,6 +1592,12 @@
 	})
 }
 
+func TestDirectoryPathIsIncompatibleWithPath(t *testing.T) {
+	d := (DirectoryPath)(&directoryPath{})
+	_, ok := d.(Path)
+	AssertBoolEquals(t, "directoryPath shouldn't implement Path", ok, false)
+}
+
 func ExampleOutputPath_ReplaceExtension() {
 	ctx := &configErrorWrapper{
 		config: TestConfig("out", nil, "", nil),
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 56de9cd..403c184 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -575,25 +575,28 @@
 		nsjailCmd.WriteString(r.outDir.String())
 		nsjailCmd.WriteString(":nsjail_build_sandbox/out")
 
-		for _, input := range inputs {
+		addBindMount := func(src, dst string) {
 			nsjailCmd.WriteString(" -R $PWD/")
-			nsjailCmd.WriteString(input.String())
+			nsjailCmd.WriteString(src)
 			nsjailCmd.WriteString(":nsjail_build_sandbox/")
-			nsjailCmd.WriteString(r.nsjailPathForInputRel(input))
+			nsjailCmd.WriteString(dst)
+		}
+
+		for _, input := range inputs {
+			addBindMount(input.String(), r.nsjailPathForInputRel(input))
 		}
 		for _, tool := range tools {
-			nsjailCmd.WriteString(" -R $PWD/")
-			nsjailCmd.WriteString(tool.String())
-			nsjailCmd.WriteString(":nsjail_build_sandbox/")
-			nsjailCmd.WriteString(nsjailPathForToolRel(r.ctx, tool))
+			addBindMount(tool.String(), nsjailPathForToolRel(r.ctx, tool))
 		}
 		inputs = append(inputs, tools...)
 		for _, c := range r.commands {
+			for _, directory := range c.implicitDirectories {
+				addBindMount(directory.String(), directory.String())
+				// TODO(b/375551969): Add implicitDirectories to BuildParams, rather than relying on implicits
+				inputs = append(inputs, SourcePath{basePath: directory.base()})
+			}
 			for _, tool := range c.packagedTools {
-				nsjailCmd.WriteString(" -R $PWD/")
-				nsjailCmd.WriteString(tool.srcPath.String())
-				nsjailCmd.WriteString(":nsjail_build_sandbox/")
-				nsjailCmd.WriteString(nsjailPathForPackagedToolRel(tool))
+				addBindMount(tool.srcPath.String(), nsjailPathForPackagedToolRel(tool))
 				inputs = append(inputs, tool.srcPath)
 			}
 		}
@@ -917,16 +920,17 @@
 type RuleBuilderCommand struct {
 	rule *RuleBuilder
 
-	buf           strings.Builder
-	inputs        Paths
-	implicits     Paths
-	orderOnlys    Paths
-	validations   Paths
-	outputs       WritablePaths
-	depFiles      WritablePaths
-	tools         Paths
-	packagedTools []PackagingSpec
-	rspFiles      []rspFileAndPaths
+	buf                 strings.Builder
+	inputs              Paths
+	implicits           Paths
+	orderOnlys          Paths
+	validations         Paths
+	outputs             WritablePaths
+	depFiles            WritablePaths
+	tools               Paths
+	packagedTools       []PackagingSpec
+	rspFiles            []rspFileAndPaths
+	implicitDirectories DirectoryPaths
 }
 
 type rspFileAndPaths struct {
@@ -951,6 +955,10 @@
 	c.implicits = append(c.implicits, path)
 }
 
+func (c *RuleBuilderCommand) addImplicitDirectory(path DirectoryPath) {
+	c.implicitDirectories = append(c.implicitDirectories, path)
+}
+
 func (c *RuleBuilderCommand) addOrderOnly(path Path) {
 	checkPathNotNil(path)
 	c.orderOnlys = append(c.orderOnlys, path)
@@ -1313,6 +1321,16 @@
 	return c
 }
 
+// ImplicitDirectory adds the specified input directory to the dependencies without modifying the
+// command line. Added directories will be bind-mounted for the nsjail.
+func (c *RuleBuilderCommand) ImplicitDirectory(path DirectoryPath) *RuleBuilderCommand {
+	if !c.rule.nsjail {
+		panic("ImplicitDirectory() must be called after Nsjail()")
+	}
+	c.addImplicitDirectory(path)
+	return c
+}
+
 // GetImplicits returns the command's implicit inputs.
 func (c *RuleBuilderCommand) GetImplicits() Paths {
 	return c.implicits
diff --git a/android/util.go b/android/util.go
index 2d269b7..3fc4608 100644
--- a/android/util.go
+++ b/android/util.go
@@ -660,3 +660,13 @@
 	v, loaded := m.Map.LoadOrStore(key, value)
 	return v.(V), loaded
 }
+
+// AppendIfNotZero append the given value to the slice if it is not the zero value
+// for its type.
+func AppendIfNotZero[T comparable](slice []T, value T) []T {
+	var zeroValue T // Get the zero value of the type T
+	if value != zeroValue {
+		return append(slice, value)
+	}
+	return slice
+}
diff --git a/android/variable.go b/android/variable.go
index c2da278..2ca7714 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -609,6 +609,9 @@
 	ProductLinkerConfigSrcs []string `json:",omitempty"`
 
 	ProductCopyFiles map[string]string `json:",omitempty"`
+
+	BuildingSystemDlkmImage bool     `json:",omitempty"`
+	SystemKernelModules     []string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/cc/cc.go b/cc/cc.go
index 551f2cd..5dee32e 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -35,6 +35,14 @@
 	"android/soong/genrule"
 )
 
+type CcMakeVarsInfo struct {
+	WarningsAllowed string
+	UsingWnoError   string
+	MissingProfile  string
+}
+
+var CcMakeVarsInfoProvider = blueprint.NewProvider[*CcMakeVarsInfo]()
+
 func init() {
 	RegisterCCBuildComponents(android.InitRegistrationContext)
 
@@ -531,6 +539,7 @@
 	getSharedFlags() *SharedFlags
 	notInPlatform() bool
 	optimizeForSize() bool
+	getOrCreateMakeVarsInfo() *CcMakeVarsInfo
 }
 
 type SharedFlags struct {
@@ -845,9 +854,10 @@
 	sourceProperties android.SourceProperties
 
 	// initialize before calling Init
-	hod        android.HostOrDeviceSupported
-	multilib   android.Multilib
-	testModule bool
+	hod         android.HostOrDeviceSupported
+	multilib    android.Multilib
+	testModule  bool
+	incremental bool
 
 	// Allowable SdkMemberTypes of this module type.
 	sdkMemberTypes []android.SdkMemberType
@@ -913,8 +923,16 @@
 	hasSysprop      bool
 	hasWinMsg       bool
 	hasYacc         bool
+
+	makeVarsInfo *CcMakeVarsInfo
 }
 
+func (c *Module) IncrementalSupported() bool {
+	return c.incremental
+}
+
+var _ blueprint.Incremental = (*Module)(nil)
+
 func (c *Module) AddJSONData(d *map[string]interface{}) {
 	c.AndroidModuleBase().AddJSONData(d)
 	(*d)["Cc"] = map[string]interface{}{
@@ -1700,6 +1718,13 @@
 	return ctx.mod.NotInPlatform()
 }
 
+func (ctx *moduleContextImpl) getOrCreateMakeVarsInfo() *CcMakeVarsInfo {
+	if ctx.mod.makeVarsInfo == nil {
+		ctx.mod.makeVarsInfo = &CcMakeVarsInfo{}
+	}
+	return ctx.mod.makeVarsInfo
+}
+
 func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
 	return &Module{
 		hod:      hod,
@@ -2091,6 +2116,10 @@
 	}
 
 	c.setOutputFiles(ctx)
+
+	if c.makeVarsInfo != nil {
+		android.SetProvider(ctx, CcMakeVarsInfoProvider, c.makeVarsInfo)
+	}
 }
 
 func (c *Module) setOutputFiles(ctx ModuleContext) {
diff --git a/cc/compiler.go b/cc/compiler.go
index f06287c..0fa058a 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -685,10 +685,10 @@
 	if len(srcs) > 0 {
 		module := ctx.ModuleDir() + "/Android.bp:" + ctx.ModuleName()
 		if inList("-Wno-error", flags.Local.CFlags) || inList("-Wno-error", flags.Local.CppFlags) {
-			addToModuleList(ctx, modulesUsingWnoErrorKey, module)
+			ctx.getOrCreateMakeVarsInfo().UsingWnoError = module
 		} else if !inList("-Werror", flags.Local.CFlags) && !inList("-Werror", flags.Local.CppFlags) {
 			if warningsAreAllowed(ctx.ModuleDir()) {
-				addToModuleList(ctx, modulesWarningsAllowedKey, module)
+				ctx.getOrCreateMakeVarsInfo().WarningsAllowed = module
 			} else {
 				flags.Local.CFlags = append([]string{"-Werror"}, flags.Local.CFlags...)
 			}
diff --git a/cc/makevars.go b/cc/makevars.go
index f82e0e9..4cb98e7 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -25,10 +25,7 @@
 )
 
 var (
-	modulesWarningsAllowedKey    = android.NewOnceKey("ModulesWarningsAllowed")
-	modulesUsingWnoErrorKey      = android.NewOnceKey("ModulesUsingWnoError")
-	modulesMissingProfileFileKey = android.NewOnceKey("ModulesMissingProfileFile")
-	sanitizerVariables           = map[string]string{
+	sanitizerVariables = map[string]string{
 		"ADDRESS_SANITIZER_RUNTIME_LIBRARY":   config.AddressSanitizerRuntimeLibrary(),
 		"HWADDRESS_SANITIZER_RUNTIME_LIBRARY": config.HWAddressSanitizerRuntimeLibrary(),
 		"HWADDRESS_SANITIZER_STATIC_LIBRARY":  config.HWAddressSanitizerStaticLibrary(),
@@ -50,15 +47,9 @@
 	}).(*sync.Map)
 }
 
-func makeStringOfKeys(ctx android.MakeVarsContext, key android.OnceKey) string {
-	set := getNamedMapForConfig(ctx.Config(), key)
-	keys := []string{}
-	set.Range(func(key interface{}, value interface{}) bool {
-		keys = append(keys, key.(string))
-		return true
-	})
-	sort.Strings(keys)
-	return strings.Join(keys, " ")
+func makeVarsString(items []string) string {
+	items = android.SortedUniqueStrings(items)
+	return strings.Join(items, " ")
 }
 
 func makeStringOfWarningAllowedProjects() string {
@@ -108,28 +99,33 @@
 	ctx.Strict("GLOBAL_CLANG_EXTERNAL_CFLAGS_NO_OVERRIDE", "${config.NoOverrideExternalGlobalCflags}")
 
 	// Filter vendor_public_library that are exported to make
-	exportedVendorPublicLibraries := []string{}
+	var exportedVendorPublicLibraries []string
+	var warningsAllowed []string
+	var usingWnoErrors []string
+	var missingProfiles []string
 	ctx.VisitAllModules(func(module android.Module) {
+		if v, ok := android.OtherModuleProvider(ctx, module, CcMakeVarsInfoProvider); ok {
+			warningsAllowed = android.AppendIfNotZero(warningsAllowed, v.WarningsAllowed)
+			usingWnoErrors = android.AppendIfNotZero(usingWnoErrors, v.UsingWnoError)
+			missingProfiles = android.AppendIfNotZero(missingProfiles, v.MissingProfile)
+		}
 		if ccModule, ok := module.(*Module); ok {
 			baseName := ccModule.BaseModuleName()
 			if ccModule.IsVendorPublicLibrary() && module.ExportedToMake() {
-				if !inList(baseName, exportedVendorPublicLibraries) {
-					exportedVendorPublicLibraries = append(exportedVendorPublicLibraries, baseName)
-				}
+				exportedVendorPublicLibraries = append(exportedVendorPublicLibraries, baseName)
 			}
 		}
 	})
-	sort.Strings(exportedVendorPublicLibraries)
-	ctx.Strict("VENDOR_PUBLIC_LIBRARIES", strings.Join(exportedVendorPublicLibraries, " "))
+	ctx.Strict("VENDOR_PUBLIC_LIBRARIES", makeVarsString(exportedVendorPublicLibraries))
 
 	lsdumpPaths := *lsdumpPaths(ctx.Config())
 	sort.Strings(lsdumpPaths)
 	ctx.Strict("LSDUMP_PATHS", strings.Join(lsdumpPaths, " "))
 
 	ctx.Strict("ANDROID_WARNING_ALLOWED_PROJECTS", makeStringOfWarningAllowedProjects())
-	ctx.Strict("SOONG_MODULES_WARNINGS_ALLOWED", makeStringOfKeys(ctx, modulesWarningsAllowedKey))
-	ctx.Strict("SOONG_MODULES_USING_WNO_ERROR", makeStringOfKeys(ctx, modulesUsingWnoErrorKey))
-	ctx.Strict("SOONG_MODULES_MISSING_PGO_PROFILE_FILE", makeStringOfKeys(ctx, modulesMissingProfileFileKey))
+	ctx.Strict("SOONG_MODULES_WARNINGS_ALLOWED", makeVarsString(warningsAllowed))
+	ctx.Strict("SOONG_MODULES_USING_WNO_ERROR", makeVarsString(usingWnoErrors))
+	ctx.Strict("SOONG_MODULES_MISSING_PGO_PROFILE_FILE", makeVarsString(missingProfiles))
 
 	ctx.Strict("CLANG_COVERAGE_CONFIG_CFLAGS", strings.Join(clangCoverageCFlags, " "))
 	ctx.Strict("CLANG_COVERAGE_CONFIG_COMMFLAGS", strings.Join(clangCoverageCommonFlags, " "))
diff --git a/cc/orderfile.go b/cc/orderfile.go
index 38b8905..6e08da7 100644
--- a/cc/orderfile.go
+++ b/cc/orderfile.go
@@ -54,10 +54,6 @@
 	})
 }
 
-func recordMissingOrderfile(ctx BaseModuleContext, missing string) {
-	getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true)
-}
-
 type OrderfileProperties struct {
 	Orderfile struct {
 		Instrumentation *bool
@@ -117,7 +113,7 @@
 
 	// Record that this module's order file is absent
 	missing := *props.Orderfile.Order_file_path + ":" + ctx.ModuleDir() + "/Android.bp:" + ctx.ModuleName()
-	recordMissingOrderfile(ctx, missing)
+	ctx.getOrCreateMakeVarsInfo().MissingProfile = missing
 
 	return android.OptionalPath{}
 }
diff --git a/docs/selects.md b/docs/selects.md
new file mode 100644
index 0000000..4f436ac
--- /dev/null
+++ b/docs/selects.md
@@ -0,0 +1,170 @@
+# Select statements
+
+## Introduction
+
+Soong currently has the arch, target, product_variables, and soong config variable properties that all support changing the values of soong properties based on some condition. These are confusing for users, and particularly the soong config variable properties require a lot of boilerplate that is annoying to write.
+
+In addition, these properties are all implemented using reflection on property structs, and can combine in ways that the original authors did not expect. For example, soong config variables can be combined with arch/target by saying that they operate on "arch.arm.enabled" instead of just "enabled". But product variables do not have the same abilities.
+
+The goal here is to combine all these different configuration mechanisms into one, to reduce complexity and boilerplate both in Android.bp files and in soong code.
+
+## Usage
+
+The soong select statements take their name and inspiration from [bazel select statements](https://bazel.build/docs/configurable-attributes).
+
+### Syntax
+
+#### Basic
+
+The basic syntax for select statements looks like:
+
+```
+my_module_type {
+  name: "my_module",
+  some_string_property: select(arch(), {
+    "arm": "foo",
+    "x86": "bar",
+    default: "baz",
+  }),
+}
+```
+
+That is, `select(` followed by a variable function, then a map of values of the variable to values to set the property to.
+
+Arguments can be passed to the "functions" that look up axes:
+
+```
+select(soong_config_variable("my_namespace", "my_variable"), {
+  "value1": "foo",
+  default: "bar",
+})
+```
+
+
+The list of functions that can currently be selected on:
+ - `arch()`
+ - `os()`
+ - `soong_config_variable(namespace, variable)`
+ - `release_flag(flag)`
+
+The functions are [defined here](https://cs.android.com/android/platform/superproject/main/+/main:build/soong/android/module.go;l=2144;drc=3f01580c04bfe37c920e247015cce93cff2451c0), and it should be easy to add more.
+
+#### Multivariable
+
+To handle multivariable selects, multiple axes can be specified within parenthesis, to look like tuple destructuring. All of the variables being selected must match the corresponding value from the branch in order for the branch to be chosen.
+
+```
+select((arch(), os()), {
+  ("arm", "linux"): "foo",
+  (default, "windows"): "bar",
+  (default, default): "baz",
+})
+```
+
+#### Unset
+
+You can have unset branches of selects using the "unset" keyword, which will act as if the property was not assigned to. This is only really useful if you’re using defaults modules.
+
+```
+cc_binary {
+  name: "my_binary",
+  enabled: select(os(), {
+    "darwin": false,
+    default: unset,
+  }),
+}
+```
+
+#### Appending
+
+You can append select statements to both scalar values and other select statements:
+
+```
+my_module_type {
+  name: "my_module",
+  // string_property will be set to something like penguin-four, apple-two, etc.
+  string_property: select(os(), {
+    "linux_glibc": "penguin",
+    "darwin": "apple",
+    default: "unknown",
+  }) + "-" + select(soong_config_variable("ANDROID", "favorite_vehicle"), {
+    "car": "four",
+    "tricycle": "three",
+    "bike": "two",
+     default: "unknown",
+  })
+}
+```
+
+
+You can also append a select with a value with another select that may not have a value, because some of its branches are "unset". If an unset branch was selected it will not append anything to the other select.
+
+#### Binding the selected value to a Blueprint variable and the "any" keyword
+
+In case you want to allow a selected value to have an unbounded number of possible values, you can bind its value to a blueprint variable and use it within the expression for that select branch.
+
+```
+my_module_type {
+  name: "my_module",
+  my_string_property: select(soong_config_variable("my_namespace", "my_variable"), {
+    "some_value": "baz",
+    any @ my_var: "foo" + my_var,
+    default: "bar",
+  }),
+}
+```
+
+The syntax is `any @ my_variable_name: <expression using my_variable_name>`. `any` is currently the only pattern that can be bound to a variable, but we may add more in the future. `any` is equivalent to `default` except it will not match undefined select conditions.
+
+#### Errors
+
+If a select statement does not have a "default" branch, and none of the other branches match the variable being selected on, it’s a compile-time error. This may be useful for enforcing a variable is 1 of only a few values.
+
+```
+# in product config:
+$(call soong_config_set,ANDROID,my_variable,foo)
+
+// in an Android.bp:
+my_module_type {
+  name: "my_module",
+  // Will error out with: soong_config_variable("ANDROID", "my_variable") had value "foo", which was not handled by the select
+  enabled: select(soong_config_variable("ANDROID", "my_variable"), {
+    "bar": true,
+    "baz": false,
+  }),
+}
+```
+
+### Changes to property structs to support selects
+
+Currently, the way configurable properties work is that there is secretly another property struct that has the `target`, `arch`, etc. properties, and then when the arch mutator (or other relevant mutator) runs, it copies the values of these properties onto the regular property structs. There’s nothing stopping you from accessing your properties from a mutator that runs before the one that updates the properties based on configurable values. This is a potential source of bugs, and we want to make sure that select statements don’t have the same pitfall. For that reason, you have to read property’s values through a getter which can do this check. This requires changing the code on a property-by-property basis to support selects.
+
+To make a property support selects, it must be of type [proptools.Configurable[T]](https://cs.android.com/android/platform/superproject/main/+/main:build/blueprint/proptools/configurable.go;l=341;drc=a52b058cccd2caa778d0f97077adcd4ef7ffb68a). T is the old type of the property. Currently we support bool, string, and []string. Configurable has a `Get(evaluator)` method to get the value of the property. The evaluator can be a ModuleContext, or if you’re in a situation where you only have a very limited context and a module, (such as in a singleton) you can use [ModuleBase.ConfigurableEvaluator](https://cs.android.com/android/platform/superproject/main/+/main:build/soong/android/module.go;l=2133;drc=e19f741052cce097da940d9083d3f29e668de5cb).
+
+`proptools.Configurable[T]` will handle unset properties for you, so you don’t need to make it a pointer type. However, there is a not-widely-known feature of property structs, where normally, properties are appended when squashing defaults. But if the property was a pointer property, later defaults replace earlier values instead of appending. With selects, to maintain this behavior, add the `android:"replace_instead_of_append"` struct tag. The "append" behavior for boolean values is to boolean OR them together, which is rarely what you want, so most boolean properties are pointers today.
+
+Old:
+```
+type commonProperties struct {
+  Enabled *bool `android:"arch_variant"`
+}
+
+func (m *ModuleBase) Enabled() *bool {
+  return m.commonProperties.Enabled
+}
+```
+
+New:
+```
+type commonProperties struct {
+  Enabled proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"`
+}
+
+func (m *ModuleBase) Enabled(ctx ConfigAndErrorContext) *bool {
+  return m.commonProperties.Enabled.Get(m.ConfigurableEvaluator(ctx))
+}
+```
+
+The `android:"arch_variant"` tag is kept to support the old `target:` and `arch:` properties with this property, but if all their usages in bp files were replaced by selects, then that tag could be removed.
+
+The enabled property underwent this migration in https://r.android.com/3066188
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 8461b3e..938f3bd 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -76,6 +76,8 @@
 	ctx.RegisterModuleType("prebuilt_res", PrebuiltResFactory)
 	ctx.RegisterModuleType("prebuilt_wlc_upt", PrebuiltWlcUptFactory)
 	ctx.RegisterModuleType("prebuilt_odm", PrebuiltOdmFactory)
+	ctx.RegisterModuleType("prebuilt_vendor_dlkm", PrebuiltVendorDlkmFactory)
+	ctx.RegisterModuleType("prebuilt_bt_firmware", PrebuiltBtFirmwareFactory)
 
 	ctx.RegisterModuleType("prebuilt_defaults", defaultsFactory)
 
@@ -932,3 +934,23 @@
 	android.InitDefaultableModule(module)
 	return module
 }
+
+// prebuilt_vendor_dlkm installs files in <partition>/vendor_dlkm directory.
+func PrebuiltVendorDlkmFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "vendor_dlkm")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_bt_firmware installs files in <partition>/bt_firmware directory.
+func PrebuiltBtFirmwareFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "bt_firmware")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index f767eae..29f9373 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -691,3 +691,27 @@
 	android.AssertStringDoesContain(t, "Could not find linker.config.json file in cmd", linkerConfigCmd, "conv_linker_config proto --force -s linker.config.json")
 	android.AssertStringDoesContain(t, "Could not find stub in `provideLibs`", linkerConfigCmd, "--key provideLibs --value libfoo_has_stubs.so")
 }
+
+// override_android_* modules implicitly override their base module.
+// If both of these are listed in `deps`, the base module should not be installed.
+func TestOverrideModulesInDeps(t *testing.T) {
+	result := fixture.RunTestWithBp(t, `
+		android_filesystem {
+			name: "myfilesystem",
+			deps: ["myapp", "myoverrideapp"],
+		}
+
+		android_app {
+			name: "myapp",
+			platform_apis: true,
+		}
+		override_android_app {
+			name: "myoverrideapp",
+			base: "myapp",
+		}
+	`)
+
+	partition := result.ModuleForTests("myfilesystem", "android_common")
+	fileList := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("fileList"))
+	android.AssertStringEquals(t, "filesystem with override app", "app/myoverrideapp/myoverrideapp.apk\n", fileList)
+}
diff --git a/fsgen/Android.bp b/fsgen/Android.bp
index baf9291..e3cbdb3 100644
--- a/fsgen/Android.bp
+++ b/fsgen/Android.bp
@@ -10,6 +10,7 @@
         "soong",
         "soong-android",
         "soong-filesystem",
+        "soong-kernel",
     ],
     srcs: [
         "filesystem_creator.go",
diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
index 06e154a..d8f679f 100644
--- a/fsgen/filesystem_creator.go
+++ b/fsgen/filesystem_creator.go
@@ -24,7 +24,9 @@
 	"sync"
 
 	"android/soong/android"
+	"android/soong/etc"
 	"android/soong/filesystem"
+	"android/soong/kernel"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/parser"
@@ -97,24 +99,304 @@
 	}
 }
 
-func createFsGenState(ctx android.LoadHookContext) *FsGenState {
+type srcBaseFileInstallBaseFileTuple struct {
+	srcBaseFile     string
+	installBaseFile string
+}
+
+// prebuilt src files grouped by the install partitions.
+// Each groups are a mapping of the relative install path to the name of the files
+type prebuiltSrcGroupByInstallPartition struct {
+	system     map[string][]srcBaseFileInstallBaseFileTuple
+	system_ext map[string][]srcBaseFileInstallBaseFileTuple
+	product    map[string][]srcBaseFileInstallBaseFileTuple
+	vendor     map[string][]srcBaseFileInstallBaseFileTuple
+}
+
+func newPrebuiltSrcGroupByInstallPartition() *prebuiltSrcGroupByInstallPartition {
+	return &prebuiltSrcGroupByInstallPartition{
+		system:     map[string][]srcBaseFileInstallBaseFileTuple{},
+		system_ext: map[string][]srcBaseFileInstallBaseFileTuple{},
+		product:    map[string][]srcBaseFileInstallBaseFileTuple{},
+		vendor:     map[string][]srcBaseFileInstallBaseFileTuple{},
+	}
+}
+
+func isSubdirectory(parent, child string) bool {
+	rel, err := filepath.Rel(parent, child)
+	if err != nil {
+		return false
+	}
+	return !strings.HasPrefix(rel, "..")
+}
+
+func appendIfCorrectInstallPartition(partitionToInstallPathList []partitionToInstallPath, destPath, srcPath string, srcGroup *prebuiltSrcGroupByInstallPartition) {
+	for _, part := range partitionToInstallPathList {
+		partition := part.name
+		installPath := part.installPath
+
+		if isSubdirectory(installPath, destPath) {
+			relativeInstallPath, _ := filepath.Rel(installPath, destPath)
+			relativeInstallDir := filepath.Dir(relativeInstallPath)
+			var srcMap map[string][]srcBaseFileInstallBaseFileTuple
+			switch partition {
+			case "system":
+				srcMap = srcGroup.system
+			case "system_ext":
+				srcMap = srcGroup.system_ext
+			case "product":
+				srcMap = srcGroup.product
+			case "vendor":
+				srcMap = srcGroup.vendor
+			}
+			if srcMap != nil {
+				srcMap[relativeInstallDir] = append(srcMap[relativeInstallDir], srcBaseFileInstallBaseFileTuple{
+					srcBaseFile:     filepath.Base(srcPath),
+					installBaseFile: filepath.Base(destPath),
+				})
+			}
+			return
+		}
+	}
+}
+
+func uniqueExistingProductCopyFileMap(ctx android.LoadHookContext) map[string]string {
+	seen := make(map[string]bool)
+	filtered := make(map[string]string)
+
+	for src, dest := range ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.ProductCopyFiles {
+		if _, ok := seen[dest]; !ok {
+			if optionalPath := android.ExistentPathForSource(ctx, src); optionalPath.Valid() {
+				seen[dest] = true
+				filtered[src] = dest
+			}
+		}
+	}
+
+	return filtered
+}
+
+type partitionToInstallPath struct {
+	name        string
+	installPath string
+}
+
+func processProductCopyFiles(ctx android.LoadHookContext) map[string]*prebuiltSrcGroupByInstallPartition {
+	// Filter out duplicate dest entries and non existing src entries
+	productCopyFileMap := uniqueExistingProductCopyFileMap(ctx)
+
+	// System is intentionally added at the last to consider the scenarios where
+	// non-system partitions are installed as part of the system partition
+	partitionToInstallPathList := []partitionToInstallPath{
+		{name: "vendor", installPath: ctx.DeviceConfig().VendorPath()},
+		{name: "product", installPath: ctx.DeviceConfig().ProductPath()},
+		{name: "system_ext", installPath: ctx.DeviceConfig().SystemExtPath()},
+		{name: "system", installPath: "system"},
+	}
+
+	groupedSources := map[string]*prebuiltSrcGroupByInstallPartition{}
+	for _, src := range android.SortedKeys(productCopyFileMap) {
+		dest := productCopyFileMap[src]
+		srcFileDir := filepath.Dir(src)
+		if _, ok := groupedSources[srcFileDir]; !ok {
+			groupedSources[srcFileDir] = newPrebuiltSrcGroupByInstallPartition()
+		}
+		appendIfCorrectInstallPartition(partitionToInstallPathList, dest, filepath.Base(src), groupedSources[srcFileDir])
+	}
+
+	return groupedSources
+}
+
+type prebuiltModuleProperties struct {
+	Name *string
+
+	Soc_specific        *bool
+	Product_specific    *bool
+	System_ext_specific *bool
+
+	Srcs []string
+	Dsts []string
+
+	No_full_install *bool
+
+	NamespaceExportedToMake bool
+
+	Visibility []string
+}
+
+// Split relative_install_path to a separate struct, because it is not supported for every
+// modules listed in [etcInstallPathToFactoryMap]
+type prebuiltSubdirProperties struct {
+	// If the base file name of the src and dst all match, dsts property does not need to be
+	// set, and only relative_install_path can be set.
+	Relative_install_path *string
+}
+
+var (
+	etcInstallPathToFactoryList = map[string]android.ModuleFactory{
+		"":                etc.PrebuiltRootFactory,
+		"avb":             etc.PrebuiltAvbFactory,
+		"bin":             etc.PrebuiltBinaryFactory,
+		"bt_firmware":     etc.PrebuiltBtFirmwareFactory,
+		"cacerts":         etc.PrebuiltEtcCaCertsFactory,
+		"dsp":             etc.PrebuiltDSPFactory,
+		"etc":             etc.PrebuiltEtcFactory,
+		"etc/dsp":         etc.PrebuiltDSPFactory,
+		"etc/firmware":    etc.PrebuiltFirmwareFactory,
+		"firmware":        etc.PrebuiltFirmwareFactory,
+		"fonts":           etc.PrebuiltFontFactory,
+		"framework":       etc.PrebuiltFrameworkFactory,
+		"lib":             etc.PrebuiltRenderScriptBitcodeFactory,
+		"lib64":           etc.PrebuiltRenderScriptBitcodeFactory,
+		"lib/rfsa":        etc.PrebuiltRFSAFactory,
+		"media":           etc.PrebuiltMediaFactory,
+		"odm":             etc.PrebuiltOdmFactory,
+		"overlay":         etc.PrebuiltOverlayFactory,
+		"priv-app":        etc.PrebuiltPrivAppFactory,
+		"res":             etc.PrebuiltResFactory,
+		"rfs":             etc.PrebuiltRfsFactory,
+		"tts":             etc.PrebuiltVoicepackFactory,
+		"usr/share":       etc.PrebuiltUserShareFactory,
+		"usr/hyphen-data": etc.PrebuiltUserHyphenDataFactory,
+		"usr/keylayout":   etc.PrebuiltUserKeyLayoutFactory,
+		"usr/keychars":    etc.PrebuiltUserKeyCharsFactory,
+		"usr/srec":        etc.PrebuiltUserSrecFactory,
+		"usr/idc":         etc.PrebuiltUserIdcFactory,
+		"vendor_dlkm":     etc.PrebuiltVendorDlkmFactory,
+		"wallpaper":       etc.PrebuiltWallpaperFactory,
+		"wlc_upt":         etc.PrebuiltWlcUptFactory,
+	}
+)
+
+func createPrebuiltEtcModule(ctx android.LoadHookContext, partition, srcDir, destDir string, destFiles []srcBaseFileInstallBaseFileTuple) string {
+	moduleProps := &prebuiltModuleProperties{}
+	propsList := []interface{}{moduleProps}
+
+	// generated module name follows the pattern:
+	// <install partition>-<src file path>-<relative install path from partition root>-<install file extension>
+	// Note that all path separators are replaced with "_" in the name
+	moduleName := partition
+	if !android.InList(srcDir, []string{"", "."}) {
+		moduleName += fmt.Sprintf("-%s", strings.ReplaceAll(srcDir, string(filepath.Separator), "_"))
+	}
+	if !android.InList(destDir, []string{"", "."}) {
+		moduleName += fmt.Sprintf("-%s", strings.ReplaceAll(destDir, string(filepath.Separator), "_"))
+	}
+	if len(destFiles) > 0 {
+		if ext := filepath.Ext(destFiles[0].srcBaseFile); ext != "" {
+			moduleName += fmt.Sprintf("-%s", strings.TrimPrefix(ext, "."))
+		}
+	}
+	moduleProps.Name = proptools.StringPtr(moduleName)
+
+	allCopyFileNamesUnchanged := true
+	var srcBaseFiles, installBaseFiles []string
+	for _, tuple := range destFiles {
+		if tuple.srcBaseFile != tuple.installBaseFile {
+			allCopyFileNamesUnchanged = false
+		}
+		srcBaseFiles = append(srcBaseFiles, tuple.srcBaseFile)
+		installBaseFiles = append(installBaseFiles, tuple.installBaseFile)
+	}
+
+	// Find out the most appropriate module type to generate
+	var etcInstallPathKey string
+	for _, etcInstallPath := range android.SortedKeys(etcInstallPathToFactoryList) {
+		// Do not break when found but iterate until the end to find a module with more
+		// specific install path
+		if strings.HasPrefix(destDir, etcInstallPath) {
+			etcInstallPathKey = etcInstallPath
+		}
+	}
+	destDir, _ = filepath.Rel(etcInstallPathKey, destDir)
+
+	// Set partition specific properties
+	switch partition {
+	case "system_ext":
+		moduleProps.System_ext_specific = proptools.BoolPtr(true)
+	case "product":
+		moduleProps.Product_specific = proptools.BoolPtr(true)
+	case "vendor":
+		moduleProps.Soc_specific = proptools.BoolPtr(true)
+	}
+
+	// Set appropriate srcs, dsts, and releative_install_path based on
+	// the source and install file names
+	if allCopyFileNamesUnchanged {
+		moduleProps.Srcs = srcBaseFiles
+
+		// Specify relative_install_path if it is not installed in the root directory of the
+		// partition
+		if !android.InList(destDir, []string{"", "."}) {
+			propsList = append(propsList, &prebuiltSubdirProperties{
+				Relative_install_path: proptools.StringPtr(destDir),
+			})
+		}
+	} else {
+		moduleProps.Srcs = srcBaseFiles
+		dsts := []string{}
+		for _, installBaseFile := range installBaseFiles {
+			dsts = append(dsts, filepath.Join(destDir, installBaseFile))
+		}
+		moduleProps.Dsts = dsts
+	}
+
+	moduleProps.No_full_install = proptools.BoolPtr(true)
+	moduleProps.NamespaceExportedToMake = true
+	moduleProps.Visibility = []string{"//visibility:public"}
+
+	ctx.CreateModuleInDirectory(etcInstallPathToFactoryList[etcInstallPathKey], srcDir, propsList...)
+
+	return moduleName
+}
+
+func createPrebuiltEtcModulesForPartition(ctx android.LoadHookContext, partition, srcDir string, destDirFilesMap map[string][]srcBaseFileInstallBaseFileTuple) (ret []string) {
+	for _, destDir := range android.SortedKeys(destDirFilesMap) {
+		ret = append(ret, createPrebuiltEtcModule(ctx, partition, srcDir, destDir, destDirFilesMap[destDir]))
+	}
+	return ret
+}
+
+// Creates prebuilt_* modules based on the install paths and returns the list of generated
+// module names
+func createPrebuiltEtcModules(ctx android.LoadHookContext) (ret []string) {
+	groupedSources := processProductCopyFiles(ctx)
+	for _, srcDir := range android.SortedKeys(groupedSources) {
+		groupedSource := groupedSources[srcDir]
+		ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "system", srcDir, groupedSource.system)...)
+		ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "system_ext", srcDir, groupedSource.system_ext)...)
+		ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "product", srcDir, groupedSource.product)...)
+		ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "vendor", srcDir, groupedSource.vendor)...)
+	}
+
+	return ret
+}
+
+func generatedPartitions(ctx android.LoadHookContext) []string {
+	generatedPartitions := []string{"system"}
+	if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
+		generatedPartitions = append(generatedPartitions, "system_ext")
+	}
+	if ctx.DeviceConfig().BuildingVendorImage() && ctx.DeviceConfig().VendorPath() == "vendor" {
+		generatedPartitions = append(generatedPartitions, "vendor")
+	}
+	if ctx.DeviceConfig().BuildingProductImage() && ctx.DeviceConfig().ProductPath() == "product" {
+		generatedPartitions = append(generatedPartitions, "product")
+	}
+	if ctx.DeviceConfig().BuildingOdmImage() && ctx.DeviceConfig().OdmPath() == "odm" {
+		generatedPartitions = append(generatedPartitions, "odm")
+	}
+	if ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BuildingSystemDlkmImage {
+		generatedPartitions = append(generatedPartitions, "system_dlkm")
+	}
+	return generatedPartitions
+}
+
+func createFsGenState(ctx android.LoadHookContext, generatedPrebuiltEtcModuleNames []string) *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")
-		}
-		if ctx.DeviceConfig().BuildingVendorImage() && ctx.DeviceConfig().VendorPath() == "vendor" {
-			generatedPartitions = append(generatedPartitions, "vendor")
-		}
-		if ctx.DeviceConfig().BuildingProductImage() && ctx.DeviceConfig().ProductPath() == "product" {
-			generatedPartitions = append(generatedPartitions, "product")
-		}
-		if ctx.DeviceConfig().BuildingOdmImage() && ctx.DeviceConfig().OdmPath() == "odm" {
-			generatedPartitions = append(generatedPartitions, "odm")
-		}
+		candidates = android.Concat(candidates, generatedPrebuiltEtcModuleNames)
 
 		return &FsGenState{
 			depCandidates: candidates,
@@ -124,19 +406,14 @@
 					"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()),
+					"init.environ.rc-soong":                     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()),
+					"update_engine_sideload":                    defaultDepCandidateProps(ctx.Config()),
 				},
 				"vendor": {
 					"fs_config_files_vendor":                               defaultDepCandidateProps(ctx.Config()),
@@ -160,8 +437,9 @@
 					"com.android.vndk.v33": defaultDepCandidateProps(ctx.Config()),
 					"com.android.vndk.v34": defaultDepCandidateProps(ctx.Config()),
 				},
+				"system_dlkm": {},
 			},
-			soongGeneratedPartitions:  generatedPartitions,
+			soongGeneratedPartitions:  generatedPartitions(ctx),
 			fsDepsMutex:               sync.Mutex{},
 			moduleToInstallationProps: map[string]installationProperties{},
 		}
@@ -374,7 +652,8 @@
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	module.AddProperties(&module.properties)
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		createFsGenState(ctx)
+		generatedPrebuiltEtcModuleNames := createPrebuiltEtcModules(ctx)
+		createFsGenState(ctx, generatedPrebuiltEtcModuleNames)
 		module.createInternalModules(ctx)
 	})
 
@@ -500,9 +779,19 @@
 		fsProps.Linkerconfig.Linker_config_srcs = f.createLinkerConfigSourceFilegroups(ctx, partitionType)
 	}
 
+	if partitionType == "system_dlkm" {
+		kernelModules := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelModules
+		f.createPrebuiltKernelModules(ctx, partitionType, kernelModules)
+	}
+
 	var module android.Module
 	if partitionType == "system" {
 		module = ctx.CreateModule(filesystem.SystemImageFactory, baseProps, fsProps)
+	} else if partitionType == "system_dlkm" {
+		// Do not set partition_type. build/soong/android/paths#modulePartition currently does not support dlkm
+		// partitions. Since `android_filesystem` uses a partition based filter, setting the partition here
+		// would result in missing in entries.
+		module = ctx.CreateModule(filesystem.FilesystemFactory, baseProps, fsProps)
 	} else {
 		// Explicitly set the partition.
 		fsProps.Partition_type = proptools.StringPtr(partitionType)
@@ -531,6 +820,41 @@
 	return true
 }
 
+// createPrebuiltKernelModules creates `prebuilt_kernel_modules`. These modules will be added to deps of the
+// autogenerated *_dlkm filsystem modules.
+// The input `kernelModules` is a space separated list of .ko files in the workspace. This will be partitioned per directory
+// and a `prebuilt_kernel_modules` will be created per partition.
+// These autogenerated modules will be subsequently added to the deps of the top level *_dlkm android_filesystem
+func (f *filesystemCreator) createPrebuiltKernelModules(ctx android.LoadHookContext, partitionType string, kernelModules []string) {
+	// Partition the files per directory
+	dirToFiles := map[string][]string{}
+	for _, kernelModule := range kernelModules {
+		dir := filepath.Dir(kernelModule)
+		base := filepath.Base(kernelModule)
+		dirToFiles[dir] = append(dirToFiles[dir], base)
+	}
+	// Create a prebuilt_kernel_modules module per partition
+	fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
+	for index, dir := range android.SortedKeys(dirToFiles) {
+		name := generatedModuleName(ctx.Config(), fmt.Sprintf("%s-kernel-modules-%s", partitionType, strconv.Itoa(index)))
+		props := &struct {
+			Name *string
+			Srcs []string
+		}{
+			Name: proptools.StringPtr(name),
+			Srcs: dirToFiles[dir],
+		}
+		kernelModule := ctx.CreateModuleInDirectory(
+			kernel.PrebuiltKernelModulesFactory,
+			dir,
+			props,
+		)
+		kernelModule.HideFromMake()
+		// Add to deps
+		(*fsGenState.fsDeps[partitionType])[name] = defaultDepCandidateProps(ctx.Config())
+	}
+}
+
 // createLinkerConfigSourceFilegroups creates filegroup modules to generate linker.config.pb for the following partitions
 // 1. vendor: Using PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS (space separated file list)
 // 1. product: Using PRODUCT_PRODUCT_LINKER_CONFIG_FRAGMENTS (space separated file list)
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 1ab1378..1282bfb 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -231,7 +231,7 @@
 
 	// For nsjail tasks
 	useNsjail bool
-	dirSrcs   android.Paths
+	dirSrcs   android.DirectoryPaths
 }
 
 func (g *Module) GeneratedSourceFiles() android.Paths {
@@ -604,7 +604,8 @@
 
 		if task.useNsjail {
 			for _, input := range task.dirSrcs {
-				cmd.Implicit(input)
+				cmd.ImplicitDirectory(input)
+				// TODO(b/375551969): remove glob
 				if paths, err := ctx.GlobWithDeps(filepath.Join(input.String(), "**/*"), nil); err == nil {
 					rule.NsjailImplicits(android.PathsForSource(ctx, paths))
 				} else {
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index acfc774..8b0ca97 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -24,7 +24,7 @@
 }
 
 func registerPlatformBootclasspathBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterParallelSingletonModuleType("platform_bootclasspath", platformBootclasspathFactory)
+	ctx.RegisterModuleType("platform_bootclasspath", platformBootclasspathFactory)
 }
 
 // The tags used for the dependencies between the platform bootclasspath and any configured boot
@@ -37,7 +37,7 @@
 )
 
 type platformBootclasspathModule struct {
-	android.SingletonModuleBase
+	android.ModuleBase
 	ClasspathFragmentBase
 
 	properties platformBootclasspathProperties
@@ -64,7 +64,7 @@
 	HiddenAPIFlagFileProperties
 }
 
-func platformBootclasspathFactory() android.SingletonModule {
+func platformBootclasspathFactory() android.Module {
 	m := &platformBootclasspathModule{}
 	m.AddProperties(&m.properties)
 	initClasspathFragment(m, BOOTCLASSPATH)
@@ -156,18 +156,6 @@
 	}
 }
 
-// GenerateSingletonBuildActions does nothing and must never do anything.
-//
-// This module only implements android.SingletonModule so that it can implement
-// android.SingletonMakeVarsProvider.
-func (b *platformBootclasspathModule) GenerateSingletonBuildActions(android.SingletonContext) {
-	// Keep empty
-}
-
-func (d *platformBootclasspathModule) MakeVars(ctx android.MakeVarsContext) {
-	d.generateHiddenApiMakeVars(ctx)
-}
-
 func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// Gather all the dependencies from the art, platform, and apex boot jars.
 	artModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathArtBootJarDepTag)
@@ -428,13 +416,3 @@
 
 	rule.Build(desc, desc)
 }
-
-// generateHiddenApiMakeVars generates make variables needed by hidden API related make rules, e.g.
-// veridex and run-appcompat.
-func (b *platformBootclasspathModule) generateHiddenApiMakeVars(ctx android.MakeVarsContext) {
-	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
-		return
-	}
-	// INTERNAL_PLATFORM_HIDDENAPI_FLAGS is used by Make rules in art/ and cts/.
-	ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_FLAGS", b.hiddenAPIFlagsCSV.String())
-}
diff --git a/kernel/prebuilt_kernel_modules.go b/kernel/prebuilt_kernel_modules.go
index e200ee2..4c0a911 100644
--- a/kernel/prebuilt_kernel_modules.go
+++ b/kernel/prebuilt_kernel_modules.go
@@ -32,7 +32,7 @@
 }
 
 func registerKernelBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("prebuilt_kernel_modules", prebuiltKernelModulesFactory)
+	ctx.RegisterModuleType("prebuilt_kernel_modules", PrebuiltKernelModulesFactory)
 }
 
 type prebuiltKernelModules struct {
@@ -58,7 +58,7 @@
 // prebuilt_kernel_modules installs a set of prebuilt kernel module files to the correct directory.
 // In addition, this module builds modules.load, modules.dep, modules.softdep and modules.alias
 // using depmod and installs them as well.
-func prebuiltKernelModulesFactory() android.Module {
+func PrebuiltKernelModulesFactory() android.Module {
 	module := &prebuiltKernelModules{}
 	module.AddProperties(&module.properties)
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
diff --git a/rust/config/global.go b/rust/config/global.go
index 68a74c2..7b79fca 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -15,6 +15,7 @@
 package config
 
 import (
+	"fmt"
 	"strings"
 
 	"android/soong/android"
@@ -93,6 +94,16 @@
 	}
 )
 
+func RustPath(ctx android.PathContext) string {
+	// I can't see any way to flatten the static variable inside Soong, so this
+	// reproduces the init logic.
+	var RustBase string = RustDefaultBase
+	if override := ctx.Config().Getenv("RUST_PREBUILTS_BASE"); override != "" {
+		RustBase = override
+	}
+	return fmt.Sprintf("%s/%s/%s", RustBase, HostPrebuiltTag(ctx.Config()), GetRustVersion(ctx))
+}
+
 func init() {
 	pctx.SourcePathVariable("RustDefaultBase", RustDefaultBase)
 	pctx.VariableConfigMethod("HostPrebuiltTag", HostPrebuiltTag)
diff --git a/rust/project_json.go b/rust/project_json.go
index 6c1e320..6e8cebe 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -19,6 +19,7 @@
 	"fmt"
 
 	"android/soong/android"
+	"android/soong/rust/config"
 )
 
 // This singleton collects Rust crate definitions and generates a JSON file
@@ -44,17 +45,19 @@
 }
 
 type rustProjectCrate struct {
-	DisplayName string            `json:"display_name"`
-	RootModule  string            `json:"root_module"`
-	Edition     string            `json:"edition,omitempty"`
-	Deps        []rustProjectDep  `json:"deps"`
-	Cfg         []string          `json:"cfg"`
-	Env         map[string]string `json:"env"`
-	ProcMacro   bool              `json:"is_proc_macro"`
+	DisplayName    string            `json:"display_name"`
+	RootModule     string            `json:"root_module"`
+	Edition        string            `json:"edition,omitempty"`
+	Deps           []rustProjectDep  `json:"deps"`
+	Cfg            []string          `json:"cfg"`
+	Env            map[string]string `json:"env"`
+	ProcMacro      bool              `json:"is_proc_macro"`
+	ProcMacroDylib *string           `json:"proc_macro_dylib_path"`
 }
 
 type rustProjectJson struct {
-	Crates []rustProjectCrate `json:"crates"`
+	Sysroot string             `json:"sysroot"`
+	Crates  []rustProjectCrate `json:"crates"`
 }
 
 // crateInfo is used during the processing to keep track of the known crates.
@@ -135,16 +138,21 @@
 		return 0, false
 	}
 
-	_, procMacro := rModule.compiler.(*procMacroDecorator)
+	var procMacroDylib *string = nil
+	if procDec, procMacro := rModule.compiler.(*procMacroDecorator); procMacro {
+		procMacroDylib = new(string)
+		*procMacroDylib = procDec.baseCompiler.unstrippedOutputFilePath().String()
+	}
 
 	crate := rustProjectCrate{
-		DisplayName: rModule.Name(),
-		RootModule:  rootModule.String(),
-		Edition:     rModule.compiler.edition(),
-		Deps:        make([]rustProjectDep, 0),
-		Cfg:         make([]string, 0),
-		Env:         make(map[string]string),
-		ProcMacro:   procMacro,
+		DisplayName:    rModule.Name(),
+		RootModule:     rootModule.String(),
+		Edition:        rModule.compiler.edition(),
+		Deps:           make([]rustProjectDep, 0),
+		Cfg:            make([]string, 0),
+		Env:            make(map[string]string),
+		ProcMacro:      procMacroDylib != nil,
+		ProcMacroDylib: procMacroDylib,
 	}
 
 	if rModule.compiler.cargoOutDir().Valid() {
@@ -197,6 +205,8 @@
 		return
 	}
 
+	singleton.project.Sysroot = config.RustPath(ctx)
+
 	singleton.knownCrates = make(map[string]crateInfo)
 	ctx.VisitAllModules(func(module android.Module) {
 		singleton.appendCrateAndDependencies(ctx, module)