Merge "Support extract_apk in soong" into main
diff --git a/Android.bp b/Android.bp
index 47a195c..98552a7 100644
--- a/Android.bp
+++ b/Android.bp
@@ -256,5 +256,8 @@
 
 all_apex_certs {
     name: "all_apex_certs",
-    visibility: ["//cts/tests/tests/security"],
+    visibility: [
+        "//cts/tests/tests/security",
+        "//cts/hostsidetests/appsecurity",
+    ],
 }
diff --git a/aconfig/all_aconfig_declarations.go b/aconfig/all_aconfig_declarations.go
index 6ad54da..3262493 100644
--- a/aconfig/all_aconfig_declarations.go
+++ b/aconfig/all_aconfig_declarations.go
@@ -65,15 +65,16 @@
 		})
 
 		var numOffendingPkg = 0
+		offendingPkgsMessage := ""
 		for pkg, cnt := range packages {
 			if cnt > 1 {
-				fmt.Printf("%d aconfig_declarations found for package %s\n", cnt, pkg)
+				offendingPkgsMessage += fmt.Sprintf("%d aconfig_declarations found for package %s\n", cnt, pkg)
 				numOffendingPkg++
 			}
 		}
 
 		if numOffendingPkg > 0 {
-			panic(fmt.Errorf("Only one aconfig_declarations allowed for each package."))
+			panic("Only one aconfig_declarations allowed for each package.\n" + offendingPkgsMessage)
 		}
 
 		// Generate build action for aconfig (binary proto output)
diff --git a/android/Android.bp b/android/Android.bp
index bb16856..d27a8fa 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -79,6 +79,7 @@
         "namespace.go",
         "neverallow.go",
         "ninja_deps.go",
+        "nothing.go",
         "notices.go",
         "onceper.go",
         "override_module.go",
diff --git a/android/android_info.go b/android/android_info.go
index a8d3d4e..225c8f0 100644
--- a/android/android_info.go
+++ b/android/android_info.go
@@ -58,17 +58,19 @@
 	androidInfoTxtName := proptools.StringDefault(p.properties.Stem, ctx.ModuleName()+".txt")
 	androidInfoTxt := PathForModuleOut(ctx, androidInfoTxtName)
 	androidInfoProp := androidInfoTxt.ReplaceExtension(ctx, "prop")
+	timestamp := PathForModuleOut(ctx, "timestamp")
 
 	if boardInfoFiles := PathsForModuleSrc(ctx, p.properties.Board_info_files); len(boardInfoFiles) > 0 {
 		ctx.Build(pctx, BuildParams{
-			Rule:   mergeAndRemoveComments,
-			Inputs: boardInfoFiles,
-			Output: androidInfoTxt,
+			Rule:       mergeAndRemoveComments,
+			Inputs:     boardInfoFiles,
+			Output:     androidInfoTxt,
+			Validation: timestamp,
 		})
 	} else if bootloaderBoardName := proptools.String(p.properties.Bootloader_board_name); bootloaderBoardName != "" {
-		WriteFileRule(ctx, androidInfoTxt, "board="+bootloaderBoardName)
+		WriteFileRule(ctx, androidInfoTxt, "board="+bootloaderBoardName, timestamp)
 	} else {
-		WriteFileRule(ctx, androidInfoTxt, "")
+		WriteFileRule(ctx, androidInfoTxt, "", timestamp)
 	}
 
 	// Create android_info.prop
@@ -79,6 +81,19 @@
 	})
 
 	ctx.SetOutputFiles(Paths{androidInfoProp}, "")
+	ctx.SetOutputFiles(Paths{androidInfoTxt}, ".txt")
+
+	builder := NewRuleBuilder(pctx, ctx)
+	builder.Command().Text("touch").Output(timestamp)
+	if !ctx.Config().KatiEnabled() {
+		cpPath := PathForModuleInPartitionInstall(ctx, "").Join(ctx, androidInfoTxtName)
+		builder.Command().
+			Text("rsync").
+			Flag("-a").
+			Input(androidInfoTxt).
+			Text(cpPath.String())
+	}
+	builder.Build("copy_android_info", "Copy android-info.txt")
 }
 
 // android_info module generate a file named android-info.txt that contains various information
@@ -86,6 +101,6 @@
 func AndroidInfoFactory() Module {
 	module := &androidInfoModule{}
 	module.AddProperties(&module.properties)
-	InitAndroidModule(module)
+	InitAndroidArchModule(module, DeviceSupported, MultilibCommon)
 	return module
 }
diff --git a/android/androidmk.go b/android/androidmk.go
index 590cce3..87a93e3 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -701,11 +701,6 @@
 type androidMkSingleton struct{}
 
 func (c *androidMkSingleton) GenerateBuildActions(ctx SingletonContext) {
-	// Skip if Soong wasn't invoked from Make.
-	if !ctx.Config().KatiEnabled() {
-		return
-	}
-
 	var androidMkModulesList []blueprint.Module
 
 	ctx.VisitAllModulesBlueprint(func(module blueprint.Module) {
@@ -718,6 +713,12 @@
 		return ctx.ModuleName(androidMkModulesList[i]) < ctx.ModuleName(androidMkModulesList[j])
 	})
 
+	// If running in soong-only mode, do a different, more limited version of this singleton
+	if !ctx.Config().KatiEnabled() {
+		c.soongOnlyBuildActions(ctx, androidMkModulesList)
+		return
+	}
+
 	transMk := PathForOutput(ctx, "Android"+String(ctx.Config().productVariables.Make_suffix)+".mk")
 	if ctx.Failed() {
 		return
@@ -736,6 +737,122 @@
 	})
 }
 
+// In soong-only mode, we don't do most of the androidmk stuff. But disted files are still largely
+// defined through the androidmk mechanisms, so this function is an alternate implementation of
+// the androidmk singleton that just focuses on getting the dist contributions
+func (c *androidMkSingleton) soongOnlyBuildActions(ctx SingletonContext, mods []blueprint.Module) {
+	allDistContributions := getDistContributionsFromMods(ctx, mods)
+
+	distMkFile := absolutePath(filepath.Join(ctx.Config().katiPackageMkDir(), "dist.mk"))
+
+	var goalOutputPairs []string
+	var srcDstPairs []string
+	for _, contributions := range allDistContributions {
+		for _, copiesForGoal := range contributions.copiesForGoals {
+			goals := strings.Fields(copiesForGoal.goals)
+			for _, copy := range copiesForGoal.copies {
+				for _, goal := range goals {
+					goalOutputPairs = append(goalOutputPairs, fmt.Sprintf(" %s:%s", goal, copy.dest))
+				}
+				srcDstPairs = append(srcDstPairs, fmt.Sprintf(" %s:%s", copy.from.String(), copy.dest))
+			}
+		}
+	}
+	// There are duplicates in the lists that we need to remove
+	goalOutputPairs = SortedUniqueStrings(goalOutputPairs)
+	srcDstPairs = SortedUniqueStrings(srcDstPairs)
+	var buf strings.Builder
+	buf.WriteString("DIST_SRC_DST_PAIRS :=")
+	for _, srcDstPair := range srcDstPairs {
+		buf.WriteString(srcDstPair)
+	}
+	buf.WriteString("\nDIST_GOAL_OUTPUT_PAIRS :=")
+	for _, goalOutputPair := range goalOutputPairs {
+		buf.WriteString(goalOutputPair)
+	}
+	buf.WriteString("\n")
+
+	writeValueIfChanged(ctx, distMkFile, buf.String())
+}
+
+func writeValueIfChanged(ctx SingletonContext, path string, value string) {
+	if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
+		ctx.Errorf("%s\n", err)
+		return
+	}
+	previousValue := ""
+	rawPreviousValue, err := os.ReadFile(path)
+	if err == nil {
+		previousValue = string(rawPreviousValue)
+	}
+
+	if previousValue != value {
+		if err = os.WriteFile(path, []byte(value), 0666); err != nil {
+			ctx.Errorf("Failed to write: %v", err)
+		}
+	}
+}
+
+func getDistContributionsFromMods(ctx fillInEntriesContext, mods []blueprint.Module) []distContributions {
+	var allDistContributions []distContributions
+	for _, mod := range mods {
+		if amod, ok := mod.(Module); ok && shouldSkipAndroidMkProcessing(ctx, amod.base()) {
+			continue
+		}
+		if info, ok := OtherModuleProvider(ctx, mod, AndroidMkInfoProvider); ok {
+			// Deep copy the provider info since we need to modify the info later
+			info := deepCopyAndroidMkProviderInfo(info)
+			info.PrimaryInfo.fillInEntries(ctx, mod)
+			if info.PrimaryInfo.disabled() {
+				continue
+			}
+			if contribution := info.PrimaryInfo.getDistContributions(ctx, mod); contribution != nil {
+				allDistContributions = append(allDistContributions, *contribution)
+			}
+			for _, ei := range info.ExtraInfo {
+				ei.fillInEntries(ctx, mod)
+				if ei.disabled() {
+					continue
+				}
+				if contribution := ei.getDistContributions(ctx, mod); contribution != nil {
+					allDistContributions = append(allDistContributions, *contribution)
+				}
+			}
+		} else {
+			switch x := mod.(type) {
+			case AndroidMkDataProvider:
+				data := x.AndroidMk()
+
+				if data.Include == "" {
+					data.Include = "$(BUILD_PREBUILT)"
+				}
+
+				data.fillInData(ctx, mod)
+				if data.Entries.disabled() {
+					continue
+				}
+				if contribution := data.Entries.getDistContributions(mod); contribution != nil {
+					allDistContributions = append(allDistContributions, *contribution)
+				}
+			case AndroidMkEntriesProvider:
+				entriesList := x.AndroidMkEntries()
+				for _, entries := range entriesList {
+					entries.fillInEntries(ctx, mod)
+					if entries.disabled() {
+						continue
+					}
+					if contribution := entries.getDistContributions(mod); contribution != nil {
+						allDistContributions = append(allDistContributions, *contribution)
+					}
+				}
+			default:
+				// Not exported to make so no make variables to set.
+			}
+		}
+	}
+	return allDistContributions
+}
+
 func translateAndroidMk(ctx SingletonContext, absMkFile string, moduleInfoJSONPath WritablePath, mods []blueprint.Module) error {
 	buf := &bytes.Buffer{}
 
diff --git a/android/compliance_metadata.go b/android/compliance_metadata.go
index 0b876c3..2f11df6 100644
--- a/android/compliance_metadata.go
+++ b/android/compliance_metadata.go
@@ -236,8 +236,8 @@
 		blueprint.RuleParams{
 			Command: `rm -rf $out && ` +
 				`${sqlite3} $out ".import --csv $in modules" && ` +
-				`${sqlite3} $out ".import --csv ${make_metadata} make_metadata" && ` +
-				`${sqlite3} $out ".import --csv ${make_modules} make_modules"`,
+				`([ -z "${make_metadata}" ] || ${sqlite3} $out ".import --csv ${make_metadata} make_metadata") && ` +
+				`([ -z "${make_modules}" ] || ${sqlite3} $out ".import --csv ${make_modules} make_modules")`,
 			CommandDeps: []string{"${sqlite3}"},
 		}, "make_metadata", "make_modules")
 )
@@ -307,24 +307,29 @@
 	modulesCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "soong-modules.csv")
 	WriteFileRuleVerbatim(ctx, modulesCsv, buffer.String())
 
-	// Metadata generated in Make
-	makeMetadataCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-metadata.csv")
-	makeModulesCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-modules.csv")
+	var implicits []Path
+	args := make(map[string]string)
+
+	if ctx.Config().KatiEnabled() {
+		// Metadata generated in Make
+		makeMetadataCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-metadata.csv")
+		makeModulesCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-modules.csv")
+		implicits = append(implicits, makeMetadataCsv, makeModulesCsv)
+		args["make_metadata"] = makeMetadataCsv.String()
+		args["make_modules"] = makeModulesCsv.String()
+	} else {
+		args["make_metadata"] = ""
+		args["make_modules"] = ""
+	}
 
 	// Import metadata from Make and Soong to sqlite3 database
 	complianceMetadataDb := PathForOutput(ctx, "compliance-metadata", deviceProduct, "compliance-metadata.db")
 	ctx.Build(pctx, BuildParams{
-		Rule:  importCsv,
-		Input: modulesCsv,
-		Implicits: []Path{
-			makeMetadataCsv,
-			makeModulesCsv,
-		},
-		Output: complianceMetadataDb,
-		Args: map[string]string{
-			"make_metadata": makeMetadataCsv.String(),
-			"make_modules":  makeModulesCsv.String(),
-		},
+		Rule:      importCsv,
+		Input:     modulesCsv,
+		Implicits: implicits,
+		Output:    complianceMetadataDb,
+		Args:      args,
 	})
 
 	// Phony rule "compliance-metadata.db". "m compliance-metadata.db" to create the compliance metadata database.
diff --git a/android/config.go b/android/config.go
index 00d5e2c..ec79a7d 100644
--- a/android/config.go
+++ b/android/config.go
@@ -83,6 +83,7 @@
 	OutDir         string
 	SoongOutDir    string
 	SoongVariables string
+	KatiSuffix     string
 
 	ModuleGraphFile   string
 	ModuleActionsFile string
@@ -349,6 +350,7 @@
 	// Changes behavior based on whether Kati runs after soong_build, or if soong_build
 	// runs standalone.
 	katiEnabled bool
+	katiSuffix  string
 
 	captureBuild      bool // true for tests, saves build parameters for each module
 	ignoreEnvironment bool // true for tests, returns empty from all Getenv calls
@@ -620,6 +622,7 @@
 		outDir:            cmdArgs.OutDir,
 		soongOutDir:       cmdArgs.SoongOutDir,
 		runGoTests:        cmdArgs.RunGoTests,
+		katiSuffix:        cmdArgs.KatiSuffix,
 		multilibConflicts: make(map[ArchType]bool),
 
 		moduleListFile: cmdArgs.ModuleListFile,
@@ -768,11 +771,7 @@
 // BlueprintToolLocation returns the directory containing build system tools
 // from Blueprint, like soong_zip and merge_zips.
 func (c *config) HostToolDir() string {
-	if c.KatiEnabled() {
-		return filepath.Join(c.outDir, "host", c.PrebuiltOS(), "bin")
-	} else {
-		return filepath.Join(c.soongOutDir, "host", c.PrebuiltOS(), "bin")
-	}
+	return filepath.Join(c.outDir, "host", c.PrebuiltOS(), "bin")
 }
 
 func (c *config) HostToolPath(ctx PathContext, tool string) Path {
@@ -1516,6 +1515,10 @@
 	return c.productVariables.GetBuildFlagBool("RELEASE_BOARD_API_LEVEL_FROZEN")
 }
 
+func (c *config) katiPackageMkDir() string {
+	return filepath.Join(c.soongOutDir, "kati_packaging"+c.katiSuffix)
+}
+
 func (c *deviceConfig) Arches() []Arch {
 	var arches []Arch
 	for _, target := range c.config.Targets[Android] {
@@ -2186,7 +2189,7 @@
 		"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES":              "com.android.adservices",
 		"RELEASE_APEX_CONTRIBUTIONS_APPSEARCH":               "com.android.appsearch",
 		"RELEASE_APEX_CONTRIBUTIONS_ART":                     "com.android.art",
-		"RELEASE_APEX_CONTRIBUTIONS_BLUETOOTH":               "com.android.btservices",
+		"RELEASE_APEX_CONTRIBUTIONS_BLUETOOTH":               "com.android.bt",
 		"RELEASE_APEX_CONTRIBUTIONS_CAPTIVEPORTALLOGIN":      "",
 		"RELEASE_APEX_CONTRIBUTIONS_CELLBROADCAST":           "com.android.cellbroadcast",
 		"RELEASE_APEX_CONTRIBUTIONS_CONFIGINFRASTRUCTURE":    "com.android.configinfrastructure",
@@ -2283,10 +2286,6 @@
 	return PathsForSource(ctx, c.productVariables.VendorPropFiles)
 }
 
-func (c *config) ExtraAllowedDepsTxt() string {
-	return String(c.productVariables.ExtraAllowedDepsTxt)
-}
-
 func (c *config) EnableUffdGc() string {
 	return String(c.productVariables.EnableUffdGc)
 }
diff --git a/android/container.go b/android/container.go
index 99e6a30..882535e 100644
--- a/android/container.go
+++ b/android/container.go
@@ -31,28 +31,25 @@
 // and the corresponding functions are called from [exceptionHandleFunctionsTable] map.
 // ----------------------------------------------------------------------------
 
-type exceptionHandleFunc func(ModuleContext, Module, Module) bool
+type exceptionHandleFunc func(ModuleContext, Module, ModuleProxy) bool
 
 type StubsAvailableModule interface {
 	IsStubsModule() bool
 }
 
 // Returns true if the dependency module is a stubs module
-var depIsStubsModule exceptionHandleFunc = func(_ ModuleContext, _, dep Module) bool {
-	if stubsModule, ok := dep.(StubsAvailableModule); ok {
-		return stubsModule.IsStubsModule()
-	}
-	return false
+var depIsStubsModule exceptionHandleFunc = func(mctx ModuleContext, _ Module, dep ModuleProxy) bool {
+	return OtherModuleProviderOrDefault(mctx, dep, CommonModuleInfoKey).IsStubsModule
 }
 
 // Returns true if the dependency module belongs to any of the apexes.
-var depIsApexModule exceptionHandleFunc = func(mctx ModuleContext, _, dep Module) bool {
+var depIsApexModule exceptionHandleFunc = func(mctx ModuleContext, _ Module, dep ModuleProxy) bool {
 	depContainersInfo, _ := getContainerModuleInfo(mctx, dep)
 	return InList(ApexContainer, depContainersInfo.belongingContainers)
 }
 
 // Returns true if the module and the dependent module belongs to common apexes.
-var belongsToCommonApexes exceptionHandleFunc = func(mctx ModuleContext, m, dep Module) bool {
+var belongsToCommonApexes exceptionHandleFunc = func(mctx ModuleContext, m Module, dep ModuleProxy) bool {
 	mContainersInfo, _ := getContainerModuleInfo(mctx, m)
 	depContainersInfo, _ := getContainerModuleInfo(mctx, dep)
 
@@ -62,7 +59,7 @@
 // Returns true when all apexes that the module belongs to are non updatable.
 // For an apex module to be allowed to depend on a non-apex partition module,
 // all apexes that the module belong to must be non updatable.
-var belongsToNonUpdatableApex exceptionHandleFunc = func(mctx ModuleContext, m, _ Module) bool {
+var belongsToNonUpdatableApex exceptionHandleFunc = func(mctx ModuleContext, m Module, _ ModuleProxy) bool {
 	mContainersInfo, _ := getContainerModuleInfo(mctx, m)
 
 	return !mContainersInfo.UpdatableApex()
@@ -70,7 +67,7 @@
 
 // Returns true if the dependency is added via dependency tags that are not used to tag dynamic
 // dependency tags.
-var depIsNotDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m, dep Module) bool {
+var depIsNotDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m Module, dep ModuleProxy) bool {
 	mInstallable, _ := m.(InstallableModule)
 	depTag := ctx.OtherModuleDependencyTag(dep)
 	return !InList(depTag, mInstallable.DynamicDependencyTags())
@@ -79,7 +76,7 @@
 // Returns true if the dependency is added via dependency tags that are not used to tag static
 // or dynamic dependency tags. These dependencies do not affect the module in compile time or in
 // runtime, thus are not significant enough to raise an error.
-var depIsNotStaticOrDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m, dep Module) bool {
+var depIsNotStaticOrDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m Module, dep ModuleProxy) bool {
 	mInstallable, _ := m.(InstallableModule)
 	depTag := ctx.OtherModuleDependencyTag(dep)
 	return !InList(depTag, append(mInstallable.StaticDependencyTags(), mInstallable.DynamicDependencyTags()...))
@@ -106,7 +103,7 @@
 }
 
 // Returns true when the dependency is globally allowlisted for inter-container dependency
-var depIsGloballyAllowlisted exceptionHandleFunc = func(_ ModuleContext, _, dep Module) bool {
+var depIsGloballyAllowlisted exceptionHandleFunc = func(_ ModuleContext, _ Module, dep ModuleProxy) bool {
 	return InList(dep.Name(), globallyAllowlistedDependencies)
 }
 
@@ -198,9 +195,7 @@
 func determineUnstableModule(mctx ModuleContext) bool {
 	module := mctx.Module()
 
-	// TODO(b/383559945) Remove "framework-minus-apex_jarjar-sharded" once
-	// we remove this module.
-	unstableModule := module.Name() == "framework-minus-apex" || module.Name() == "framework-minus-apex_jarjar-sharded"
+	unstableModule := module.Name() == "framework-minus-apex"
 	if installable, ok := module.(InstallableModule); ok {
 		for _, staticDepTag := range installable.StaticDependencyTags() {
 			mctx.VisitDirectDepsWithTag(staticDepTag, func(dep Module) {
@@ -403,7 +398,7 @@
 
 var ContainersInfoProvider = blueprint.NewProvider[ContainersInfo]()
 
-func satisfyAllowedExceptions(ctx ModuleContext, allowedExceptionLabels []exceptionHandleFuncLabel, m, dep Module) bool {
+func satisfyAllowedExceptions(ctx ModuleContext, allowedExceptionLabels []exceptionHandleFuncLabel, m Module, dep ModuleProxy) bool {
 	for _, label := range allowedExceptionLabels {
 		if exceptionHandleFunctionsTable[label](ctx, m, dep) {
 			return true
@@ -412,7 +407,7 @@
 	return false
 }
 
-func (c *ContainersInfo) GetViolations(mctx ModuleContext, m, dep Module, depInfo ContainersInfo) []string {
+func (c *ContainersInfo) GetViolations(mctx ModuleContext, m Module, dep ModuleProxy, depInfo ContainersInfo) []string {
 	var violations []string
 
 	// Any containers that the module belongs to but the dependency does not belong to must be examined.
@@ -458,7 +453,7 @@
 }
 
 func getContainerModuleInfo(ctx ModuleContext, module Module) (ContainersInfo, bool) {
-	if ctx.Module() == module {
+	if ctx.EqualModules(ctx.Module(), module) {
 		return ctx.getContainersInfo(), true
 	}
 
@@ -482,8 +477,8 @@
 func checkContainerViolations(ctx ModuleContext) {
 	if _, ok := ctx.Module().(InstallableModule); ok {
 		containersInfo, _ := getContainerModuleInfo(ctx, ctx.Module())
-		ctx.VisitDirectDeps(func(dep Module) {
-			if !dep.Enabled(ctx) {
+		ctx.VisitDirectDepsProxy(func(dep ModuleProxy) {
+			if !OtherModuleProviderOrDefault(ctx, dep, CommonModuleInfoKey).Enabled {
 				return
 			}
 
diff --git a/android/container_violations.go b/android/container_violations.go
index ba8f7d5..e1583c5 100644
--- a/android/container_violations.go
+++ b/android/container_violations.go
@@ -32,22 +32,22 @@
 	},
 
 	"Bluetooth": {
-		"app-compat-annotations",         // apex [com.android.btservices] -> system
-		"framework-bluetooth-pre-jarjar", // apex [com.android.btservices] -> system
+		"app-compat-annotations",         // apex [com.android.bt] -> system
+		"framework-bluetooth-pre-jarjar", // apex [com.android.bt] -> system
 	},
 
 	"bluetooth-nano-protos": {
-		"libprotobuf-java-nano", // apex [com.android.btservices] -> apex [com.android.wifi, test_com.android.wifi]
+		"libprotobuf-java-nano", // apex [com.android.bt] -> apex [com.android.wifi, test_com.android.wifi]
 	},
 
 	"bluetooth.change-ids": {
-		"app-compat-annotations", // apex [com.android.btservices] -> system
+		"app-compat-annotations", // apex [com.android.bt] -> system
 	},
 
 	"CarServiceUpdatable": {
 		"modules-utils-os",                    // apex [com.android.car.framework] -> apex [com.android.permission, test_com.android.permission]
 		"modules-utils-preconditions",         // apex [com.android.car.framework] -> apex [com.android.adservices, com.android.appsearch, com.android.cellbroadcast, com.android.extservices, com.android.ondevicepersonalization, com.android.tethering, com.android.uwb, com.android.wifi, test_com.android.cellbroadcast, test_com.android.wifi]
-		"modules-utils-shell-command-handler", // apex [com.android.car.framework] -> apex [com.android.adservices, com.android.art, com.android.art.debug, com.android.art.testing, com.android.btservices, com.android.configinfrastructure, com.android.mediaprovider, com.android.nfcservices, com.android.permission, com.android.scheduling, com.android.tethering, com.android.uwb, com.android.wifi, test_com.android.mediaprovider, test_com.android.permission, test_com.android.wifi, test_imgdiag_com.android.art, test_jitzygote_com.android.art]
+		"modules-utils-shell-command-handler", // apex [com.android.car.framework] -> apex [com.android.adservices, com.android.art, com.android.art.debug, com.android.art.testing, com.android.bt, com.android.configinfrastructure, com.android.mediaprovider, com.android.nfcservices, com.android.permission, com.android.scheduling, com.android.tethering, com.android.uwb, com.android.wifi, test_com.android.mediaprovider, test_com.android.permission, test_com.android.wifi, test_imgdiag_com.android.art, test_jitzygote_com.android.art]
 	},
 
 	"cellbroadcastreceiver_aconfig_flags_lib": {
@@ -414,10 +414,6 @@
 		"framework", // cts -> unstable
 	},
 
-	"CtsMediaBetterTogetherTestCases": {
-		"framework", // cts -> unstable
-	},
-
 	"CtsMediaCodecTestCases": {
 		"framework", // cts -> unstable
 	},
@@ -478,6 +474,11 @@
 		"framework", // cts -> unstable
 	},
 
+	// TODO(b/387499846): Remove once migrated to sdk_version.
+	"CtsMediaRouterTestCases": {
+		"framework", // cts -> unstable
+	},
+
 	"CtsMediaRouterHostSideTestBluetoothPermissionsApp": {
 		"framework", // cts -> unstable
 	},
@@ -490,6 +491,11 @@
 		"framework", // cts -> unstable
 	},
 
+	// TODO(b/387500109): Remove once migrated to sdk_version.
+	"CtsMediaSessionTestCases": {
+		"framework", // cts -> unstable
+	},
+
 	"CtsMediaV2TestCases": {
 		"framework", // cts -> unstable
 	},
@@ -824,7 +830,7 @@
 	},
 
 	"devicelockcontroller-lib": {
-		"modules-utils-expresslog", // apex [com.android.devicelock] -> apex [com.android.btservices, com.android.car.framework]
+		"modules-utils-expresslog", // apex [com.android.devicelock] -> apex [com.android.bt, com.android.car.framework]
 	},
 
 	"FederatedCompute": {
@@ -836,7 +842,7 @@
 	},
 
 	"framework-bluetooth.impl": {
-		"app-compat-annotations", // apex [com.android.btservices] -> system
+		"app-compat-annotations", // apex [com.android.bt] -> system
 	},
 
 	"framework-configinfrastructure.impl": {
@@ -907,10 +913,6 @@
 		"libnativeloader_vendor_shared_lib", // system -> vendor
 	},
 
-	"MctsMediaBetterTogetherTestCases": {
-		"framework", // cts -> unstable
-	},
-
 	"MctsMediaCodecTestCases": {
 		"framework", // cts -> unstable
 	},
@@ -947,6 +949,16 @@
 		"framework", // cts -> unstable
 	},
 
+	// TODO(b/387499846): Remove once migrated to sdk_version.
+	"MctsMediaRouterTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	// TODO(b/387500109): Remove once migrated to sdk_version.
+	"MctsMediaSessionTestCases": {
+		"framework", // cts -> unstable
+	},
+
 	"MctsMediaTranscodingTestCases": {
 		"framework", // cts -> unstable
 	},
@@ -1005,7 +1017,7 @@
 	},
 
 	"PlatformProperties": {
-		"sysprop-library-stub-platform", // apex [com.android.btservices, com.android.nfcservices, com.android.tethering, com.android.virt, com.android.wifi, test_com.android.wifi] -> system
+		"sysprop-library-stub-platform", // apex [com.android.bt, com.android.nfcservices, com.android.tethering, com.android.virt, com.android.wifi, test_com.android.wifi] -> system
 	},
 
 	"safety-center-config": {
@@ -1041,8 +1053,8 @@
 	},
 
 	"service-bluetooth-pre-jarjar": {
-		"framework-bluetooth-pre-jarjar", // apex [com.android.btservices] -> system
-		"service-bluetooth.change-ids",   // apex [com.android.btservices] -> system
+		"framework-bluetooth-pre-jarjar", // apex [com.android.bt] -> system
+		"service-bluetooth.change-ids",   // apex [com.android.bt] -> system
 	},
 
 	"service-connectivity": {
diff --git a/android/module.go b/android/module.go
index b8f2cae..9e620fb 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1679,14 +1679,13 @@
 		}
 	})
 
-	var deps Paths
-
 	var namespacePrefix string
 	nameSpace := ctx.Namespace().Path
 	if nameSpace != "." {
 		namespacePrefix = strings.ReplaceAll(nameSpace, "/", ".") + "-"
 	}
 
+	var deps Paths
 	var info FinalModuleBuildTargetsInfo
 
 	if len(allInstalledFiles) > 0 {
@@ -1853,9 +1852,9 @@
 
 var SourceFilesInfoKey = blueprint.NewProvider[SourceFilesInfo]()
 
+// FinalModuleBuildTargetsInfo is used by buildTargetSingleton to create checkbuild and
+// per-directory build targets. Only set on the final variant of each module
 type FinalModuleBuildTargetsInfo struct {
-	// Used by buildTargetSingleton to create checkbuild and per-directory build targets
-	// Only set on the final variant of each module
 	InstallTarget    WritablePath
 	CheckbuildTarget WritablePath
 	BlueprintDir     string
@@ -1868,12 +1867,14 @@
 	// Whether the module has been replaced by a prebuilt
 	ReplacedByPrebuilt bool
 	// The Target of artifacts that this module variant is responsible for creating.
-	CompileTarget           Target
+	Target                  Target
 	SkipAndroidMkProcessing bool
 	BaseModuleName          string
 	CanHaveApexVariants     bool
 	MinSdkVersion           string
 	NotAvailableForPlatform bool
+	// There some subtle differences between this one and the one above.
+	NotInPlatform bool
 	// UninstallableApexPlatformVariant is set by MakeUninstallable called by the apex
 	// mutator.  MakeUninstallable also sets HideFromMake.  UninstallableApexPlatformVariant
 	// is used to avoid adding install or packaging dependencies into libraries provided
@@ -1881,15 +1882,17 @@
 	UninstallableApexPlatformVariant bool
 	HideFromMake                     bool
 	SkipInstall                      bool
+	IsStubsModule                    bool
+	Host                             bool
 }
 
 var CommonModuleInfoKey = blueprint.NewProvider[CommonModuleInfo]()
 
-type PrebuiltModuleProviderData struct {
-	// Empty for now
+type PrebuiltModuleInfo struct {
+	SourceExists bool
 }
 
-var PrebuiltModuleProviderKey = blueprint.NewProvider[PrebuiltModuleProviderData]()
+var PrebuiltModuleInfoProvider = blueprint.NewProvider[PrebuiltModuleInfo]()
 
 type HostToolProviderData struct {
 	HostToolPath OptionalPath
@@ -1897,6 +1900,20 @@
 
 var HostToolProviderKey = blueprint.NewProvider[HostToolProviderData]()
 
+type SourceFileGenerator interface {
+	GeneratedSourceFiles() Paths
+	GeneratedHeaderDirs() Paths
+	GeneratedDeps() Paths
+}
+
+type GeneratedSourceInfo struct {
+	GeneratedSourceFiles Paths
+	GeneratedHeaderDirs  Paths
+	GeneratedDeps        Paths
+}
+
+var GeneratedSourceInfoProvider = blueprint.NewProvider[GeneratedSourceInfo]()
+
 func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) {
 	ctx := &moduleContext{
 		module:            m.module,
@@ -2147,12 +2164,13 @@
 
 	commonData := CommonModuleInfo{
 		ReplacedByPrebuilt:               m.commonProperties.ReplacedByPrebuilt,
-		CompileTarget:                    m.commonProperties.CompileTarget,
+		Target:                           m.commonProperties.CompileTarget,
 		SkipAndroidMkProcessing:          shouldSkipAndroidMkProcessing(ctx, m),
 		BaseModuleName:                   m.BaseModuleName(),
 		UninstallableApexPlatformVariant: m.commonProperties.UninstallableApexPlatformVariant,
 		HideFromMake:                     m.commonProperties.HideFromMake,
 		SkipInstall:                      m.commonProperties.SkipInstall,
+		Host:                             m.Host(),
 	}
 	if mm, ok := m.module.(interface {
 		MinSdkVersion(ctx EarlyModuleContext) ApiLevel
@@ -2173,10 +2191,16 @@
 	if am, ok := m.module.(ApexModule); ok {
 		commonData.CanHaveApexVariants = am.CanHaveApexVariants()
 		commonData.NotAvailableForPlatform = am.NotAvailableForPlatform()
+		commonData.NotInPlatform = am.NotInPlatform()
+	}
+	if st, ok := m.module.(StubsAvailableModule); ok {
+		commonData.IsStubsModule = st.IsStubsModule()
 	}
 	SetProvider(ctx, CommonModuleInfoKey, commonData)
 	if p, ok := m.module.(PrebuiltInterface); ok && p.Prebuilt() != nil {
-		SetProvider(ctx, PrebuiltModuleProviderKey, PrebuiltModuleProviderData{})
+		SetProvider(ctx, PrebuiltModuleInfoProvider, PrebuiltModuleInfo{
+			SourceExists: p.Prebuilt().SourceExists(),
+		})
 	}
 	if h, ok := m.module.(HostToolProvider); ok {
 		SetProvider(ctx, HostToolProviderKey, HostToolProviderData{
@@ -2186,6 +2210,14 @@
 	if p, ok := m.module.(AndroidMkProviderInfoProducer); ok && !commonData.SkipAndroidMkProcessing {
 		SetProvider(ctx, AndroidMkInfoProvider, p.PrepareAndroidMKProviderInfo(ctx.Config()))
 	}
+
+	if s, ok := m.module.(SourceFileGenerator); ok {
+		SetProvider(ctx, GeneratedSourceInfoProvider, GeneratedSourceInfo{
+			GeneratedSourceFiles: s.GeneratedSourceFiles(),
+			GeneratedHeaderDirs:  s.GeneratedHeaderDirs(),
+			GeneratedDeps:        s.GeneratedDeps(),
+		})
+	}
 }
 
 func SetJarJarPrefixHandler(handler func(ModuleContext)) {
diff --git a/android/module_context.go b/android/module_context.go
index b59e33d..f6a676d 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -75,7 +75,7 @@
 	// Validations is a slice of output path for a validation action. Validation outputs imply lower
 	// non-blocking priority to building non-validation outputs.
 	Validations Paths
-	// Whether to skip outputting a default target statement which will be built by Ninja when no
+	// Whether to output a default target statement which will be built by Ninja when no
 	// targets are specified on Ninja's command line.
 	Default bool
 	// Args is a key value mapping for replacements of variables within the Rule
@@ -343,7 +343,7 @@
 		OrderOnly:       params.OrderOnly.Strings(),
 		Validations:     params.Validations.Strings(),
 		Args:            params.Args,
-		Optional:        !params.Default,
+		Default:         params.Default,
 	}
 
 	if params.Depfile != nil {
@@ -688,7 +688,6 @@
 				Input:       srcPath,
 				Implicits:   implicitDeps,
 				OrderOnly:   orderOnlyDeps,
-				Default:     !m.Config().KatiEnabled(),
 				Args: map[string]string{
 					"extraCmds": extraCmds,
 				},
@@ -735,7 +734,6 @@
 				Description: "install symlink " + fullInstallPath.Base(),
 				Output:      fullInstallPath,
 				Input:       srcPath,
-				Default:     !m.Config().KatiEnabled(),
 				Args: map[string]string{
 					"fromPath": relPath,
 				},
@@ -782,7 +780,6 @@
 				Rule:        Symlink,
 				Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath,
 				Output:      fullInstallPath,
-				Default:     !m.Config().KatiEnabled(),
 				Args: map[string]string{
 					"fromPath": absPath,
 				},
diff --git a/android/neverallow.go b/android/neverallow.go
index fdcbe1c..e12e8b7 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -230,7 +230,7 @@
 func createUncompressDexRules() []Rule {
 	return []Rule{
 		NeverAllow().
-			NotIn("art").
+			NotIn("art", "cts/hostsidetests/compilation").
 			WithMatcher("uncompress_dex", isSetMatcherInstance).
 			Because("uncompress_dex is only allowed for certain jars for test in art."),
 	}
@@ -322,9 +322,6 @@
 
 func createKotlinPluginRule() []Rule {
 	kotlinPluginProjectsAllowedList := []string{
-		// TODO: Migrate compose plugin to the bundled compiler plugin
-		// Actual path prebuilts/sdk/current/androidx/m2repository/androidx/compose/compiler/compiler-hosted
-		"prebuilts/sdk/current/androidx",
 		"external/kotlinc",
 	}
 
diff --git a/android/nothing.go b/android/nothing.go
new file mode 100644
index 0000000..18bf85b
--- /dev/null
+++ b/android/nothing.go
@@ -0,0 +1,34 @@
+// Copyright 2025 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+func init() {
+	RegisterParallelSingletonType("nothing_singleton", nothingSingletonFactory)
+}
+
+func nothingSingletonFactory() Singleton {
+	return &nothingSingleton{}
+}
+
+type nothingSingleton struct{}
+
+func (s *nothingSingleton) GenerateBuildActions(ctx SingletonContext) {
+	rule := NewRuleBuilder(pctx, ctx)
+	rule.SetPhonyOutput()
+	rule.Command().
+		Text("echo Successfully read the makefiles.").
+		ImplicitOutput(PathForPhony(ctx, "nothing"))
+	rule.Build("nothing", "nothing")
+}
diff --git a/android/phony.go b/android/phony.go
index f8db88d..7bdd9d3 100644
--- a/android/phony.go
+++ b/android/phony.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"strings"
 	"sync"
 
 	"github.com/google/blueprint"
@@ -68,13 +69,25 @@
 	}
 
 	if !ctx.Config().KatiEnabled() {
+		// In soong-only builds, the phonies can conflict with dist targets that will
+		// be generated in the packaging step. Instead of emitting a blueprint/ninja phony directly,
+		// create a makefile that defines the phonies that will be included in the packaging step.
+		// Make will dedup the phonies there.
+		var buildPhonyFileContents strings.Builder
 		for _, phony := range p.phonyList {
-			ctx.Build(pctx, BuildParams{
-				Rule:      blueprint.Phony,
-				Outputs:   []WritablePath{PathForPhony(ctx, phony)},
-				Implicits: p.phonyMap[phony],
-			})
+			buildPhonyFileContents.WriteString(".PHONY: ")
+			buildPhonyFileContents.WriteString(phony)
+			buildPhonyFileContents.WriteString("\n")
+			buildPhonyFileContents.WriteString(phony)
+			buildPhonyFileContents.WriteString(":")
+			for _, dep := range p.phonyMap[phony] {
+				buildPhonyFileContents.WriteString(" ")
+				buildPhonyFileContents.WriteString(dep.String())
+			}
+			buildPhonyFileContents.WriteString("\n")
 		}
+		buildPhonyFile := PathForOutput(ctx, "soong_phony_targets.mk")
+		writeValueIfChanged(ctx, absolutePath(buildPhonyFile.String()), buildPhonyFileContents.String())
 	}
 }
 
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 0ac67b3..bf27178 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -384,7 +384,7 @@
 	if !OtherModuleProviderOrDefault(ctx, module, CommonModuleInfoKey).ReplacedByPrebuilt {
 		return module
 	}
-	if _, ok := OtherModuleProvider(ctx, module, PrebuiltModuleProviderKey); ok {
+	if _, ok := OtherModuleProvider(ctx, module, PrebuiltModuleInfoProvider); ok {
 		// If we're given a prebuilt then assume there's no source module around.
 		return module
 	}
diff --git a/android/raw_files.go b/android/raw_files.go
index 9d7f5e8..fd37196 100644
--- a/android/raw_files.go
+++ b/android/raw_files.go
@@ -18,7 +18,6 @@
 	"crypto/sha1"
 	"encoding/hex"
 	"fmt"
-	"github.com/google/blueprint"
 	"io"
 	"io/fs"
 	"os"
@@ -26,25 +25,27 @@
 	"strings"
 	"testing"
 
+	"github.com/google/blueprint"
+
 	"github.com/google/blueprint/proptools"
 )
 
 // WriteFileRule creates a ninja rule to write contents to a file by immediately writing the
 // contents, plus a trailing newline, to a file in out/soong/raw-${TARGET_PRODUCT}, and then creating
 // a ninja rule to copy the file into place.
-func WriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) {
-	writeFileRule(ctx, outputFile, content, true, false)
+func WriteFileRule(ctx BuilderContext, outputFile WritablePath, content string, validations ...Path) {
+	writeFileRule(ctx, outputFile, content, true, false, validations)
 }
 
 // WriteFileRuleVerbatim creates a ninja rule to write contents to a file by immediately writing the
 // contents to a file in out/soong/raw-${TARGET_PRODUCT}, and then creating a ninja rule to copy the file into place.
-func WriteFileRuleVerbatim(ctx BuilderContext, outputFile WritablePath, content string) {
-	writeFileRule(ctx, outputFile, content, false, false)
+func WriteFileRuleVerbatim(ctx BuilderContext, outputFile WritablePath, content string, validations ...Path) {
+	writeFileRule(ctx, outputFile, content, false, false, validations)
 }
 
 // WriteExecutableFileRuleVerbatim is the same as WriteFileRuleVerbatim, but runs chmod +x on the result
-func WriteExecutableFileRuleVerbatim(ctx BuilderContext, outputFile WritablePath, content string) {
-	writeFileRule(ctx, outputFile, content, false, true)
+func WriteExecutableFileRuleVerbatim(ctx BuilderContext, outputFile WritablePath, content string, validations ...Path) {
+	writeFileRule(ctx, outputFile, content, false, true, validations)
 }
 
 // tempFile provides a testable wrapper around a file in out/soong/.temp.  It writes to a temporary file when
@@ -124,7 +125,7 @@
 	return tempFile, hex.EncodeToString(hash.Sum(nil))
 }
 
-func writeFileRule(ctx BuilderContext, outputFile WritablePath, content string, newline bool, executable bool) {
+func writeFileRule(ctx BuilderContext, outputFile WritablePath, content string, newline bool, executable bool, validations Paths) {
 	// Write the contents to a temporary file while computing its hash.
 	tempFile, hash := writeContentToTempFileAndHash(ctx, content, newline)
 
@@ -186,6 +187,7 @@
 		Input:       rawPath,
 		Output:      outputFile,
 		Description: "raw " + outputFile.Base(),
+		Validations: validations,
 	})
 }
 
diff --git a/android/test_asserts.go b/android/test_asserts.go
index c33ade5..22472c5 100644
--- a/android/test_asserts.go
+++ b/android/test_asserts.go
@@ -33,6 +33,23 @@
 	}
 }
 
+// AssertSame checks if the expected and actual values are equal and if they are not then
+// it reports an error prefixed with the supplied message and including a reason for why it failed.
+func AssertSameArray[T comparable](t *testing.T, message string, expected []T, actual []T) {
+	t.Helper()
+	if len(actual) != len(expected) {
+		t.Errorf("%s: expected %d (%v), actual (%d) %v", message, len(expected), expected, len(actual), actual)
+		return
+	}
+	for i := range actual {
+		if actual[i] != expected[i] {
+			t.Errorf("%s: expected %d-th, %v (%v), actual %v (%v)",
+				message, i, expected[i], expected, actual[i], actual)
+			return
+		}
+	}
+}
+
 // AssertBoolEquals checks if the expected and actual values are equal and if they are not then it
 // reports an error prefixed with the supplied message and including a reason for why it failed.
 func AssertBoolEquals(t *testing.T, message string, expected bool, actual bool) {
diff --git a/android/util.go b/android/util.go
index 3fc4608..30d8ec6 100644
--- a/android/util.go
+++ b/android/util.go
@@ -308,6 +308,20 @@
 	return
 }
 
+// FilterListByPrefixes performs the same splitting as FilterList does, but treats the passed
+// filters as prefixes
+func FilterListByPrefix(list []string, filter []string) (remainder []string, filtered []string) {
+	for _, l := range list {
+		if HasAnyPrefix(l, filter) {
+			filtered = append(filtered, l)
+		} else {
+			remainder = append(remainder, l)
+		}
+	}
+
+	return
+}
+
 // FilterListPred returns the elements of the given list for which the predicate
 // returns true. Order is kept.
 func FilterListPred(list []string, pred func(s string) bool) (filtered []string) {
diff --git a/android/variable.go b/android/variable.go
index ec84274..1de8796 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -162,6 +162,7 @@
 			Optimize struct {
 				Enabled *bool
 			}
+			Aaptflags []string
 		}
 
 		Uml struct {
@@ -542,8 +543,6 @@
 
 	PartitionVarsForSoongMigrationOnlyDoNotUse PartitionVariables
 
-	ExtraAllowedDepsTxt *string `json:",omitempty"`
-
 	AdbKeys *string `json:",omitempty"`
 
 	DeviceMatrixFile       []string `json:",omitempty"`
@@ -696,6 +695,8 @@
 	TargetScreenDensity string `json:",omitempty"`
 
 	PrivateRecoveryUiProperties map[string]string `json:",omitempty"`
+
+	PrebuiltBootloader string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/apex/apex.go b/apex/apex.go
index 7734391..d39a17f 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1056,7 +1056,7 @@
 		"com.android.appsearch",
 		"com.android.art",
 		"com.android.art.debug",
-		"com.android.btservices",
+		"com.android.bt",
 		"com.android.cellbroadcast",
 		"com.android.configinfrastructure",
 		"com.android.conscrypt",
@@ -2588,7 +2588,7 @@
 	})
 
 	a.WalkPayloadDepsProxy(ctx, func(ctx android.BaseModuleContext, from, to android.ModuleProxy, externalDep bool) bool {
-		if ccInfo, ok := android.OtherModuleProvider(ctx, to, cc.CcInfoProvider); ok {
+		if info, ok := android.OtherModuleProvider(ctx, to, cc.LinkableInfoProvider); ok {
 			// If `to` is not actually in the same APEX as `from` then it does not need
 			// apex_available and neither do any of its dependencies.
 			if externalDep {
@@ -2609,7 +2609,7 @@
 				return false
 			}
 
-			isStubLibraryFromOtherApex := ccInfo.HasStubsVariants && !librariesDirectlyInApex[toName]
+			isStubLibraryFromOtherApex := info.HasStubsVariants && !librariesDirectlyInApex[toName]
 			if isStubLibraryFromOtherApex && !externalDep {
 				ctx.ModuleErrorf("%q required by %q is a native library providing stub. "+
 					"It shouldn't be included in this APEX via static linking. Dependency path: %s", to.String(), fromName, ctx.GetPathString(false))
@@ -2740,7 +2740,7 @@
 			return
 		}
 
-		if android.OtherModuleProviderOrDefault(ctx, module, cc.LinkableInfoKey).StaticExecutable {
+		if android.OtherModuleProviderOrDefault(ctx, module, cc.LinkableInfoProvider).StaticExecutable {
 			apex := a.ApexVariationName()
 			exec := ctx.OtherModuleName(module)
 			if isStaticExecutableAllowed(apex, exec) {
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index d46104e..263e0a5 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -59,9 +59,9 @@
 
 	// Diff two given lists while ignoring comments in the allowed deps file.
 	diffAllowedApexDepsInfoRule = pctx.AndroidStaticRule("diffAllowedApexDepsInfoRule", blueprint.RuleParams{
-		Description: "Diff ${allowed_deps_list} and ${new_allowed_deps}",
+		Description: "Diff ${allowed_deps} and ${new_allowed_deps}",
 		Command: `
-			if grep -v -h '^#' ${allowed_deps_list} | sort -u -f| diff -B -u - ${new_allowed_deps}; then
+			if grep -v '^#' ${allowed_deps} | diff -B - ${new_allowed_deps}; then
 			   touch ${out};
 			else
 				echo;
@@ -85,62 +85,54 @@
 				exit 1;
 			fi;
 		`,
-	}, "allowed_deps_list", "new_allowed_deps")
+	}, "allowed_deps", "new_allowed_deps")
 )
 
 func (s *apexDepsInfoSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	allowedDepsSources := []android.OptionalPath{android.ExistentPathForSource(ctx, "packages/modules/common/build/allowed_deps.txt")}
-	extraAllowedDepsPath := ctx.Config().ExtraAllowedDepsTxt()
-	if extraAllowedDepsPath != "" {
-		allowedDepsSources = append(allowedDepsSources, android.ExistentPathForSource(ctx, extraAllowedDepsPath))
-	}
 	updatableFlatLists := android.Paths{}
 	ctx.VisitAllModules(func(module android.Module) {
 		if binaryInfo, ok := module.(android.ApexBundleDepsInfoIntf); ok {
 			apexInfo, _ := android.OtherModuleProvider(ctx, module, android.ApexInfoProvider)
 			if path := binaryInfo.FlatListPath(); path != nil {
 				if binaryInfo.Updatable() || apexInfo.Updatable {
-					updatableFlatLists = append(updatableFlatLists, path)
+					if strings.HasPrefix(module.String(), "com.android.") {
+						updatableFlatLists = append(updatableFlatLists, path)
+					}
 				}
 			}
 		}
 	})
+
+	allowedDepsSource := android.ExistentPathForSource(ctx, "packages/modules/common/build/allowed_deps.txt")
 	newAllowedDeps := android.PathForOutput(ctx, "apex", "depsinfo", "new-allowed-deps.txt")
 	s.allowedApexDepsInfoCheckResult = android.PathForOutput(ctx, newAllowedDeps.Rel()+".check")
-	hasOneValidDepsPath := false
-	for _, allowedDepsSource := range allowedDepsSources {
-		if allowedDepsSource.Valid() {
-			hasOneValidDepsPath = true
-			updatableFlatLists = append(updatableFlatLists, allowedDepsSource.Path())
-		}
-	}
-	allowedDepsStrList := make([]string, len(allowedDepsSources))
-	for _, value := range allowedDepsSources {
-		allowedDepsStrList = append(allowedDepsStrList, value.String())
-	}
-	allowedDepsListString := strings.Join(allowedDepsStrList, " ")
-	if !hasOneValidDepsPath {
+
+	if !allowedDepsSource.Valid() {
 		// Unbundled projects may not have packages/modules/common/ checked out; ignore those.
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   android.Touch,
 			Output: s.allowedApexDepsInfoCheckResult,
 		})
 	} else {
+		allowedDeps := allowedDepsSource.Path()
+
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   generateApexDepsInfoFilesRule,
-			Inputs: updatableFlatLists,
+			Inputs: append(updatableFlatLists, allowedDeps),
 			Output: newAllowedDeps,
 		})
+
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   diffAllowedApexDepsInfoRule,
 			Input:  newAllowedDeps,
 			Output: s.allowedApexDepsInfoCheckResult,
 			Args: map[string]string{
-				"allowed_deps_list": allowedDepsListString,
-				"new_allowed_deps":  newAllowedDeps.String(),
+				"allowed_deps":     allowedDeps.String(),
+				"new_allowed_deps": newAllowedDeps.String(),
 			},
 		})
 	}
+
 	ctx.Phony("apex-allowed-deps-check", s.allowedApexDepsInfoCheckResult)
 }
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index e1a9582..465789c 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -2129,7 +2129,79 @@
 	android.AssertStringDoesContain(t, "cflags", cflags, "-target aarch64-linux-android29")
 }
 
-func TestTrackAllowedDeps(t *testing.T) {
+func TestTrackAllowedDepsForAndroidApex(t *testing.T) {
+	t.Parallel()
+	ctx := testApex(t, `
+		apex {
+			name: "com.android.myapex",
+			key: "myapex.key",
+			updatable: true,
+			native_shared_libs: [
+				"mylib",
+				"yourlib",
+			],
+			min_sdk_version: "29",
+		}
+
+		apex {
+			name: "myapex2",
+			key: "myapex.key",
+			updatable: false,
+			native_shared_libs: ["yourlib"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["libbar"],
+			min_sdk_version: "29",
+			apex_available: ["com.android.myapex"],
+		}
+
+		cc_library {
+			name: "libbar",
+			stubs: { versions: ["29", "30"] },
+		}
+
+		cc_library {
+			name: "yourlib",
+			srcs: ["mylib.cpp"],
+			min_sdk_version: "29",
+			apex_available: ["com.android.myapex", "myapex2", "//apex_available:platform"],
+		}
+	`, withFiles(android.MockFS{
+		"packages/modules/common/build/allowed_deps.txt": nil,
+	}),
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/com.android.myapex-file_contexts": nil,
+		}))
+
+	depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton")
+	inputs := depsinfo.Rule("generateApexDepsInfoFilesRule").BuildParams.Inputs.Strings()
+
+	android.AssertStringListContains(t, "updatable com.android.myapex should generate depsinfo file", inputs,
+		"out/soong/.intermediates/com.android.myapex/android_common_com.android.myapex/depsinfo/flatlist.txt")
+	android.AssertStringListDoesNotContain(t, "non-updatable myapex2 should not generate depsinfo file", inputs,
+		"out/soong/.intermediates/myapex2/android_common_myapex2/depsinfo/flatlist.txt")
+
+	myapex := ctx.ModuleForTests("com.android.myapex", "android_common_com.android.myapex")
+	flatlist := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
+		myapex.Output("depsinfo/flatlist.txt")), "\n")
+	android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep",
+		flatlist, "libbar(minSdkVersion:(no version)) (external)")
+	android.AssertStringListDoesNotContain(t, "do not track if not available for platform",
+		flatlist, "mylib:(minSdkVersion:29)")
+	android.AssertStringListContains(t, "track platform-available lib",
+		flatlist, "yourlib(minSdkVersion:29)")
+}
+
+func TestNotTrackAllowedDepsForNonAndroidApex(t *testing.T) {
 	t.Parallel()
 	ctx := testApex(t, `
 		apex {
@@ -2181,174 +2253,17 @@
 
 	depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton")
 	inputs := depsinfo.Rule("generateApexDepsInfoFilesRule").BuildParams.Inputs.Strings()
-	android.AssertStringListContains(t, "updatable myapex should generate depsinfo file", inputs,
+	android.AssertStringListDoesNotContain(t, "updatable myapex should generate depsinfo file", inputs,
 		"out/soong/.intermediates/myapex/android_common_myapex/depsinfo/flatlist.txt")
 	android.AssertStringListDoesNotContain(t, "non-updatable myapex2 should not generate depsinfo file", inputs,
 		"out/soong/.intermediates/myapex2/android_common_myapex2/depsinfo/flatlist.txt")
-
-	myapex := ctx.ModuleForTests("myapex", "android_common_myapex")
-	flatlist := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
-		myapex.Output("depsinfo/flatlist.txt")), "\n")
-	android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep",
-		flatlist, "libbar(minSdkVersion:(no version)) (external)")
-	android.AssertStringListDoesNotContain(t, "do not track if not available for platform",
-		flatlist, "mylib:(minSdkVersion:29)")
-	android.AssertStringListContains(t, "track platform-available lib",
-		flatlist, "yourlib(minSdkVersion:29)")
-}
-
-func TestTrackCustomAllowedDepsInvalidDefaultTxt(t *testing.T) {
-	t.Parallel()
-	ctx := testApex(t, `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			updatable: true,
-			native_shared_libs: [
-				"mylib",
-				"yourlib",
-			],
-			min_sdk_version: "29",
-		}
-
-		apex {
-			name: "myapex2",
-			key: "myapex.key",
-			updatable: false,
-			native_shared_libs: ["yourlib"],
-		}
-
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		cc_library {
-			name: "mylib",
-			srcs: ["mylib.cpp"],
-			shared_libs: ["libbar"],
-			min_sdk_version: "29",
-			apex_available: ["myapex"],
-		}
-
-		cc_library {
-			name: "libbar",
-			stubs: { versions: ["29", "30"] },
-		}
-
-		cc_library {
-			name: "yourlib",
-			srcs: ["mylib.cpp"],
-			min_sdk_version: "29",
-			apex_available: ["myapex", "myapex2", "//apex_available:platform"],
-		}
-	`, withFiles(android.MockFS{
-		"packages/modules/common/build/custom_allowed_deps.txt": nil,
-	}),
-		android.FixtureModifyProductVariables(
-			func(variables android.FixtureProductVariables) {
-				variables.ExtraAllowedDepsTxt = proptools.StringPtr("packages/modules/common/build/custom_allowed_deps.txt")
-			},
-		))
-
-	depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton")
-	inputs := depsinfo.Rule("generateApexDepsInfoFilesRule").BuildParams.Inputs.Strings()
-	android.AssertStringListContains(t, "updatable myapex should generate depsinfo file", inputs,
-		"out/soong/.intermediates/myapex/android_common_myapex/depsinfo/flatlist.txt")
-	android.AssertStringListDoesNotContain(t, "non-updatable myapex2 should not generate depsinfo file", inputs,
-		"out/soong/.intermediates/myapex2/android_common_myapex2/depsinfo/flatlist.txt")
-
-	myapex := ctx.ModuleForTests("myapex", "android_common_myapex")
-	flatlist := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
-		myapex.Output("depsinfo/flatlist.txt")), "\n")
-	android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep",
-		flatlist, "libbar(minSdkVersion:(no version)) (external)")
-	android.AssertStringListDoesNotContain(t, "do not track if not available for platform",
-		flatlist, "mylib:(minSdkVersion:29)")
-	android.AssertStringListContains(t, "track platform-available lib",
-		flatlist, "yourlib(minSdkVersion:29)")
-}
-
-func TestTrackCustomAllowedDepsWithDefaultTxt(t *testing.T) {
-	t.Parallel()
-	ctx := testApex(t, `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			updatable: true,
-			native_shared_libs: [
-				"mylib",
-				"yourlib",
-			],
-			min_sdk_version: "29",
-		}
-
-		apex {
-			name: "myapex2",
-			key: "myapex.key",
-			updatable: false,
-			native_shared_libs: ["yourlib"],
-		}
-
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		cc_library {
-			name: "mylib",
-			srcs: ["mylib.cpp"],
-			shared_libs: ["libbar"],
-			min_sdk_version: "29",
-			apex_available: ["myapex"],
-		}
-
-		cc_library {
-			name: "libbar",
-			stubs: { versions: ["29", "30"] },
-		}
-
-		cc_library {
-			name: "yourlib",
-			srcs: ["mylib.cpp"],
-			min_sdk_version: "29",
-			apex_available: ["myapex", "myapex2", "//apex_available:platform"],
-		}
-	`, withFiles(android.MockFS{
-		"packages/modules/common/build/custom_allowed_deps.txt": nil,
-		"packages/modules/common/build/allowed_deps.txt":        nil,
-	}),
-		android.FixtureModifyProductVariables(
-			func(variables android.FixtureProductVariables) {
-				variables.ExtraAllowedDepsTxt = proptools.StringPtr("packages/modules/common/build/custom_allowed_deps.txt")
-			},
-		))
-
-	depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton")
-	inputs := depsinfo.Rule("generateApexDepsInfoFilesRule").BuildParams.Inputs.Strings()
-	android.AssertStringListContains(t, "updatable myapex should generate depsinfo file", inputs,
-		"out/soong/.intermediates/myapex/android_common_myapex/depsinfo/flatlist.txt")
-	android.AssertStringListDoesNotContain(t, "non-updatable myapex2 should not generate depsinfo file", inputs,
-		"out/soong/.intermediates/myapex2/android_common_myapex2/depsinfo/flatlist.txt")
-
-	myapex := ctx.ModuleForTests("myapex", "android_common_myapex")
-	flatlist := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
-		myapex.Output("depsinfo/flatlist.txt")), "\n")
-	android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep",
-		flatlist, "libbar(minSdkVersion:(no version)) (external)")
-	android.AssertStringListDoesNotContain(t, "do not track if not available for platform",
-		flatlist, "mylib:(minSdkVersion:29)")
-	android.AssertStringListContains(t, "track platform-available lib",
-		flatlist, "yourlib(minSdkVersion:29)")
 }
 
 func TestTrackAllowedDeps_SkipWithoutAllowedDepsTxt(t *testing.T) {
 	t.Parallel()
 	ctx := testApex(t, `
 		apex {
-			name: "myapex",
+			name: "com.android.myapex",
 			key: "myapex.key",
 			updatable: true,
 			min_sdk_version: "29",
@@ -2359,7 +2274,10 @@
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
 		}
-	`)
+	`,
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/com.android.myapex-file_contexts": nil,
+		}))
 	depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton")
 	if nil != depsinfo.MaybeRule("generateApexDepsInfoFilesRule").Output {
 		t.Error("apex_depsinfo_singleton shouldn't run when allowed_deps.txt doesn't exist")
diff --git a/apex/builder.go b/apex/builder.go
index c85d0a0..d9348c5 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -73,6 +73,7 @@
 	pctx.HostBinToolVariable("make_erofs", "mkfs.erofs")
 	pctx.HostBinToolVariable("apex_compression_tool", "apex_compression_tool")
 	pctx.HostBinToolVariable("dexdeps", "dexdeps")
+	pctx.HostBinToolVariable("apex_ls", "apex-ls")
 	pctx.HostBinToolVariable("apex_sepolicy_tests", "apex_sepolicy_tests")
 	pctx.HostBinToolVariable("deapexer", "deapexer")
 	pctx.HostBinToolVariable("debugfs_static", "debugfs_static")
@@ -210,11 +211,11 @@
 	}, "image_dir", "readelf")
 
 	apexSepolicyTestsRule = pctx.StaticRule("apexSepolicyTestsRule", blueprint.RuleParams{
-		Command: `${deapexer} --debugfs_path ${debugfs_static} list -Z ${in} > ${out}.fc` +
-			` && ${apex_sepolicy_tests} -f ${out}.fc && touch ${out}`,
-		CommandDeps: []string{"${apex_sepolicy_tests}", "${deapexer}", "${debugfs_static}"},
+		Command: `${apex_ls} -Z ${in} > ${out}.fc` +
+			` && ${apex_sepolicy_tests} -f ${out}.fc --partition ${partition_tag} && touch ${out}`,
+		CommandDeps: []string{"${apex_sepolicy_tests}", "${apex_ls}"},
 		Description: "run apex_sepolicy_tests",
-	})
+	}, "partition_tag")
 
 	apexLinkerconfigValidationRule = pctx.StaticRule("apexLinkerconfigValidationRule", blueprint.RuleParams{
 		Command:     `${conv_linker_config} validate --type apex ${image_dir} && touch ${out}`,
@@ -918,9 +919,8 @@
 	}
 	var validations android.Paths
 	validations = append(validations, runApexLinkerconfigValidation(ctx, unsignedOutputFile, imageDir))
-	// TODO(b/279688635) deapexer supports [ext4]
-	if !a.skipValidation(apexSepolicyTests) && suffix == imageApexSuffix && ext4 == a.payloadFsType {
-		validations = append(validations, runApexSepolicyTests(ctx, unsignedOutputFile))
+	if !a.skipValidation(apexSepolicyTests) && android.InList(a.payloadFsType, []fsType{ext4, erofs}) {
+		validations = append(validations, runApexSepolicyTests(ctx, a, unsignedOutputFile))
 	}
 	if !a.testApex && len(a.properties.Unwanted_transitive_deps) > 0 {
 		validations = append(validations,
@@ -1204,14 +1204,17 @@
 
 // Runs apex_sepolicy_tests
 //
-// $ deapexer list -Z {apex_file} > {file_contexts}
+// $ apex-ls -Z {apex_file} > {file_contexts}
 // $ apex_sepolicy_tests -f {file_contexts}
-func runApexSepolicyTests(ctx android.ModuleContext, apexFile android.Path) android.Path {
+func runApexSepolicyTests(ctx android.ModuleContext, a *apexBundle, apexFile android.Path) android.Path {
 	timestamp := android.PathForModuleOut(ctx, "apex_sepolicy_tests.timestamp")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   apexSepolicyTestsRule,
 		Input:  apexFile,
 		Output: timestamp,
+		Args: map[string]string{
+			"partition_tag": a.PartitionTag(ctx.DeviceConfig()),
+		},
 	})
 	return timestamp
 }
diff --git a/apex/key.go b/apex/key.go
index 9fa9d1e..1622c65 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -182,6 +182,7 @@
 }
 
 func (_ *allApexCerts) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	var avbpubkeys android.Paths
 	var certificatesPem android.Paths
 	ctx.VisitDirectDeps(func(m android.Module) {
 		if apex, ok := m.(*apexBundle); ok {
@@ -194,9 +195,12 @@
 				}
 			}
 			certificatesPem = append(certificatesPem, pem)
+			// avbpubkey for signing the apex payload
+			avbpubkeys = append(avbpubkeys, apex.publicKeyFile)
 		}
 	})
 	certificatesPem = android.SortedUniquePaths(certificatesPem) // For hermiticity
+	avbpubkeys = android.SortedUniquePaths(avbpubkeys)           // For hermiticity
 	var certificatesDer android.Paths
 	for index, certificatePem := range certificatesPem {
 		certificateDer := android.PathForModuleOut(ctx, fmt.Sprintf("x509.%v.der", index))
@@ -209,6 +213,7 @@
 	}
 	ctx.SetOutputFiles(certificatesPem, ".pem")
 	ctx.SetOutputFiles(certificatesDer, ".der")
+	ctx.SetOutputFiles(avbpubkeys, ".avbpubkey")
 }
 
 func (_ *allApexCerts) GenerateSingletonBuildActions(ctx android.SingletonContext) {
diff --git a/cc/cc.go b/cc/cc.go
index 97d4533..af1b259 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -34,7 +34,6 @@
 	"android/soong/android"
 	"android/soong/cc/config"
 	"android/soong/fuzz"
-	"android/soong/genrule"
 )
 
 type CcMakeVarsInfo struct {
@@ -79,49 +78,91 @@
 	// list of modules that should be dynamically linked into this module.
 	SharedLibs proptools.Configurable[[]string]
 	// list of modules that should only provide headers for this module.
-	HeaderLibs           proptools.Configurable[[]string]
-	UnstrippedOutputFile android.Path
+	HeaderLibs               proptools.Configurable[[]string]
+	ImplementationModuleName *string
 
 	BinaryDecoratorInfo    *BinaryDecoratorInfo
 	LibraryDecoratorInfo   *LibraryDecoratorInfo
 	TestBinaryInfo         *TestBinaryInfo
 	BenchmarkDecoratorInfo *BenchmarkDecoratorInfo
 	ObjectLinkerInfo       *ObjectLinkerInfo
+	StubDecoratorInfo      *StubDecoratorInfo
 }
 
 type BinaryDecoratorInfo struct{}
 type LibraryDecoratorInfo struct {
 	ExportIncludeDirs proptools.Configurable[[]string]
+	InjectBsslHash    bool
 }
 
-type LibraryInfo struct {
-	StubsVersion string
+type SnapshotInfo struct {
+	SnapshotAndroidMkSuffix string
 }
 
 type TestBinaryInfo struct {
 	Gtest bool
 }
 type BenchmarkDecoratorInfo struct{}
+
+type StubDecoratorInfo struct{}
+
 type ObjectLinkerInfo struct{}
 
+type LibraryInfo struct {
+	BuildStubs bool
+}
+
 // Common info about the cc module.
 type CcInfo struct {
-	HasStubsVariants       bool
 	IsPrebuilt             bool
 	CmakeSnapshotSupported bool
+	HasLlndkStubs          bool
 	CompilerInfo           *CompilerInfo
 	LinkerInfo             *LinkerInfo
+	SnapshotInfo           *SnapshotInfo
 	LibraryInfo            *LibraryInfo
 }
 
-var CcInfoProvider = blueprint.NewProvider[CcInfo]()
+var CcInfoProvider = blueprint.NewProvider[*CcInfo]()
 
 type LinkableInfo struct {
 	// StaticExecutable returns true if this is a binary module with "static_executable: true".
-	StaticExecutable bool
+	StaticExecutable     bool
+	Static               bool
+	Shared               bool
+	HasStubsVariants     bool
+	StubsVersion         string
+	IsStubs              bool
+	UnstrippedOutputFile android.Path
+	OutputFile           android.OptionalPath
+	CoverageFiles        android.Paths
+	SAbiDumpFiles        android.Paths
+	CcLibrary            bool
+	CcLibraryInterface   bool
+	RustLibraryInterface bool
+	// CrateName returns the crateName for a Rust library
+	CrateName string
+	// DepFlags returns a slice of Rustc string flags
+	ExportedCrateLinkDirs []string
+	// This can be different from the one on CommonModuleInfo
+	BaseModuleName       string
+	HasNonSystemVariants bool
+	IsLlndk              bool
+	InVendorOrProduct    bool
+	// SubName returns the modules SubName, used for image and NDK/SDK variations.
+	SubName             string
+	InRamdisk           bool
+	OnlyInRamdisk       bool
+	InVendorRamdisk     bool
+	OnlyInVendorRamdisk bool
+	InRecovery          bool
+	OnlyInRecovery      bool
+	Installable         *bool
+	// RelativeInstallPath returns the relative install path for this module.
+	RelativeInstallPath string
 }
 
-var LinkableInfoKey = blueprint.NewProvider[LinkableInfo]()
+var LinkableInfoProvider = blueprint.NewProvider[*LinkableInfo]()
 
 func init() {
 	RegisterCCBuildComponents(android.InitRegistrationContext)
@@ -2181,14 +2222,24 @@
 		android.SetProvider(ctx, CcObjectInfoProvider, ccObjectInfo)
 	}
 
-	android.SetProvider(ctx, LinkableInfoKey, LinkableInfo{
-		StaticExecutable: c.StaticExecutable(),
-	})
+	linkableInfo := CreateCommonLinkableInfo(c)
+	if lib, ok := c.linker.(versionedInterface); ok {
+		linkableInfo.StubsVersion = lib.stubsVersion()
+	}
+	if c.linker != nil {
+		if library, ok := c.linker.(libraryInterface); ok {
+			linkableInfo.Static = library.static()
+			linkableInfo.Shared = library.shared()
+			linkableInfo.CoverageFiles = library.objs().coverageFiles
+			linkableInfo.SAbiDumpFiles = library.objs().sAbiDumpFiles
+		}
+	}
+	android.SetProvider(ctx, LinkableInfoProvider, linkableInfo)
 
 	ccInfo := CcInfo{
-		HasStubsVariants:       c.HasStubsVariants(),
 		IsPrebuilt:             c.IsPrebuilt(),
 		CmakeSnapshotSupported: proptools.Bool(c.Properties.Cmake_snapshot_supported),
+		HasLlndkStubs:          c.HasLlndkStubs(),
 	}
 	if c.compiler != nil {
 		ccInfo.CompilerInfo = &CompilerInfo{
@@ -2210,17 +2261,18 @@
 	}
 	if c.linker != nil {
 		ccInfo.LinkerInfo = &LinkerInfo{
-			WholeStaticLibs:      c.linker.baseLinkerProps().Whole_static_libs,
-			StaticLibs:           c.linker.baseLinkerProps().Static_libs,
-			SharedLibs:           c.linker.baseLinkerProps().Shared_libs,
-			HeaderLibs:           c.linker.baseLinkerProps().Header_libs,
-			UnstrippedOutputFile: c.UnstrippedOutputFile(),
+			WholeStaticLibs: c.linker.baseLinkerProps().Whole_static_libs,
+			StaticLibs:      c.linker.baseLinkerProps().Static_libs,
+			SharedLibs:      c.linker.baseLinkerProps().Shared_libs,
+			HeaderLibs:      c.linker.baseLinkerProps().Header_libs,
 		}
 		switch decorator := c.linker.(type) {
 		case *binaryDecorator:
 			ccInfo.LinkerInfo.BinaryDecoratorInfo = &BinaryDecoratorInfo{}
 		case *libraryDecorator:
-			ccInfo.LinkerInfo.LibraryDecoratorInfo = &LibraryDecoratorInfo{}
+			ccInfo.LinkerInfo.LibraryDecoratorInfo = &LibraryDecoratorInfo{
+				InjectBsslHash: Bool(c.linker.(*libraryDecorator).Properties.Inject_bssl_hash),
+			}
 		case *testBinary:
 			ccInfo.LinkerInfo.TestBinaryInfo = &TestBinaryInfo{
 				Gtest: decorator.testDecorator.gtest(),
@@ -2229,14 +2281,26 @@
 			ccInfo.LinkerInfo.BenchmarkDecoratorInfo = &BenchmarkDecoratorInfo{}
 		case *objectLinker:
 			ccInfo.LinkerInfo.ObjectLinkerInfo = &ObjectLinkerInfo{}
+		case *stubDecorator:
+			ccInfo.LinkerInfo.StubDecoratorInfo = &StubDecoratorInfo{}
+		}
+
+		if s, ok := c.linker.(SnapshotInterface); ok {
+			ccInfo.SnapshotInfo = &SnapshotInfo{
+				SnapshotAndroidMkSuffix: s.SnapshotAndroidMkSuffix(),
+			}
+		}
+		if v, ok := c.linker.(versionedInterface); ok {
+			name := v.implementationModuleName(ctx.OtherModuleName(c))
+			ccInfo.LinkerInfo.ImplementationModuleName = &name
 		}
 	}
 	if c.library != nil {
 		ccInfo.LibraryInfo = &LibraryInfo{
-			StubsVersion: c.library.stubsVersion(),
+			BuildStubs: c.library.buildStubs(),
 		}
 	}
-	android.SetProvider(ctx, CcInfoProvider, ccInfo)
+	android.SetProvider(ctx, CcInfoProvider, &ccInfo)
 
 	c.setOutputFiles(ctx)
 
@@ -2245,6 +2309,32 @@
 	}
 }
 
+func CreateCommonLinkableInfo(mod LinkableInterface) *LinkableInfo {
+	return &LinkableInfo{
+		StaticExecutable:     mod.StaticExecutable(),
+		HasStubsVariants:     mod.HasStubsVariants(),
+		OutputFile:           mod.OutputFile(),
+		UnstrippedOutputFile: mod.UnstrippedOutputFile(),
+		IsStubs:              mod.IsStubs(),
+		CcLibrary:            mod.CcLibrary(),
+		CcLibraryInterface:   mod.CcLibraryInterface(),
+		RustLibraryInterface: mod.RustLibraryInterface(),
+		BaseModuleName:       mod.BaseModuleName(),
+		IsLlndk:              mod.IsLlndk(),
+		HasNonSystemVariants: mod.HasNonSystemVariants(),
+		SubName:              mod.SubName(),
+		InVendorOrProduct:    mod.InVendorOrProduct(),
+		InRamdisk:            mod.InRamdisk(),
+		OnlyInRamdisk:        mod.OnlyInRamdisk(),
+		InVendorRamdisk:      mod.InVendorRamdisk(),
+		OnlyInVendorRamdisk:  mod.OnlyInVendorRamdisk(),
+		InRecovery:           mod.InRecovery(),
+		OnlyInRecovery:       mod.OnlyInRecovery(),
+		Installable:          mod.Installable(),
+		RelativeInstallPath:  mod.RelativeInstallPath(),
+	}
+}
+
 func setOutputFilesIfNotEmpty(ctx ModuleContext, files android.Paths, tag string) {
 	if len(files) > 0 {
 		ctx.SetOutputFiles(files, tag)
@@ -3070,7 +3160,7 @@
 
 	skipModuleList := map[string]bool{}
 
-	ctx.VisitDirectDeps(func(dep android.Module) {
+	ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
 		depName := ctx.OtherModuleName(dep)
 		depTag := ctx.OtherModuleDependencyTag(dep)
 
@@ -3079,8 +3169,17 @@
 			return
 		}
 
+		var ccInfo *CcInfo
+		v, hasCcInfo := android.OtherModuleProvider(ctx, dep, CcInfoProvider)
+		if hasCcInfo {
+			ccInfo = v
+		}
+		linkableInfo, hasLinkableInfo := android.OtherModuleProvider(ctx, dep, LinkableInfoProvider)
 		if depTag == android.DarwinUniversalVariantTag {
-			depPaths.DarwinSecondArchOutput = dep.(*Module).OutputFile()
+			if !hasCcInfo {
+				panic(fmt.Errorf("dep is not a cc module: %s", dep.String()))
+			}
+			depPaths.DarwinSecondArchOutput = linkableInfo.OutputFile
 			return
 		}
 
@@ -3093,34 +3192,32 @@
 			}
 		}
 
-		ccDep, ok := dep.(LinkableInterface)
-		if !ok {
-
+		if !hasLinkableInfo {
 			// handling for a few module types that aren't cc Module but that are also supported
+			genRule, ok := android.OtherModuleProvider(ctx, dep, android.GeneratedSourceInfoProvider)
 			switch depTag {
 			case genSourceDepTag:
-				if genRule, ok := dep.(genrule.SourceFileGenerator); ok {
+				if ok {
 					depPaths.GeneratedSources = append(depPaths.GeneratedSources,
-						genRule.GeneratedSourceFiles()...)
+						genRule.GeneratedSourceFiles...)
 				} else {
 					ctx.ModuleErrorf("module %q is not a gensrcs or genrule", depName)
 				}
 				// Support exported headers from a generated_sources dependency
 				fallthrough
 			case genHeaderDepTag, genHeaderExportDepTag:
-				if genRule, ok := dep.(genrule.SourceFileGenerator); ok {
+				if ok {
 					depPaths.GeneratedDeps = append(depPaths.GeneratedDeps,
-						genRule.GeneratedDeps()...)
-					dirs := genRule.GeneratedHeaderDirs()
+						genRule.GeneratedDeps...)
+					dirs := genRule.GeneratedHeaderDirs
 					depPaths.IncludeDirs = append(depPaths.IncludeDirs, dirs...)
 					if depTag == genHeaderExportDepTag {
 						depPaths.ReexportedDirs = append(depPaths.ReexportedDirs, dirs...)
 						depPaths.ReexportedGeneratedHeaders = append(depPaths.ReexportedGeneratedHeaders,
-							genRule.GeneratedSourceFiles()...)
-						depPaths.ReexportedDeps = append(depPaths.ReexportedDeps, genRule.GeneratedDeps()...)
+							genRule.GeneratedSourceFiles...)
+						depPaths.ReexportedDeps = append(depPaths.ReexportedDeps, genRule.GeneratedDeps...)
 						// Add these re-exported flags to help header-abi-dumper to infer the abi exported by a library.
 						c.sabi.Properties.ReexportedIncludes = append(c.sabi.Properties.ReexportedIncludes, dirs.Strings()...)
-
 					}
 				} else {
 					ctx.ModuleErrorf("module %q is not a genrule", depName)
@@ -3141,13 +3238,14 @@
 			return
 		}
 
-		if dep.Target().Os != ctx.Os() {
+		commonInfo := android.OtherModuleProviderOrDefault(ctx, dep, android.CommonModuleInfoKey)
+		if commonInfo.Target.Os != ctx.Os() {
 			ctx.ModuleErrorf("OS mismatch between %q (%s) and %q (%s)", ctx.ModuleName(), ctx.Os().Name, depName, dep.Target().Os.Name)
 			return
 		}
-		if dep.Target().Arch.ArchType != ctx.Arch().ArchType {
+		if commonInfo.Target.Arch.ArchType != ctx.Arch().ArchType {
 			ctx.ModuleErrorf("Arch mismatch between %q(%v) and %q(%v)",
-				ctx.ModuleName(), ctx.Arch().ArchType, depName, dep.Target().Arch.ArchType)
+				ctx.ModuleName(), ctx.Arch().ArchType, depName, commonInfo.Target.Arch.ArchType)
 			return
 		}
 
@@ -3172,7 +3270,7 @@
 			depPaths.LlndkSystemIncludeDirs = append(depPaths.LlndkSystemIncludeDirs, depExporterInfo.SystemIncludeDirs...)
 		}
 
-		linkFile := ccDep.OutputFile()
+		linkFile := linkableInfo.OutputFile
 
 		if libDepTag, ok := depTag.(libraryDependencyTag); ok {
 			// Only use static unwinder for legacy (min_sdk_version = 29) apexes (b/144430859)
@@ -3250,8 +3348,8 @@
 				}
 
 			case libDepTag.static():
-				if ccDep.RustLibraryInterface() {
-					rlibDep := RustRlibDep{LibPath: linkFile.Path(), CrateName: ccDep.CrateName(), LinkDirs: ccDep.ExportedCrateLinkDirs()}
+				if linkableInfo.RustLibraryInterface {
+					rlibDep := RustRlibDep{LibPath: linkFile.Path(), CrateName: linkableInfo.CrateName, LinkDirs: linkableInfo.ExportedCrateLinkDirs}
 					depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, rlibDep)
 					depPaths.IncludeDirs = append(depPaths.IncludeDirs, depExporterInfo.IncludeDirs...)
 					if libDepTag.wholeStatic {
@@ -3328,8 +3426,8 @@
 				}
 			}
 
-			if libDepTag.static() && !libDepTag.wholeStatic && !ccDep.RustLibraryInterface() {
-				if !ccDep.CcLibraryInterface() || !ccDep.Static() {
+			if libDepTag.static() && !libDepTag.wholeStatic && !linkableInfo.RustLibraryInterface {
+				if !linkableInfo.CcLibraryInterface || !linkableInfo.Static {
 					ctx.ModuleErrorf("module %q not a static library", depName)
 					return
 				}
@@ -3337,16 +3435,15 @@
 				// When combining coverage files for shared libraries and executables, coverage files
 				// in static libraries act as if they were whole static libraries. The same goes for
 				// source based Abi dump files.
-				if c, ok := ccDep.(*Module); ok {
-					staticLib := c.linker.(libraryInterface)
+				if hasCcInfo {
 					depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles,
-						staticLib.objs().coverageFiles...)
+						linkableInfo.CoverageFiles...)
 					depPaths.StaticLibObjs.sAbiDumpFiles = append(depPaths.StaticLibObjs.sAbiDumpFiles,
-						staticLib.objs().sAbiDumpFiles...)
+						linkableInfo.SAbiDumpFiles...)
 				} else {
 					// Handle non-CC modules here
 					depPaths.StaticLibObjs.coverageFiles = append(depPaths.StaticLibObjs.coverageFiles,
-						ccDep.CoverageFiles()...)
+						linkableInfo.CoverageFiles...)
 				}
 			}
 
@@ -3392,7 +3489,7 @@
 					c.sabi.Properties.ReexportedSystemIncludes, depExporterInfo.SystemIncludeDirs.Strings()...)
 			}
 
-			makeLibName := MakeLibName(ctx, c, ccDep, ccDep.BaseModuleName()) + libDepTag.makeSuffix
+			makeLibName := MakeLibName(ccInfo, linkableInfo, &commonInfo, linkableInfo.BaseModuleName) + libDepTag.makeSuffix
 			switch {
 			case libDepTag.header():
 				c.Properties.AndroidMkHeaderLibs = append(
@@ -3403,7 +3500,7 @@
 				c.Properties.AndroidMkSharedLibs = append(
 					c.Properties.AndroidMkSharedLibs, makeLibName)
 			case libDepTag.static():
-				if !ccDep.RustLibraryInterface() {
+				if !linkableInfo.RustLibraryInterface {
 					if libDepTag.wholeStatic {
 						c.Properties.AndroidMkWholeStaticLibs = append(
 							c.Properties.AndroidMkWholeStaticLibs, makeLibName)
@@ -3419,7 +3516,7 @@
 			switch depTag {
 			case runtimeDepTag:
 				c.Properties.AndroidMkRuntimeLibs = append(
-					c.Properties.AndroidMkRuntimeLibs, MakeLibName(ctx, c, ccDep, ccDep.BaseModuleName())+libDepTag.makeSuffix)
+					c.Properties.AndroidMkRuntimeLibs, MakeLibName(ccInfo, linkableInfo, &commonInfo, linkableInfo.BaseModuleName)+libDepTag.makeSuffix)
 			case objDepTag:
 				depPaths.Objs.objFiles = append(depPaths.Objs.objFiles, linkFile.Path())
 			case CrtBeginDepTag:
@@ -3473,7 +3570,7 @@
 
 	useStubs := false
 
-	if lib := moduleLibraryInterface(dep); lib.buildStubs() && inVendorOrProduct { // LLNDK
+	if android.OtherModuleProviderOrDefault(ctx, dep, LinkableInfoProvider).IsStubs && inVendorOrProduct { // LLNDK
 		if !apexInfo.IsForPlatform() {
 			// For platform libraries, use current version of LLNDK
 			// If this is for use_vendor apex we will apply the same rules
@@ -3485,7 +3582,7 @@
 		// platform APIs, use stubs only when it is from an APEX (and not from
 		// platform) However, for host, ramdisk, vendor_ramdisk, recovery or
 		// bootstrap modules, always link to non-stub variant
-		isNotInPlatform := dep.(android.ApexModule).NotInPlatform()
+		isNotInPlatform := android.OtherModuleProviderOrDefault(ctx, dep, android.CommonModuleInfoKey).NotInPlatform
 
 		useStubs = isNotInPlatform && !bootstrap
 	} else {
@@ -3564,32 +3661,29 @@
 	return libName
 }
 
-func MakeLibName(ctx android.ModuleContext, c LinkableInterface, ccDep LinkableInterface, depName string) string {
+func MakeLibName(ccInfo *CcInfo, linkableInfo *LinkableInfo, commonInfo *android.CommonModuleInfo, depName string) string {
 	libName := BaseLibName(depName)
-	ccDepModule, _ := ccDep.(*Module)
-	isLLndk := ccDepModule != nil && ccDepModule.IsLlndk()
-	nonSystemVariantsExist := ccDep.HasNonSystemVariants() || isLLndk
+	isLLndk := ccInfo != nil && linkableInfo.IsLlndk
+	nonSystemVariantsExist := linkableInfo.HasNonSystemVariants || isLLndk
 
-	if ccDepModule != nil {
+	if ccInfo != nil {
 		// Use base module name for snapshots when exporting to Makefile.
-		if snapshotPrebuilt, ok := ccDepModule.linker.(SnapshotInterface); ok {
-			baseName := ccDepModule.BaseModuleName()
-
-			return baseName + snapshotPrebuilt.SnapshotAndroidMkSuffix()
+		if ccInfo.SnapshotInfo != nil {
+			return linkableInfo.BaseModuleName + ccInfo.SnapshotInfo.SnapshotAndroidMkSuffix
 		}
 	}
 
-	if ccDep.InVendorOrProduct() && nonSystemVariantsExist {
+	if linkableInfo.InVendorOrProduct && nonSystemVariantsExist {
 		// The vendor and product modules in Make will have been renamed to not conflict with the
 		// core module, so update the dependency name here accordingly.
-		return libName + ccDep.SubName()
-	} else if ccDep.InRamdisk() && !ccDep.OnlyInRamdisk() {
+		return libName + linkableInfo.SubName
+	} else if linkableInfo.InRamdisk && !linkableInfo.OnlyInRamdisk {
 		return libName + RamdiskSuffix
-	} else if ccDep.InVendorRamdisk() && !ccDep.OnlyInVendorRamdisk() {
+	} else if linkableInfo.InVendorRamdisk && !linkableInfo.OnlyInVendorRamdisk {
 		return libName + VendorRamdiskSuffix
-	} else if ccDep.InRecovery() && !ccDep.OnlyInRecovery() {
+	} else if linkableInfo.InRecovery && !linkableInfo.OnlyInRecovery {
 		return libName + RecoverySuffix
-	} else if ccDep.Target().NativeBridge == android.NativeBridgeEnabled {
+	} else if commonInfo.Target.NativeBridge == android.NativeBridgeEnabled {
 		return libName + NativeBridgeSuffix
 	} else {
 		return libName
diff --git a/cc/cmake_snapshot.go b/cc/cmake_snapshot.go
index a40b863..71fbcce 100644
--- a/cc/cmake_snapshot.go
+++ b/cc/cmake_snapshot.go
@@ -393,7 +393,7 @@
 		}{
 			&ctx,
 			dep,
-			&ccInfo,
+			ccInfo,
 			m,
 			&pprop,
 		})
diff --git a/cc/config/darwin_host.go b/cc/config/darwin_host.go
index 1783f49..716965a 100644
--- a/cc/config/darwin_host.go
+++ b/cc/config/darwin_host.go
@@ -54,6 +54,7 @@
 		"12",
 		"13",
 		"14",
+		"15",
 	}
 
 	darwinAvailableLibraries = append(
diff --git a/cc/config/global.go b/cc/config/global.go
index b19682d..d969e97 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -366,8 +366,6 @@
 		"-Wno-array-parameter",
 		"-Wno-gnu-offsetof-extensions",
 		"-Wno-pessimizing-move",
-		// TODO: Enable this warning http://b/315245071
-		"-Wno-fortify-source",
 	}
 
 	llvmNextExtraCommonGlobalCflags = []string{
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index 1f6cf23..505ddfa 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -112,6 +112,7 @@
 		"imagehlp",
 		"iphlpapi",
 		"netapi32",
+		"ntdll",
 		"oleaut32",
 		"ole32",
 		"opengl32",
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 911a81c..056b0da 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -211,29 +211,30 @@
 	moduleInfoJSON.Class = []string{"EXECUTABLES"}
 }
 
-// IsValidSharedDependency takes a module and determines if it is a unique shared library
+// isValidSharedDependency takes a module and determines if it is a unique shared library
 // that should be installed in the fuzz target output directories. This function
 // returns true, unless:
 //   - The module is not an installable shared library, or
 //   - The module is a header or stub, or
 //   - The module is a prebuilt and its source is available, or
 //   - The module is a versioned member of an SDK snapshot.
-func IsValidSharedDependency(dependency android.Module) bool {
+func isValidSharedDependency(ctx android.ModuleContext, dependency android.ModuleProxy) bool {
 	// TODO(b/144090547): We should be parsing these modules using
 	// ModuleDependencyTag instead of the current brute-force checking.
 
-	linkable, ok := dependency.(LinkableInterface)
-	if !ok || !linkable.CcLibraryInterface() {
+	linkable, ok := android.OtherModuleProvider(ctx, dependency, LinkableInfoProvider)
+	if !ok || !linkable.CcLibraryInterface {
 		// Discard non-linkables.
 		return false
 	}
 
-	if !linkable.Shared() {
+	if !linkable.Shared {
 		// Discard static libs.
 		return false
 	}
 
-	if lib := moduleLibraryInterface(dependency); lib != nil && lib.buildStubs() && linkable.CcLibrary() {
+	ccInfo, hasCcInfo := android.OtherModuleProvider(ctx, dependency, CcInfoProvider)
+	if hasCcInfo && ccInfo.LibraryInfo != nil && ccInfo.LibraryInfo.BuildStubs && linkable.CcLibrary {
 		// Discard stubs libs (only CCLibrary variants). Prebuilt libraries should not
 		// be excluded on the basis of they're not CCLibrary()'s.
 		return false
@@ -242,13 +243,13 @@
 	// We discarded module stubs libraries above, but the LLNDK prebuilts stubs
 	// libraries must be handled differently - by looking for the stubDecorator.
 	// Discard LLNDK prebuilts stubs as well.
-	if ccLibrary, isCcLibrary := dependency.(*Module); isCcLibrary {
-		if _, isLLndkStubLibrary := ccLibrary.linker.(*stubDecorator); isLLndkStubLibrary {
+	if hasCcInfo {
+		if ccInfo.LinkerInfo.StubDecoratorInfo != nil {
 			return false
 		}
 		// Discard installable:false libraries because they are expected to be absent
 		// in runtime.
-		if !proptools.BoolDefault(ccLibrary.Installable(), true) {
+		if !proptools.BoolDefault(linkable.Installable, true) {
 			return false
 		}
 	}
@@ -256,7 +257,7 @@
 	// If the same library is present both as source and a prebuilt we must pick
 	// only one to avoid a conflict. Always prefer the source since the prebuilt
 	// probably won't be built with sanitizers enabled.
-	if prebuilt := android.GetEmbeddedPrebuilt(dependency); prebuilt != nil && prebuilt.SourceExists() {
+	if prebuilt, ok := android.OtherModuleProvider(ctx, dependency, android.PrebuiltModuleInfoProvider); ok && prebuilt.SourceExists {
 		return false
 	}
 
@@ -607,17 +608,17 @@
 // VisitDirectDeps is used first to avoid incorrectly using the core libraries (sanitizer
 // runtimes, libc, libdl, etc.) from a dependency. This may cause issues when dependencies
 // have explicit sanitizer tags, as we may get a dependency on an unsanitized libc, etc.
-func CollectAllSharedDependencies(ctx android.ModuleContext) (android.RuleBuilderInstalls, []android.Module) {
+func CollectAllSharedDependencies(ctx android.ModuleContext) (android.RuleBuilderInstalls, []android.ModuleProxy) {
 	seen := make(map[string]bool)
 	recursed := make(map[string]bool)
-	deps := []android.Module{}
+	deps := []android.ModuleProxy{}
 
 	var sharedLibraries android.RuleBuilderInstalls
 
 	// Enumerate the first level of dependencies, as we discard all non-library
 	// modules in the BFS loop below.
-	ctx.VisitDirectDeps(func(dep android.Module) {
-		if !IsValidSharedDependency(dep) {
+	ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
+		if !isValidSharedDependency(ctx, dep) {
 			return
 		}
 		sharedLibraryInfo, hasSharedLibraryInfo := android.OtherModuleProvider(ctx, dep, SharedLibraryInfoProvider)
@@ -635,19 +636,21 @@
 		sharedLibraries = append(sharedLibraries, ruleBuilderInstall)
 	})
 
-	ctx.WalkDeps(func(child, parent android.Module) bool {
+	ctx.WalkDepsProxy(func(child, _ android.ModuleProxy) bool {
 
 		// If this is a Rust module which is not rust_ffi_shared, we still want to bundle any transitive
 		// shared dependencies (even for rust_ffi_static)
-		if rustmod, ok := child.(LinkableInterface); ok && rustmod.RustLibraryInterface() && !rustmod.Shared() {
-			if recursed[ctx.OtherModuleName(child)] {
-				return false
+		if info, ok := android.OtherModuleProvider(ctx, child, LinkableInfoProvider); ok {
+			if info.RustLibraryInterface && !info.Shared {
+				if recursed[ctx.OtherModuleName(child)] {
+					return false
+				}
+				recursed[ctx.OtherModuleName(child)] = true
+				return true
 			}
-			recursed[ctx.OtherModuleName(child)] = true
-			return true
 		}
 
-		if !IsValidSharedDependency(child) {
+		if !isValidSharedDependency(ctx, child) {
 			return false
 		}
 		sharedLibraryInfo, hasSharedLibraryInfo := android.OtherModuleProvider(ctx, child, SharedLibraryInfoProvider)
diff --git a/cc/library.go b/cc/library.go
index c9114fd..0566182 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -1195,7 +1195,7 @@
 	library.linkSAbiDumpFiles(ctx, deps, objs, fileName, unstrippedOutputFile)
 
 	var transitiveStaticLibrariesForOrdering depset.DepSet[android.Path]
-	if static := ctx.GetDirectDepsWithTag(staticVariantTag); len(static) > 0 {
+	if static := ctx.GetDirectDepsProxyWithTag(staticVariantTag); len(static) > 0 {
 		s, _ := android.OtherModuleProvider(ctx, static[0], StaticLibraryInfoProvider)
 		transitiveStaticLibrariesForOrdering = s.TransitiveStaticLibrariesForOrdering
 	}
@@ -1225,12 +1225,11 @@
 				continue
 			}
 			flagInfo, _ := android.OtherModuleProvider(ctx, stub, FlagExporterInfoProvider)
-			ccInfo, ok := android.OtherModuleProvider(ctx, stub, CcInfoProvider)
-			if !ok || ccInfo.LibraryInfo == nil {
-				panic(fmt.Errorf("couldn't find library info for %s", stub))
+			if _, ok = android.OtherModuleProvider(ctx, stub, CcInfoProvider); !ok {
+				panic(fmt.Errorf("stub is not a cc module %s", stub))
 			}
 			stubsInfo = append(stubsInfo, SharedStubLibrary{
-				Version:           ccInfo.LibraryInfo.StubsVersion,
+				Version:           android.OtherModuleProviderOrDefault(ctx, stub, LinkableInfoProvider).StubsVersion,
 				SharedLibraryInfo: stubInfo,
 				FlagExporterInfo:  flagInfo,
 			})
@@ -1387,6 +1386,11 @@
 		extraFlags = append(extraFlags,
 			"-allow-unreferenced-changes",
 			"-allow-unreferenced-elf-symbol-changes")
+		// The functions in standard libraries are not always declared in the headers.
+		// Allow them to be added or removed without changing the symbols.
+		if isBionic(ctx.ModuleName()) {
+			extraFlags = append(extraFlags, "-allow-adding-removing-referenced-apis")
+		}
 	}
 	if isLlndk {
 		extraFlags = append(extraFlags, "-consider-opaque-types-different")
@@ -2409,13 +2413,11 @@
 	inject *bool, fileName string) android.ModuleOutPath {
 	// TODO(b/137267623): Remove this in favor of a cc_genrule when they support operating on shared libraries.
 	injectBoringSSLHash := Bool(inject)
-	ctx.VisitDirectDeps(func(dep android.Module) {
+	ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
 		if tag, ok := ctx.OtherModuleDependencyTag(dep).(libraryDependencyTag); ok && tag.static() {
-			if cc, ok := dep.(*Module); ok {
-				if library, ok := cc.linker.(*libraryDecorator); ok {
-					if Bool(library.Properties.Inject_bssl_hash) {
-						injectBoringSSLHash = true
-					}
+			if ccInfo, ok := android.OtherModuleProvider(ctx, dep, CcInfoProvider); ok && ccInfo.LinkerInfo.LibraryDecoratorInfo != nil {
+				if ccInfo.LinkerInfo.LibraryDecoratorInfo.InjectBsslHash {
+					injectBoringSSLHash = true
 				}
 			}
 		}
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 197a4b2..27a9f66 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -302,12 +302,11 @@
 		ctx.ModuleErrorf("Could not find implementation for stub: ")
 		return nil
 	}
-	info, ok := android.OtherModuleProvider(ctx, *dep, CcInfoProvider)
-	if !ok {
+	if _, ok := android.OtherModuleProvider(ctx, *dep, CcInfoProvider); !ok {
 		ctx.ModuleErrorf("Implementation for stub is not correct module type")
 		return nil
 	}
-	output := info.LinkerInfo.UnstrippedOutputFile
+	output := android.OtherModuleProviderOrDefault(ctx, *dep, LinkableInfoProvider).UnstrippedOutputFile
 	if output == nil {
 		ctx.ModuleErrorf("implementation module (%s) has no output", *dep)
 		return nil
diff --git a/cc/stub_library.go b/cc/stub_library.go
index 5911be0..75d649f 100644
--- a/cc/stub_library.go
+++ b/cc/stub_library.go
@@ -38,8 +38,8 @@
 }
 
 // Check if the module defines stub, or itself is stub
-func IsStubTarget(m *Module) bool {
-	return m.IsStubs() || m.HasStubsVariants()
+func IsStubTarget(info *LinkableInfo) bool {
+	return info != nil && (info.IsStubs || info.HasStubsVariants)
 }
 
 // Get target file name to be installed from this module
@@ -59,7 +59,7 @@
 	vendorStubLibraryMap := make(map[string]bool)
 	ctx.VisitAllModules(func(module android.Module) {
 		if m, ok := module.(*Module); ok {
-			if IsStubTarget(m) {
+			if IsStubTarget(android.OtherModuleProviderOrDefault(ctx, m, LinkableInfoProvider)) {
 				if name := getInstalledFileName(ctx, m); name != "" {
 					stubLibraryMap[name] = true
 					if m.InVendor() {
diff --git a/cc/test.go b/cc/test.go
index abec19a..abf9162 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -342,28 +342,28 @@
 		test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
 	}
 
-	ctx.VisitDirectDepsWithTag(dataLibDepTag, func(dep android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(dataLibDepTag, func(dep android.ModuleProxy) {
 		depName := ctx.OtherModuleName(dep)
-		linkableDep, ok := dep.(LinkableInterface)
+		linkableDep, ok := android.OtherModuleProvider(ctx, dep, LinkableInfoProvider)
 		if !ok {
 			ctx.ModuleErrorf("data_lib %q is not a LinkableInterface module", depName)
 		}
-		if linkableDep.OutputFile().Valid() {
+		if linkableDep.OutputFile.Valid() {
 			test.data = append(test.data,
-				android.DataPath{SrcPath: linkableDep.OutputFile().Path(),
-					RelativeInstallPath: linkableDep.RelativeInstallPath()})
+				android.DataPath{SrcPath: linkableDep.OutputFile.Path(),
+					RelativeInstallPath: linkableDep.RelativeInstallPath})
 		}
 	})
-	ctx.VisitDirectDepsWithTag(dataBinDepTag, func(dep android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(dataBinDepTag, func(dep android.ModuleProxy) {
 		depName := ctx.OtherModuleName(dep)
-		linkableDep, ok := dep.(LinkableInterface)
+		linkableDep, ok := android.OtherModuleProvider(ctx, dep, LinkableInfoProvider)
 		if !ok {
 			ctx.ModuleErrorf("data_bin %q is not a LinkableInterface module", depName)
 		}
-		if linkableDep.OutputFile().Valid() {
+		if linkableDep.OutputFile.Valid() {
 			test.data = append(test.data,
-				android.DataPath{SrcPath: linkableDep.OutputFile().Path(),
-					RelativeInstallPath: linkableDep.RelativeInstallPath()})
+				android.DataPath{SrcPath: linkableDep.OutputFile.Path(),
+					RelativeInstallPath: linkableDep.RelativeInstallPath})
 		}
 	})
 
diff --git a/cc/tidy.go b/cc/tidy.go
index 18e6f35..2373658 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -220,7 +220,7 @@
 
 	// (1) Collect all obj/tidy files into OS-specific groups.
 	ctx.VisitAllModuleVariantProxies(module, func(variant android.ModuleProxy) {
-		osName := android.OtherModuleProviderOrDefault(ctx, variant, android.CommonModuleInfoKey).CompileTarget.Os.Name
+		osName := android.OtherModuleProviderOrDefault(ctx, variant, android.CommonModuleInfoKey).Target.Os.Name
 		info := android.OtherModuleProviderOrDefault(ctx, variant, CcObjectInfoProvider)
 		addToOSGroup(osName, info.ObjFiles, allObjFileGroups, subsetObjFileGroups)
 		addToOSGroup(osName, info.TidyFiles, allTidyFileGroups, subsetTidyFileGroups)
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 6642023..cd4e9bd 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -64,6 +64,7 @@
 	flag.StringVar(&usedEnvFile, "used_env", "", "File containing used environment variables")
 	flag.StringVar(&cmdlineArgs.OutDir, "out", "", "the ninja builddir directory")
 	flag.StringVar(&cmdlineArgs.ModuleListFile, "l", "", "file that lists filepaths to parse")
+	flag.StringVar(&cmdlineArgs.KatiSuffix, "kati_suffix", "", "the suffix for kati and ninja files, so that different configurations don't clobber each other")
 
 	// Debug flags
 	flag.StringVar(&delveListen, "delve_listen", "", "Delve port to listen on for debugging")
diff --git a/filesystem/android_device.go b/filesystem/android_device.go
index 19e93ae..6a939e8 100644
--- a/filesystem/android_device.go
+++ b/filesystem/android_device.go
@@ -15,6 +15,9 @@
 package filesystem
 
 import (
+	"strings"
+	"sync/atomic"
+
 	"android/soong/android"
 
 	"github.com/google/blueprint"
@@ -44,21 +47,41 @@
 	Vbmeta_partitions []string
 	// Name of the userdata partition filesystem module
 	Userdata_partition_name *string
+	// Name of the system_dlkm partition filesystem module
+	System_dlkm_partition_name *string
+	// Name of the vendor_dlkm partition filesystem module
+	Vendor_dlkm_partition_name *string
+	// Name of the odm_dlkm partition filesystem module
+	Odm_dlkm_partition_name *string
+}
+
+type DeviceProperties struct {
+	// Path to the prebuilt bootloader that would be copied to PRODUCT_OUT
+	Bootloader *string `android:"path"`
+	// Path to android-info.txt file containing board specific info.
+	Android_info *string `android:"path"`
 }
 
 type androidDevice struct {
 	android.ModuleBase
 
 	partitionProps PartitionNameProperties
+
+	deviceProps DeviceProperties
+
+	// copyToProductOutTimestamp for copying necessary files to PRODUCT_OUT
+	copyToProductOutTimestamp android.WritablePath
 }
 
 func AndroidDeviceFactory() android.Module {
 	module := &androidDevice{}
-	module.AddProperties(&module.partitionProps)
-	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	module.AddProperties(&module.partitionProps, &module.deviceProps)
+	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibFirst)
 	return module
 }
 
+var numAutogeneratedAndroidDevicesOnceKey android.OnceKey = android.NewOnceKey("num_auto_generated_anroid_devices")
+
 type partitionDepTagType struct {
 	blueprint.BaseDependencyTag
 }
@@ -68,24 +91,95 @@
 func (a *androidDevice) DepsMutator(ctx android.BottomUpMutatorContext) {
 	addDependencyIfDefined := func(dep *string) {
 		if dep != nil {
-			ctx.AddFarVariationDependencies(nil, filesystemDepTag, proptools.String(dep))
+			ctx.AddDependency(ctx.Module(), filesystemDepTag, proptools.String(dep))
 		}
 	}
 
 	addDependencyIfDefined(a.partitionProps.Boot_partition_name)
+	addDependencyIfDefined(a.partitionProps.Init_boot_partition_name)
+	addDependencyIfDefined(a.partitionProps.Vendor_boot_partition_name)
 	addDependencyIfDefined(a.partitionProps.System_partition_name)
 	addDependencyIfDefined(a.partitionProps.System_ext_partition_name)
 	addDependencyIfDefined(a.partitionProps.Product_partition_name)
 	addDependencyIfDefined(a.partitionProps.Vendor_partition_name)
 	addDependencyIfDefined(a.partitionProps.Odm_partition_name)
 	addDependencyIfDefined(a.partitionProps.Userdata_partition_name)
+	addDependencyIfDefined(a.partitionProps.System_dlkm_partition_name)
+	addDependencyIfDefined(a.partitionProps.Vendor_dlkm_partition_name)
+	addDependencyIfDefined(a.partitionProps.Odm_dlkm_partition_name)
+	addDependencyIfDefined(a.partitionProps.Recovery_partition_name)
 	for _, vbmetaPartition := range a.partitionProps.Vbmeta_partitions {
 		ctx.AddDependency(ctx.Module(), filesystemDepTag, vbmetaPartition)
 	}
 }
 
+func (a *androidDevice) copyToProductOut(ctx android.ModuleContext, builder *android.RuleBuilder, src android.Path, dest string) {
+	destPath := android.PathForModuleInPartitionInstall(ctx, "").Join(ctx, dest)
+	builder.Command().Text("rsync").Flag("-a").Flag("--checksum").Input(src).Text(destPath.String())
+}
+
+func (a *androidDevice) copyFilesToProductOut(ctx android.ModuleContext) {
+	a.copyToProductOutTimestamp = android.PathForModuleOut(ctx, "timestamp")
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().Text("touch").Output(a.copyToProductOutTimestamp)
+
+	// List all individual files to be copied to PRODUCT_OUT here
+	if a.deviceProps.Bootloader != nil {
+		a.copyToProductOut(ctx, builder, android.PathForModuleSrc(ctx, proptools.String(a.deviceProps.Bootloader)), "bootloader")
+	}
+
+	builder.Build("copy_to_product_out", "Copy files to PRODUCT_OUT")
+}
+
 func (a *androidDevice) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.buildTargetFilesZip(ctx)
+	var deps []android.Path
+	ctx.VisitDirectDepsWithTag(filesystemDepTag, func(m android.Module) {
+		imageOutput, ok := android.OtherModuleProvider(ctx, m, android.OutputFilesProvider)
+		if !ok {
+			ctx.ModuleErrorf("Partition module %s doesn't set OutputfilesProvider", m.Name())
+		}
+		if len(imageOutput.DefaultOutputFiles) != 1 {
+			ctx.ModuleErrorf("Partition module %s should provide exact 1 output file", m.Name())
+		}
+		deps = append(deps, imageOutput.DefaultOutputFiles[0])
+	})
+
+	a.copyFilesToProductOut(ctx)
+
+	allImagesStamp := android.PathForModuleOut(ctx, "all_images_stamp")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:       android.Touch,
+		Output:     allImagesStamp,
+		Implicits:  deps,
+		Validation: a.copyToProductOutTimestamp,
+	})
+	ctx.SetOutputFiles(android.Paths{allImagesStamp}, "")
+	ctx.CheckbuildFile(allImagesStamp)
+
+	if ctx.OtherModuleIsAutoGenerated(ctx.Module()) {
+		numAutogeneratedAndroidDevices := ctx.Config().Once(numAutogeneratedAndroidDevicesOnceKey, func() interface{} {
+			return &atomic.Int32{}
+		}).(*atomic.Int32)
+		total := numAutogeneratedAndroidDevices.Add(1)
+		if total > 1 {
+			// There should only be 1 autogenerated android_device module. That one will be
+			// made the default thing to build in soong-only builds.
+			ctx.ModuleErrorf("There cannot be more than 1 autogenerated android_device module")
+		}
+	}
+
+	if !ctx.Config().KatiEnabled() && ctx.OtherModuleIsAutoGenerated(ctx.Module()) {
+		// In soong-only builds, build this module by default.
+		// This is the analogue to this make code:
+		// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/main.mk;l=1396;drc=6595459cdd8164a6008335f6372c9f97b9094060
+		ctx.Phony("droidcore-unbundled", allImagesStamp)
+	}
+}
+
+type targetFilesZipCopy struct {
+	srcModule  *string
+	destSubdir string
 }
 
 func (a *androidDevice) buildTargetFilesZip(ctx android.ModuleContext) {
@@ -95,14 +189,88 @@
 	builder := android.NewRuleBuilder(pctx, ctx)
 	builder.Command().Textf("rm -rf %s", targetFilesDir.String())
 	builder.Command().Textf("mkdir -p %s", targetFilesDir.String())
-	if a.partitionProps.Vendor_partition_name != nil {
-		fsInfo := a.getFilesystemInfo(ctx, *a.partitionProps.Vendor_partition_name)
-		builder.Command().Textf("mkdir -p %s/VENDOR", targetFilesDir.String())
+	toCopy := []targetFilesZipCopy{
+		targetFilesZipCopy{a.partitionProps.System_partition_name, "SYSTEM"},
+		targetFilesZipCopy{a.partitionProps.System_ext_partition_name, "SYSTEM_EXT"},
+		targetFilesZipCopy{a.partitionProps.Product_partition_name, "PRODUCT"},
+		targetFilesZipCopy{a.partitionProps.Vendor_partition_name, "VENDOR"},
+		targetFilesZipCopy{a.partitionProps.Odm_partition_name, "ODM"},
+		targetFilesZipCopy{a.partitionProps.System_dlkm_partition_name, "SYSTEM_DLKM"},
+		targetFilesZipCopy{a.partitionProps.Vendor_dlkm_partition_name, "VENDOR_DLKM"},
+		targetFilesZipCopy{a.partitionProps.Odm_dlkm_partition_name, "ODM_DLKM"},
+		targetFilesZipCopy{a.partitionProps.Init_boot_partition_name, "BOOT/RAMDISK"},
+		targetFilesZipCopy{a.partitionProps.Init_boot_partition_name, "INIT_BOOT/RAMDISK"},
+		targetFilesZipCopy{a.partitionProps.Vendor_boot_partition_name, "VENDOR_BOOT/RAMDISK"},
+	}
+	// TODO: Handle cases where recovery files are copied to BOOT/ or RECOVERY/
+	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=6211-6219?q=core%2FMakefile&ss=android%2Fplatform%2Fsuperproject%2Fmain
+	if ctx.DeviceConfig().BoardMoveRecoveryResourcesToVendorBoot() {
+		toCopy = append(toCopy, targetFilesZipCopy{a.partitionProps.Recovery_partition_name, "VENDOR_BOOT/RAMDISK"})
+	}
+
+	// Create an IMAGES/ subdirectory
+	builder.Command().Textf("mkdir -p %s/IMAGES/", targetFilesDir.String())
+	if a.deviceProps.Bootloader != nil {
+		builder.Command().Textf("cp ").Input(android.PathForModuleSrc(ctx, proptools.String(a.deviceProps.Bootloader))).Textf(" %s/IMAGES/bootloader", targetFilesDir.String())
+	}
+
+	for _, zipCopy := range toCopy {
+		if zipCopy.srcModule == nil {
+			continue
+		}
+		fsInfo := a.getFilesystemInfo(ctx, *zipCopy.srcModule)
+		subdir := zipCopy.destSubdir
+		rootDirString := fsInfo.RootDir.String()
+		if subdir == "SYSTEM" {
+			rootDirString = rootDirString + "/system"
+		}
+		builder.Command().Textf("mkdir -p %s/%s", targetFilesDir.String(), subdir)
 		builder.Command().
 			BuiltTool("acp").
-			Textf("-rd %s/. %s/VENDOR", fsInfo.RootDir, targetFilesDir).
+			Textf("-rd %s/. %s/%s", rootDirString, targetFilesDir, subdir).
 			Implicit(fsInfo.Output) // so that the staging dir is built
+
+		if subdir == "SYSTEM" {
+			// Create the ROOT partition in target_files.zip
+			builder.Command().Textf("rsync --links --exclude=system/* %s/ -r %s/ROOT", fsInfo.RootDir, targetFilesDir.String())
+		}
+		builder.Command().Textf("cp %s %s/IMAGES/", fsInfo.Output, targetFilesDir.String())
 	}
+	// Copy cmdline, kernel etc. files of boot images
+	if a.partitionProps.Vendor_boot_partition_name != nil {
+		bootImg := ctx.GetDirectDepWithTag(proptools.String(a.partitionProps.Vendor_boot_partition_name), filesystemDepTag)
+		bootImgInfo, _ := android.OtherModuleProvider(ctx, bootImg, BootimgInfoProvider)
+		builder.Command().Textf("echo %s > %s/VENDOR_BOOT/cmdline", proptools.ShellEscape(strings.Join(bootImgInfo.Cmdline, " ")), targetFilesDir)
+		builder.Command().Textf("echo %s > %s/VENDOR_BOOT/vendor_cmdline", proptools.ShellEscape(strings.Join(bootImgInfo.Cmdline, " ")), targetFilesDir)
+		if bootImgInfo.Dtb != nil {
+			builder.Command().Textf("cp %s %s/VENDOR_BOOT/dtb", bootImgInfo.Dtb, targetFilesDir)
+		}
+		if bootImgInfo.Bootconfig != nil {
+			builder.Command().Textf("cp %s %s/VENDOR_BOOT/vendor_bootconfig", bootImgInfo.Bootconfig, targetFilesDir)
+		}
+	}
+	if a.partitionProps.Boot_partition_name != nil {
+		bootImg := ctx.GetDirectDepWithTag(proptools.String(a.partitionProps.Boot_partition_name), filesystemDepTag)
+		bootImgInfo, _ := android.OtherModuleProvider(ctx, bootImg, BootimgInfoProvider)
+		builder.Command().Textf("echo %s > %s/BOOT/cmdline", proptools.ShellEscape(strings.Join(bootImgInfo.Cmdline, " ")), targetFilesDir)
+		if bootImgInfo.Dtb != nil {
+			builder.Command().Textf("cp %s %s/BOOT/dtb", bootImgInfo.Dtb, targetFilesDir)
+		}
+		if bootImgInfo.Kernel != nil {
+			builder.Command().Textf("cp %s %s/BOOT/kernel", bootImgInfo.Kernel, targetFilesDir)
+			// Even though kernel is not used to build vendor_boot, copy the kernel to VENDOR_BOOT to match the behavior of make packaging.
+			builder.Command().Textf("cp %s %s/VENDOR_BOOT/kernel", bootImgInfo.Kernel, targetFilesDir)
+		}
+		if bootImgInfo.Bootconfig != nil {
+			builder.Command().Textf("cp %s %s/BOOT/bootconfig", bootImgInfo.Bootconfig, targetFilesDir)
+		}
+	}
+
+	if a.deviceProps.Android_info != nil {
+		builder.Command().Textf("mkdir -p %s/OTA", targetFilesDir)
+		builder.Command().Textf("cp %s %s/OTA/android-info.txt", android.PathForModuleSrc(ctx, proptools.String(a.deviceProps.Android_info)), targetFilesDir)
+	}
+
 	builder.Command().
 		BuiltTool("soong_zip").
 		Text("-d").
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index 36b1a18..0a3a177 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -197,12 +197,8 @@
 		ctx.PropertyErrorf("kernel_prebuilt", "boot partition must have kernel")
 		return
 	}
-	var kernel android.Path
-	if kernelProp != "" {
-		kernel = android.PathForModuleSrc(ctx, kernelProp)
-	}
 
-	unsignedOutput := b.buildBootImage(ctx, kernel)
+	unsignedOutput := b.buildBootImage(ctx, b.getKernelPath(ctx))
 
 	output := unsignedOutput
 	if proptools.Bool(b.properties.Use_avb) {
@@ -213,7 +209,7 @@
 		case "default":
 			output = b.signImage(ctx, unsignedOutput)
 		case "make_legacy":
-			output = b.addAvbFooter(ctx, unsignedOutput, kernel)
+			output = b.addAvbFooter(ctx, unsignedOutput, b.getKernelPath(ctx))
 		default:
 			ctx.PropertyErrorf("avb_mode", `Unknown value for avb_mode, expected "default" or "make_legacy", got: %q`, *b.properties.Avb_mode)
 		}
@@ -224,6 +220,58 @@
 
 	ctx.SetOutputFiles([]android.Path{output}, "")
 	b.output = output
+
+	// Set the Filesystem info of the ramdisk dependency.
+	// `android_device` will use this info to package `target_files.zip`
+	if ramdisk := proptools.String(b.properties.Ramdisk_module); ramdisk != "" {
+		ramdiskModule := ctx.GetDirectDepWithTag(ramdisk, bootimgRamdiskDep)
+		fsInfo, _ := android.OtherModuleProvider(ctx, ramdiskModule, FilesystemProvider)
+		android.SetProvider(ctx, FilesystemProvider, fsInfo)
+	}
+
+	// Set BootimgInfo for building target_files.zip
+	android.SetProvider(ctx, BootimgInfoProvider, BootimgInfo{
+		Cmdline:    b.properties.Cmdline,
+		Kernel:     b.getKernelPath(ctx),
+		Dtb:        b.getDtbPath(ctx),
+		Bootconfig: b.getBootconfigPath(ctx),
+	})
+}
+
+var BootimgInfoProvider = blueprint.NewProvider[BootimgInfo]()
+
+type BootimgInfo struct {
+	Cmdline    []string
+	Kernel     android.Path
+	Dtb        android.Path
+	Bootconfig android.Path
+}
+
+func (b *bootimg) getKernelPath(ctx android.ModuleContext) android.Path {
+	var kernelPath android.Path
+	kernelName := proptools.String(b.properties.Kernel_prebuilt)
+	if kernelName != "" {
+		kernelPath = android.PathForModuleSrc(ctx, kernelName)
+	}
+	return kernelPath
+}
+
+func (b *bootimg) getDtbPath(ctx android.ModuleContext) android.Path {
+	var dtbPath android.Path
+	dtbName := proptools.String(b.properties.Dtb_prebuilt)
+	if dtbName != "" {
+		dtbPath = android.PathForModuleSrc(ctx, dtbName)
+	}
+	return dtbPath
+}
+
+func (b *bootimg) getBootconfigPath(ctx android.ModuleContext) android.Path {
+	var bootconfigPath android.Path
+	bootconfigName := proptools.String(b.properties.Bootconfig)
+	if bootconfigName != "" {
+		bootconfigPath = android.PathForModuleSrc(ctx, bootconfigName)
+	}
+	return bootconfigPath
 }
 
 func (b *bootimg) buildBootImage(ctx android.ModuleContext, kernel android.Path) android.Path {
@@ -242,10 +290,8 @@
 		cmd.FlagWithArg("--os_patch_level ", ctx.Config().PlatformSecurityPatch())
 	}
 
-	dtbName := proptools.String(b.properties.Dtb_prebuilt)
-	if dtbName != "" {
-		dtb := android.PathForModuleSrc(ctx, dtbName)
-		cmd.FlagWithInput("--dtb ", dtb)
+	if b.getDtbPath(ctx) != nil {
+		cmd.FlagWithInput("--dtb ", b.getDtbPath(ctx))
 	}
 
 	cmdline := strings.Join(b.properties.Cmdline, " ")
@@ -372,6 +418,10 @@
 		cmd.FlagWithArg("--rollback_index ", strconv.FormatInt(*b.properties.Avb_rollback_index, 10))
 	}
 
+	if !ctx.Config().KatiEnabled() {
+		copyImageFileToProductOut(ctx, builder, b.bootImageType.String(), output)
+	}
+
 	builder.Build("add_avb_footer", fmt.Sprintf("Adding avb footer to %s", b.BaseModuleName()))
 	return output
 }
@@ -387,6 +437,10 @@
 		Implicits(toolDeps).
 		Output(output)
 
+	if !ctx.Config().KatiEnabled() {
+		copyImageFileToProductOut(ctx, builder, b.bootImageType.String(), output)
+	}
+
 	builder.Build("sign_bootimg", fmt.Sprintf("Signing %s", b.BaseModuleName()))
 	return output
 }
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 43fd390..993c46e 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -215,6 +215,9 @@
 
 	// Additional dependencies used for building android products
 	Android_filesystem_deps AndroidFilesystemDeps
+
+	// Name of the output. Default is $(module_name).img
+	Stem *string
 }
 
 type AndroidFilesystemDeps struct {
@@ -357,6 +360,14 @@
 
 var FilesystemProvider = blueprint.NewProvider[FilesystemInfo]()
 
+type FilesystemDefaultsInfo struct {
+	// Identifies which partition this is for //visibility:any_system_image (and others) visibility
+	// checks, and will be used in the future for API surface checks.
+	PartitionType string
+}
+
+var FilesystemDefaultsInfoProvider = blueprint.NewProvider[FilesystemDefaultsInfo]()
+
 func GetFsTypeFromString(ctx android.EarlyModuleContext, typeStr string) fsType {
 	switch typeStr {
 	case "ext4":
@@ -384,7 +395,7 @@
 }
 
 func (f *filesystem) installFileName() string {
-	return f.BaseModuleName() + ".img"
+	return proptools.StringDefault(f.properties.Stem, f.BaseModuleName()+".img")
 }
 
 func (f *filesystem) partitionName() string {
@@ -459,6 +470,7 @@
 		FileListFile: fileListFile,
 		RootDir:      rootDir,
 	})
+
 	f.fileListFile = fileListFile
 
 	if proptools.Bool(f.properties.Unchecked_module) {
@@ -524,12 +536,12 @@
 		ctx.PropertyErrorf("partition_type", "partition_type must be one of %s, found: %s", validPartitions, p.PartitionType())
 	}
 
-	ctx.VisitDirectDepsWithTag(android.DefaultsDepTag, func(m android.Module) {
-		if fdm, ok := m.(*filesystemDefaults); ok {
-			if p.PartitionType() != fdm.PartitionType() {
+	ctx.VisitDirectDepsProxyWithTag(android.DefaultsDepTag, func(m android.ModuleProxy) {
+		if fdm, ok := android.OtherModuleProvider(ctx, m, FilesystemDefaultsInfoProvider); ok {
+			if p.PartitionType() != fdm.PartitionType {
 				ctx.PropertyErrorf("partition_type",
 					"%s doesn't match with the partition type %s of the filesystem default module %s",
-					p.PartitionType(), fdm.PartitionType(), m.Name())
+					p.PartitionType(), fdm.PartitionType, m.Name())
 			}
 		}
 	})
@@ -593,11 +605,16 @@
 }
 
 func (f *filesystem) copyFilesToProductOut(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) {
-	if f.Name() != ctx.Config().SoongDefinedSystemImage() {
+	if !(f.Name() == ctx.Config().SoongDefinedSystemImage() || proptools.Bool(f.properties.Is_auto_generated)) {
 		return
 	}
 	installPath := android.PathForModuleInPartitionInstall(ctx, f.partitionName())
-	builder.Command().Textf("cp -prf %s/* %s", rebasedDir, installPath)
+	builder.Command().Textf("rsync --checksum %s %s", rebasedDir, installPath)
+}
+
+func copyImageFileToProductOut(ctx android.ModuleContext, builder *android.RuleBuilder, partition string, output android.Path) {
+	copyDir := android.PathForModuleInPartitionInstall(ctx, "").Join(ctx, fmt.Sprintf("%s.img", partition))
+	builder.Command().Textf("rsync -a %s %s", output, copyDir)
 }
 
 func (f *filesystem) rootDirString() string {
@@ -651,6 +668,10 @@
 		Output(output).
 		Text(rootDir.String()) // directory where to find fs_config_files|dirs
 
+	if !ctx.Config().KatiEnabled() {
+		copyImageFileToProductOut(ctx, builder, f.partitionName(), output)
+	}
+
 	// rootDir is not deleted. Might be useful for quick inspection.
 	builder.Build("build_filesystem_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
 
@@ -751,7 +772,10 @@
 	}
 	if timestamp := proptools.String(f.properties.Fake_timestamp); timestamp != "" {
 		addStr("timestamp", timestamp)
+	} else if ctx.Config().Getenv("USE_FIXED_TIMESTAMP_IMG_FILES") == "true" {
+		addStr("use_fixed_timestamp", "true")
 	}
+
 	if uuid := proptools.String(f.properties.Uuid); uuid != "" {
 		addStr("uuid", uuid)
 		addStr("hash_seed", uuid)
@@ -1063,6 +1087,9 @@
 
 func (f *filesystemDefaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	validatePartitionType(ctx, f)
+	android.SetProvider(ctx, FilesystemDefaultsInfoProvider, FilesystemDefaultsInfo{
+		PartitionType: f.PartitionType(),
+	})
 }
 
 // getLibsForLinkerConfig returns
@@ -1072,13 +1099,16 @@
 // `linkerconfig.BuildLinkerConfig` will convert these two to a linker.config.pb for the filesystem
 // (1) will be added to --provideLibs if they are C libraries with a stable interface (has stubs)
 // (2) will be added to --requireLibs if they are C libraries with a stable interface (has stubs)
-func (f *filesystem) getLibsForLinkerConfig(ctx android.ModuleContext) ([]android.Module, []android.Module) {
+func (f *filesystem) getLibsForLinkerConfig(ctx android.ModuleContext) ([]android.ModuleProxy, []android.ModuleProxy) {
 	// we need "Module"s for packaging items
-	modulesInPackageByModule := make(map[android.Module]bool)
+	modulesInPackageByModule := make(map[android.ModuleProxy]bool)
 	modulesInPackageByName := make(map[string]bool)
 
 	deps := f.gatherFilteredPackagingSpecs(ctx)
-	ctx.WalkDeps(func(child, parent android.Module) bool {
+	ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
+		if !android.OtherModuleProviderOrDefault(ctx, child, android.CommonModuleInfoKey).Enabled {
+			return false
+		}
 		for _, ps := range android.OtherModuleProviderOrDefault(
 			ctx, child, android.InstallFilesProvider).PackagingSpecs {
 			if _, ok := deps[ps.RelPathInPackage()]; ok && ps.Partition() == f.PartitionType() {
@@ -1090,13 +1120,16 @@
 		return true
 	})
 
-	provideModules := make([]android.Module, 0, len(modulesInPackageByModule))
+	provideModules := make([]android.ModuleProxy, 0, len(modulesInPackageByModule))
 	for mod := range modulesInPackageByModule {
 		provideModules = append(provideModules, mod)
 	}
 
-	var requireModules []android.Module
-	ctx.WalkDeps(func(child, parent android.Module) bool {
+	var requireModules []android.ModuleProxy
+	ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
+		if !android.OtherModuleProviderOrDefault(ctx, child, android.CommonModuleInfoKey).Enabled {
+			return false
+		}
 		_, parentInPackage := modulesInPackageByModule[parent]
 		_, childInPackageName := modulesInPackageByName[child.Name()]
 
diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go
index 6a47859..c8b4675 100644
--- a/filesystem/vbmeta.go
+++ b/filesystem/vbmeta.go
@@ -281,6 +281,10 @@
 		FlagWithArg("-s ", strconv.Itoa(vbmetaMaxSize)).
 		Output(output)
 
+	if !ctx.Config().KatiEnabled() {
+		copyImageFileToProductOut(ctx, builder, v.partitionName(), output)
+	}
+
 	builder.Build("vbmeta", fmt.Sprintf("vbmeta %s", ctx.ModuleName()))
 
 	v.installDir = android.PathForModuleInstall(ctx, "etc")
diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
index 0160159..a070e01 100644
--- a/fsgen/filesystem_creator.go
+++ b/fsgen/filesystem_creator.go
@@ -128,6 +128,8 @@
 			f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, partitionType)
 		}
 	}
+	// Create android_info.prop
+	f.createAndroidInfo(ctx)
 
 	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
 	dtbImg := createDtbImgFilegroup(ctx)
@@ -161,7 +163,7 @@
 
 	if buildingSuperImage(partitionVars) {
 		createSuperImage(ctx, finalSoongGeneratedPartitions, partitionVars)
-		f.properties.Super_image = ":" + generatedModuleName(ctx.Config(), "super")
+		f.properties.Super_image = ":" + generatedModuleNameForPartition(ctx.Config(), "super")
 	}
 
 	ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions = finalSoongGeneratedPartitions
@@ -180,15 +182,37 @@
 	return generatedModuleName(cfg, fmt.Sprintf("%s_image", partitionType))
 }
 
+func (f *filesystemCreator) createBootloaderFilegroup(ctx android.LoadHookContext) (string, bool) {
+	bootloaderPath := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.PrebuiltBootloader
+	if len(bootloaderPath) == 0 {
+		return "", false
+	}
+
+	bootloaderFilegroupName := generatedModuleName(ctx.Config(), "bootloader")
+	filegroupProps := &struct {
+		Name       *string
+		Srcs       []string
+		Visibility []string
+	}{
+		Name:       proptools.StringPtr(bootloaderFilegroupName),
+		Srcs:       []string{bootloaderPath},
+		Visibility: []string{"//visibility:public"},
+	}
+	ctx.CreateModuleInDirectory(android.FileGroupFactory, ".", filegroupProps)
+	return bootloaderFilegroupName, true
+}
+
 func (f *filesystemCreator) createDeviceModule(
 	ctx android.LoadHookContext,
 	generatedPartitionTypes []string,
 	vbmetaPartitions []string,
 ) {
 	baseProps := &struct {
-		Name *string
+		Name         *string
+		Android_info *string
 	}{
-		Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "device")),
+		Name:         proptools.StringPtr(generatedModuleName(ctx.Config(), "device")),
+		Android_info: proptools.StringPtr(":" + generatedModuleName(ctx.Config(), "android_info.prop{.txt}")),
 	}
 
 	// Currently, only the system and system_ext partition module is created.
@@ -214,6 +238,15 @@
 	if android.InList("recovery", f.properties.Generated_partition_types) {
 		partitionProps.Recovery_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "recovery"))
 	}
+	if android.InList("system_dlkm", f.properties.Generated_partition_types) {
+		partitionProps.System_dlkm_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_dlkm"))
+	}
+	if android.InList("vendor_dlkm", f.properties.Generated_partition_types) {
+		partitionProps.Vendor_dlkm_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_dlkm"))
+	}
+	if android.InList("odm_dlkm", f.properties.Generated_partition_types) {
+		partitionProps.Odm_dlkm_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "odm_dlkm"))
+	}
 	if f.properties.Boot_image != "" {
 		partitionProps.Boot_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "boot"))
 	}
@@ -225,7 +258,12 @@
 	}
 	partitionProps.Vbmeta_partitions = vbmetaPartitions
 
-	ctx.CreateModule(filesystem.AndroidDeviceFactory, baseProps, partitionProps)
+	deviceProps := &filesystem.DeviceProperties{}
+	if bootloader, ok := f.createBootloaderFilegroup(ctx); ok {
+		deviceProps.Bootloader = proptools.StringPtr(":" + bootloader)
+	}
+
+	ctx.CreateModule(filesystem.AndroidDeviceFactory, baseProps, partitionProps, deviceProps)
 }
 
 func partitionSpecificFsProps(ctx android.EarlyModuleContext, fsProps *filesystem.FilesystemProperties, partitionVars android.PartitionVariables, partitionType string) {
@@ -283,6 +321,7 @@
 		if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
 			fsProps.Import_aconfig_flags_from = []string{generatedModuleNameForPartition(ctx.Config(), "system_ext")}
 		}
+		fsProps.Stem = proptools.StringPtr("system.img")
 	case "system_ext":
 		if partitionVars.ProductFsverityGenerateMetadata {
 			fsProps.Fsverity.Inputs = []string{
@@ -293,6 +332,7 @@
 			fsProps.Fsverity.Libs = []string{":framework-res{.export-package.apk}"}
 		}
 		fsProps.Security_patch = proptools.StringPtr(ctx.Config().PlatformSecurityPatch())
+		fsProps.Stem = proptools.StringPtr("system_ext.img")
 	case "product":
 		fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
 		fsProps.Android_filesystem_deps.System = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
@@ -300,6 +340,7 @@
 			fsProps.Android_filesystem_deps.System_ext = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
 		}
 		fsProps.Security_patch = proptools.StringPtr(ctx.Config().PlatformSecurityPatch())
+		fsProps.Stem = proptools.StringPtr("product.img")
 	case "vendor":
 		fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
 		fsProps.Symlinks = []filesystem.SymlinkDefinition{
@@ -317,6 +358,7 @@
 			fsProps.Android_filesystem_deps.System_ext = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
 		}
 		fsProps.Security_patch = proptools.StringPtr(partitionVars.VendorSecurityPatch)
+		fsProps.Stem = proptools.StringPtr("vendor.img")
 	case "odm":
 		fsProps.Symlinks = []filesystem.SymlinkDefinition{
 			filesystem.SymlinkDefinition{
@@ -325,8 +367,10 @@
 			},
 		}
 		fsProps.Security_patch = proptools.StringPtr(partitionVars.OdmSecurityPatch)
+		fsProps.Stem = proptools.StringPtr("odm.img")
 	case "userdata":
 		fsProps.Base_dir = proptools.StringPtr("data")
+		fsProps.Stem = proptools.StringPtr("userdata.img")
 	case "ramdisk":
 		// Following the logic in https://cs.android.com/android/platform/superproject/main/+/c3c5063df32748a8806ce5da5dd0db158eab9ad9:build/make/core/Makefile;l=1307
 		fsProps.Dirs = android.NewSimpleConfigurable([]string{
@@ -349,6 +393,7 @@
 				"first_stage_ramdisk/sys",
 			})
 		}
+		fsProps.Stem = proptools.StringPtr("ramdisk.img")
 	case "recovery":
 		dirs := append(commonPartitionDirs, []string{
 			"sdcard",
@@ -364,16 +409,21 @@
 			Target: proptools.StringPtr("prop.default"),
 			Name:   proptools.StringPtr("default.prop"),
 		}), "root")
+		fsProps.Stem = proptools.StringPtr("recovery.img")
 	case "system_dlkm":
 		fsProps.Security_patch = proptools.StringPtr(partitionVars.SystemDlkmSecurityPatch)
+		fsProps.Stem = proptools.StringPtr("system_dlkm.img")
 	case "vendor_dlkm":
 		fsProps.Security_patch = proptools.StringPtr(partitionVars.VendorDlkmSecurityPatch)
+		fsProps.Stem = proptools.StringPtr("vendor_dlkm.img")
 	case "odm_dlkm":
 		fsProps.Security_patch = proptools.StringPtr(partitionVars.OdmDlkmSecurityPatch)
+		fsProps.Stem = proptools.StringPtr("odm_dlkm.img")
 	case "vendor_ramdisk":
 		if android.InList("recovery", generatedPartitions(ctx)) {
 			fsProps.Include_files_of = []string{generatedModuleNameForPartition(ctx.Config(), "recovery")}
 		}
+		fsProps.Stem = proptools.StringPtr("vendor_ramdisk.img")
 	}
 }
 
@@ -566,8 +616,8 @@
 	(*fsGenState.fsDeps[partitionType])[name] = defaultDepCandidateProps(ctx.Config())
 }
 
-// Create a build_prop and android_info module. This will be used to create /vendor/build.prop
-func (f *filesystemCreator) createVendorBuildProp(ctx android.LoadHookContext) {
+// Create an android_info module. This will be used to create /vendor/build.prop
+func (f *filesystemCreator) createAndroidInfo(ctx android.LoadHookContext) {
 	// Create a android_info for vendor
 	// The board info files might be in a directory outside the root soong namespace, so create
 	// the module in "."
@@ -580,7 +630,7 @@
 	}{
 		Name:             proptools.StringPtr(generatedModuleName(ctx.Config(), "android_info.prop")),
 		Board_info_files: partitionVars.BoardInfoFiles,
-		Stem:             proptools.StringPtr("android_info.txt"),
+		Stem:             proptools.StringPtr("android-info.txt"),
 	}
 	if len(androidInfoProps.Board_info_files) == 0 {
 		androidInfoProps.Bootloader_board_name = proptools.StringPtr(partitionVars.BootLoaderBoardName)
@@ -591,7 +641,9 @@
 		androidInfoProps,
 	)
 	androidInfoProp.HideFromMake()
-	// Create a build prop for vendor
+}
+
+func (f *filesystemCreator) createVendorBuildProp(ctx android.LoadHookContext) {
 	vendorBuildProps := &struct {
 		Name           *string
 		Vendor         *bool
@@ -604,7 +656,7 @@
 		Vendor:         proptools.BoolPtr(true),
 		Stem:           proptools.StringPtr("build.prop"),
 		Product_config: proptools.StringPtr(":product_config"),
-		Android_info:   proptools.StringPtr(":" + androidInfoProp.Name()),
+		Android_info:   proptools.StringPtr(":" + generatedModuleName(ctx.Config(), "android_info.prop")),
 		Licenses:       []string{"Android-Apache-2.0"},
 	}
 	vendorBuildProp := ctx.CreateModule(
@@ -849,8 +901,8 @@
 
 func (f *filesystemCreator) createFileListDiffTest(ctx android.ModuleContext, partitionType string) android.Path {
 	partitionModuleName := generatedModuleNameForPartition(ctx.Config(), partitionType)
-	systemImage := ctx.GetDirectDepWithTag(partitionModuleName, generatedFilesystemDepTag)
-	filesystemInfo, ok := android.OtherModuleProvider(ctx, systemImage, filesystem.FilesystemProvider)
+	partitionImage := ctx.GetDirectDepWithTag(partitionModuleName, generatedFilesystemDepTag)
+	filesystemInfo, ok := android.OtherModuleProvider(ctx, partitionImage, filesystem.FilesystemProvider)
 	if !ok {
 		ctx.ModuleErrorf("Expected module %s to provide FileysystemInfo", partitionModuleName)
 	}
@@ -905,12 +957,12 @@
 	builder.Build("diff test "+diffTestResultFile.String(), "diff test")
 }
 
-type systemImageDepTagType struct {
+type imageDepTagType struct {
 	blueprint.BaseDependencyTag
 }
 
-var generatedFilesystemDepTag systemImageDepTagType
-var generatedVbmetaPartitionDepTag systemImageDepTagType
+var generatedFilesystemDepTag imageDepTagType
+var generatedVbmetaPartitionDepTag imageDepTagType
 
 func (f *filesystemCreator) DepsMutator(ctx android.BottomUpMutatorContext) {
 	for _, partitionType := range f.properties.Generated_partition_types {
diff --git a/fsgen/super_img.go b/fsgen/super_img.go
index 8ee3bf2..a36f614 100644
--- a/fsgen/super_img.go
+++ b/fsgen/super_img.go
@@ -19,6 +19,7 @@
 
 	"android/soong/android"
 	"android/soong/filesystem"
+
 	"github.com/google/blueprint/proptools"
 )
 
@@ -30,7 +31,7 @@
 	baseProps := &struct {
 		Name *string
 	}{
-		Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "super")),
+		Name: proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "super")),
 	}
 
 	superImageProps := &filesystem.SuperImageProperties{
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 6137c70..9a8524b 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -88,9 +88,7 @@
 }
 
 type SourceFileGenerator interface {
-	GeneratedSourceFiles() android.Paths
-	GeneratedHeaderDirs() android.Paths
-	GeneratedDeps() android.Paths
+	android.SourceFileGenerator
 }
 
 // Alias for android.HostToolProvider
diff --git a/golang/golang_test.go b/golang/golang_test.go
index 0a4baed..d555d14 100644
--- a/golang/golang_test.go
+++ b/golang/golang_test.go
@@ -47,7 +47,7 @@
 
 	bin := result.ModuleForTests("gobin", result.Config.BuildOSTarget.String())
 
-	expected := "^out/soong/host/" + result.Config.PrebuiltOS() + "/bin/go/gobin/?[^/]*/obj/gobin$"
+	expected := "^out/host/" + result.Config.PrebuiltOS() + "/bin/go/gobin/?[^/]*/obj/gobin$"
 	actual := android.PathsRelativeToTop(bin.OutputFiles(result.TestContext, t, ""))
 	if len(actual) != 1 {
 		t.Fatalf("Expected 1 output file, got %v", actual)
diff --git a/java/aar.go b/java/aar.go
index 3c62441..7c63a29 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -391,8 +391,9 @@
 		versionName = proptools.NinjaEscape(versionName)
 		linkFlags = append(linkFlags, "--version-name ", versionName)
 	}
-
-	linkFlags, compileFlags = android.FilterList(linkFlags, []string{"--legacy"})
+	// Split the flags by prefix, as --png-compression-level has the "=value" suffix.
+	linkFlags, compileFlags = android.FilterListByPrefix(linkFlags,
+		[]string{"--legacy", "--png-compression-level"})
 
 	// Always set --pseudo-localize, it will be stripped out later for release
 	// builds that don't want it.
@@ -875,13 +876,15 @@
 	rroDirsDepSetBuilder := depset.NewBuilder[rroDir](depset.TOPOLOGICAL)
 	manifestsDepSetBuilder := depset.NewBuilder[android.Path](depset.TOPOLOGICAL)
 
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		depTag := ctx.OtherModuleDependencyTag(module)
 
 		var exportPackage android.Path
-		aarDep, _ := module.(AndroidLibraryDependency)
-		if aarDep != nil {
-			exportPackage = aarDep.ExportPackage()
+		var aarDep *AndroidLibraryDependencyInfo
+		javaInfo, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider)
+		if ok && javaInfo.AndroidLibraryDependencyInfo != nil {
+			aarDep = javaInfo.AndroidLibraryDependencyInfo
+			exportPackage = aarDep.ExportPackage
 		}
 
 		switch depTag {
@@ -889,7 +892,7 @@
 			// Nothing, instrumentationForTag is treated as libTag for javac but not for aapt2.
 		case sdkLibTag, libTag, rroDepTag:
 			if exportPackage != nil {
-				sharedResourcesNodeDepSets = append(sharedResourcesNodeDepSets, aarDep.ResourcesNodeDepSet())
+				sharedResourcesNodeDepSets = append(sharedResourcesNodeDepSets, aarDep.ResourcesNodeDepSet)
 				sharedLibs = append(sharedLibs, exportPackage)
 			}
 		case frameworkResTag:
@@ -898,9 +901,9 @@
 			}
 		case staticLibTag:
 			if exportPackage != nil {
-				staticResourcesNodeDepSets = append(staticResourcesNodeDepSets, aarDep.ResourcesNodeDepSet())
-				rroDirsDepSetBuilder.Transitive(aarDep.RRODirsDepSet())
-				manifestsDepSetBuilder.Transitive(aarDep.ManifestsDepSet())
+				staticResourcesNodeDepSets = append(staticResourcesNodeDepSets, aarDep.ResourcesNodeDepSet)
+				rroDirsDepSetBuilder.Transitive(aarDep.RRODirsDepSet)
+				manifestsDepSetBuilder.Transitive(aarDep.ManifestsDepSet)
 			}
 		}
 
@@ -1022,7 +1025,7 @@
 		extraSrcJars = android.Paths{a.aapt.aaptSrcJar}
 	}
 
-	a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars, nil)
+	javaInfo := a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars, nil)
 
 	a.aarFile = android.PathForModuleOut(ctx, ctx.ModuleName()+".aar")
 	var res android.Paths
@@ -1048,6 +1051,11 @@
 
 	android.SetProvider(ctx, AndroidLibraryInfoProvider, AndroidLibraryInfo{})
 
+	if javaInfo != nil {
+		setExtraJavaInfo(ctx, a, javaInfo)
+		android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+	}
+
 	a.setOutputFiles(ctx)
 }
 
@@ -1536,7 +1544,7 @@
 		ctx.CheckbuildFile(a.implementationJarFile)
 	}
 
-	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+	javaInfo := &JavaInfo{
 		HeaderJars:                             android.PathsIfNonNil(a.headerJarFile),
 		LocalHeaderJars:                        android.PathsIfNonNil(classpathFile),
 		TransitiveStaticLibsHeaderJars:         completeStaticLibsHeaderJars,
@@ -1549,7 +1557,9 @@
 		ImplementationJars:                     android.PathsIfNonNil(a.implementationJarFile),
 		StubsLinkType:                          Implementation,
 		// TransitiveAconfigFiles: // TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
-	})
+	}
+	setExtraJavaInfo(ctx, a, javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
 
 	if proptools.Bool(a.properties.Extract_jni) {
 		for _, t := range ctx.MultiTargets() {
diff --git a/java/app.go b/java/app.go
index b0dcbb0..31c338f 100644
--- a/java/app.go
+++ b/java/app.go
@@ -495,18 +495,24 @@
 // This check is enforced for "updatable" APKs (including APK-in-APEX).
 func (a *AndroidApp) checkJniLibsSdkVersion(ctx android.ModuleContext, minSdkVersion android.ApiLevel) {
 	// It's enough to check direct JNI deps' sdk_version because all transitive deps from JNI deps are checked in cc.checkLinkType()
-	ctx.VisitDirectDeps(func(m android.Module) {
+	ctx.VisitDirectDepsProxy(func(m android.ModuleProxy) {
 		if !IsJniDepTag(ctx.OtherModuleDependencyTag(m)) {
 			return
 		}
-		dep, _ := m.(*cc.Module)
+		if _, ok := android.OtherModuleProvider(ctx, m, cc.CcInfoProvider); !ok {
+			panic(fmt.Errorf("jni dependency is not a cc module: %v", m))
+		}
+		commonInfo, ok := android.OtherModuleProvider(ctx, m, android.CommonModuleInfoKey)
+		if !ok {
+			panic(fmt.Errorf("jni dependency doesn't have CommonModuleInfo provider: %v", m))
+		}
 		// The domain of cc.sdk_version is "current" and <number>
 		// We can rely on android.SdkSpec to convert it to <number> so that "current" is
 		// handled properly regardless of sdk finalization.
-		jniSdkVersion, err := android.SdkSpecFrom(ctx, dep.MinSdkVersion()).EffectiveVersion(ctx)
+		jniSdkVersion, err := android.SdkSpecFrom(ctx, commonInfo.MinSdkVersion).EffectiveVersion(ctx)
 		if err != nil || minSdkVersion.LessThan(jniSdkVersion) {
-			ctx.OtherModuleErrorf(dep, "min_sdk_version(%v) is higher than min_sdk_version(%v) of the containing android_app(%v)",
-				dep.MinSdkVersion(), minSdkVersion, ctx.ModuleName())
+			ctx.OtherModuleErrorf(m, "min_sdk_version(%v) is higher than min_sdk_version(%v) of the containing android_app(%v)",
+				commonInfo.MinSdkVersion, minSdkVersion, ctx.ModuleName())
 			return
 		}
 
@@ -569,7 +575,7 @@
 }
 
 func getAconfigFilePaths(ctx android.ModuleContext) (aconfigTextFilePaths android.Paths) {
-	ctx.VisitDirectDeps(func(dep android.Module) {
+	ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(dep)
 		switch tag {
 		case staticLibTag:
@@ -724,6 +730,7 @@
 
 	var packageResources = a.exportPackage
 
+	javaInfo := &JavaInfo{}
 	if ctx.ModuleName() != "framework-res" {
 		if a.dexProperties.resourceShrinkingEnabled(ctx) {
 			protoFile := android.PathForModuleOut(ctx, packageResources.Base()+".proto.apk")
@@ -747,13 +754,17 @@
 			extraSrcJars = android.Paths{a.aapt.aaptSrcJar}
 		}
 
-		a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars, nil)
+		javaInfo = a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars, nil)
 		if a.dexProperties.resourceShrinkingEnabled(ctx) {
 			binaryResources := android.PathForModuleOut(ctx, packageResources.Base()+".binary.out.apk")
 			aapt2Convert(ctx, binaryResources, a.dexer.resourcesOutput.Path(), "binary")
 			packageResources = binaryResources
 		}
 	}
+	if javaInfo != nil {
+		setExtraJavaInfo(ctx, a, javaInfo)
+		android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+	}
 
 	return a.dexJarFile.PathOrNil(), packageResources
 }
@@ -1565,6 +1576,9 @@
 	}
 	a.generateAndroidBuildActions(ctx)
 
+	for _, c := range a.testProperties.Test_options.Tradefed_options {
+		configs = append(configs, c)
+	}
 	for _, module := range a.testProperties.Test_mainline_modules {
 		configs = append(configs, tradefed.Option{Name: "config-descriptor:metadata", Key: "mainline-param", Value: module})
 	}
@@ -1684,7 +1698,7 @@
 	module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
 	module.appProperties.AlwaysPackageNativeLibs = true
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 
 	module.addHostAndDeviceProperties()
 	module.AddProperties(
@@ -1735,12 +1749,14 @@
 
 	// TODO(b/192032291): Disable by default after auditing downstream usage.
 	module.Module.dexProperties.Optimize.EnabledByDefault = true
+	module.Module.dexProperties.Optimize.Ignore_library_extends_program = proptools.BoolPtr(true)
+	module.Module.dexProperties.Optimize.Proguard_compatibility = proptools.BoolPtr(false)
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
 	module.appProperties.AlwaysPackageNativeLibs = true
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 
 	module.addHostAndDeviceProperties()
 	module.AddProperties(
diff --git a/java/app_import.go b/java/app_import.go
index e209135..b77e31a 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -43,6 +43,12 @@
 		Description: "Uncompress embedded JNI libs",
 	})
 
+	stripEmbeddedJniLibsUnusedArchRule = pctx.AndroidStaticRule("strip-embedded-jni-libs-from-unused-arch", blueprint.RuleParams{
+		Command:     `${config.Zip2ZipCmd} -i $in -o $out -x 'lib/**/*.so' $extraArgs`,
+		CommandDeps: []string{"${config.Zip2ZipCmd}"},
+		Description: "Remove all JNI libs from unused architectures",
+	}, "extraArgs")
+
 	uncompressDexRule = pctx.AndroidStaticRule("uncompress-dex", blueprint.RuleParams{
 		Command: `if (zipinfo $in '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then ` +
 			`${config.Zip2ZipCmd} -i $in -o $out -0 'classes*.dex'` +
@@ -155,6 +161,9 @@
 	// the prebuilt is Name() without "prebuilt_" prefix
 	Source_module_name *string
 
+	// Whether stripping all libraries from unused architectures.
+	Strip_unused_jni_arch *bool
+
 	// Path to the .prebuilt_info file of the prebuilt app.
 	// In case of mainline modules, the .prebuilt_info file contains the build_id that was used
 	// to generate the prebuilt.
@@ -313,6 +322,26 @@
 	return shouldUncompressDex(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), &a.dexpreopter)
 }
 
+func (a *AndroidAppImport) stripEmbeddedJniLibsUnusedArch(
+	ctx android.ModuleContext, inputPath android.Path, outputPath android.WritablePath) {
+	var wantedJniLibSlice []string
+	for _, target := range ctx.MultiTargets() {
+		supported_abis := target.Arch.Abi
+		for _, arch := range supported_abis {
+			wantedJniLibSlice = append(wantedJniLibSlice, " -X lib/"+arch+"/*.so")
+		}
+	}
+	wantedJniLibString := strings.Join(wantedJniLibSlice, " ")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   stripEmbeddedJniLibsUnusedArchRule,
+		Input:  inputPath,
+		Output: outputPath,
+		Args: map[string]string{
+			"extraArgs": wantedJniLibString,
+		},
+	})
+}
+
 func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.generateAndroidBuildActions(ctx)
 }
@@ -372,6 +401,13 @@
 	jnisUncompressed := android.PathForModuleOut(ctx, "jnis-uncompressed", ctx.ModuleName()+".apk")
 	a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed)
 
+	// Strip all embedded JNI libs and include only required ones accordingly to the module's compile_multilib
+	if Bool(a.properties.Strip_unused_jni_arch) {
+		jnisStripped := android.PathForModuleOut(ctx, "jnis-stripped", ctx.ModuleName()+".apk")
+		a.stripEmbeddedJniLibsUnusedArch(ctx, jnisUncompressed, jnisStripped)
+		jnisUncompressed = jnisStripped
+	}
+
 	var pathFragments []string
 	relInstallPath := String(a.properties.Relative_install_path)
 
diff --git a/java/app_test.go b/java/app_test.go
index 4e915d2..bde801b 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"path/filepath"
 	"reflect"
+	"regexp"
 	"sort"
 	"strings"
 	"testing"
@@ -4589,6 +4590,75 @@
 	assertTestOnlyAndTopLevel(t, ctx, expectedTestOnly, expectedTopLevel)
 }
 
+func TestTestConfigTemplate(t *testing.T) {
+	t.Parallel()
+	ctx := android.GroupFixturePreparers(
+		prepareForJavaTest,
+	).RunTestWithBp(t, `
+		android_test {
+			name: "android-test",
+			test_config_template: "AndroidTestTemplate.xml",
+			test_options: {
+				tradefed_options: [
+					{
+						name: "name1",
+						key: "key1",
+						value: "value1",
+					},
+					{
+						name: "name2",
+						key: "key2",
+						value: "value2",
+					},
+				],
+				test_runner_options: [
+					{
+						name: "name3",
+						key: "key3",
+						value: "value3",
+					},
+					{
+						name: "name4",
+						key: "key4",
+						value: "value4",
+					},
+				],
+			},
+		}
+	`)
+	type option struct {
+		name  string
+		key   string
+		value string
+	}
+	re := regexp.MustCompile(`<option name="(.*)" key="(.*)" value="(.*)" />`)
+	parse_options := func(optionsString string) []option {
+		lines := strings.Split(optionsString, `\n`)
+		var ret []option
+		for _, l := range lines {
+			sm := re.FindStringSubmatch(l)
+			if sm == nil {
+				continue
+			}
+			ret = append(ret, option{sm[1], sm[2], sm[3]})
+		}
+		return ret
+	}
+	rule := ctx.ModuleForTests("android-test", "android_common").Rule("autogenInstrumentationTest")
+	android.AssertSameArray(t, "extraConfigs mismatch",
+		[]option{
+			{"name1", "key1", "value1"},
+			{"name2", "key2", "value2"},
+		},
+		parse_options(rule.Args["extraConfigs"]))
+	android.AssertSameArray(t, "extraTestRunnerConfigs mismatch",
+		[]option{
+			{"name3", "key3", "value3"},
+			{"name4", "key4", "value4"},
+		},
+		parse_options(rule.Args["extraTestRunnerConfigs"]))
+}
+
 func TestAppStem(t *testing.T) {
 	ctx := testApp(t, `
 				android_app {
diff --git a/java/base.go b/java/base.go
index 1aef37c..f8050de 100644
--- a/java/base.go
+++ b/java/base.go
@@ -90,6 +90,10 @@
 	// list of module-specific flags that will be used for kotlinc compiles
 	Kotlincflags []string `android:"arch_variant"`
 
+	// Kotlin language version to target. Currently only 1.9 and 2 are supported.
+	// See kotlinc's `-language-version` flag.
+	Kotlin_lang_version *string
+
 	// list of java libraries that will be in the classpath
 	Libs []string `android:"arch_variant"`
 
@@ -1153,7 +1157,7 @@
 	j.properties.Generated_srcjars = append(j.properties.Generated_srcjars, path)
 }
 
-func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspathJars, extraCombinedJars, extraDepCombinedJars android.Paths) {
+func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspathJars, extraCombinedJars, extraDepCombinedJars android.Paths) *JavaInfo {
 	// Auto-propagating jarjar rules
 	jarjarProviderData := j.collectJarJarRules(ctx)
 	if jarjarProviderData != nil {
@@ -1287,7 +1291,7 @@
 			transitiveStaticLibsHeaderJars = nil
 		}
 		if ctx.Failed() {
-			return
+			return nil
 		}
 		j.headerJarFile = combinedHeaderJarFile
 
@@ -1302,7 +1306,8 @@
 			ctx.CheckbuildFile(j.headerJarFile)
 		}
 
-		android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+		j.outputFile = j.headerJarFile
+		return &JavaInfo{
 			HeaderJars:                          android.PathsIfNonNil(j.headerJarFile),
 			LocalHeaderJars:                     localHeaderJars,
 			TransitiveStaticLibsHeaderJars:      depset.New(depset.PREORDER, localHeaderJars, transitiveStaticLibsHeaderJars),
@@ -1315,10 +1320,7 @@
 			StubsLinkType:                       j.stubsLinkType,
 			AconfigIntermediateCacheOutputPaths: deps.aconfigProtoFiles,
 			SdkVersion:                          j.SdkVersion(ctx),
-		})
-
-		j.outputFile = j.headerJarFile
-		return
+		}
 	}
 
 	if srcFiles.HasExt(".kt") {
@@ -1331,6 +1333,16 @@
 		kotlincFlags := j.properties.Kotlincflags
 		CheckKotlincFlags(ctx, kotlincFlags)
 
+		kotlin_lang_version := proptools.StringDefault(j.properties.Kotlin_lang_version, "1.9")
+		if kotlin_lang_version == "1.9" {
+			kotlincFlags = append(kotlincFlags, "-language-version 1.9")
+		} else if kotlin_lang_version == "2" {
+			kotlincFlags = append(kotlincFlags, "-Xsuppress-version-warnings", "-Xconsistent-data-class-copy-visibility")
+		} else {
+			ctx.PropertyErrorf("kotlin_lang_version", "Must be one of `1.9` or `2`")
+
+		}
+
 		// Workaround for KT-46512
 		kotlincFlags = append(kotlincFlags, "-Xsam-conversions=class")
 
@@ -1375,7 +1387,7 @@
 		kotlinHeaderJar := android.PathForModuleOut(ctx, "kotlin_headers", jarName)
 		j.kotlinCompile(ctx, kotlinJar, kotlinHeaderJar, uniqueSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
 		if ctx.Failed() {
-			return
+			return nil
 		}
 
 		kotlinJarPath, _ := j.repackageFlagsIfNecessary(ctx, kotlinJar, jarName, "kotlinc")
@@ -1502,7 +1514,7 @@
 			localImplementationJars = append(localImplementationJars, classes)
 		}
 		if ctx.Failed() {
-			return
+			return nil
 		}
 	}
 
@@ -1542,7 +1554,7 @@
 		resourceJar := android.PathForModuleOut(ctx, "res", jarName)
 		TransformResourcesToJar(ctx, resourceJar, resArgs, resDeps)
 		if ctx.Failed() {
-			return
+			return nil
 		}
 		localResourceJars = append(localResourceJars, resourceJar)
 	}
@@ -1664,7 +1676,7 @@
 	}
 
 	if ctx.Failed() {
-		return
+		return nil
 	}
 
 	if j.ravenizer.enabled {
@@ -1728,7 +1740,7 @@
 		CheckJarPackages(ctx, pkgckFile, outputFile, j.properties.Permitted_packages)
 
 		if ctx.Failed() {
-			return
+			return nil
 		}
 	}
 
@@ -1753,7 +1765,7 @@
 	// enforce syntax check to jacoco filters for any build (http://b/183622051)
 	specs := j.jacocoModuleToZipCommand(ctx)
 	if ctx.Failed() {
-		return
+		return nil
 	}
 
 	completeStaticLibsImplementationJarsToCombine := completeStaticLibsImplementationJars
@@ -1821,7 +1833,7 @@
 			}
 			dexOutputFile, dexArtProfileOutput := j.dexer.compileDex(ctx, params)
 			if ctx.Failed() {
-				return
+				return nil
 			}
 
 			// If r8/d8 provides a profile that matches the optimized dex, use that for dexpreopt.
@@ -1880,7 +1892,7 @@
 		}
 
 		if ctx.Failed() {
-			return
+			return nil
 		}
 	}
 
@@ -1927,7 +1939,10 @@
 		ctx.CheckbuildFile(j.headerJarFile)
 	}
 
-	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
+	j.outputFile = outputFile.WithoutRel()
+
+	return &JavaInfo{
 		HeaderJars:           android.PathsIfNonNil(j.headerJarFile),
 		RepackagedHeaderJars: android.PathsIfNonNil(repackagedHeaderJarFile),
 
@@ -1952,10 +1967,7 @@
 		StubsLinkType:                       j.stubsLinkType,
 		AconfigIntermediateCacheOutputPaths: j.aconfigCacheFiles,
 		SdkVersion:                          j.SdkVersion(ctx),
-	})
-
-	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
-	j.outputFile = outputFile.WithoutRel()
+	}
 }
 
 func (j *Module) useCompose(ctx android.BaseModuleContext) bool {
@@ -2059,7 +2071,9 @@
 		} else if strings.HasPrefix(flag, "-Xintellij-plugin-root") {
 			ctx.PropertyErrorf("kotlincflags",
 				"Bad flag: `%s`, only use internal compiler for consistency.", flag)
-		} else if inList(flag, config.KotlincIllegalFlags) {
+		} else if slices.ContainsFunc(config.KotlincIllegalFlags, func(f string) bool {
+			return strings.HasPrefix(flag, f)
+		}) {
 			ctx.PropertyErrorf("kotlincflags", "Flag `%s` already used by build system", flag)
 		} else if flag == "-include-runtime" {
 			ctx.PropertyErrorf("kotlincflags", "Bad flag: `%s`, do not include runtime.", flag)
@@ -2220,7 +2234,7 @@
 
 func (j *Module) hasCode(ctx android.ModuleContext) bool {
 	srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
-	return len(srcFiles) > 0 || len(ctx.GetDirectDepsWithTag(staticLibTag)) > 0
+	return len(srcFiles) > 0 || len(ctx.GetDirectDepsProxyWithTag(staticLibTag)) > 0
 }
 
 // Implements android.ApexModule
diff --git a/java/builder.go b/java/builder.go
index 88058e0..f1d5e99 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -56,7 +56,7 @@
 				`$zipTemplate${config.SoongZipCmd} -jar -o $out.tmp -C $outDir -D $outDir && ` +
 				`if ! cmp -s "$out.tmp" "$out"; then mv "$out.tmp" "$out"; fi && ` +
 				`if ! cmp -s "$annoSrcJar.tmp" "$annoSrcJar"; then mv "$annoSrcJar.tmp" "$annoSrcJar"; fi && ` +
-				`if [[ -f "$out.pc_state.new" ]]; then mv "$out.pc_state.new" "$out.pc_state"; fi && ` +
+				`if [ -f "$out.pc_state.new" ]; then mv "$out.pc_state.new" "$out.pc_state"; fi && ` +
 				`rm -rf "$srcJarDir" "$outDir"`,
 			CommandDeps: []string{
 				"${config.FindInputDeltaCmd}",
@@ -226,6 +226,12 @@
 		},
 		"jarArgs")
 
+	extractR8Rules = pctx.AndroidStaticRule("extractR8Rules",
+		blueprint.RuleParams{
+			Command:     `${config.ExtractR8RulesCmd} --rules-output $out --include-origin-comments $in`,
+			CommandDeps: []string{"${config.ExtractR8RulesCmd}"},
+		})
+
 	jarjar = pctx.AndroidStaticRule("jarjar",
 		blueprint.RuleParams{
 			Command: "" +
@@ -462,6 +468,7 @@
 		android.WriteFileRule(ctx, srcJarRspFile, strings.Join(srcJars.Strings(), "\n"))
 		srcJarArgs = "@" + srcJarRspFile.String()
 		implicits = append(implicits, srcJarRspFile)
+		rspFiles = append(rspFiles, srcJarRspFile)
 		rbeInputs = append(rbeInputs, srcJarRspFile)
 	} else {
 		rbeInputs = append(rbeInputs, srcJars...)
@@ -739,6 +746,16 @@
 	})
 }
 
+func TransformJarToR8Rules(ctx android.ModuleContext, outputFile android.WritablePath,
+	jar android.Path) {
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        extractR8Rules,
+		Output:      outputFile,
+		Input:       jar,
+	})
+}
+
 func convertImplementationJarToHeaderJar(ctx android.ModuleContext, implementationJarFile android.Path,
 	headerJarFile android.WritablePath) {
 	ctx.Build(pctx, android.BuildParams{
diff --git a/java/config/config.go b/java/config/config.go
index 19d6952..71025de 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -42,9 +42,6 @@
 	InstrumentFrameworkModules = []string{
 		"framework",
 		"framework-minus-apex",
-		// TODO(b/383559945) Remove "framework-minus-apex_jarjar-sharded" once
-		// we remove this module.
-		"framework-minus-apex_jarjar-sharded",
 		"ims-common",
 		"telephony-common",
 		"services",
@@ -174,6 +171,7 @@
 	pctx.HostBinToolVariable("ApiCheckCmd", "apicheck")
 	pctx.HostBinToolVariable("D8Cmd", "d8")
 	pctx.HostBinToolVariable("R8Cmd", "r8")
+	pctx.HostBinToolVariable("ExtractR8RulesCmd", "extract-r8-rules")
 	pctx.HostBinToolVariable("ResourceShrinkerCmd", "resourceshrinker")
 	pctx.HostBinToolVariable("HiddenAPICmd", "hiddenapi")
 	pctx.HostBinToolVariable("ExtractApksCmd", "extract_apks")
diff --git a/java/config/kotlin.go b/java/config/kotlin.go
index bf4c886..ffb025d 100644
--- a/java/config/kotlin.go
+++ b/java/config/kotlin.go
@@ -21,6 +21,7 @@
 	KotlincIllegalFlags = []string{
 		"-no-jdk",
 		"-no-stdlib",
+		"-language-version",
 	}
 )
 
@@ -49,12 +50,12 @@
 		"-J--add-opens=java.base/java.util=ALL-UNNAMED", // https://youtrack.jetbrains.com/issue/KT-43704
 	}, " "))
 
-	pctx.StaticVariable("KotlincGlobalFlags", strings.Join([]string{"-language-version 1.9"}, " "))
+	pctx.StaticVariable("KotlincGlobalFlags", strings.Join([]string{}, " "))
 	// Use KotlincKytheGlobalFlags to prevent kotlinc version skew issues between android and
 	// g3 kythe indexers.
 	// This is necessary because there might be instances of kotlin code in android
 	// platform that are not fully compatible with the kotlinc used in g3 kythe indexers.
 	// e.g. uninitialized variables are a warning in 1.*, but an error in 2.*
 	// https://github.com/JetBrains/kotlin/blob/master/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt#L748
-	pctx.StaticVariable("KotlincKytheGlobalFlags", strings.Join([]string{"-language-version 1.9"}, " "))
+	pctx.StaticVariable("KotlincKytheGlobalFlags", strings.Join([]string{}, " "))
 }
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index bfacea6..04def3e 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -140,7 +140,7 @@
 		d.combinedHeaderJar = d.headerJars[0]
 	}
 
-	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+	javaInfo := &JavaInfo{
 		HeaderJars:                             d.headerJars,
 		LocalHeaderJars:                        d.headerJars,
 		TransitiveStaticLibsHeaderJars:         depset.New(depset.PREORDER, nil, transitiveHeaderJars),
@@ -154,7 +154,9 @@
 		StubsLinkType:                          Implementation,
 		// TODO: Not sure if aconfig flags that have been moved between device and host variants
 		// make sense.
-	})
+	}
+	setExtraJavaInfo(ctx, d, javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
 
 }
 
diff --git a/java/dex.go b/java/dex.go
index 168a231..7b99549 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -42,11 +42,15 @@
 		// True if the module containing this has it set by default.
 		EnabledByDefault bool `blueprint:"mutated"`
 
+		// Whether to allow that library classes inherit from program classes.
+		// Defaults to false.
+		Ignore_library_extends_program *bool
+
 		// Whether to continue building even if warnings are emitted.  Defaults to true.
 		Ignore_warnings *bool
 
 		// If true, runs R8 in Proguard compatibility mode, otherwise runs R8 in full mode.
-		// Defaults to false for apps, true for libraries and tests.
+		// Defaults to false for apps and tests, true for libraries.
 		Proguard_compatibility *bool
 
 		// If true, optimize for size by removing unused code.  Defaults to true for apps,
@@ -356,6 +360,10 @@
 
 	r8Flags = append(r8Flags, opt.Proguard_flags...)
 
+	if BoolDefault(opt.Ignore_library_extends_program, false) {
+		r8Flags = append(r8Flags, "--ignore-library-extends-program")
+	}
+
 	if BoolDefault(opt.Proguard_compatibility, true) {
 		r8Flags = append(r8Flags, "--force-proguard-compatibility")
 	}
diff --git a/java/droidstubs.go b/java/droidstubs.go
index e955949..fa1fb86 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -629,8 +629,8 @@
 					cmd.Implicit(dep)
 				} else if filename != "android.jar" && depBase == "android.jar" {
 					// Metalava implicitly searches these patterns:
-					//  prebuilts/tools/common/api-versions/android-%/android.jar
-					//  prebuilts/sdk/%/public/android.jar
+					//  prebuilts/tools/common/api-versions/android-{version:level}/android.jar
+					//  prebuilts/sdk/{version:level}/public/android.jar
 					// Add android.jar files from the api_levels_annotations_dirs directories to try
 					// to satisfy these patterns.  If Metalava can't find a match for an API level
 					// between 1 and 28 in at least one pattern it will fail.
@@ -646,11 +646,11 @@
 	})
 
 	// Generate the list of --android-jar-pattern options. The order matters so the first one which
-	// matches will be the one that is used for a specific api level..
+	// matches will be the one that is used for a specific api level.
 	for _, sdkDir := range sdkDirs {
 		for _, dir := range dirs {
 			addPattern := func(jarFilename string) {
-				cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, jarFilename))
+				cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/{version:level}/%s/%s", dir, sdkDir, jarFilename))
 			}
 
 			if sdkDir == "module-lib" || sdkDir == "system-server" {
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index 1e8362c..37740ae 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -88,7 +88,7 @@
 		cmdline := String(sboxProto.Commands[0].Command)
 		android.AssertStringContainsEquals(t, "api-versions generation flag", cmdline, "--generate-api-levels", c.generate_xml)
 		if c.expectedJarFilename != "" {
-			expected := "--android-jar-pattern ./%/public/" + c.expectedJarFilename
+			expected := "--android-jar-pattern ./{version:level}/public/" + c.expectedJarFilename
 			if !strings.Contains(cmdline, expected) {
 				t.Errorf("For %q, expected metalava argument %q, but was not found %q", c.moduleName, expected, cmdline)
 			}
@@ -142,8 +142,8 @@
 	patterns := getAndroidJarPatternsForDroidstubs(t, "public")
 
 	android.AssertArrayString(t, "order of patterns", []string{
-		"--android-jar-pattern somedir/%/public/android.jar",
-		"--android-jar-pattern someotherdir/%/public/android.jar",
+		"--android-jar-pattern somedir/{version:level}/public/android.jar",
+		"--android-jar-pattern someotherdir/{version:level}/public/android.jar",
 	}, patterns)
 }
 
@@ -151,10 +151,10 @@
 	patterns := getAndroidJarPatternsForDroidstubs(t, "system")
 
 	android.AssertArrayString(t, "order of patterns", []string{
-		"--android-jar-pattern somedir/%/system/android.jar",
-		"--android-jar-pattern someotherdir/%/system/android.jar",
-		"--android-jar-pattern somedir/%/public/android.jar",
-		"--android-jar-pattern someotherdir/%/public/android.jar",
+		"--android-jar-pattern somedir/{version:level}/system/android.jar",
+		"--android-jar-pattern someotherdir/{version:level}/system/android.jar",
+		"--android-jar-pattern somedir/{version:level}/public/android.jar",
+		"--android-jar-pattern someotherdir/{version:level}/public/android.jar",
 	}, patterns)
 }
 
@@ -162,12 +162,12 @@
 	patterns := getAndroidJarPatternsForDroidstubs(t, "module-lib")
 
 	android.AssertArrayString(t, "order of patterns", []string{
-		"--android-jar-pattern somedir/%/module-lib/android.jar",
-		"--android-jar-pattern someotherdir/%/module-lib/android.jar",
-		"--android-jar-pattern somedir/%/system/android.jar",
-		"--android-jar-pattern someotherdir/%/system/android.jar",
-		"--android-jar-pattern somedir/%/public/android.jar",
-		"--android-jar-pattern someotherdir/%/public/android.jar",
+		"--android-jar-pattern somedir/{version:level}/module-lib/android.jar",
+		"--android-jar-pattern someotherdir/{version:level}/module-lib/android.jar",
+		"--android-jar-pattern somedir/{version:level}/system/android.jar",
+		"--android-jar-pattern someotherdir/{version:level}/system/android.jar",
+		"--android-jar-pattern somedir/{version:level}/public/android.jar",
+		"--android-jar-pattern someotherdir/{version:level}/public/android.jar",
 	}, patterns)
 }
 
@@ -175,14 +175,14 @@
 	patterns := getAndroidJarPatternsForDroidstubs(t, "system-server")
 
 	android.AssertArrayString(t, "order of patterns", []string{
-		"--android-jar-pattern somedir/%/system-server/android.jar",
-		"--android-jar-pattern someotherdir/%/system-server/android.jar",
-		"--android-jar-pattern somedir/%/module-lib/android.jar",
-		"--android-jar-pattern someotherdir/%/module-lib/android.jar",
-		"--android-jar-pattern somedir/%/system/android.jar",
-		"--android-jar-pattern someotherdir/%/system/android.jar",
-		"--android-jar-pattern somedir/%/public/android.jar",
-		"--android-jar-pattern someotherdir/%/public/android.jar",
+		"--android-jar-pattern somedir/{version:level}/system-server/android.jar",
+		"--android-jar-pattern someotherdir/{version:level}/system-server/android.jar",
+		"--android-jar-pattern somedir/{version:level}/module-lib/android.jar",
+		"--android-jar-pattern someotherdir/{version:level}/module-lib/android.jar",
+		"--android-jar-pattern somedir/{version:level}/system/android.jar",
+		"--android-jar-pattern someotherdir/{version:level}/system/android.jar",
+		"--android-jar-pattern somedir/{version:level}/public/android.jar",
+		"--android-jar-pattern someotherdir/{version:level}/public/android.jar",
 	}, patterns)
 }
 
diff --git a/java/fuzz.go b/java/fuzz.go
index 79cd042..5973957 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -63,7 +63,7 @@
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 	module.Module.sourceProperties.Test_only = proptools.BoolPtr(true)
 	module.Module.sourceProperties.Top_level_test_target = true
 
diff --git a/java/java.go b/java/java.go
index a975ca6..48ba8de 100644
--- a/java/java.go
+++ b/java/java.go
@@ -250,6 +250,32 @@
 
 var ProguardSpecInfoProvider = blueprint.NewProvider[ProguardSpecInfo]()
 
+type AndroidLibraryDependencyInfo struct {
+	ExportPackage       android.Path
+	ResourcesNodeDepSet depset.DepSet[*resourcesNode]
+	RRODirsDepSet       depset.DepSet[rroDir]
+	ManifestsDepSet     depset.DepSet[android.Path]
+}
+
+type UsesLibraryDependencyInfo struct {
+	DexJarBuildPath     OptionalDexJarPath
+	DexJarInstallPath   android.Path
+	ClassLoaderContexts dexpreopt.ClassLoaderContextMap
+}
+
+type SdkLibraryComponentDependencyInfo struct {
+	// The name of the implementation library for the optional SDK library or nil, if there isn't one.
+	OptionalSdkLibraryImplementation *string
+}
+
+type ProvidesUsesLibInfo struct {
+	ProvidesUsesLib *string
+}
+
+type ModuleWithUsesLibraryInfo struct {
+	UsesLibrary *usesLibrary
+}
+
 // JavaInfo contains information about a java module for use by modules that depend on it.
 type JavaInfo struct {
 	// HeaderJars is a list of jars that can be passed as the javac classpath in order to link
@@ -328,6 +354,16 @@
 	AconfigIntermediateCacheOutputPaths android.Paths
 
 	SdkVersion android.SdkSpec
+
+	AndroidLibraryDependencyInfo *AndroidLibraryDependencyInfo
+
+	UsesLibraryDependencyInfo *UsesLibraryDependencyInfo
+
+	SdkLibraryComponentDependencyInfo *SdkLibraryComponentDependencyInfo
+
+	ProvidesUsesLibInfo *ProvidesUsesLibInfo
+
+	ModuleWithUsesLibraryInfo *ModuleWithUsesLibraryInfo
 }
 
 var JavaInfoProvider = blueprint.NewProvider[*JavaInfo]()
@@ -1002,7 +1038,7 @@
 			j.dexpreopter.disableDexpreopt()
 		}
 	}
-	j.compile(ctx, nil, nil, nil, nil)
+	javaInfo := j.compile(ctx, nil, nil, nil, nil)
 
 	j.setInstallRules(ctx)
 
@@ -1011,6 +1047,11 @@
 		TopLevelTarget: j.sourceProperties.Top_level_test_target,
 	})
 
+	if javaInfo != nil {
+		setExtraJavaInfo(ctx, j, javaInfo)
+		android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+	}
+
 	setOutputFiles(ctx, j.Module)
 }
 
@@ -1740,7 +1781,7 @@
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 	module.Module.sourceProperties.Test_only = proptools.BoolPtr(true)
 	module.Module.sourceProperties.Top_level_test_target = true
 
@@ -1757,7 +1798,7 @@
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 	module.Module.sourceProperties.Test_only = proptools.BoolPtr(true)
 
 	InitJavaModule(module, android.HostAndDeviceSupported)
@@ -2427,7 +2468,7 @@
 
 	ctx.Phony(ctx.ModuleName(), al.stubsJar)
 
-	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+	javaInfo := &JavaInfo{
 		HeaderJars:                             android.PathsIfNonNil(al.stubsJar),
 		LocalHeaderJars:                        android.PathsIfNonNil(al.stubsJar),
 		TransitiveStaticLibsHeaderJars:         depset.New(depset.PREORDER, android.PathsIfNonNil(al.stubsJar), nil),
@@ -2437,7 +2478,9 @@
 		AidlIncludeDirs:                        android.Paths{},
 		StubsLinkType:                          Stubs,
 		// No aconfig libraries on api libraries
-	})
+	}
+	setExtraJavaInfo(ctx, al, javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
 }
 
 func (al *ApiLibrary) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
@@ -2827,6 +2870,23 @@
 		outputFile = combinedJar
 	}
 
+	proguardFlags := android.PathForModuleOut(ctx, "proguard_flags")
+	TransformJarToR8Rules(ctx, proguardFlags, outputFile)
+
+	transitiveProguardFlags, transitiveUnconditionalExportedFlags := collectDepProguardSpecInfo(ctx)
+	android.SetProvider(ctx, ProguardSpecInfoProvider, ProguardSpecInfo{
+		ProguardFlagsFiles: depset.New[android.Path](
+			depset.POSTORDER,
+			android.Paths{proguardFlags},
+			transitiveProguardFlags,
+		),
+		UnconditionallyExportedProguardFlags: depset.New[android.Path](
+			depset.POSTORDER,
+			nil,
+			transitiveUnconditionalExportedFlags,
+		),
+	})
+
 	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource.
 	// Also strip the relative path from the header output file so that the reuseImplementationJarAsHeaderJar check
 	// in a module that depends on this module considers them equal.
@@ -2895,7 +2955,7 @@
 		}
 	}
 
-	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+	javaInfo := &JavaInfo{
 		HeaderJars:                             android.PathsIfNonNil(j.combinedHeaderFile),
 		LocalHeaderJars:                        android.PathsIfNonNil(j.combinedHeaderFile),
 		TransitiveLibsHeaderJarsForR8:          j.transitiveLibsHeaderJarsForR8,
@@ -2909,7 +2969,9 @@
 		AidlIncludeDirs:                        j.exportAidlIncludeDirs,
 		StubsLinkType:                          j.stubsLinkType,
 		// TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
-	})
+	}
+	setExtraJavaInfo(ctx, j, javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
 
 	ctx.SetOutputFiles(android.Paths{j.combinedImplementationFile}, "")
 	ctx.SetOutputFiles(android.Paths{j.combinedImplementationFile}, ".jar")
@@ -3323,8 +3385,8 @@
 func addCLCFromDep(ctx android.ModuleContext, depModule android.Module,
 	clcMap dexpreopt.ClassLoaderContextMap) {
 
-	dep, ok := depModule.(UsesLibraryDependency)
-	if !ok {
+	dep, ok := android.OtherModuleProvider(ctx, depModule, JavaInfoProvider)
+	if !ok || dep.UsesLibraryDependencyInfo == nil {
 		return
 	}
 
@@ -3334,14 +3396,14 @@
 	if lib, ok := android.OtherModuleProvider(ctx, depModule, SdkLibraryInfoProvider); ok && lib.SharedLibrary {
 		// A shared SDK library. This should be added as a top-level CLC element.
 		sdkLib = &depName
-	} else if lib, ok := depModule.(SdkLibraryComponentDependency); ok && lib.OptionalSdkLibraryImplementation() != nil {
-		if depModule.Name() == proptools.String(lib.OptionalSdkLibraryImplementation())+".impl" {
-			sdkLib = lib.OptionalSdkLibraryImplementation()
+	} else if lib := dep.SdkLibraryComponentDependencyInfo; lib != nil && lib.OptionalSdkLibraryImplementation != nil {
+		if depModule.Name() == proptools.String(lib.OptionalSdkLibraryImplementation)+".impl" {
+			sdkLib = lib.OptionalSdkLibraryImplementation
 		}
-	} else if ulib, ok := depModule.(ProvidesUsesLib); ok {
+	} else if ulib := dep.ProvidesUsesLibInfo; ulib != nil {
 		// A non-SDK library disguised as an SDK library by the means of `provides_uses_lib`
 		// property. This should be handled in the same way as a shared SDK library.
-		sdkLib = ulib.ProvidesUsesLib()
+		sdkLib = ulib.ProvidesUsesLib
 	}
 
 	depTag := ctx.OtherModuleDependencyTag(depModule)
@@ -3373,21 +3435,22 @@
 			}
 		}
 		clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, optional,
-			dep.DexJarBuildPath(ctx).PathOrNil(), dep.DexJarInstallPath(), dep.ClassLoaderContexts())
+			dep.UsesLibraryDependencyInfo.DexJarBuildPath.PathOrNil(),
+			dep.UsesLibraryDependencyInfo.DexJarInstallPath, dep.UsesLibraryDependencyInfo.ClassLoaderContexts)
 	} else {
-		clcMap.AddContextMap(dep.ClassLoaderContexts(), depName)
+		clcMap.AddContextMap(dep.UsesLibraryDependencyInfo.ClassLoaderContexts, depName)
 	}
 }
 
 func addMissingOptionalUsesLibsFromDep(ctx android.ModuleContext, depModule android.Module,
 	usesLibrary *usesLibrary) {
 
-	dep, ok := depModule.(ModuleWithUsesLibrary)
-	if !ok {
+	dep, ok := android.OtherModuleProvider(ctx, depModule, JavaInfoProvider)
+	if !ok || dep.ModuleWithUsesLibraryInfo == nil {
 		return
 	}
 
-	for _, lib := range dep.UsesLibrary().usesLibraryProperties.Missing_optional_uses_libs {
+	for _, lib := range dep.ModuleWithUsesLibraryInfo.UsesLibrary.usesLibraryProperties.Missing_optional_uses_libs {
 		if !android.InList(lib, usesLibrary.usesLibraryProperties.Missing_optional_uses_libs) {
 			usesLibrary.usesLibraryProperties.Missing_optional_uses_libs =
 				append(usesLibrary.usesLibraryProperties.Missing_optional_uses_libs, lib)
@@ -3443,3 +3506,40 @@
 func (ap *JavaApiContributionImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	ap.JavaApiContribution.GenerateAndroidBuildActions(ctx)
 }
+
+func setExtraJavaInfo(ctx android.ModuleContext, module android.Module, javaInfo *JavaInfo) {
+	if alDep, ok := module.(AndroidLibraryDependency); ok {
+		javaInfo.AndroidLibraryDependencyInfo = &AndroidLibraryDependencyInfo{
+			ExportPackage:       alDep.ExportPackage(),
+			ResourcesNodeDepSet: alDep.ResourcesNodeDepSet(),
+			RRODirsDepSet:       alDep.RRODirsDepSet(),
+			ManifestsDepSet:     alDep.ManifestsDepSet(),
+		}
+	}
+
+	if ulDep, ok := module.(UsesLibraryDependency); ok {
+		javaInfo.UsesLibraryDependencyInfo = &UsesLibraryDependencyInfo{
+			DexJarBuildPath:     ulDep.DexJarBuildPath(ctx),
+			DexJarInstallPath:   ulDep.DexJarInstallPath(),
+			ClassLoaderContexts: ulDep.ClassLoaderContexts(),
+		}
+	}
+
+	if slcDep, ok := module.(SdkLibraryComponentDependency); ok {
+		javaInfo.SdkLibraryComponentDependencyInfo = &SdkLibraryComponentDependencyInfo{
+			OptionalSdkLibraryImplementation: slcDep.OptionalSdkLibraryImplementation(),
+		}
+	}
+
+	if pul, ok := module.(ProvidesUsesLib); ok {
+		javaInfo.ProvidesUsesLibInfo = &ProvidesUsesLibInfo{
+			ProvidesUsesLib: pul.ProvidesUsesLib(),
+		}
+	}
+
+	if mwul, ok := module.(ModuleWithUsesLibrary); ok {
+		javaInfo.ModuleWithUsesLibraryInfo = &ModuleWithUsesLibraryInfo{
+			UsesLibrary: mwul.UsesLibrary(),
+		}
+	}
+}
diff --git a/java/kotlin.go b/java/kotlin.go
index e1a3f71..308bb03 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -198,7 +198,7 @@
 			`$kaptProcessor ` +
 			`-Xbuild-file=$kotlinBuildFile && ` +
 			`${config.SoongZipCmd} -jar -write_if_changed -o $out -C $kaptDir/stubs -D $kaptDir/stubs && ` +
-			`if [[ -f "$out.pc_state.new" ]]; then mv "$out.pc_state.new" "$out.pc_state"; fi && ` +
+			`if [ -f "$out.pc_state.new" ]; then mv "$out.pc_state.new" "$out.pc_state"; fi && ` +
 			`rm -rf "$srcJarDir"`,
 		CommandDeps: []string{
 			"${config.FindInputDeltaCmd}",
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
index 6128ce3..4be7d04 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -28,9 +28,6 @@
 	"FloralClocks",
 	"framework-jobscheduler",
 	"framework-minus-apex",
-	// TODO(b/383559945) Remove "framework-minus-apex_jarjar-sharded" once
-	// we remove this module.
-	"framework-minus-apex_jarjar-sharded",
 	"framework-minus-apex-headers",
 	"framework-minus-apex-intdefs",
 	"FrameworksCoreTests",
diff --git a/java/lint.go b/java/lint.go
index ac90e19..cee25a8 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -69,6 +69,11 @@
 		// If soong gets support for testonly, this flag should be replaced with that.
 		Test *bool
 
+		// Same as the regular Test property, but set by internal soong code based on if the module
+		// type is a test module type. This will act as the default value for the test property,
+		// but can be overridden by the user.
+		Test_module_type *bool `blueprint:"mutated"`
+
 		// Whether to ignore the exit code of Android lint. This is the --exit_code
 		// option. Defaults to false.
 		Suppress_exit_code *bool
@@ -257,7 +262,12 @@
 	if l.library {
 		cmd.Flag("--library")
 	}
-	if proptools.BoolDefault(l.properties.Lint.Test, false) {
+
+	test := proptools.BoolDefault(l.properties.Lint.Test_module_type, false)
+	if l.properties.Lint.Test != nil {
+		test = *l.properties.Lint.Test
+	}
+	if test {
 		cmd.Flag("--test")
 	}
 	if l.manifest != nil {
@@ -470,7 +480,7 @@
 
 	cmd := rule.Command()
 
-	cmd.Flag(`JAVA_OPTS="-Xmx3072m --add-opens java.base/java.util=ALL-UNNAMED"`).
+	cmd.Flag(`JAVA_OPTS="-Xmx4096m --add-opens java.base/java.util=ALL-UNNAMED"`).
 		FlagWithArg("ANDROID_SDK_HOME=", lintPaths.homeDir.String()).
 		FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath).
 		FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath)
diff --git a/java/lint_test.go b/java/lint_test.go
index afe3914..617dc54 100644
--- a/java/lint_test.go
+++ b/java/lint_test.go
@@ -276,3 +276,50 @@
 		ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern("Don't use --disable, --enable, or --check in the flags field, instead use the dedicated disabled_checks, warning_checks, error_checks, or fatal_checks fields")).
 		RunTestWithBp(t, bp)
 }
+
+// b/358643466
+func TestNotTestViaDefault(t *testing.T) {
+	bp := `
+		java_defaults {
+			name: "mydefaults",
+			lint: {
+				test: false,
+			},
+		}
+		android_test {
+			name: "foo",
+			srcs: [
+				"a.java",
+			],
+			min_sdk_version: "29",
+			sdk_version: "current",
+			defaults: ["mydefaults"],
+		}
+		android_test {
+			name: "foo2",
+			srcs: [
+				"a.java",
+			],
+			min_sdk_version: "29",
+			sdk_version: "current",
+		}
+	`
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, bp)
+	ctx := result.TestContext
+
+	foo := ctx.ModuleForTests("foo", "android_common")
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, foo.Output("lint.sbox.textproto"))
+	command := *sboxProto.Commands[0].Command
+
+	if strings.Contains(command, "--test") {
+		t.Fatalf("Expected command to not contain --test")
+	}
+
+	foo2 := ctx.ModuleForTests("foo2", "android_common")
+	sboxProto2 := android.RuleBuilderSboxProtoForTests(t, ctx, foo2.Output("lint.sbox.textproto"))
+	command2 := *sboxProto2.Commands[0].Command
+
+	if !strings.Contains(command2, "--test") {
+		t.Fatalf("Expected command to contain --test")
+	}
+}
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index d09a02e..152eb1e 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -172,7 +172,7 @@
 	// Do not add implLibModule to allModules as the impl lib is only used to collect the
 	// transitive source files
 	var implLibModule []android.Module
-	ctx.VisitDirectDepsWithTag(implLibraryTag, func(m android.Module) {
+	ctx.VisitDirectDepsWithTag(platformBootclasspathImplLibDepTag, func(m android.Module) {
 		implLibModule = append(implLibModule, m)
 	})
 
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index f2768db..1f691a0 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -30,18 +30,23 @@
 	preparer := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		FixtureConfigureBootJars("platform:foo", "system_ext:bar"),
+		android.FixtureMergeMockFs(android.MockFS{
+			"api/current.txt": nil,
+			"api/removed.txt": nil,
+		}),
 		android.FixtureWithRootAndroidBp(`
 			platform_bootclasspath {
 				name: "platform-bootclasspath",
 			}
 
-			java_library {
+			java_sdk_library {
 				name: "bar",
 				srcs: ["a.java"],
 				system_modules: "none",
 				sdk_version: "none",
 				compile_dex: true,
 				system_ext_specific: true,
+				unsafe_ignore_missing_latest_api: true,
 			}
 		`),
 	)
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 5b145c6..bb98944 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -51,6 +51,10 @@
 
 type platformCompatConfigProperties struct {
 	Src *string `android:"path"`
+
+	// If true, we include it in the "merged" XML (merged_compat_config.xml).
+	// Default is true.
+	Include_in_merged_xml *bool
 }
 
 type platformCompatConfig struct {
@@ -60,6 +64,7 @@
 	installDirPath android.InstallPath
 	configFile     android.OutputPath
 	metadataFile   android.OutputPath
+	doMerge        bool
 
 	installConfigFile android.InstallPath
 }
@@ -68,6 +73,10 @@
 	return p.metadataFile
 }
 
+func (p *platformCompatConfig) includeInMergedXml() bool {
+	return p.doMerge
+}
+
 func (p *platformCompatConfig) CompatConfig() android.OutputPath {
 	return p.configFile
 }
@@ -78,6 +87,9 @@
 
 type platformCompatConfigMetadataProvider interface {
 	compatConfigMetadata() android.Path
+
+	// Whether to include it in the "merged" XML (merged_compat_config.xml) or not.
+	includeInMergedXml() bool
 }
 
 type PlatformCompatConfigIntf interface {
@@ -98,6 +110,7 @@
 	metadataFileName := p.Name() + "_meta.xml"
 	p.configFile = android.PathForModuleOut(ctx, configFileName).OutputPath
 	p.metadataFile = android.PathForModuleOut(ctx, metadataFileName).OutputPath
+	p.doMerge = proptools.BoolDefault(p.properties.Include_in_merged_xml, true)
 	path := android.PathForModuleSrc(ctx, String(p.properties.Src))
 
 	rule.Command().
@@ -201,6 +214,10 @@
 	return module.metadataFile
 }
 
+func (module *prebuiltCompatConfigModule) includeInMergedXml() bool {
+	return true // Always include in merged.xml
+}
+
 func (module *prebuiltCompatConfigModule) BaseModuleName() string {
 	return proptools.StringDefault(module.properties.Source_module_name, module.ModuleBase.Name())
 }
@@ -237,6 +254,9 @@
 			if !android.IsModulePreferred(module) {
 				return
 			}
+			if !c.includeInMergedXml() {
+				return
+			}
 			metadata := c.compatConfigMetadata()
 			compatConfigMetadata = append(compatConfigMetadata, metadata)
 		}
diff --git a/java/platform_compat_config_test.go b/java/platform_compat_config_test.go
index 80d991c..f7529a7 100644
--- a/java/platform_compat_config_test.go
+++ b/java/platform_compat_config_test.go
@@ -26,6 +26,7 @@
 		android.FixtureWithRootAndroidBp(`
 			platform_compat_config {
 				name: "myconfig2",
+				include_in_merged_xml: false,
 			}
 			platform_compat_config {
 				name: "myconfig1",
@@ -38,7 +39,6 @@
 
 	CheckMergedCompatConfigInputs(t, result, "myconfig",
 		"out/soong/.intermediates/myconfig1/myconfig1_meta.xml",
-		"out/soong/.intermediates/myconfig2/myconfig2_meta.xml",
 		"out/soong/.intermediates/myconfig3/myconfig3_meta.xml",
 	)
 }
diff --git a/java/ravenwood.go b/java/ravenwood.go
index 4c43a9f..84d6a9f 100644
--- a/java/ravenwood.go
+++ b/java/ravenwood.go
@@ -110,7 +110,7 @@
 	module.AddProperties(&module.testProperties, &module.ravenwoodTestProperties)
 
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 
 	module.testProperties.Test_suites = []string{
 		"general-tests",
diff --git a/java/robolectric.go b/java/robolectric.go
index 3b2c656..29aa2f0 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -21,6 +21,7 @@
 	"android/soong/java/config"
 	"android/soong/tradefed"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -28,6 +29,17 @@
 	RegisterRobolectricBuildComponents(android.InitRegistrationContext)
 }
 
+type roboRuntimeOnlyDependencyTag struct {
+	blueprint.BaseDependencyTag
+}
+
+var roboRuntimeOnlyDepTag roboRuntimeOnlyDependencyTag
+
+// Mark this tag so dependencies that use it are excluded from visibility enforcement.
+func (t roboRuntimeOnlyDependencyTag) ExcludeFromVisibilityEnforcement() {}
+
+var _ android.ExcludeFromVisibilityEnforcementTag = roboRuntimeOnlyDepTag
+
 func RegisterRobolectricBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("android_robolectric_test", RobolectricTestFactory)
 	ctx.RegisterModuleType("android_robolectric_runtimes", robolectricRuntimesFactory)
@@ -47,7 +59,6 @@
 var (
 	roboCoverageLibsTag = dependencyTag{name: "roboCoverageLibs"}
 	roboRuntimesTag     = dependencyTag{name: "roboRuntimes"}
-	roboRuntimeOnlyTag  = dependencyTag{name: "roboRuntimeOnlyTag"}
 )
 
 type robolectricProperties struct {
@@ -65,10 +76,6 @@
 		Shards *int64
 	}
 
-	// The version number of a robolectric prebuilt to use from prebuilts/misc/common/robolectric
-	// instead of the one built from source in external/robolectric-shadows.
-	Robolectric_prebuilt_version *string
-
 	// Use /external/robolectric rather than /external/robolectric-shadows as the version of robolectric
 	// to use.  /external/robolectric closely tracks github's master, and will fully replace /external/robolectric-shadows
 	Upstream *bool
@@ -107,23 +114,12 @@
 		ctx.PropertyErrorf("instrumentation_for", "missing required instrumented module")
 	}
 
-	ctx.AddVariationDependencies(nil, staticLibTag, clearcutJunitLib)
-
-	if v := String(r.robolectricProperties.Robolectric_prebuilt_version); v != "" {
-		ctx.AddVariationDependencies(nil, staticLibTag, fmt.Sprintf(robolectricPrebuiltLibPattern, v))
-	} else if !proptools.BoolDefault(r.robolectricProperties.Strict_mode, true) {
-		if proptools.Bool(r.robolectricProperties.Upstream) {
-			ctx.AddVariationDependencies(nil, staticLibTag, robolectricCurrentLib+"_upstream")
-		} else {
-			ctx.AddVariationDependencies(nil, staticLibTag, robolectricCurrentLib)
-		}
-	}
+	ctx.AddVariationDependencies(nil, roboRuntimeOnlyDepTag, clearcutJunitLib)
 
 	if proptools.BoolDefault(r.robolectricProperties.Strict_mode, true) {
-		ctx.AddVariationDependencies(nil, roboRuntimeOnlyTag, robolectricCurrentLib+"_upstream")
+		ctx.AddVariationDependencies(nil, roboRuntimeOnlyDepTag, robolectricCurrentLib)
 	} else {
-		// opting out from strict mode, robolectric_non_strict_mode_permission lib should be added
-		ctx.AddVariationDependencies(nil, staticLibTag, "robolectric_non_strict_mode_permission")
+		ctx.AddVariationDependencies(nil, staticLibTag, robolectricCurrentLib)
 	}
 
 	ctx.AddVariationDependencies(nil, staticLibTag, robolectricDefaultLibs...)
@@ -197,7 +193,7 @@
 		handleLibDeps(dep)
 	}
 	// handle the runtimeOnly tag for strict_mode
-	for _, dep := range ctx.GetDirectDepsWithTag(roboRuntimeOnlyTag) {
+	for _, dep := range ctx.GetDirectDepsWithTag(roboRuntimeOnlyDepTag) {
 		handleLibDeps(dep)
 	}
 
@@ -208,7 +204,7 @@
 	r.stem = proptools.StringDefault(r.overridableProperties.Stem, ctx.ModuleName())
 	r.classLoaderContexts = r.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
 	r.dexpreopter.disableDexpreopt()
-	r.compile(ctx, nil, nil, nil, extraCombinedJars)
+	javaInfo := r.compile(ctx, nil, nil, nil, extraCombinedJars)
 
 	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
 	var installDeps android.InstallPaths
@@ -245,6 +241,11 @@
 	}
 
 	r.installFile = ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.outputFile, installDeps...)
+
+	if javaInfo != nil {
+		setExtraJavaInfo(ctx, r, javaInfo)
+		android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+	}
 }
 
 func generateSameDirRoboTestConfigJar(ctx android.ModuleContext, outputFile android.ModuleOutPath) {
@@ -295,7 +296,7 @@
 		&module.testProperties)
 
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 
 	module.testProperties.Test_suites = []string{"robolectric-tests"}
 
diff --git a/java/robolectric_test.go b/java/robolectric_test.go
index a2474e8..78326ab 100644
--- a/java/robolectric_test.go
+++ b/java/robolectric_test.go
@@ -32,6 +32,11 @@
 	}
 
 	java_library {
+		name: "Robolectric_all-target",
+		srcs: ["Robo.java"]
+	}
+
+	java_library {
 		name: "mockito-robolectric-prebuilt",
 		srcs: ["Mockito.java"]
 	}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index a0affe0..155bea4 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -964,6 +964,10 @@
 		removedApiFilePaths[kind] = removedApiFilePath
 	}
 
+	javaInfo := &JavaInfo{}
+	setExtraJavaInfo(ctx, ctx.Module(), javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+
 	return SdkLibraryInfo{
 		EverythingStubDexJarPaths: everythingStubPaths,
 		ExportableStubDexJarPaths: exportableStubPaths,
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index 7684db2..4f1ef9d 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -91,8 +91,8 @@
 func BuildLinkerConfig(
 	ctx android.ModuleContext,
 	inputs android.Paths,
-	provideModules []android.Module,
-	requireModules []android.Module,
+	provideModules []android.ModuleProxy,
+	requireModules []android.ModuleProxy,
 	output android.WritablePath,
 ) {
 	// First, convert the input json to protobuf format
@@ -110,9 +110,10 @@
 	// Secondly, if there's provideLibs gathered from provideModules, append them
 	var provideLibs []string
 	for _, m := range provideModules {
-		if c, ok := m.(*cc.Module); ok && (cc.IsStubTarget(c) || c.HasLlndkStubs()) {
+		ccInfo, ok := android.OtherModuleProvider(ctx, m, cc.CcInfoProvider)
+		if ok && (cc.IsStubTarget(android.OtherModuleProviderOrDefault(ctx, m, cc.LinkableInfoProvider)) || ccInfo.HasLlndkStubs) {
 			for _, ps := range android.OtherModuleProviderOrDefault(
-				ctx, c, android.InstallFilesProvider).PackagingSpecs {
+				ctx, m, android.InstallFilesProvider).PackagingSpecs {
 				provideLibs = append(provideLibs, ps.FileName())
 			}
 		}
@@ -122,8 +123,15 @@
 
 	var requireLibs []string
 	for _, m := range requireModules {
-		if c, ok := m.(*cc.Module); ok && c.HasStubsVariants() && !c.Host() {
-			requireLibs = append(requireLibs, c.ImplementationModuleName(ctx)+".so")
+		if _, ok := android.OtherModuleProvider(ctx, m, cc.CcInfoProvider); ok {
+			if android.OtherModuleProviderOrDefault(ctx, m, cc.LinkableInfoProvider).HasStubsVariants &&
+				!android.OtherModuleProviderOrDefault(ctx, m, android.CommonModuleInfoKey).Host {
+				name := ctx.OtherModuleName(m)
+				if ccInfo, ok := android.OtherModuleProvider(ctx, m, cc.CcInfoProvider); ok && ccInfo.LinkerInfo != nil && ccInfo.LinkerInfo.ImplementationModuleName != nil {
+					name = *ccInfo.LinkerInfo.ImplementationModuleName
+				}
+				requireLibs = append(requireLibs, name+".so")
+			}
 		}
 	}
 
diff --git a/python/python.go b/python/python.go
index be9411b..914b77e 100644
--- a/python/python.go
+++ b/python/python.go
@@ -95,6 +95,11 @@
 	// device.
 	Device_common_data []string `android:"path_device_common"`
 
+	// Same as data, but will add dependencies on modules via a device os variation and the
+	// device's first supported arch's variation. Useful for a host test that wants to embed a
+	// module built for device.
+	Device_first_data []string `android:"path_device_first"`
+
 	// list of java modules that provide data that should be installed alongside the test.
 	Java_data []string
 
@@ -456,6 +461,7 @@
 	// expand data files from "data" property.
 	expandedData := android.PathsForModuleSrc(ctx, p.properties.Data)
 	expandedData = append(expandedData, android.PathsForModuleSrc(ctx, p.properties.Device_common_data)...)
+	expandedData = append(expandedData, android.PathsForModuleSrc(ctx, p.properties.Device_first_data)...)
 
 	// Emulate the data property for java_data dependencies.
 	for _, javaData := range ctx.GetDirectDepsWithTag(javaDataTag) {
diff --git a/python/test.go b/python/test.go
index 9f57bea..37947dd 100644
--- a/python/test.go
+++ b/python/test.go
@@ -68,6 +68,11 @@
 	// device.
 	Device_common_data []string `android:"path_device_common"`
 
+	// Same as data, but will add dependencies on modules via a device os variation and the
+	// device's first supported arch's variation. Useful for a host test that wants to embed a
+	// module built for device.
+	Device_first_data []string `android:"path_device_first"`
+
 	// list of java modules that provide data that should be installed alongside the test.
 	Java_data []string
 
@@ -189,6 +194,9 @@
 	for _, dataSrcPath := range android.PathsForModuleSrc(ctx, p.testProperties.Device_common_data) {
 		p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath})
 	}
+	for _, dataSrcPath := range android.PathsForModuleSrc(ctx, p.testProperties.Device_first_data) {
+		p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath})
+	}
 
 	if p.isTestHost() && len(p.testProperties.Data_device_bins_both) > 0 {
 		ctx.VisitDirectDepsWithTag(dataDeviceBinsTag, func(dep android.Module) {
diff --git a/rust/builder.go b/rust/builder.go
index e5434ef..8a869aa 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -264,7 +264,7 @@
 		libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String())
 	}
 	for _, lib := range deps.DyLibs {
-		libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String())
+		libFlags = append(libFlags, "--extern force:"+lib.CrateName+"="+lib.Path.String())
 	}
 	for _, proc_macro := range deps.ProcMacros {
 		libFlags = append(libFlags, "--extern "+proc_macro.CrateName+"="+proc_macro.Path.String())
diff --git a/rust/config/global.go b/rust/config/global.go
index 66ffc0b..0a4b314 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -42,6 +42,8 @@
 	}
 
 	GlobalRustFlags = []string{
+		// Allow `--extern force:foo` for dylib support
+		"-Z unstable-options",
 		"-Z stack-protector=strong",
 		"-Z remap-cwd-prefix=.",
 		"-C debuginfo=2",
diff --git a/rust/library.go b/rust/library.go
index 14a2b65..2d62dcf 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -457,13 +457,6 @@
 
 	cfgs := library.baseCompiler.Properties.Cfgs.GetOrDefault(ctx, nil)
 
-	if library.dylib() {
-		// We need to add a dependency on std in order to link crates as dylibs.
-		// The hack to add this dependency is guarded by the following cfg so
-		// that we don't force a dependency when it isn't needed.
-		cfgs = append(cfgs, "android_dylib")
-	}
-
 	cfgFlags := cfgsToFlags(cfgs)
 
 	flags.RustFlags = append(flags.RustFlags, cfgFlags...)
diff --git a/rust/library_test.go b/rust/library_test.go
index 256cb35..e5fd5e0 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -89,22 +89,6 @@
 	}
 }
 
-// Check that we are passing the android_dylib config flag
-func TestAndroidDylib(t *testing.T) {
-	ctx := testRust(t, `
-		rust_library_host_dylib {
-			name: "libfoo",
-			srcs: ["foo.rs"],
-			crate_name: "foo",
-		}`)
-
-	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
-
-	if !strings.Contains(libfooDylib.Args["rustcFlags"], "--cfg 'android_dylib'") {
-		t.Errorf("missing android_dylib cfg flag for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"])
-	}
-}
-
 func TestValidateLibraryStem(t *testing.T) {
 	testRustError(t, "crate_name must be defined.", `
 			rust_library_host {
diff --git a/rust/rust.go b/rust/rust.go
index 246670f..ba6e293 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -34,6 +34,35 @@
 
 var pctx = android.NewPackageContext("android/soong/rust")
 
+type LibraryInfo struct {
+	Rlib  bool
+	Dylib bool
+}
+
+type CompilerInfo struct {
+	StdLinkageForDevice    RustLinkage
+	StdLinkageForNonDevice RustLinkage
+	NoStdlibs              bool
+	LibraryInfo            *LibraryInfo
+}
+
+type ProtobufDecoratorInfo struct{}
+
+type SourceProviderInfo struct {
+	ProtobufDecoratorInfo *ProtobufDecoratorInfo
+}
+
+type RustInfo struct {
+	AndroidMkSuffix               string
+	RustSubName                   string
+	TransitiveAndroidMkSharedLibs depset.DepSet[string]
+	CompilerInfo                  *CompilerInfo
+	SnapshotInfo                  *cc.SnapshotInfo
+	SourceProviderInfo            *SourceProviderInfo
+}
+
+var RustInfoProvider = blueprint.NewProvider[*RustInfo]()
+
 func init() {
 	android.RegisterModuleType("rust_defaults", defaultsFactory)
 	android.PreDepsMutators(registerPreDepsMutators)
@@ -996,9 +1025,44 @@
 		ctx.Phony("rust", ctx.RustModule().OutputFile().Path())
 	}
 
-	android.SetProvider(ctx, cc.LinkableInfoKey, cc.LinkableInfo{
-		StaticExecutable: mod.StaticExecutable(),
-	})
+	linkableInfo := cc.CreateCommonLinkableInfo(mod)
+	linkableInfo.Static = mod.Static()
+	linkableInfo.Shared = mod.Shared()
+	linkableInfo.CrateName = mod.CrateName()
+	linkableInfo.ExportedCrateLinkDirs = mod.ExportedCrateLinkDirs()
+	android.SetProvider(ctx, cc.LinkableInfoProvider, linkableInfo)
+
+	rustInfo := &RustInfo{
+		AndroidMkSuffix:               mod.AndroidMkSuffix(),
+		RustSubName:                   mod.Properties.RustSubName,
+		TransitiveAndroidMkSharedLibs: mod.transitiveAndroidMkSharedLibs,
+	}
+	if mod.compiler != nil {
+		rustInfo.CompilerInfo = &CompilerInfo{
+			NoStdlibs:              mod.compiler.noStdlibs(),
+			StdLinkageForDevice:    mod.compiler.stdLinkage(true),
+			StdLinkageForNonDevice: mod.compiler.stdLinkage(false),
+		}
+		if lib, ok := mod.compiler.(libraryInterface); ok {
+			rustInfo.CompilerInfo.LibraryInfo = &LibraryInfo{
+				Dylib: lib.dylib(),
+				Rlib:  lib.rlib(),
+			}
+		}
+		if lib, ok := mod.compiler.(cc.SnapshotInterface); ok {
+			rustInfo.SnapshotInfo = &cc.SnapshotInfo{
+				SnapshotAndroidMkSuffix: lib.SnapshotAndroidMkSuffix(),
+			}
+		}
+	}
+	if mod.sourceProvider != nil {
+		if _, ok := mod.sourceProvider.(*protobufDecorator); ok {
+			rustInfo.SourceProviderInfo = &SourceProviderInfo{
+				ProtobufDecoratorInfo: &ProtobufDecoratorInfo{},
+			}
+		}
+	}
+	android.SetProvider(ctx, RustInfoProvider, rustInfo)
 
 	mod.setOutputFiles(ctx)
 
@@ -1175,21 +1239,21 @@
 	return nil
 }
 
-func rustMakeLibName(ctx android.ModuleContext, c cc.LinkableInterface, dep cc.LinkableInterface, depName string) string {
-	if rustDep, ok := dep.(*Module); ok {
+func rustMakeLibName(rustInfo *RustInfo, linkableInfo *cc.LinkableInfo, commonInfo *android.CommonModuleInfo, depName string) string {
+	if rustInfo != nil {
 		// Use base module name for snapshots when exporting to Makefile.
-		if snapshotPrebuilt, ok := rustDep.compiler.(cc.SnapshotInterface); ok {
-			baseName := rustDep.BaseModuleName()
-			return baseName + snapshotPrebuilt.SnapshotAndroidMkSuffix() + rustDep.AndroidMkSuffix()
+		if rustInfo.SnapshotInfo != nil {
+			baseName := linkableInfo.BaseModuleName
+			return baseName + rustInfo.SnapshotInfo.SnapshotAndroidMkSuffix + rustInfo.AndroidMkSuffix
 		}
 	}
-	return cc.MakeLibName(ctx, c, dep, depName)
+	return cc.MakeLibName(nil, linkableInfo, commonInfo, depName)
 }
 
-func collectIncludedProtos(mod *Module, dep *Module) {
+func collectIncludedProtos(mod *Module, rustInfo *RustInfo, linkableInfo *cc.LinkableInfo) {
 	if protoMod, ok := mod.sourceProvider.(*protobufDecorator); ok {
-		if _, ok := dep.sourceProvider.(*protobufDecorator); ok {
-			protoMod.additionalCrates = append(protoMod.additionalCrates, dep.CrateName())
+		if rustInfo.SourceProviderInfo.ProtobufDecoratorInfo != nil {
+			protoMod.additionalCrates = append(protoMod.additionalCrates, linkableInfo.CrateName)
 		}
 	}
 }
@@ -1197,13 +1261,13 @@
 func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 	var depPaths PathDeps
 
-	directRlibDeps := []*Module{}
-	directDylibDeps := []*Module{}
-	directProcMacroDeps := []*Module{}
+	directRlibDeps := []*cc.LinkableInfo{}
+	directDylibDeps := []*cc.LinkableInfo{}
+	directProcMacroDeps := []*cc.LinkableInfo{}
 	directSharedLibDeps := []cc.SharedLibraryInfo{}
-	directStaticLibDeps := [](cc.LinkableInterface){}
-	directSrcProvidersDeps := []*Module{}
-	directSrcDeps := [](android.SourceFileProducer){}
+	directStaticLibDeps := [](*cc.LinkableInfo){}
+	directSrcProvidersDeps := []*android.ModuleProxy{}
+	directSrcDeps := []android.SourceFilesInfo{}
 
 	// For the dependency from platform to apex, use the latest stubs
 	mod.apexSdkVersion = android.FutureApiLevel
@@ -1224,7 +1288,7 @@
 	var transitiveAndroidMkSharedLibs []depset.DepSet[string]
 	var directAndroidMkSharedLibs []string
 
-	ctx.VisitDirectDeps(func(dep android.Module) {
+	ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
 		depName := ctx.OtherModuleName(dep)
 		depTag := ctx.OtherModuleDependencyTag(dep)
 		modStdLinkage := mod.compiler.stdLinkage(ctx.Device())
@@ -1237,18 +1301,22 @@
 			return
 		}
 
-		if rustDep, ok := dep.(*Module); ok && !rustDep.Static() && !rustDep.Shared() {
+		rustInfo, hasRustInfo := android.OtherModuleProvider(ctx, dep, RustInfoProvider)
+		ccInfo, _ := android.OtherModuleProvider(ctx, dep, cc.CcInfoProvider)
+		linkableInfo, hasLinkableInfo := android.OtherModuleProvider(ctx, dep, cc.LinkableInfoProvider)
+		commonInfo := android.OtherModuleProviderOrDefault(ctx, dep, android.CommonModuleInfoKey)
+		if hasRustInfo && !linkableInfo.Static && !linkableInfo.Shared {
 			//Handle Rust Modules
-			makeLibName := rustMakeLibName(ctx, mod, rustDep, depName+rustDep.Properties.RustSubName)
+			makeLibName := rustMakeLibName(rustInfo, linkableInfo, &commonInfo, depName+rustInfo.RustSubName)
 
 			switch {
 			case depTag == dylibDepTag:
-				dylib, ok := rustDep.compiler.(libraryInterface)
-				if !ok || !dylib.dylib() {
+				dylib := rustInfo.CompilerInfo.LibraryInfo
+				if dylib == nil || !dylib.Dylib {
 					ctx.ModuleErrorf("mod %q not an dylib library", depName)
 					return
 				}
-				directDylibDeps = append(directDylibDeps, rustDep)
+				directDylibDeps = append(directDylibDeps, linkableInfo)
 				mod.Properties.AndroidMkDylibs = append(mod.Properties.AndroidMkDylibs, makeLibName)
 				mod.Properties.SnapshotDylibs = append(mod.Properties.SnapshotDylibs, cc.BaseLibName(depName))
 
@@ -1257,8 +1325,11 @@
 					depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
 				}
 
-				if !rustDep.compiler.noStdlibs() {
-					rustDepStdLinkage := rustDep.compiler.stdLinkage(ctx.Device())
+				if !rustInfo.CompilerInfo.NoStdlibs {
+					rustDepStdLinkage := rustInfo.CompilerInfo.StdLinkageForNonDevice
+					if ctx.Device() {
+						rustDepStdLinkage = rustInfo.CompilerInfo.StdLinkageForDevice
+					}
 					if rustDepStdLinkage != modStdLinkage {
 						ctx.ModuleErrorf("Rust dependency %q has the wrong StdLinkage; expected %#v, got %#v", depName, modStdLinkage, rustDepStdLinkage)
 						return
@@ -1266,27 +1337,30 @@
 				}
 
 			case depTag == rlibDepTag:
-				rlib, ok := rustDep.compiler.(libraryInterface)
-				if !ok || !rlib.rlib() {
+				rlib := rustInfo.CompilerInfo.LibraryInfo
+				if rlib == nil || !rlib.Rlib {
 					ctx.ModuleErrorf("mod %q not an rlib library", makeLibName)
 					return
 				}
-				directRlibDeps = append(directRlibDeps, rustDep)
+				directRlibDeps = append(directRlibDeps, linkableInfo)
 				mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, makeLibName)
 				mod.Properties.SnapshotRlibs = append(mod.Properties.SnapshotRlibs, cc.BaseLibName(depName))
 
 				// rust_ffi rlibs may export include dirs, so collect those here.
 				exportedInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider)
 				depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
-				depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path()))
+				depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(linkableInfo.OutputFile.Path()))
 
 				// rlibs are not installed, so don't add the output file to directImplementationDeps
 				if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
 					depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
 				}
 
-				if !rustDep.compiler.noStdlibs() {
-					rustDepStdLinkage := rustDep.compiler.stdLinkage(ctx.Device())
+				if !rustInfo.CompilerInfo.NoStdlibs {
+					rustDepStdLinkage := rustInfo.CompilerInfo.StdLinkageForNonDevice
+					if ctx.Device() {
+						rustDepStdLinkage = rustInfo.CompilerInfo.StdLinkageForDevice
+					}
 					if rustDepStdLinkage != modStdLinkage {
 						ctx.ModuleErrorf("Rust dependency %q has the wrong StdLinkage; expected %#v, got %#v", depName, modStdLinkage, rustDepStdLinkage)
 						return
@@ -1294,14 +1368,14 @@
 				}
 
 			case depTag == procMacroDepTag:
-				directProcMacroDeps = append(directProcMacroDeps, rustDep)
+				directProcMacroDeps = append(directProcMacroDeps, linkableInfo)
 				mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName)
 				// proc_macro link dirs need to be exported, so collect those here.
-				depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path()))
+				depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(linkableInfo.OutputFile.Path()))
 
 			case depTag == sourceDepTag:
 				if _, ok := mod.sourceProvider.(*protobufDecorator); ok {
-					collectIncludedProtos(mod, rustDep)
+					collectIncludedProtos(mod, rustInfo, linkableInfo)
 				}
 			case cc.IsStaticDepTag(depTag):
 				// Rust FFI rlibs should not be declared in a Rust modules
@@ -1314,7 +1388,7 @@
 
 			}
 
-			transitiveAndroidMkSharedLibs = append(transitiveAndroidMkSharedLibs, rustDep.transitiveAndroidMkSharedLibs)
+			transitiveAndroidMkSharedLibs = append(transitiveAndroidMkSharedLibs, rustInfo.TransitiveAndroidMkSharedLibs)
 
 			if android.IsSourceDepTagWithOutputTag(depTag, "") {
 				// Since these deps are added in path_properties.go via AddDependencies, we need to ensure the correct
@@ -1326,14 +1400,14 @@
 					helper = "device module defined?"
 				}
 
-				if dep.Target().Os != ctx.Os() {
+				if commonInfo.Target.Os != ctx.Os() {
 					ctx.ModuleErrorf("OS mismatch on dependency %q (%s)", dep.Name(), helper)
 					return
-				} else if dep.Target().Arch.ArchType != ctx.Arch().ArchType {
+				} else if commonInfo.Target.Arch.ArchType != ctx.Arch().ArchType {
 					ctx.ModuleErrorf("Arch mismatch on dependency %q (%s)", dep.Name(), helper)
 					return
 				}
-				directSrcProvidersDeps = append(directSrcProvidersDeps, rustDep)
+				directSrcProvidersDeps = append(directSrcProvidersDeps, &dep)
 			}
 
 			exportedInfo, _ := android.OtherModuleProvider(ctx, dep, FlagExporterInfoProvider)
@@ -1345,7 +1419,7 @@
 			}
 
 			if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
-				linkFile := rustDep.UnstrippedOutputFile()
+				linkFile := linkableInfo.UnstrippedOutputFile
 				linkDir := linkPathFromFilePath(linkFile)
 				if lib, ok := mod.compiler.(exportedFlagsProducer); ok {
 					lib.exportLinkDirs(linkDir)
@@ -1354,26 +1428,26 @@
 
 			if depTag == sourceDepTag {
 				if _, ok := mod.sourceProvider.(*protobufDecorator); ok && mod.Source() {
-					if _, ok := rustDep.sourceProvider.(*protobufDecorator); ok {
+					if rustInfo.SourceProviderInfo.ProtobufDecoratorInfo != nil {
 						exportedInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider)
 						depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
 					}
 				}
 			}
-		} else if ccDep, ok := dep.(cc.LinkableInterface); ok {
+		} else if hasLinkableInfo {
 			//Handle C dependencies
-			makeLibName := cc.MakeLibName(ctx, mod, ccDep, depName)
-			if _, ok := ccDep.(*Module); !ok {
-				if ccDep.Module().Target().Os != ctx.Os() {
+			makeLibName := cc.MakeLibName(ccInfo, linkableInfo, &commonInfo, depName)
+			if !hasRustInfo {
+				if commonInfo.Target.Os != ctx.Os() {
 					ctx.ModuleErrorf("OS mismatch between %q and %q", ctx.ModuleName(), depName)
 					return
 				}
-				if ccDep.Module().Target().Arch.ArchType != ctx.Arch().ArchType {
+				if commonInfo.Target.Arch.ArchType != ctx.Arch().ArchType {
 					ctx.ModuleErrorf("Arch mismatch between %q and %q", ctx.ModuleName(), depName)
 					return
 				}
 			}
-			linkObject := ccDep.OutputFile()
+			linkObject := linkableInfo.OutputFile
 			if !linkObject.Valid() {
 				if !ctx.Config().AllowMissingDependencies() {
 					ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
@@ -1413,7 +1487,7 @@
 				depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
 				depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
 				depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
-				directStaticLibDeps = append(directStaticLibDeps, ccDep)
+				directStaticLibDeps = append(directStaticLibDeps, linkableInfo)
 
 				// Record baseLibName for snapshots.
 				mod.Properties.SnapshotStaticLibs = append(mod.Properties.SnapshotStaticLibs, cc.BaseLibName(depName))
@@ -1484,7 +1558,7 @@
 			}
 		}
 
-		if srcDep, ok := dep.(android.SourceFileProducer); ok {
+		if srcDep, ok := android.OtherModuleProvider(ctx, dep, android.SourceFilesInfoKey); ok {
 			if android.IsSourceDepTagWithOutputTag(depTag, "") {
 				// These are usually genrules which don't have per-target variants.
 				directSrcDeps = append(directSrcDeps, srcDep)
@@ -1497,32 +1571,32 @@
 	var rlibDepFiles RustLibraries
 	aliases := mod.compiler.Aliases()
 	for _, dep := range directRlibDeps {
-		crateName := dep.CrateName()
+		crateName := dep.CrateName
 		if alias, aliased := aliases[crateName]; aliased {
 			crateName = alias
 		}
-		rlibDepFiles = append(rlibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: crateName})
+		rlibDepFiles = append(rlibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile, CrateName: crateName})
 	}
 	var dylibDepFiles RustLibraries
 	for _, dep := range directDylibDeps {
-		crateName := dep.CrateName()
+		crateName := dep.CrateName
 		if alias, aliased := aliases[crateName]; aliased {
 			crateName = alias
 		}
-		dylibDepFiles = append(dylibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: crateName})
+		dylibDepFiles = append(dylibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile, CrateName: crateName})
 	}
 	var procMacroDepFiles RustLibraries
 	for _, dep := range directProcMacroDeps {
-		crateName := dep.CrateName()
+		crateName := dep.CrateName
 		if alias, aliased := aliases[crateName]; aliased {
 			crateName = alias
 		}
-		procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: crateName})
+		procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile, CrateName: crateName})
 	}
 
 	var staticLibDepFiles android.Paths
 	for _, dep := range directStaticLibDeps {
-		staticLibDepFiles = append(staticLibDepFiles, dep.OutputFile().Path())
+		staticLibDepFiles = append(staticLibDepFiles, dep.OutputFile.Path())
 	}
 
 	var sharedLibFiles android.Paths
@@ -1538,11 +1612,11 @@
 
 	var srcProviderDepFiles android.Paths
 	for _, dep := range directSrcProvidersDeps {
-		srcs := android.OutputFilesForModule(ctx, dep, "")
+		srcs := android.OutputFilesForModule(ctx, *dep, "")
 		srcProviderDepFiles = append(srcProviderDepFiles, srcs...)
 	}
 	for _, dep := range directSrcDeps {
-		srcs := dep.Srcs()
+		srcs := dep.Srcs
 		srcProviderDepFiles = append(srcProviderDepFiles, srcs...)
 	}
 
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 90c1f61..9f65dec 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -424,11 +424,11 @@
 		}`)
 
 	fooRustc := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustc")
-	if !strings.Contains(fooRustc.Args["libFlags"], "--extern bar_renamed=out/soong/.intermediates/libbar/android_arm64_armv8-a_dylib/unstripped/libbar.dylib.so") {
-		t.Errorf("--extern bar_renamed=out/soong/.intermediates/libbar/android_arm64_armv8-a_dylib/unstripped/libbar.dylib.so flag not being passed to rustc for rust_binary with aliases. libFlags: %#v", fooRustc.Args["libFlags"])
+	if !strings.Contains(fooRustc.Args["libFlags"], "--extern force:bar_renamed=out/soong/.intermediates/libbar/android_arm64_armv8-a_dylib/unstripped/libbar.dylib.so") {
+		t.Errorf("--extern force:bar_renamed=out/soong/.intermediates/libbar/android_arm64_armv8-a_dylib/unstripped/libbar.dylib.so flag not being passed to rustc for rust_binary with aliases. libFlags: %#v", fooRustc.Args["libFlags"])
 	}
-	if !strings.Contains(fooRustc.Args["libFlags"], "--extern baz=out/soong/.intermediates/libbaz/android_arm64_armv8-a_dylib/unstripped/libbaz.dylib.so") {
-		t.Errorf("--extern baz=out/soong/.intermediates/libbaz/android_arm64_armv8-a_dylib/unstripped/libbaz.dylib.so flag not being passed to rustc for rust_binary with aliases. libFlags: %#v", fooRustc.Args["libFlags"])
+	if !strings.Contains(fooRustc.Args["libFlags"], "--extern force:baz=out/soong/.intermediates/libbaz/android_arm64_armv8-a_dylib/unstripped/libbaz.dylib.so") {
+		t.Errorf("--extern force:baz=out/soong/.intermediates/libbaz/android_arm64_armv8-a_dylib/unstripped/libbaz.dylib.so flag not being passed to rustc for rust_binary with aliases. libFlags: %#v", fooRustc.Args["libFlags"])
 	}
 }
 
diff --git a/ui/build/build.go b/ui/build/build.go
index 110ee95..26f5969 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -87,6 +87,17 @@
 	// without changing the command line every time.  Avoids rebuilds
 	// when using ninja.
 	writeValueIfChanged(ctx, config, config.SoongOutDir(), "build_number.txt", buildNumber)
+
+	hostname, ok := config.environ.Get("BUILD_HOSTNAME")
+	if !ok {
+		var err error
+		hostname, err = os.Hostname()
+		if err != nil {
+			ctx.Println("Failed to read hostname:", err)
+			hostname = "unknown"
+		}
+	}
+	writeValueIfChanged(ctx, config, config.SoongOutDir(), "build_hostname.txt", hostname)
 }
 
 var combinedBuildNinjaTemplate = template.Must(template.New("combined").Parse(`
@@ -96,8 +107,11 @@
 {{end -}}
 pool highmem_pool
  depth = {{.HighmemParallel}}
-{{if and (not .SkipKatiNinja) .HasKatiSuffix}}subninja {{.KatiBuildNinjaFile}}
+{{if and (not .SkipKatiNinja) .HasKatiSuffix}}
+subninja {{.KatiBuildNinjaFile}}
 subninja {{.KatiPackageNinjaFile}}
+{{else}}
+subninja {{.KatiSoongOnlyPackageNinjaFile}}
 {{end -}}
 subninja {{.SoongNinjaFile}}
 `))
@@ -335,25 +349,31 @@
 		return
 	}
 
+	// Still generate the kati suffix in soong-only builds because soong-only still uses kati for
+	// the packaging step. Also, the kati suffix is used for the combined ninja file.
+	genKatiSuffix(ctx, config)
+
 	if what&RunSoong != 0 {
 		runSoong(ctx, config)
 	}
 
 	if what&RunKati != 0 {
-		genKatiSuffix(ctx, config)
 		runKatiCleanSpec(ctx, config)
 		runKatiBuild(ctx, config)
-		runKatiPackage(ctx, config)
+		runKatiPackage(ctx, config, false)
 
-		ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0666) // a+rw
 	} else if what&RunKatiNinja != 0 {
 		// Load last Kati Suffix if it exists
-		if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil {
+		if katiSuffix, err := os.ReadFile(config.LastKatiSuffixFile()); err == nil {
 			ctx.Verboseln("Loaded previous kati config:", string(katiSuffix))
 			config.SetKatiSuffix(string(katiSuffix))
 		}
+	} else if what&RunSoong != 0 {
+		runKatiPackage(ctx, config, true)
 	}
 
+	os.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0666) // a+rw
+
 	// Write combined ninja file
 	createCombinedBuildNinjaFile(ctx, config)
 
diff --git a/ui/build/config.go b/ui/build/config.go
index dc468c2..4f2d213 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -1592,6 +1592,10 @@
 	return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+katiPackageSuffix+".ninja")
 }
 
+func (c *configImpl) KatiSoongOnlyPackageNinjaFile() string {
+	return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+katiSoongOnlyPackageSuffix+".ninja")
+}
+
 func (c *configImpl) SoongVarsFile() string {
 	targetProduct, err := c.TargetProductOrErr()
 	if err != nil {
@@ -1647,7 +1651,7 @@
 }
 
 func (c *configImpl) KatiPackageMkDir() string {
-	return filepath.Join(c.ProductOut(), "obj", "CONFIG", "kati_packaging")
+	return filepath.Join(c.SoongOutDir(), "kati_packaging"+c.KatiSuffix())
 }
 
 func (c *configImpl) hostOutRoot() string {
diff --git a/ui/build/kati.go b/ui/build/kati.go
index 4dfb710..6519573 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -31,6 +31,7 @@
 const katiBuildSuffix = ""
 const katiCleanspecSuffix = "-cleanspec"
 const katiPackageSuffix = "-package"
+const katiSoongOnlyPackageSuffix = "-soong-only-package"
 
 // genKatiSuffix creates a filename suffix for kati-generated files so that we
 // can cache them based on their inputs. Such files include the generated Ninja
@@ -40,8 +41,12 @@
 // Currently that includes the TARGET_PRODUCT and kati-processed command line
 // arguments.
 func genKatiSuffix(ctx Context, config Config) {
+	targetProduct := "unknown"
+	if p, err := config.TargetProductOrErr(); err == nil {
+		targetProduct = p
+	}
 	// Construct the base suffix.
-	katiSuffix := "-" + config.TargetProduct() + config.CoverageSuffix()
+	katiSuffix := "-" + targetProduct + config.CoverageSuffix()
 
 	// Append kati arguments to the suffix.
 	if args := config.KatiArgs(); len(args) > 0 {
@@ -68,13 +73,13 @@
 func writeValueIfChanged(ctx Context, config Config, dir string, filename string, value string) {
 	filePath := filepath.Join(dir, filename)
 	previousValue := ""
-	rawPreviousValue, err := ioutil.ReadFile(filePath)
+	rawPreviousValue, err := os.ReadFile(filePath)
 	if err == nil {
 		previousValue = string(rawPreviousValue)
 	}
 
 	if previousValue != value {
-		if err = ioutil.WriteFile(filePath, []byte(value), 0666); err != nil {
+		if err = os.WriteFile(filePath, []byte(value), 0666); err != nil {
 			ctx.Fatalf("Failed to write: %v", err)
 		}
 	}
@@ -200,18 +205,10 @@
 	//     fi
 	cmd.Environment.Unset("SOONG_USE_PARTIAL_COMPILE")
 
-	hostname, ok := cmd.Environment.Get("BUILD_HOSTNAME")
 	// Unset BUILD_HOSTNAME during kati run to avoid kati rerun, kati will use BUILD_HOSTNAME from a file.
 	cmd.Environment.Unset("BUILD_HOSTNAME")
-	if !ok {
-		hostname, err = os.Hostname()
-		if err != nil {
-			ctx.Println("Failed to read hostname:", err)
-			hostname = "unknown"
-		}
-	}
-	writeValueIfChanged(ctx, config, config.SoongOutDir(), "build_hostname.txt", hostname)
-	_, ok = cmd.Environment.Get("BUILD_NUMBER")
+
+	_, ok := cmd.Environment.Get("BUILD_NUMBER")
 	// Unset BUILD_NUMBER during kati run to avoid kati rerun, kati will use BUILD_NUMBER from a file.
 	cmd.Environment.Unset("BUILD_NUMBER")
 	if ok {
@@ -342,10 +339,19 @@
 
 // Generate the Ninja file containing the packaging command lines for the dist
 // dir.
-func runKatiPackage(ctx Context, config Config) {
+func runKatiPackage(ctx Context, config Config, soongOnly bool) {
 	ctx.BeginTrace(metrics.RunKati, "kati package")
 	defer ctx.EndTrace()
 
+	entryPoint := "build/make/packaging/main.mk"
+	suffix := katiPackageSuffix
+	ninjaFile := config.KatiPackageNinjaFile()
+	if soongOnly {
+		entryPoint = "build/make/packaging/main_soong_only.mk"
+		suffix = katiSoongOnlyPackageSuffix
+		ninjaFile = config.KatiSoongOnlyPackageNinjaFile()
+	}
+
 	args := []string{
 		// Mark the dist dir as writable.
 		"--writable", config.DistDir() + "/",
@@ -354,14 +360,14 @@
 		// Fail when redefining / duplicating a target.
 		"--werror_overriding_commands",
 		// Entry point.
-		"-f", "build/make/packaging/main.mk",
+		"-f", entryPoint,
 		// Directory containing .mk files for packaging purposes, such as
 		// the dist.mk file, containing dist-for-goals data.
 		"KATI_PACKAGE_MK_DIR=" + config.KatiPackageMkDir(),
 	}
 
 	// Run Kati against a restricted set of environment variables.
-	runKati(ctx, config, katiPackageSuffix, args, func(env *Environment) {
+	runKati(ctx, config, suffix, args, func(env *Environment) {
 		env.Allow([]string{
 			// Some generic basics
 			"LANG",
@@ -389,7 +395,7 @@
 	})
 
 	// Compress and dist the packaging Ninja file.
-	distGzipFile(ctx, config, config.KatiPackageNinjaFile())
+	distGzipFile(ctx, config, ninjaFile)
 }
 
 // Run Kati on the cleanspec files to clean the build.
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 82e5c96..58334a9 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -197,6 +197,8 @@
 func (pb PrimaryBuilderFactory) primaryBuilderInvocation(config Config) bootstrap.PrimaryBuilderInvocation {
 	commonArgs := make([]string, 0, 0)
 
+	commonArgs = append(commonArgs, "--kati_suffix", config.KatiSuffix())
+
 	if !pb.config.skipSoongTests {
 		commonArgs = append(commonArgs, "-t")
 	}
diff --git a/ui/metrics/proc/status_linux_test.go b/ui/metrics/proc/status_linux_test.go
index 6709850..0edc400 100644
--- a/ui/metrics/proc/status_linux_test.go
+++ b/ui/metrics/proc/status_linux_test.go
@@ -1,7 +1,6 @@
 package proc
 
 import (
-	"fmt"
 	"path/filepath"
 	"reflect"
 	"strconv"
@@ -29,7 +28,6 @@
 		t.Fatalf("got %v, want nil for error", err)
 	}
 
-	fmt.Printf("%d %d\b", status.VmPeak, expectedStatus.VmPeak)
 	if !reflect.DeepEqual(status, expectedStatus) {
 		t.Errorf("got %v, expecting %v for ProcStatus", status, expectedStatus)
 	}