Merge tag 'android-14.0.0_r50' into leaf-3.2

Android 14.0.0 Release 50 (AP2A.240605.024)

* tag 'android-14.0.0_r50': (355 commits)
  release-config: sync from goog/main
  Merge "Add secretkeeper-v1-java to platform APIs" into main am: f28b5697b8 am: 196729ffda
  Create rule to generate the exportable api files when checkapi is disabled
  Revert "Additional cleanup prior to adding the runtime stubs"
  Revert "Generate runtime stubs in droidstubs"
  Support partial module variants with apex_contributions
  Fix non-determinism in prebuilt selection
  Use BaseModuleName for LOCAL_MODULE
  Disable source javalib dexpreopt when using prebuilt apex
  Allow clippy::disallowed_names since it can be used for debugging
  Add the build flags for apex contribution contents
  Add a source_module_name to android_app
  Revert "Fix non-determinism in prebuilt selection"
  Fix non-determinism in prebuilt selection
  Copy imported APKs to output filename
  disablePrebuilt: also clear sanitizer-dependent Srcs
  Define libc++ config macros to nothing
  Remove --multitree-build
  Some tweaks to the aconfig flag collection logic
  Use R.txt files from aar files directly
  ...

Change-Id: I7c536315dd82ae00be6798089e7007e08c594606
diff --git a/OWNERS b/OWNERS
index 58edc89..01025fb 100644
--- a/OWNERS
+++ b/OWNERS
@@ -12,4 +12,6 @@
 mrziwang@google.com
 spandandas@google.com
 weiwli@google.com
-yudiliu@google.com
\ No newline at end of file
+yudiliu@google.com
+
+per-file build/soong/ui/build/androidmk_denylist.go = joeo@google.com, weiwli@google.com
\ No newline at end of file
diff --git a/README.md b/README.md
index 5e9e04a..93260e6 100644
--- a/README.md
+++ b/README.md
@@ -314,6 +314,9 @@
 * `["//visibility:override"]`: Discards any rules inherited from defaults or a
 creating module. Can only be used at the beginning of a list of visibility
 rules.
+* `["//visibility:any_partition"]`: Any modules of type android_filesystem
+or android_system_image can use this module. Intended for modules that no one
+should link against, but should still be included in soong-built partitions.
 * `["//some/package:__pkg__", "//other/package:__pkg__"]`: Only modules in
 `some/package` and `other/package` (defined in `some/package/*.bp` and
 `other/package/*.bp`) have access to this module. Note that sub-packages do not
@@ -603,7 +606,7 @@
 * [Build Performance](docs/perf.md)
 * [Generating CLion Projects](docs/clion.md)
 * [Generating YouCompleteMe/VSCode compile\_commands.json file](docs/compdb.md)
-* Make-specific documentation: [build/make/README.md](https://android.googlesource.com/platform/build/+/master/README.md)
+* Make-specific documentation: [build/make/README.md](https://android.googlesource.com/platform/build/+/main/README.md)
 
 ## Developing for Soong
 
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index 3c79f19..402cf16 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -25,6 +25,7 @@
         "aconfig_declarations_test.go",
         "aconfig_values_test.go",
         "aconfig_value_set_test.go",
+        "all_aconfig_declarations_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go
index 697dc22..392e819 100644
--- a/aconfig/aconfig_declarations.go
+++ b/aconfig/aconfig_declarations.go
@@ -40,6 +40,9 @@
 
 		// Container(system/vendor/apex) that this module belongs to
 		Container string
+
+		// The flags will only be repackaged if this prop is true.
+		Exportable bool
 	}
 
 	intermediatePath android.WritablePath
@@ -112,38 +115,17 @@
 	return sb.String()
 }
 
-// Provider published by aconfig_value_set
-type DeclarationsProviderData struct {
-	Package                     string
-	Container                   string
-	IntermediateCacheOutputPath android.WritablePath
-	IntermediateDumpOutputPath  android.WritablePath
-}
-
-var DeclarationsProviderKey = blueprint.NewProvider(DeclarationsProviderData{})
-
-// This is used to collect the aconfig declarations info on the transitive closure,
-// the data is keyed on the container.
-type TransitiveDeclarationsInfo struct {
-	AconfigFiles map[string]android.Paths
-}
-
-var TransitiveDeclarationsInfoProvider = blueprint.NewProvider(TransitiveDeclarationsInfo{})
-
 func (module *DeclarationsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// Get the values that came from the global RELEASE_ACONFIG_VALUE_SETS flag
 	valuesFiles := make([]android.Path, 0)
 	ctx.VisitDirectDeps(func(dep android.Module) {
-		if !ctx.OtherModuleHasProvider(dep, valueSetProviderKey) {
-			// Other modules get injected as dependencies too, for example the license modules
-			return
-		}
-		depData := ctx.OtherModuleProvider(dep, valueSetProviderKey).(valueSetProviderData)
-		paths, ok := depData.AvailablePackages[module.properties.Package]
-		if ok {
-			valuesFiles = append(valuesFiles, paths...)
-			for _, path := range paths {
-				module.properties.Values = append(module.properties.Values, path.String())
+		if depData, ok := android.OtherModuleProvider(ctx, dep, valueSetProviderKey); ok {
+			paths, ok := depData.AvailablePackages[module.properties.Package]
+			if ok {
+				valuesFiles = append(valuesFiles, paths...)
+				for _, path := range paths {
+					module.properties.Values = append(module.properties.Values, path.String())
+				}
 			}
 		}
 	})
@@ -177,56 +159,12 @@
 		Description: "aconfig_text",
 	})
 
-	ctx.SetProvider(DeclarationsProviderKey, DeclarationsProviderData{
+	android.SetProvider(ctx, android.AconfigDeclarationsProviderKey, android.AconfigDeclarationsProviderData{
 		Package:                     module.properties.Package,
 		Container:                   module.properties.Container,
+		Exportable:                  module.properties.Exportable,
 		IntermediateCacheOutputPath: intermediateCacheFilePath,
 		IntermediateDumpOutputPath:  intermediateDumpFilePath,
 	})
 
 }
-func CollectDependencyAconfigFiles(ctx android.ModuleContext, mergedAconfigFiles *map[string]android.Paths) {
-	if *mergedAconfigFiles == nil {
-		*mergedAconfigFiles = make(map[string]android.Paths)
-	}
-	ctx.VisitDirectDeps(func(module android.Module) {
-		if dep := ctx.OtherModuleProvider(module, DeclarationsProviderKey).(DeclarationsProviderData); dep.IntermediateCacheOutputPath != nil {
-			(*mergedAconfigFiles)[dep.Container] = append((*mergedAconfigFiles)[dep.Container], dep.IntermediateCacheOutputPath)
-			return
-		}
-		if dep := ctx.OtherModuleProvider(module, TransitiveDeclarationsInfoProvider).(TransitiveDeclarationsInfo); len(dep.AconfigFiles) > 0 {
-			for container, v := range dep.AconfigFiles {
-				(*mergedAconfigFiles)[container] = append((*mergedAconfigFiles)[container], v...)
-			}
-		}
-	})
-
-	for container, aconfigFiles := range *mergedAconfigFiles {
-		(*mergedAconfigFiles)[container] = mergeAconfigFiles(ctx, aconfigFiles)
-	}
-
-	ctx.SetProvider(TransitiveDeclarationsInfoProvider, TransitiveDeclarationsInfo{
-		AconfigFiles: *mergedAconfigFiles,
-	})
-}
-
-func mergeAconfigFiles(ctx android.ModuleContext, inputs android.Paths) android.Paths {
-	inputs = android.LastUniquePaths(inputs)
-	if len(inputs) == 1 {
-		return android.Paths{inputs[0]}
-	}
-
-	output := android.PathForModuleOut(ctx, "aconfig_merged.pb")
-
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        mergeAconfigFilesRule,
-		Description: "merge aconfig files",
-		Inputs:      inputs,
-		Output:      output,
-		Args: map[string]string{
-			"flags": android.JoinWithPrefix(inputs.Strings(), "--cache "),
-		},
-	})
-
-	return android.Paths{output}
-}
diff --git a/aconfig/aconfig_declarations_test.go b/aconfig/aconfig_declarations_test.go
index 9035c71..1fe3c86 100644
--- a/aconfig/aconfig_declarations_test.go
+++ b/aconfig/aconfig_declarations_test.go
@@ -27,6 +27,7 @@
 			name: "module_name",
 			package: "com.example.package",
 			container: "com.android.foo",
+			exportable: true,
 			srcs: [
 				"foo.aconfig",
 				"bar.aconfig",
@@ -38,9 +39,10 @@
 	module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule)
 
 	// Check that the provider has the right contents
-	depData := result.ModuleProvider(module, DeclarationsProviderKey).(DeclarationsProviderData)
+	depData, _ := android.SingletonModuleProvider(result, module, android.AconfigDeclarationsProviderKey)
 	android.AssertStringEquals(t, "package", depData.Package, "com.example.package")
 	android.AssertStringEquals(t, "container", depData.Container, "com.android.foo")
+	android.AssertBoolEquals(t, "exportable", depData.Exportable, true)
 	if !strings.HasSuffix(depData.IntermediateCacheOutputPath.String(), "/intermediate.pb") {
 		t.Errorf("Missing intermediates proto path in provider: %s", depData.IntermediateCacheOutputPath.String())
 	}
@@ -48,3 +50,22 @@
 		t.Errorf("Missing intermediates text path in provider: %s", depData.IntermediateDumpOutputPath.String())
 	}
 }
+
+func TestAconfigDeclarationsWithExportableUnset(t *testing.T) {
+	bp := `
+		aconfig_declarations {
+			name: "module_name",
+			package: "com.example.package",
+			container: "com.android.foo",
+			srcs: [
+				"foo.aconfig",
+				"bar.aconfig",
+			],
+		}
+	`
+	result := runTest(t, android.FixtureExpectsNoErrors, bp)
+
+	module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule)
+	depData, _ := android.SingletonModuleProvider(result, module, android.AconfigDeclarationsProviderKey)
+	android.AssertBoolEquals(t, "exportable", depData.Exportable, false)
+}
diff --git a/aconfig/aconfig_value_set.go b/aconfig/aconfig_value_set.go
index 94c322a..7ba76c0 100644
--- a/aconfig/aconfig_value_set.go
+++ b/aconfig/aconfig_value_set.go
@@ -54,7 +54,7 @@
 	AvailablePackages map[string]android.Paths
 }
 
-var valueSetProviderKey = blueprint.NewProvider(valueSetProviderData{})
+var valueSetProviderKey = blueprint.NewProvider[valueSetProviderData]()
 
 func (module *ValueSetModule) DepsMutator(ctx android.BottomUpMutatorContext) {
 	deps := ctx.AddDependency(ctx.Module(), valueSetTag, module.properties.Values...)
@@ -73,18 +73,14 @@
 	// to append values to their aconfig actions.
 	packages := make(map[string]android.Paths)
 	ctx.VisitDirectDeps(func(dep android.Module) {
-		if !ctx.OtherModuleHasProvider(dep, valuesProviderKey) {
-			// Other modules get injected as dependencies too, for example the license modules
-			return
+		if depData, ok := android.OtherModuleProvider(ctx, dep, valuesProviderKey); ok {
+			srcs := make([]android.Path, len(depData.Values))
+			copy(srcs, depData.Values)
+			packages[depData.Package] = srcs
 		}
-		depData := ctx.OtherModuleProvider(dep, valuesProviderKey).(valuesProviderData)
-
-		srcs := make([]android.Path, len(depData.Values))
-		copy(srcs, depData.Values)
-		packages[depData.Package] = srcs
 
 	})
-	ctx.SetProvider(valueSetProviderKey, valueSetProviderData{
+	android.SetProvider(ctx, valueSetProviderKey, valueSetProviderData{
 		AvailablePackages: packages,
 	})
 }
diff --git a/aconfig/aconfig_value_set_test.go b/aconfig/aconfig_value_set_test.go
index 9127872..7d18999 100644
--- a/aconfig/aconfig_value_set_test.go
+++ b/aconfig/aconfig_value_set_test.go
@@ -38,6 +38,6 @@
 	module := result.ModuleForTests("module_name", "").Module().(*ValueSetModule)
 
 	// Check that the provider has the right contents
-	depData := result.ModuleProvider(module, valueSetProviderKey).(valueSetProviderData)
+	depData, _ := android.SingletonModuleProvider(result, module, valueSetProviderKey)
 	android.AssertStringEquals(t, "AvailablePackages", "blah.aconfig_values", depData.AvailablePackages["foo.package"][0].String())
 }
diff --git a/aconfig/aconfig_values.go b/aconfig/aconfig_values.go
index 621aae8..239b10c 100644
--- a/aconfig/aconfig_values.go
+++ b/aconfig/aconfig_values.go
@@ -52,7 +52,7 @@
 	Values android.Paths
 }
 
-var valuesProviderKey = blueprint.NewProvider(valuesProviderData{})
+var valuesProviderKey = blueprint.NewProvider[valuesProviderData]()
 
 func (module *ValuesModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if len(module.properties.Package) == 0 {
@@ -64,5 +64,5 @@
 		Package: module.properties.Package,
 		Values:  android.PathsForModuleSrc(ctx, module.properties.Srcs),
 	}
-	ctx.SetProvider(valuesProviderKey, providerData)
+	android.SetProvider(ctx, valuesProviderKey, providerData)
 }
diff --git a/aconfig/aconfig_values_test.go b/aconfig/aconfig_values_test.go
index ab457f0..526579c 100644
--- a/aconfig/aconfig_values_test.go
+++ b/aconfig/aconfig_values_test.go
@@ -33,7 +33,7 @@
 	module := result.ModuleForTests("module_name", "").Module().(*ValuesModule)
 
 	// Check that the provider has the right contents
-	depData := result.ModuleProvider(module, valuesProviderKey).(valuesProviderData)
+	depData, _ := android.SingletonModuleProvider(result, module, valuesProviderKey)
 	android.AssertStringEquals(t, "package", "foo.package", depData.Package)
 	android.AssertPathsEndWith(t, "srcs", []string{"blah.aconfig_values"}, depData.Values)
 }
diff --git a/aconfig/all_aconfig_declarations.go b/aconfig/all_aconfig_declarations.go
index 2686e76..72fe495 100644
--- a/aconfig/all_aconfig_declarations.go
+++ b/aconfig/all_aconfig_declarations.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"fmt"
 )
 
 // A singleton module that collects all of the aconfig flags declared in the
@@ -30,34 +31,66 @@
 }
 
 type allAconfigDeclarationsSingleton struct {
-	intermediatePath android.OutputPath
+	intermediateBinaryProtoPath android.OutputPath
+	intermediateTextProtoPath   android.OutputPath
 }
 
 func (this *allAconfigDeclarationsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	// Find all of the aconfig_declarations modules
+	var packages = make(map[string]int)
 	var cacheFiles android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
-		if !ctx.ModuleHasProvider(module, DeclarationsProviderKey) {
+		decl, ok := android.SingletonModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey)
+		if !ok {
 			return
 		}
-		decl := ctx.ModuleProvider(module, DeclarationsProviderKey).(DeclarationsProviderData)
 		cacheFiles = append(cacheFiles, decl.IntermediateCacheOutputPath)
+		packages[decl.Package]++
 	})
 
-	// Generate build action for aconfig
-	this.intermediatePath = android.PathForIntermediates(ctx, "all_aconfig_declarations.pb")
+	var numOffendingPkg = 0
+	for pkg, cnt := range packages {
+		if cnt > 1 {
+			fmt.Printf("%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."))
+	}
+
+	// Generate build action for aconfig (binary proto output)
+	this.intermediateBinaryProtoPath = android.PathForIntermediates(ctx, "all_aconfig_declarations.pb")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        AllDeclarationsRule,
 		Inputs:      cacheFiles,
-		Output:      this.intermediatePath,
+		Output:      this.intermediateBinaryProtoPath,
 		Description: "all_aconfig_declarations",
 		Args: map[string]string{
 			"cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "),
 		},
 	})
-	ctx.Phony("all_aconfig_declarations", this.intermediatePath)
+	ctx.Phony("all_aconfig_declarations", this.intermediateBinaryProtoPath)
+
+	// Generate build action for aconfig (text proto output)
+	this.intermediateTextProtoPath = android.PathForIntermediates(ctx, "all_aconfig_declarations.textproto")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        AllDeclarationsRuleTextProto,
+		Inputs:      cacheFiles,
+		Output:      this.intermediateTextProtoPath,
+		Description: "all_aconfig_declarations_textproto",
+		Args: map[string]string{
+			"cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "),
+		},
+	})
+	ctx.Phony("all_aconfig_declarations_textproto", this.intermediateTextProtoPath)
 }
 
 func (this *allAconfigDeclarationsSingleton) MakeVars(ctx android.MakeVarsContext) {
-	ctx.DistForGoal("droid", this.intermediatePath)
+	ctx.DistForGoal("droid", this.intermediateBinaryProtoPath)
+	for _, goal := range []string{"droid", "sdk"} {
+		ctx.DistForGoalWithFilename(goal, this.intermediateBinaryProtoPath, "flags.pb")
+		ctx.DistForGoalWithFilename(goal, this.intermediateTextProtoPath, "flags.textproto")
+	}
 }
diff --git a/aconfig/all_aconfig_declarations_test.go b/aconfig/all_aconfig_declarations_test.go
new file mode 100644
index 0000000..0b2021e
--- /dev/null
+++ b/aconfig/all_aconfig_declarations_test.go
@@ -0,0 +1,48 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aconfig
+
+import (
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestTwoAconfigDeclarationsPerPackage(t *testing.T) {
+	bp := `
+		aconfig_declarations {
+			name: "module_name.foo",
+			package: "com.example.package",
+			container: "com.android.foo",
+			srcs: [
+				"foo.aconfig",
+			],
+		}
+
+		aconfig_declarations {
+			name: "module_name.bar",
+			package: "com.example.package",
+			container: "com.android.foo",
+			srcs: [
+				"bar.aconfig",
+			],
+		}
+	`
+	errMsg := "Only one aconfig_declarations allowed for each package."
+	android.GroupFixturePreparers(
+		PrepareForTestWithAconfigBuildComponents).
+		ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(errMsg)).
+		RunTestWithBp(t, bp)
+}
diff --git a/aconfig/codegen/Android.bp b/aconfig/codegen/Android.bp
index 494f7e6..0c78b94 100644
--- a/aconfig/codegen/Android.bp
+++ b/aconfig/codegen/Android.bp
@@ -17,6 +17,7 @@
         "soong-rust",
     ],
     srcs: [
+        "aconfig_declarations_group.go",
         "cc_aconfig_library.go",
         "init.go",
         "java_aconfig_library.go",
@@ -24,6 +25,7 @@
         "testing.go",
     ],
     testSrcs: [
+        "aconfig_declarations_group_test.go",
         "java_aconfig_library_test.go",
         "cc_aconfig_library_test.go",
         "rust_aconfig_library_test.go",
diff --git a/aconfig/codegen/aconfig_declarations_group.go b/aconfig/codegen/aconfig_declarations_group.go
new file mode 100644
index 0000000..203b6be
--- /dev/null
+++ b/aconfig/codegen/aconfig_declarations_group.go
@@ -0,0 +1,129 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package codegen
+
+import (
+	"android/soong/aconfig"
+	"android/soong/android"
+	"fmt"
+
+	"github.com/google/blueprint"
+)
+
+type dependencyTag struct {
+	blueprint.BaseDependencyTag
+	name string
+}
+
+var (
+	aconfigDeclarationsGroupTag = dependencyTag{name: "aconfigDeclarationsGroup"}
+	javaAconfigLibraryTag       = dependencyTag{name: "javaAconfigLibrary"}
+	ccAconfigLibraryTag         = dependencyTag{name: "ccAconfigLibrary"}
+	rustAconfigLibraryTag       = dependencyTag{name: "rustAconfigLibrary"}
+)
+
+type AconfigDeclarationsGroup struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	properties AconfigDeclarationsGroupProperties
+
+	aconfigDeclarationNames      []string
+	intermediateCacheOutputPaths android.Paths
+	javaSrcjars                  android.Paths
+}
+
+type AconfigDeclarationsGroupProperties struct {
+
+	// Name of the aconfig_declarations_group modules
+	Aconfig_declarations_groups []string
+
+	// Name of the java_aconfig_library modules
+	Java_aconfig_libraries []string
+
+	// Name of the cc_aconfig_library modules
+	Cc_aconfig_libraries []string
+
+	// Name of the rust_aconfig_library modules
+	Rust_aconfig_libraries []string
+}
+
+func AconfigDeclarationsGroupFactory() android.Module {
+	module := &AconfigDeclarationsGroup{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidModule(module)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+func (adg *AconfigDeclarationsGroup) DepsMutator(ctx android.BottomUpMutatorContext) {
+	ctx.AddDependency(ctx.Module(), aconfigDeclarationsGroupTag, adg.properties.Aconfig_declarations_groups...)
+	ctx.AddDependency(ctx.Module(), javaAconfigLibraryTag, adg.properties.Java_aconfig_libraries...)
+	ctx.AddDependency(ctx.Module(), ccAconfigLibraryTag, adg.properties.Cc_aconfig_libraries...)
+	ctx.AddDependency(ctx.Module(), rustAconfigLibraryTag, adg.properties.Rust_aconfig_libraries...)
+}
+
+func (adg *AconfigDeclarationsGroup) VisitDeps(ctx android.ModuleContext) {
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		tag := ctx.OtherModuleDependencyTag(dep)
+		if provider, ok := android.OtherModuleProvider(ctx, dep, aconfig.CodegenInfoProvider); ok {
+
+			// aconfig declaration names and cache files are collected for all aconfig library dependencies
+			adg.aconfigDeclarationNames = append(adg.aconfigDeclarationNames, provider.AconfigDeclarations...)
+			adg.intermediateCacheOutputPaths = append(adg.intermediateCacheOutputPaths, provider.IntermediateCacheOutputPaths...)
+
+			switch tag {
+			case aconfigDeclarationsGroupTag:
+				// Will retrieve outputs from another language codegen modules when support is added
+				adg.javaSrcjars = append(adg.javaSrcjars, provider.Srcjars...)
+			case javaAconfigLibraryTag:
+				adg.javaSrcjars = append(adg.javaSrcjars, provider.Srcjars...)
+			}
+		}
+	})
+}
+
+func (adg *AconfigDeclarationsGroup) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	adg.VisitDeps(ctx)
+	adg.aconfigDeclarationNames = android.FirstUniqueStrings(adg.aconfigDeclarationNames)
+	adg.intermediateCacheOutputPaths = android.FirstUniquePaths(adg.intermediateCacheOutputPaths)
+
+	android.SetProvider(ctx, aconfig.CodegenInfoProvider, aconfig.CodegenInfo{
+		AconfigDeclarations:          adg.aconfigDeclarationNames,
+		IntermediateCacheOutputPaths: adg.intermediateCacheOutputPaths,
+		Srcjars:                      adg.javaSrcjars,
+	})
+}
+
+var _ android.OutputFileProducer = (*AconfigDeclarationsGroup)(nil)
+
+func (adg *AconfigDeclarationsGroup) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return adg.intermediateCacheOutputPaths, nil
+	case ".srcjars":
+		return adg.javaSrcjars, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %s", tag)
+	}
+}
+
+func (adg *AconfigDeclarationsGroup) Srcjars() android.Paths {
+	return adg.javaSrcjars
+}
+
+func (adg *AconfigDeclarationsGroup) AconfigDeclarations() []string {
+	return adg.aconfigDeclarationNames
+}
diff --git a/aconfig/codegen/aconfig_declarations_group_test.go b/aconfig/codegen/aconfig_declarations_group_test.go
new file mode 100644
index 0000000..ec7cea3
--- /dev/null
+++ b/aconfig/codegen/aconfig_declarations_group_test.go
@@ -0,0 +1,79 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package codegen
+
+import (
+	"android/soong/android"
+	"android/soong/java"
+	"testing"
+)
+
+func TestAconfigDeclarationsGroup(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithAconfigBuildComponents,
+		java.PrepareForTestWithJavaDefaultModules,
+	).RunTestWithBp(t, `
+		aconfig_declarations {
+			name: "foo-aconfig",
+			package: "com.example.package",
+			srcs: ["foo.aconfig"],
+		}
+
+		java_aconfig_library {
+			name: "foo-java",
+			aconfig_declarations: "foo-aconfig",
+		}
+
+		aconfig_declarations {
+			name: "bar-aconfig",
+			package: "com.example.package",
+			srcs: ["foo.aconfig"],
+		}
+
+		java_aconfig_library {
+			name: "bar-java",
+			aconfig_declarations: "bar-aconfig",
+		}
+
+		aconfig_declarations_group {
+			name: "my_group",
+			java_aconfig_libraries: [
+				"foo-java",
+				"bar-java",
+			],
+		}
+
+		java_library {
+			name: "baz",
+			srcs: [
+				":my_group{.srcjars}",
+			],
+		}
+	`)
+
+	// Check if aconfig_declarations_group module depends on the aconfig_library modules
+	java.CheckModuleDependencies(t, result.TestContext, "my_group", "", []string{
+		`bar-java`,
+		`foo-java`,
+	})
+
+	// Check if srcjar files are correctly passed to the reverse dependency of
+	// aconfig_declarations_group module
+	bazModule := result.ModuleForTests("baz", "android_common")
+	bazJavacSrcjars := bazModule.Rule("javac").Args["srcJars"]
+	errorMessage := "baz javac argument expected to contain srcjar provided by aconfig_declrations_group"
+	android.AssertStringDoesContain(t, errorMessage, bazJavacSrcjars, "foo-java.srcjar")
+	android.AssertStringDoesContain(t, errorMessage, bazJavacSrcjars, "bar-java.srcjar")
+}
diff --git a/aconfig/codegen/cc_aconfig_library.go b/aconfig/codegen/cc_aconfig_library.go
index 30f6863..50cd4de 100644
--- a/aconfig/codegen/cc_aconfig_library.go
+++ b/aconfig/codegen/cc_aconfig_library.go
@@ -15,7 +15,6 @@
 package codegen
 
 import (
-	"android/soong/aconfig"
 	"android/soong/android"
 	"android/soong/cc"
 
@@ -41,6 +40,7 @@
 	// default mode is "production", the other accepted modes are:
 	// "test": to generate test mode version of the library
 	// "exported": to generate exported mode version of the library
+	// "force-read-only": to generate force-read-only mode version of the library
 	// an error will be thrown if the mode is not supported
 	Mode *string
 }
@@ -77,8 +77,12 @@
 		ctx.AddDependency(ctx.Module(), ccDeclarationsTag, declarations)
 	}
 
-	// Add a dependency for the aconfig flags base library
-	deps.SharedLibs = append(deps.SharedLibs, baseLibDep)
+	mode := proptools.StringDefault(this.properties.Mode, "production")
+
+	// Add a dependency for the aconfig flags base library if it is not forced read only
+	if mode != "force-read-only" {
+		deps.SharedLibs = append(deps.SharedLibs, baseLibDep)
+	}
 	// TODO: It'd be really nice if we could reexport this library and not make everyone do it.
 
 	return deps
@@ -92,7 +96,7 @@
 	if len(declarationsModules) != 1 {
 		panic(fmt.Errorf("Exactly one aconfig_declarations property required"))
 	}
-	declarations := ctx.OtherModuleProvider(declarationsModules[0], aconfig.DeclarationsProviderKey).(aconfig.DeclarationsProviderData)
+	declarations, _ := android.OtherModuleProvider(ctx, declarationsModules[0], android.AconfigDeclarationsProviderKey)
 
 	// Figure out the generated file paths.  This has to match aconfig's codegen_cpp.rs.
 	this.generatedDir = android.PathForModuleGen(ctx)
@@ -122,7 +126,7 @@
 	if len(declarationsModules) != 1 {
 		panic(fmt.Errorf("Exactly one aconfig_declarations property required"))
 	}
-	declarations := ctx.OtherModuleProvider(declarationsModules[0], aconfig.DeclarationsProviderKey).(aconfig.DeclarationsProviderData)
+	declarations, _ := android.OtherModuleProvider(ctx, declarationsModules[0], android.AconfigDeclarationsProviderKey)
 
 	mode := proptools.StringDefault(this.properties.Mode, "production")
 	if !isModeSupported(mode) {
diff --git a/aconfig/codegen/cc_aconfig_library_test.go b/aconfig/codegen/cc_aconfig_library_test.go
index 0c8a969..05449bc 100644
--- a/aconfig/codegen/cc_aconfig_library_test.go
+++ b/aconfig/codegen/cc_aconfig_library_test.go
@@ -20,6 +20,8 @@
 
 	"android/soong/android"
 	"android/soong/cc"
+
+	"github.com/google/blueprint"
 )
 
 var ccCodegenModeTestData = []struct {
@@ -29,6 +31,7 @@
 	{"mode: `production`,", "production"},
 	{"mode: `test`,", "test"},
 	{"mode: `exported`,", "exported"},
+	{"mode: `force-read-only`,", "force-read-only"},
 }
 
 func TestCCCodegenMode(t *testing.T) {
@@ -104,3 +107,92 @@
 			}
 		`, bpMode))
 }
+
+func TestAndroidMkCcLibrary(t *testing.T) {
+	bp := `
+		aconfig_declarations {
+			name: "my_aconfig_declarations_foo",
+			package: "com.example.package",
+			srcs: ["foo.aconfig"],
+			container: "vendor",
+		}
+
+		cc_aconfig_library {
+			name: "my_cc_aconfig_library_foo",
+			aconfig_declarations: "my_aconfig_declarations_foo",
+			vendor_available: true,
+		}
+
+		aconfig_declarations {
+			name: "my_aconfig_declarations_bar",
+			package: "com.example.package",
+			srcs: ["bar.aconfig"],
+		}
+
+		cc_aconfig_library {
+			name: "my_cc_aconfig_library_bar",
+			aconfig_declarations: "my_aconfig_declarations_bar",
+			vendor_available: true,
+		}
+
+		cc_library {
+			name: "my_cc_library",
+			srcs: [
+				"src/foo.cc",
+			],
+			static_libs: [
+				"my_cc_aconfig_library_foo",
+				"my_cc_aconfig_library_bar",
+			],
+			vendor: true,
+		}
+
+		cc_library {
+			name: "server_configurable_flags",
+			srcs: ["server_configurable_flags.cc"],
+			vendor_available: true,
+		}
+	`
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithAconfigBuildComponents,
+		cc.PrepareForTestWithCcDefaultModules).
+		ExtendWithErrorHandler(android.FixtureExpectsNoErrors).RunTestWithBp(t, bp)
+
+	module := result.ModuleForTests("my_cc_library", "android_vendor_arm64_armv8-a_shared").Module()
+
+	entry := android.AndroidMkEntriesForTest(t, result.TestContext, module)[0]
+
+	makeVar := entry.EntryMap["LOCAL_ACONFIG_FILES"]
+	android.EnsureListContainsSuffix(t, makeVar, "my_aconfig_declarations_foo/intermediate.pb")
+}
+
+func TestForceReadOnly(t *testing.T) {
+	t.Helper()
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithAconfigBuildComponents,
+		cc.PrepareForTestWithCcDefaultModules).
+		ExtendWithErrorHandler(android.FixtureExpectsNoErrors).
+		RunTestWithBp(t, fmt.Sprintf(`
+			aconfig_declarations {
+				name: "my_aconfig_declarations",
+				package: "com.example.package",
+				srcs: ["foo.aconfig"],
+			}
+
+			cc_aconfig_library {
+				name: "my_cc_aconfig_library",
+				aconfig_declarations: "my_aconfig_declarations",
+				mode: "force-read-only",
+			}
+		`))
+
+	module := result.ModuleForTests("my_cc_aconfig_library", "android_arm64_armv8-a_shared").Module()
+	dependOnBaseLib := false
+	result.VisitDirectDeps(module, func(dep blueprint.Module) {
+		if dep.Name() == baseLibDep {
+			dependOnBaseLib = true
+		}
+	})
+	android.AssertBoolEquals(t, "should not have dependency on server_configuriable_flags",
+		dependOnBaseLib, false)
+}
diff --git a/aconfig/codegen/init.go b/aconfig/codegen/init.go
index 0bff9d2..73a8951 100644
--- a/aconfig/codegen/init.go
+++ b/aconfig/codegen/init.go
@@ -77,6 +77,7 @@
 }
 
 func RegisterBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("aconfig_declarations_group", AconfigDeclarationsGroupFactory)
 	ctx.RegisterModuleType("cc_aconfig_library", CcAconfigLibraryFactory)
 	ctx.RegisterModuleType("java_aconfig_library", JavaDeclarationsLibraryFactory)
 	ctx.RegisterModuleType("rust_aconfig_library", RustAconfigLibraryFactory)
diff --git a/aconfig/codegen/java_aconfig_library.go b/aconfig/codegen/java_aconfig_library.go
index 202e358..7d7296e 100644
--- a/aconfig/codegen/java_aconfig_library.go
+++ b/aconfig/codegen/java_aconfig_library.go
@@ -31,7 +31,7 @@
 
 var declarationsTag = declarationsTagType{}
 
-var aconfigSupportedModes = []string{"production", "test", "exported"}
+var aconfigSupportedModes = []string{"production", "test", "exported", "force-read-only"}
 
 type JavaAconfigDeclarationsLibraryProperties struct {
 	// name of the aconfig_declarations module to generate a library for
@@ -40,6 +40,7 @@
 	// default mode is "production", the other accepted modes are:
 	// "test": to generate test mode version of the library
 	// "exported": to generate exported mode version of the library
+	// "force-read-only": to generate force-read-only mode version of the library
 	// an error will be thrown if the mode is not supported
 	Mode *string
 }
@@ -62,10 +63,18 @@
 		ctx.AddDependency(ctx.Module(), declarationsTag, declarations)
 	}
 
-	// Add aconfig-annotations-lib as a dependency for the optimization / code stripping annotations
-	module.AddSharedLibrary("aconfig-annotations-lib")
-	// TODO(b/303773055): Remove the annotation after access issue is resolved.
-	module.AddSharedLibrary("unsupportedappusage")
+	// "libcore_aconfig_flags_lib" module has a circular dependency because the shared libraries
+	// are built on core_current and the module is used to flag the APIs in the core_current.
+	// http://b/316554963#comment2 has the details of the circular dependency chain.
+	// If a java_aconfig_library uses "none" sdk_version, it should include and build these
+	// annotation files as the shared library themselves.
+	var addLibraries bool = module.Library.Module.SdkVersion(ctx).Kind != android.SdkNone
+	if addLibraries {
+		// Add aconfig-annotations-lib as a dependency for the optimization / code stripping annotations
+		module.AddSharedLibrary("aconfig-annotations-lib")
+		// TODO(b/303773055): Remove the annotation after access issue is resolved.
+		module.AddSharedLibrary("unsupportedappusage")
+	}
 }
 
 func (callbacks *JavaAconfigDeclarationsLibraryCallbacks) GenerateSourceJarBuildActions(module *java.GeneratedJavaLibraryModule, ctx android.ModuleContext) android.Path {
@@ -74,7 +83,7 @@
 	if len(declarationsModules) != 1 {
 		panic(fmt.Errorf("Exactly one aconfig_declarations property required"))
 	}
-	declarations := ctx.OtherModuleProvider(declarationsModules[0], aconfig.DeclarationsProviderKey).(aconfig.DeclarationsProviderData)
+	declarations, _ := android.OtherModuleProvider(ctx, declarationsModules[0], android.AconfigDeclarationsProviderKey)
 
 	// Generate the action to build the srcjar
 	srcJarPath := android.PathForModuleGen(ctx, ctx.ModuleName()+".srcjar")
@@ -83,6 +92,12 @@
 	if !isModeSupported(mode) {
 		ctx.PropertyErrorf("mode", "%q is not a supported mode", mode)
 	}
+	// TODO: uncomment this part after internal clean up
+	//if mode == "exported" && !declarations.Exportable {
+	//	// if mode is exported, the corresponding aconfig_declaration must mark its
+	//	// exportable property true
+	//	ctx.PropertyErrorf("mode", "exported mode requires its aconfig_declaration has exportable prop true")
+	//}
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        javaRule,
@@ -94,6 +109,22 @@
 		},
 	})
 
+	if declarations.Exportable {
+		// Mark our generated code as possibly needing jarjar repackaging
+		// The repackaging only happens when the corresponding aconfig_declaration
+		// has property exportable true
+		module.AddJarJarRenameRule(declarations.Package+".Flags", "")
+		module.AddJarJarRenameRule(declarations.Package+".FeatureFlags", "")
+		module.AddJarJarRenameRule(declarations.Package+".FeatureFlagsImpl", "")
+		module.AddJarJarRenameRule(declarations.Package+".FakeFeatureFlagsImpl", "")
+	}
+
+	android.SetProvider(ctx, aconfig.CodegenInfoProvider, aconfig.CodegenInfo{
+		AconfigDeclarations:          []string{declarationsModules[0].Name()},
+		IntermediateCacheOutputPaths: android.Paths{declarations.IntermediateCacheOutputPath},
+		Srcjars:                      android.Paths{srcJarPath},
+	})
+
 	return srcJarPath
 }
 
diff --git a/aconfig/codegen/java_aconfig_library_test.go b/aconfig/codegen/java_aconfig_library_test.go
index 2523abc..de45b5c 100644
--- a/aconfig/codegen/java_aconfig_library_test.go
+++ b/aconfig/codegen/java_aconfig_library_test.go
@@ -34,7 +34,7 @@
 		RunTestWithBp(t, bp+`
 			aconfig_declarations {
 				name: "my_aconfig_declarations_foo",
-				package: "com.example.package",
+				package: "com.example.package.foo",
 				srcs: ["foo.aconfig"],
 			}
 
@@ -45,7 +45,7 @@
 
 			aconfig_declarations {
 				name: "my_aconfig_declarations_bar",
-				package: "com.example.package",
+				package: "com.example.package.bar",
 				srcs: ["bar.aconfig"],
 			}
 
@@ -60,7 +60,6 @@
 	entry := android.AndroidMkEntriesForTest(t, result.TestContext, module)[0]
 
 	makeVar := entry.EntryMap["LOCAL_ACONFIG_FILES"]
-	android.AssertIntEquals(t, "len(LOCAL_ACONFIG_FILES)", 1, len(makeVar))
 	android.EnsureListContainsSuffix(t, makeVar, "android_common/aconfig_merged.pb")
 }
 
@@ -177,6 +176,7 @@
 				name: "my_aconfig_declarations",
 				package: "com.example.package",
 				srcs: ["foo.aconfig"],
+				exportable: true,
 			}
 
 			java_aconfig_library {
@@ -227,6 +227,10 @@
 	testCodegenMode(t, "mode: `exported`,", "exported")
 }
 
+func TestForceReadOnlyMode(t *testing.T) {
+	testCodegenMode(t, "mode: `force-read-only`,", "force-read-only")
+}
+
 func TestUnsupportedMode(t *testing.T) {
 	testCodegenModeWithError(t, "mode: `unsupported`,", "mode: \"unsupported\" is not a supported mode")
 }
diff --git a/aconfig/codegen/rust_aconfig_library.go b/aconfig/codegen/rust_aconfig_library.go
index e587056..2ab54b6 100644
--- a/aconfig/codegen/rust_aconfig_library.go
+++ b/aconfig/codegen/rust_aconfig_library.go
@@ -3,7 +3,6 @@
 import (
 	"fmt"
 
-	"android/soong/aconfig"
 	"android/soong/android"
 	"android/soong/rust"
 
@@ -24,6 +23,7 @@
 	// default mode is "production", the other accepted modes are:
 	// "test": to generate test mode version of the library
 	// "exported": to generate exported mode version of the library
+	// "force-read-only": to generate force-read-only mode version of the library
 	// an error will be thrown if the mode is not supported
 	Mode *string
 }
@@ -65,7 +65,7 @@
 	if len(declarationsModules) != 1 {
 		panic(fmt.Errorf("Exactly one aconfig_declarations property required"))
 	}
-	declarations := ctx.OtherModuleProvider(declarationsModules[0], aconfig.DeclarationsProviderKey).(aconfig.DeclarationsProviderData)
+	declarations, _ := android.OtherModuleProvider(ctx, declarationsModules[0], android.AconfigDeclarationsProviderKey)
 
 	mode := proptools.StringDefault(a.Properties.Mode, "production")
 	if !isModeSupported(mode) {
diff --git a/aconfig/codegen/rust_aconfig_library_test.go b/aconfig/codegen/rust_aconfig_library_test.go
index c09f701..60bc9f7 100644
--- a/aconfig/codegen/rust_aconfig_library_test.go
+++ b/aconfig/codegen/rust_aconfig_library_test.go
@@ -72,6 +72,7 @@
 	{"mode: `production`,", "production"},
 	{"mode: `test`,", "test"},
 	{"mode: `exported`,", "exported"},
+	{"mode: `force-read-only`,", "force-read-only"},
 }
 
 func TestRustCodegenMode(t *testing.T) {
diff --git a/aconfig/exported_java_aconfig_library.go b/aconfig/exported_java_aconfig_library.go
index 45c5e39..291938f 100644
--- a/aconfig/exported_java_aconfig_library.go
+++ b/aconfig/exported_java_aconfig_library.go
@@ -30,10 +30,10 @@
 	// Find all of the aconfig_declarations modules
 	var cacheFiles android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
-		if !ctx.ModuleHasProvider(module, DeclarationsProviderKey) {
+		decl, ok := android.SingletonModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey)
+		if !ok {
 			return
 		}
-		decl := ctx.ModuleProvider(module, DeclarationsProviderKey).(DeclarationsProviderData)
 		cacheFiles = append(cacheFiles, decl.IntermediateCacheOutputPath)
 	})
 
diff --git a/aconfig/init.go b/aconfig/init.go
index 05fab4c..e64429f 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -20,6 +20,20 @@
 	"github.com/google/blueprint"
 )
 
+type CodegenInfo struct {
+	// AconfigDeclarations is the name of the aconfig_declarations modules that
+	// the codegen module is associated with
+	AconfigDeclarations []string
+
+	// Paths to the cache files of the associated aconfig_declaration modules
+	IntermediateCacheOutputPaths android.Paths
+
+	// Paths to the srcjar files generated from the java_aconfig_library modules
+	Srcjars android.Paths
+}
+
+var CodegenInfoProvider = blueprint.NewProvider[CodegenInfo]()
+
 var (
 	pctx = android.NewPackageContext("android/soong/aconfig")
 
@@ -43,7 +57,7 @@
 	// For create-device-config-sysprops: Generate aconfig flag value map text file
 	aconfigTextRule = pctx.AndroidStaticRule("aconfig_text",
 		blueprint.RuleParams{
-			Command: `${aconfig} dump --format bool` +
+			Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}={state:bool}'` +
 				` --cache ${in}` +
 				` --out ${out}.tmp` +
 				` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
@@ -56,24 +70,42 @@
 	// For all_aconfig_declarations: Combine all parsed_flags proto files
 	AllDeclarationsRule = pctx.AndroidStaticRule("All_aconfig_declarations_dump",
 		blueprint.RuleParams{
-			Command: `${aconfig} dump --format protobuf --out ${out} ${cache_files}`,
+			Command: `${aconfig} dump-cache --dedup --format protobuf --out ${out} ${cache_files}`,
+			CommandDeps: []string{
+				"${aconfig}",
+			},
+		}, "cache_files")
+	AllDeclarationsRuleTextProto = pctx.AndroidStaticRule("All_aconfig_declarations_dump_textproto",
+		blueprint.RuleParams{
+			Command: `${aconfig} dump-cache --dedup --format textproto --out ${out} ${cache_files}`,
 			CommandDeps: []string{
 				"${aconfig}",
 			},
 		}, "cache_files")
 
-	mergeAconfigFilesRule = pctx.AndroidStaticRule("mergeAconfigFilesRule",
+	CreateStorageRule = pctx.AndroidStaticRule("aconfig_create_storage",
 		blueprint.RuleParams{
-			Command:     `${aconfig} dump --dedup --format protobuf --out $out $flags`,
-			CommandDeps: []string{"${aconfig}"},
-		}, "flags")
+			Command: `${aconfig} create-storage --container ${container} --file ${file_type} --out ${out} ${cache_files}`,
+			CommandDeps: []string{
+				"${aconfig}",
+			},
+		}, "container", "file_type", "cache_files")
+
 	// For exported_java_aconfig_library: Generate a JAR from all
 	// java_aconfig_libraries to be consumed by apps built outside the
 	// platform
 	exportedJavaRule = pctx.AndroidStaticRule("exported_java_aconfig_library",
+		// For each aconfig cache file, if the cache contains any
+		// exported flags, generate Java flag lookup code for the
+		// exported flags (only). Finally collect all generated code
+		// into the ${out} JAR file.
 		blueprint.RuleParams{
 			Command: `rm -rf ${out}.tmp` +
-				`&& for cache in ${cache_files}; do ${aconfig} create-java-lib --cache $$cache --out ${out}.tmp; done` +
+				`&& for cache in ${cache_files}; do ` +
+				`  if [ -n "$$(${aconfig} dump-cache --dedup --cache $$cache --filter=is_exported:true --format='{fully_qualified_name}')" ]; then ` +
+				`    ${aconfig} create-java-lib --cache $$cache --mode=exported --out ${out}.tmp; ` +
+				`  fi ` +
+				`done` +
 				`&& $soong_zip -write_if_changed -jar -o ${out} -C ${out}.tmp -D ${out}.tmp` +
 				`&& rm -rf ${out}.tmp`,
 			CommandDeps: []string{
diff --git a/aidl_library/aidl_library.go b/aidl_library/aidl_library.go
index b49c516..0141545 100644
--- a/aidl_library/aidl_library.go
+++ b/aidl_library/aidl_library.go
@@ -64,7 +64,7 @@
 }
 
 // AidlLibraryProvider provides the srcs and the transitive include dirs
-var AidlLibraryProvider = blueprint.NewProvider(AidlLibraryInfo{})
+var AidlLibraryProvider = blueprint.NewProvider[AidlLibraryInfo]()
 
 func (lib *AidlLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	includeDirsDepSetBuilder := android.NewDepSetBuilder[android.Path](android.PREORDER)
@@ -99,14 +99,13 @@
 	includeDirsDepSetBuilder.Direct(includeDir)
 
 	for _, dep := range ctx.GetDirectDepsWithTag(aidlLibraryTag) {
-		if ctx.OtherModuleHasProvider(dep, AidlLibraryProvider) {
-			info := ctx.OtherModuleProvider(dep, AidlLibraryProvider).(AidlLibraryInfo)
+		if info, ok := android.OtherModuleProvider(ctx, dep, AidlLibraryProvider); ok {
 			includeDirsDepSetBuilder.Transitive(&info.IncludeDirs)
 			hdrsDepSetBuilder.Transitive(&info.Hdrs)
 		}
 	}
 
-	ctx.SetProvider(AidlLibraryProvider, AidlLibraryInfo{
+	android.SetProvider(ctx, AidlLibraryProvider, AidlLibraryInfo{
 		Srcs:        srcs,
 		IncludeDirs: *includeDirsDepSetBuilder.Build(),
 		Hdrs:        *hdrsDepSetBuilder.Build(),
diff --git a/aidl_library/aidl_library_test.go b/aidl_library/aidl_library_test.go
index 0205629..01eab0e 100644
--- a/aidl_library/aidl_library_test.go
+++ b/aidl_library/aidl_library_test.go
@@ -46,7 +46,7 @@
 	).RunTest(t).TestContext
 
 	foo := ctx.ModuleForTests("foo", "").Module().(*AidlLibrary)
-	actualInfo := ctx.ModuleProvider(foo, AidlLibraryProvider).(AidlLibraryInfo)
+	actualInfo, _ := android.SingletonModuleProvider(ctx, foo, AidlLibraryProvider)
 
 	android.AssertArrayString(
 		t,
@@ -95,7 +95,7 @@
 	).RunTest(t).TestContext
 
 	foo := ctx.ModuleForTests("foo", "").Module().(*AidlLibrary)
-	actualInfo := ctx.ModuleProvider(foo, AidlLibraryProvider).(AidlLibraryInfo)
+	actualInfo, _ := android.SingletonModuleProvider(ctx, foo, AidlLibraryProvider)
 
 	android.AssertArrayString(
 		t,
diff --git a/android/Android.bp b/android/Android.bp
index 2ac1d5f..e73f355 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -11,6 +11,7 @@
         "blueprint-metrics",
         "sbox_proto",
         "soong",
+        "soong-android_team_proto",
         "soong-android-soongconfig",
         "soong-remoteexec",
         "soong-response",
@@ -27,6 +28,8 @@
         "androidmk-parser",
     ],
     srcs: [
+        "aconfig_providers.go",
+        "all_teams.go",
         "androidmk.go",
         "apex.go",
         "apex_contributions.go",
@@ -34,6 +37,7 @@
         "api_levels.go",
         "arch.go",
         "arch_list.go",
+        "arch_module_context.go",
         "base_module_context.go",
         "buildinfo_prop.go",
         "config.go",
@@ -62,6 +66,7 @@
         "metrics.go",
         "module.go",
         "module_context.go",
+        "module_info_json.go",
         "mutator.go",
         "namespace.go",
         "neverallow.go",
@@ -79,6 +84,8 @@
         "prebuilt.go",
         "prebuilt_build_tool.go",
         "proto.go",
+        "provider.go",
+        "raw_files.go",
         "register.go",
         "rule_builder.go",
         "sandbox.go",
@@ -87,6 +94,7 @@
         "singleton.go",
         "singleton_module.go",
         "soong_config_modules.go",
+        "team.go",
         "test_asserts.go",
         "test_suites.go",
         "testing.go",
@@ -101,7 +109,6 @@
         "apex_test.go",
         "arch_test.go",
         "config_test.go",
-        "config_bp2build_test.go",
         "configured_jars_test.go",
         "csuite_config_test.go",
         "defaults_test.go",
diff --git a/android/aconfig_providers.go b/android/aconfig_providers.go
new file mode 100644
index 0000000..74c1a5e
--- /dev/null
+++ b/android/aconfig_providers.go
@@ -0,0 +1,227 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+	"fmt"
+	"io"
+	"reflect"
+
+	"github.com/google/blueprint"
+)
+
+var (
+	mergeAconfigFilesRule = pctx.AndroidStaticRule("mergeAconfigFilesRule",
+		blueprint.RuleParams{
+			Command:     `${aconfig} dump --dedup --format protobuf --out $out $flags`,
+			CommandDeps: []string{"${aconfig}"},
+		}, "flags")
+	_ = pctx.HostBinToolVariable("aconfig", "aconfig")
+)
+
+// Provider published by aconfig_value_set
+type AconfigDeclarationsProviderData struct {
+	Package                     string
+	Container                   string
+	Exportable                  bool
+	IntermediateCacheOutputPath WritablePath
+	IntermediateDumpOutputPath  WritablePath
+}
+
+var AconfigDeclarationsProviderKey = blueprint.NewProvider[AconfigDeclarationsProviderData]()
+
+// This is used to collect the aconfig declarations info on the transitive closure,
+// the data is keyed on the container.
+type AconfigTransitiveDeclarationsInfo struct {
+	AconfigFiles map[string]Paths
+}
+
+var AconfigTransitiveDeclarationsInfoProvider = blueprint.NewProvider[AconfigTransitiveDeclarationsInfo]()
+
+// CollectDependencyAconfigFiles is used by some module types to provide finer dependency graphing than
+// we can do in ModuleBase.
+func CollectDependencyAconfigFiles(ctx ModuleContext, mergedAconfigFiles *map[string]Paths) {
+	if *mergedAconfigFiles == nil {
+		*mergedAconfigFiles = make(map[string]Paths)
+	}
+	ctx.VisitDirectDepsIgnoreBlueprint(func(module Module) {
+		if dep, _ := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); dep.IntermediateCacheOutputPath != nil {
+			(*mergedAconfigFiles)[dep.Container] = append((*mergedAconfigFiles)[dep.Container], dep.IntermediateCacheOutputPath)
+			return
+		}
+		if dep, ok := OtherModuleProvider(ctx, module, aconfigPropagatingProviderKey); ok {
+			for container, v := range dep.AconfigFiles {
+				(*mergedAconfigFiles)[container] = append((*mergedAconfigFiles)[container], v...)
+			}
+		}
+		// We process these last, so that they determine the final value, eliminating any duplicates that we picked up
+		// from UpdateAndroidBuildActions.
+		if dep, ok := OtherModuleProvider(ctx, module, AconfigTransitiveDeclarationsInfoProvider); ok {
+			for container, v := range dep.AconfigFiles {
+				(*mergedAconfigFiles)[container] = append((*mergedAconfigFiles)[container], v...)
+			}
+		}
+	})
+
+	for container, aconfigFiles := range *mergedAconfigFiles {
+		(*mergedAconfigFiles)[container] = mergeAconfigFiles(ctx, container, aconfigFiles, false)
+	}
+
+	SetProvider(ctx, AconfigTransitiveDeclarationsInfoProvider, AconfigTransitiveDeclarationsInfo{
+		AconfigFiles: *mergedAconfigFiles,
+	})
+}
+
+func SetAconfigFileMkEntries(m *ModuleBase, entries *AndroidMkEntries, aconfigFiles map[string]Paths) {
+	setAconfigFileMkEntries(m, entries, aconfigFiles)
+}
+
+type aconfigPropagatingDeclarationsInfo struct {
+	AconfigFiles map[string]Paths
+}
+
+var aconfigPropagatingProviderKey = blueprint.NewProvider[aconfigPropagatingDeclarationsInfo]()
+
+func aconfigUpdateAndroidBuildActions(ctx ModuleContext) {
+	mergedAconfigFiles := make(map[string]Paths)
+	ctx.VisitDirectDepsIgnoreBlueprint(func(module Module) {
+		// If any of our dependencies have aconfig declarations (directly or propagated), then merge those and provide them.
+		if dep, ok := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); ok {
+			mergedAconfigFiles[dep.Container] = append(mergedAconfigFiles[dep.Container], dep.IntermediateCacheOutputPath)
+		}
+		if dep, ok := OtherModuleProvider(ctx, module, aconfigPropagatingProviderKey); ok {
+			for container, v := range dep.AconfigFiles {
+				mergedAconfigFiles[container] = append(mergedAconfigFiles[container], v...)
+			}
+		}
+		if dep, ok := OtherModuleProvider(ctx, module, AconfigTransitiveDeclarationsInfoProvider); ok {
+			for container, v := range dep.AconfigFiles {
+				mergedAconfigFiles[container] = append(mergedAconfigFiles[container], v...)
+			}
+		}
+	})
+	// We only need to set the provider if we have aconfig files.
+	if len(mergedAconfigFiles) > 0 {
+		for container, aconfigFiles := range mergedAconfigFiles {
+			mergedAconfigFiles[container] = mergeAconfigFiles(ctx, container, aconfigFiles, true)
+		}
+
+		SetProvider(ctx, aconfigPropagatingProviderKey, aconfigPropagatingDeclarationsInfo{
+			AconfigFiles: mergedAconfigFiles,
+		})
+	}
+}
+
+func aconfigUpdateAndroidMkData(ctx fillInEntriesContext, mod Module, data *AndroidMkData) {
+	info, ok := SingletonModuleProvider(ctx, mod, aconfigPropagatingProviderKey)
+	// If there is no aconfigPropagatingProvider, or there are no AconfigFiles, then we are done.
+	if !ok || len(info.AconfigFiles) == 0 {
+		return
+	}
+	data.Extra = append(data.Extra, func(w io.Writer, outputFile Path) {
+		AndroidMkEmitAssignList(w, "LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles).Strings())
+	})
+	// If there is a Custom writer, it needs to support this provider.
+	if data.Custom != nil {
+		switch reflect.TypeOf(mod).String() {
+		case "*aidl.aidlApi": // writes non-custom before adding .phony
+		case "*android_sdk.sdkRepoHost": // doesn't go through base_rules
+		case "*apex.apexBundle": // aconfig_file properties written
+		case "*bpf.bpf": // properties written (both for module and objs)
+		case "*genrule.Module": // writes non-custom before adding .phony
+		case "*java.SystemModules": // doesn't go through base_rules
+		case "*phony.phony": // properties written
+		case "*phony.PhonyRule": // writes phony deps and acts like `.PHONY`
+		case "*sysprop.syspropLibrary": // properties written
+		default:
+			panic(fmt.Errorf("custom make rules do not handle aconfig files for %q (%q) module %q", ctx.ModuleType(mod), reflect.TypeOf(mod), mod))
+		}
+	}
+}
+
+func aconfigUpdateAndroidMkEntries(ctx fillInEntriesContext, mod Module, entries *[]AndroidMkEntries) {
+	// If there are no entries, then we can ignore this module, even if it has aconfig files.
+	if len(*entries) == 0 {
+		return
+	}
+	info, ok := SingletonModuleProvider(ctx, mod, aconfigPropagatingProviderKey)
+	if !ok || len(info.AconfigFiles) == 0 {
+		return
+	}
+	// All of the files in the module potentially depend on the aconfig flag values.
+	for idx, _ := range *entries {
+		(*entries)[idx].ExtraEntries = append((*entries)[idx].ExtraEntries,
+			func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
+				setAconfigFileMkEntries(mod.base(), entries, info.AconfigFiles)
+			},
+		)
+
+	}
+}
+
+func mergeAconfigFiles(ctx ModuleContext, container string, inputs Paths, generateRule bool) Paths {
+	inputs = SortedUniquePaths(inputs)
+	if len(inputs) == 1 {
+		return Paths{inputs[0]}
+	}
+
+	output := PathForModuleOut(ctx, container, "aconfig_merged.pb")
+
+	if generateRule {
+		ctx.Build(pctx, BuildParams{
+			Rule:        mergeAconfigFilesRule,
+			Description: "merge aconfig files",
+			Inputs:      inputs,
+			Output:      output,
+			Args: map[string]string{
+				"flags": JoinWithPrefix(inputs.Strings(), "--cache "),
+			},
+		})
+	}
+
+	return Paths{output}
+}
+
+func setAconfigFileMkEntries(m *ModuleBase, entries *AndroidMkEntries, aconfigFiles map[string]Paths) {
+	entries.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(m, aconfigFiles))
+}
+
+func getAconfigFilePaths(m *ModuleBase, aconfigFiles map[string]Paths) (paths Paths) {
+	// TODO(b/311155208): The default container here should be system.
+	container := "system"
+
+	if m.SocSpecific() {
+		container = "vendor"
+	} else if m.ProductSpecific() {
+		container = "product"
+	} else if m.SystemExtSpecific() {
+		container = "system_ext"
+	}
+
+	paths = append(paths, aconfigFiles[container]...)
+	if container == "system" {
+		// TODO(b/311155208): Once the default container is system, we can drop this.
+		paths = append(paths, aconfigFiles[""]...)
+	}
+	if container != "system" {
+		if len(aconfigFiles[container]) == 0 && len(aconfigFiles[""]) > 0 {
+			// TODO(b/308625757): Either we guessed the container wrong, or the flag is misdeclared.
+			// For now, just include the system (aka "") container if we get here.
+			//fmt.Printf("container_mismatch: module=%v container=%v files=%v\n", m, container, aconfigFiles)
+		}
+		paths = append(paths, aconfigFiles[""]...)
+	}
+	return
+}
diff --git a/android/all_teams.go b/android/all_teams.go
new file mode 100644
index 0000000..b177e20
--- /dev/null
+++ b/android/all_teams.go
@@ -0,0 +1,153 @@
+package android
+
+import (
+	"android/soong/android/team_proto"
+	"path/filepath"
+
+	"google.golang.org/protobuf/proto"
+)
+
+const ownershipDirectory = "ownership"
+const allTeamsFile = "all_teams.pb"
+
+func AllTeamsFactory() Singleton {
+	return &allTeamsSingleton{}
+}
+
+func init() {
+	registerAllTeamBuildComponents(InitRegistrationContext)
+}
+
+func registerAllTeamBuildComponents(ctx RegistrationContext) {
+	ctx.RegisterParallelSingletonType("all_teams", AllTeamsFactory)
+}
+
+// For each module, list the team or the bpFile the module is defined in.
+type moduleTeamInfo struct {
+	teamName string
+	bpFile   string
+}
+
+type allTeamsSingleton struct {
+	// Path where the collected metadata is stored after successful validation.
+	outputPath OutputPath
+
+	// Map of all package modules we visit during GenerateBuildActions
+	packages map[string]packageProperties
+	// Map of all team modules we visit during GenerateBuildActions
+	teams map[string]teamProperties
+	// Keeps track of team information or bp file for each module we visit.
+	teams_for_mods map[string]moduleTeamInfo
+}
+
+// See if there is a package module for the given bpFilePath with a team defined, if so return the team.
+// If not ascend up to the parent directory and do the same.
+func (this *allTeamsSingleton) lookupDefaultTeam(bpFilePath string) (teamProperties, bool) {
+	// return the Default_team listed in the package if is there.
+	if p, ok := this.packages[bpFilePath]; ok {
+		if t := p.Default_team; t != nil {
+			return this.teams[*p.Default_team], true
+		}
+	}
+	// Strip a directory and go up.
+	// Does android/paths.go basePath,SourcePath help?
+	current, base := filepath.Split(bpFilePath)
+	current = filepath.Clean(current) // removes trailing slash, convert "" -> "."
+	parent, _ := filepath.Split(current)
+	if current == "." {
+		return teamProperties{}, false
+	}
+	return this.lookupDefaultTeam(filepath.Join(parent, base))
+}
+
+// Create a rule to run a tool to collect all the intermediate files
+// which list the team per module into one proto file.
+func (this *allTeamsSingleton) GenerateBuildActions(ctx SingletonContext) {
+	this.packages = make(map[string]packageProperties)
+	this.teams = make(map[string]teamProperties)
+	this.teams_for_mods = make(map[string]moduleTeamInfo)
+
+	ctx.VisitAllModules(func(module Module) {
+		bpFile := ctx.BlueprintFile(module)
+
+		// Package Modules and Team Modules are stored in a map so we can look them up by name for
+		// modules without a team.
+		if pack, ok := module.(*packageModule); ok {
+			// Packages don't have names, use the blueprint file as the key. we can't get qualifiedModuleId in this context.
+			pkgKey := bpFile
+			this.packages[pkgKey] = pack.properties
+			return
+		}
+		if team, ok := module.(*teamModule); ok {
+			this.teams[team.Name()] = team.properties
+			return
+		}
+
+		// If a team name is given for a module, store it.
+		// Otherwise store the bpFile so we can do a package walk later.
+		if module.base().Team() != "" {
+			this.teams_for_mods[module.Name()] = moduleTeamInfo{teamName: module.base().Team(), bpFile: bpFile}
+		} else {
+			this.teams_for_mods[module.Name()] = moduleTeamInfo{bpFile: bpFile}
+		}
+	})
+
+	// Visit all modules again and lookup the team name in the package or parent package if the team
+	// isn't assignged at the module level.
+	allTeams := this.lookupTeamForAllModules()
+
+	this.outputPath = PathForOutput(ctx, ownershipDirectory, allTeamsFile)
+	data, err := proto.Marshal(allTeams)
+	if err != nil {
+		ctx.Errorf("Unable to marshal team data. %s", err)
+	}
+
+	WriteFileRuleVerbatim(ctx, this.outputPath, string(data))
+	ctx.Phony("all_teams", this.outputPath)
+}
+
+func (this *allTeamsSingleton) MakeVars(ctx MakeVarsContext) {
+	ctx.DistForGoal("all_teams", this.outputPath)
+}
+
+// Visit every (non-package, non-team) module and write out a proto containing
+// either the declared team data for that module or the package default team data for that module.
+func (this *allTeamsSingleton) lookupTeamForAllModules() *team_proto.AllTeams {
+	teamsProto := make([]*team_proto.Team, len(this.teams_for_mods))
+	for i, moduleName := range SortedKeys(this.teams_for_mods) {
+		m, _ := this.teams_for_mods[moduleName]
+		teamName := m.teamName
+		var teamProperties teamProperties
+		found := false
+		if teamName != "" {
+			teamProperties, found = this.teams[teamName]
+		} else {
+			teamProperties, found = this.lookupDefaultTeam(m.bpFile)
+		}
+
+		trendy_team_id := ""
+		if found {
+			trendy_team_id = *teamProperties.Trendy_team_id
+		}
+
+		var files []string
+		teamData := new(team_proto.Team)
+		if trendy_team_id != "" {
+			*teamData = team_proto.Team{
+				TrendyTeamId: proto.String(trendy_team_id),
+				TargetName:   proto.String(moduleName),
+				Path:         proto.String(m.bpFile),
+				File:         files,
+			}
+		} else {
+			// Clients rely on the TrendyTeamId optional field not being set.
+			*teamData = team_proto.Team{
+				TargetName: proto.String(moduleName),
+				Path:       proto.String(m.bpFile),
+				File:       files,
+			}
+		}
+		teamsProto[i] = teamData
+	}
+	return &team_proto.AllTeams{Teams: teamsProto}
+}
diff --git a/android/all_teams_test.go b/android/all_teams_test.go
new file mode 100644
index 0000000..a02b86e
--- /dev/null
+++ b/android/all_teams_test.go
@@ -0,0 +1,208 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package android
+
+import (
+	"android/soong/android/team_proto"
+	"log"
+	"testing"
+
+	"google.golang.org/protobuf/proto"
+)
+
+func TestAllTeams(t *testing.T) {
+	t.Parallel()
+	ctx := GroupFixturePreparers(
+		PrepareForTestWithTeamBuildComponents,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterModuleType("fake", fakeModuleFactory)
+			ctx.RegisterParallelSingletonType("all_teams", AllTeamsFactory)
+		}),
+	).RunTestWithBp(t, `
+		fake {
+			name: "main_test",
+			team: "someteam",
+		}
+		team {
+			name: "someteam",
+			trendy_team_id: "cool_team",
+		}
+
+		team {
+			name: "team2",
+			trendy_team_id: "22222",
+		}
+
+		fake {
+			name: "tool",
+			team: "team2",
+		}
+
+		fake {
+			name: "noteam",
+		}
+	`)
+
+	var teams *team_proto.AllTeams
+	teams = getTeamProtoOutput(t, ctx)
+
+	// map of module name -> trendy team name.
+	actualTeams := make(map[string]*string)
+	for _, teamProto := range teams.Teams {
+		actualTeams[teamProto.GetTargetName()] = teamProto.TrendyTeamId
+	}
+	expectedTeams := map[string]*string{
+		"main_test": proto.String("cool_team"),
+		"tool":      proto.String("22222"),
+		"noteam":    nil,
+	}
+
+	AssertDeepEquals(t, "compare maps", expectedTeams, actualTeams)
+}
+
+func getTeamProtoOutput(t *testing.T, ctx *TestResult) *team_proto.AllTeams {
+	teams := new(team_proto.AllTeams)
+	config := ctx.SingletonForTests("all_teams")
+	allOutputs := config.AllOutputs()
+
+	protoPath := allOutputs[0]
+
+	out := config.MaybeOutput(protoPath)
+	outProto := []byte(ContentFromFileRuleForTests(t, ctx.TestContext, out))
+	if err := proto.Unmarshal(outProto, teams); err != nil {
+		log.Fatalln("Failed to parse teams proto:", err)
+	}
+	return teams
+}
+
+// Android.bp
+//
+//	team: team_top
+//
+// # dir1 has no modules with teams,
+// # but has a dir with no Android.bp
+// dir1/Android.bp
+//
+//	module_dir1
+//
+// # dirs without and Android.bp should be fine.
+// dir1/dir2/dir3/Android.bp
+//
+//	package {}
+//	module_dir123
+//
+// teams_dir/Android.bp
+//
+//	module_with_team1: team1
+//	team1: 111
+//
+// # team comes from upper package default
+// teams_dir/deeper/Android.bp
+//
+//	module2_with_team1: team1
+//
+// package_defaults/Android.bp
+// package_defaults/pd2/Android.bp
+//
+//	package{ default_team: team_top}
+//	module_pd2   ## should get team_top
+//
+// package_defaults/pd2/pd3/Android.bp
+//
+//	module_pd3  ## should get team_top
+func TestPackageLookup(t *testing.T) {
+	t.Parallel()
+	rootBp := `
+		team {
+			name: "team_top",
+			trendy_team_id: "trendy://team_top",
+		} `
+
+	dir1Bp := `
+		fake {
+			name: "module_dir1",
+		} `
+	dir3Bp := `
+                package {}
+		fake {
+			name: "module_dir123",
+		} `
+	teamsDirBp := `
+		fake {
+			name: "module_with_team1",
+                        team: "team1"
+
+		}
+		team {
+			name: "team1",
+			trendy_team_id: "111",
+		} `
+	teamsDirDeeper := `
+		fake {
+			name: "module2_with_team1",
+                        team: "team1"
+		} `
+	// create an empty one.
+	packageDefaultsBp := ""
+	packageDefaultspd2 := `
+                package { default_team: "team_top"}
+		fake {
+			name: "modulepd2",
+		} `
+
+	packageDefaultspd3 := `
+		fake {
+			name: "modulepd3",
+		}
+		fake {
+			name: "modulepd3b",
+			team: "team1"
+		} `
+
+	ctx := GroupFixturePreparers(
+		PrepareForTestWithTeamBuildComponents,
+		PrepareForTestWithPackageModule,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterModuleType("fake", fakeModuleFactory)
+			ctx.RegisterParallelSingletonType("all_teams", AllTeamsFactory)
+		}),
+		FixtureAddTextFile("Android.bp", rootBp),
+		FixtureAddTextFile("dir1/Android.bp", dir1Bp),
+		FixtureAddTextFile("dir1/dir2/dir3/Android.bp", dir3Bp),
+		FixtureAddTextFile("teams_dir/Android.bp", teamsDirBp),
+		FixtureAddTextFile("teams_dir/deeper/Android.bp", teamsDirDeeper),
+		FixtureAddTextFile("package_defaults/Android.bp", packageDefaultsBp),
+		FixtureAddTextFile("package_defaults/pd2/Android.bp", packageDefaultspd2),
+		FixtureAddTextFile("package_defaults/pd2/pd3/Android.bp", packageDefaultspd3),
+	).RunTest(t)
+
+	var teams *team_proto.AllTeams
+	teams = getTeamProtoOutput(t, ctx)
+
+	// map of module name -> trendy team name.
+	actualTeams := make(map[string]*string)
+	for _, teamProto := range teams.Teams {
+		actualTeams[teamProto.GetTargetName()] = teamProto.TrendyTeamId
+	}
+	expectedTeams := map[string]*string{
+		"module_with_team1":  proto.String("111"),
+		"module2_with_team1": proto.String("111"),
+		"modulepd2":          proto.String("trendy://team_top"),
+		"modulepd3":          proto.String("trendy://team_top"),
+		"modulepd3b":         proto.String("111"),
+		"module_dir1":        nil,
+		"module_dir123":      nil,
+	}
+	AssertDeepEquals(t, "compare maps", expectedTeams, actualTeams)
+}
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 07e1f12..a52bff3 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -14,30 +14,7 @@
 
 package allowlists
 
-// Configuration to decide if modules in a directory should default to true/false for bp2build_available
-type Bp2BuildConfig map[string]BazelConversionConfigEntry
-type BazelConversionConfigEntry int
-
 const (
-	// iota + 1 ensures that the int value is not 0 when used in the Bp2buildAllowlist map,
-	// which can also mean that the key doesn't exist in a lookup.
-
-	// all modules in this package and subpackages default to bp2build_available: true.
-	// allows modules to opt-out.
-	Bp2BuildDefaultTrueRecursively BazelConversionConfigEntry = iota + 1
-
-	// all modules in this package (not recursively) default to bp2build_available: true.
-	// allows modules to opt-out.
-	Bp2BuildDefaultTrue
-
-	// all modules in this package (not recursively) default to bp2build_available: false.
-	// allows modules to opt-in.
-	Bp2BuildDefaultFalse
-
-	// all modules in this package and subpackages default to bp2build_available: false.
-	// allows modules to opt-in.
-	Bp2BuildDefaultFalseRecursively
-
 	// Modules with build time of more than half a minute should have high priority.
 	DEFAULT_PRIORITIZED_WEIGHT = 1000
 	// Modules with build time of more than a few minute should have higher priority.
@@ -48,1633 +25,6 @@
 )
 
 var (
-	Bp2buildDefaultConfig = Bp2BuildConfig{
-		"art":                                   Bp2BuildDefaultTrue,
-		"art/libartbase":                        Bp2BuildDefaultTrueRecursively,
-		"art/libartpalette":                     Bp2BuildDefaultTrueRecursively,
-		"art/libdexfile":                        Bp2BuildDefaultTrueRecursively,
-		"art/libnativebridge":                   Bp2BuildDefaultTrueRecursively,
-		"art/runtime":                           Bp2BuildDefaultTrueRecursively,
-		"art/tools":                             Bp2BuildDefaultTrue,
-		"bionic":                                Bp2BuildDefaultTrueRecursively,
-		"bootable/recovery/applypatch":          Bp2BuildDefaultTrue,
-		"bootable/recovery/minadbd":             Bp2BuildDefaultTrue,
-		"bootable/recovery/minui":               Bp2BuildDefaultTrue,
-		"bootable/recovery/recovery_utils":      Bp2BuildDefaultTrue,
-		"bootable/recovery/tools/recovery_l10n": Bp2BuildDefaultTrue,
-
-		"build/bazel":                        Bp2BuildDefaultTrueRecursively,
-		"build/make/target/product/security": Bp2BuildDefaultTrue,
-		"build/make/tools":                   Bp2BuildDefaultTrue,
-		"build/make/tools/protos":            Bp2BuildDefaultTrue,
-		"build/make/tools/releasetools":      Bp2BuildDefaultTrue,
-		"build/make/tools/sbom":              Bp2BuildDefaultTrue,
-		"build/make/tools/signapk":           Bp2BuildDefaultTrue,
-		"build/make/tools/zipalign":          Bp2BuildDefaultTrueRecursively,
-		"build/soong":                        Bp2BuildDefaultTrue,
-		"build/soong/cc/libbuildversion":     Bp2BuildDefaultTrue, // Skip tests subdir
-		"build/soong/cc/ndkstubgen":          Bp2BuildDefaultTrue,
-		"build/soong/cc/symbolfile":          Bp2BuildDefaultTrue,
-		"build/soong/jar":                    Bp2BuildDefaultTrue,
-		"build/soong/licenses":               Bp2BuildDefaultTrue,
-		"build/soong/linkerconfig":           Bp2BuildDefaultTrueRecursively,
-		"build/soong/response":               Bp2BuildDefaultTrue,
-		"build/soong/scripts":                Bp2BuildDefaultTrueRecursively,
-		"build/soong/third_party/zip":        Bp2BuildDefaultTrue,
-
-		"cts/common/device-side/nativetesthelper/jni": Bp2BuildDefaultTrueRecursively,
-		"cts/flags/cc_tests":                          Bp2BuildDefaultTrueRecursively,
-		"cts/libs/json":                               Bp2BuildDefaultTrueRecursively,
-		"cts/tests/tests/gesture":                     Bp2BuildDefaultTrueRecursively,
-
-		"dalvik/tools/dexdeps": Bp2BuildDefaultTrueRecursively,
-
-		"development/apps/DevelopmentSettings":        Bp2BuildDefaultTrue,
-		"development/apps/Fallback":                   Bp2BuildDefaultTrue,
-		"development/apps/WidgetPreview":              Bp2BuildDefaultTrue,
-		"development/python-packages/adb":             Bp2BuildDefaultTrueRecursively,
-		"development/samples/BasicGLSurfaceView":      Bp2BuildDefaultTrue,
-		"development/samples/BluetoothChat":           Bp2BuildDefaultTrue,
-		"development/samples/BrokenKeyDerivation":     Bp2BuildDefaultTrue,
-		"development/samples/Compass":                 Bp2BuildDefaultTrue,
-		"development/samples/ContactManager":          Bp2BuildDefaultTrue,
-		"development/samples/FixedGridLayout":         Bp2BuildDefaultTrue,
-		"development/samples/HelloEffects":            Bp2BuildDefaultTrue,
-		"development/samples/Home":                    Bp2BuildDefaultTrue,
-		"development/samples/HoneycombGallery":        Bp2BuildDefaultTrue,
-		"development/samples/JetBoy":                  Bp2BuildDefaultTrue,
-		"development/samples/KeyChainDemo":            Bp2BuildDefaultTrue,
-		"development/samples/LceDemo":                 Bp2BuildDefaultTrue,
-		"development/samples/LunarLander":             Bp2BuildDefaultTrue,
-		"development/samples/MultiResolution":         Bp2BuildDefaultTrue,
-		"development/samples/MultiWindow":             Bp2BuildDefaultTrue,
-		"development/samples/NotePad":                 Bp2BuildDefaultTrue,
-		"development/samples/Obb":                     Bp2BuildDefaultTrue,
-		"development/samples/RSSReader":               Bp2BuildDefaultTrue,
-		"development/samples/ReceiveShareDemo":        Bp2BuildDefaultTrue,
-		"development/samples/SearchableDictionary":    Bp2BuildDefaultTrue,
-		"development/samples/SipDemo":                 Bp2BuildDefaultTrue,
-		"development/samples/SkeletonApp":             Bp2BuildDefaultTrue,
-		"development/samples/Snake":                   Bp2BuildDefaultTrue,
-		"development/samples/SpellChecker/":           Bp2BuildDefaultTrueRecursively,
-		"development/samples/ThemedNavBarKeyboard":    Bp2BuildDefaultTrue,
-		"development/samples/ToyVpn":                  Bp2BuildDefaultTrue,
-		"development/samples/TtsEngine":               Bp2BuildDefaultTrue,
-		"development/samples/USB/AdbTest":             Bp2BuildDefaultTrue,
-		"development/samples/USB/MissileLauncher":     Bp2BuildDefaultTrue,
-		"development/samples/VoiceRecognitionService": Bp2BuildDefaultTrue,
-		"development/samples/VoicemailProviderDemo":   Bp2BuildDefaultTrue,
-		"development/samples/WiFiDirectDemo":          Bp2BuildDefaultTrue,
-		"development/sdk":                             Bp2BuildDefaultTrueRecursively,
-
-		"external/aac":                             Bp2BuildDefaultTrueRecursively,
-		"external/abseil-cpp":                      Bp2BuildDefaultTrueRecursively,
-		"external/arm-optimized-routines":          Bp2BuildDefaultTrueRecursively,
-		"external/auto":                            Bp2BuildDefaultTrue,
-		"external/auto/android-annotation-stubs":   Bp2BuildDefaultTrueRecursively,
-		"external/auto/common":                     Bp2BuildDefaultTrueRecursively,
-		"external/auto/service":                    Bp2BuildDefaultTrueRecursively,
-		"external/auto/value":                      Bp2BuildDefaultTrueRecursively,
-		"external/boringssl":                       Bp2BuildDefaultTrueRecursively,
-		"external/bouncycastle":                    Bp2BuildDefaultTrue,
-		"external/brotli":                          Bp2BuildDefaultTrue,
-		"external/bsdiff":                          Bp2BuildDefaultTrueRecursively,
-		"external/bzip2":                           Bp2BuildDefaultTrueRecursively,
-		"external/clang/lib":                       Bp2BuildDefaultTrue,
-		"external/conscrypt":                       Bp2BuildDefaultTrue,
-		"external/dexmaker":                        Bp2BuildDefaultTrueRecursively,
-		"external/e2fsprogs":                       Bp2BuildDefaultTrueRecursively,
-		"external/eigen":                           Bp2BuildDefaultTrueRecursively,
-		"external/erofs-utils":                     Bp2BuildDefaultTrueRecursively,
-		"external/error_prone":                     Bp2BuildDefaultTrueRecursively,
-		"external/escapevelocity":                  Bp2BuildDefaultTrueRecursively,
-		"external/expat":                           Bp2BuildDefaultTrueRecursively,
-		"external/f2fs-tools":                      Bp2BuildDefaultTrue,
-		"external/flac":                            Bp2BuildDefaultTrueRecursively,
-		"external/flatbuffers":                     Bp2BuildDefaultTrueRecursively,
-		"external/fmtlib":                          Bp2BuildDefaultTrueRecursively,
-		"external/fsverity-utils":                  Bp2BuildDefaultTrueRecursively,
-		"external/gflags":                          Bp2BuildDefaultTrueRecursively,
-		"external/google-benchmark":                Bp2BuildDefaultTrueRecursively,
-		"external/googletest":                      Bp2BuildDefaultTrueRecursively,
-		"external/guava":                           Bp2BuildDefaultTrueRecursively,
-		"external/gwp_asan":                        Bp2BuildDefaultTrueRecursively,
-		"external/hamcrest":                        Bp2BuildDefaultTrueRecursively,
-		"external/icu":                             Bp2BuildDefaultTrueRecursively,
-		"external/icu/android_icu4j":               Bp2BuildDefaultFalse, // java rules incomplete
-		"external/icu/icu4j":                       Bp2BuildDefaultFalse, // java rules incomplete
-		"external/jacoco":                          Bp2BuildDefaultTrueRecursively,
-		"external/jarjar":                          Bp2BuildDefaultTrueRecursively,
-		"external/javaparser":                      Bp2BuildDefaultTrueRecursively,
-		"external/javapoet":                        Bp2BuildDefaultTrueRecursively,
-		"external/javassist":                       Bp2BuildDefaultTrueRecursively,
-		"external/jemalloc_new":                    Bp2BuildDefaultTrueRecursively,
-		"external/jsoncpp":                         Bp2BuildDefaultTrueRecursively,
-		"external/jsr305":                          Bp2BuildDefaultTrueRecursively,
-		"external/jsr330":                          Bp2BuildDefaultTrueRecursively,
-		"external/junit":                           Bp2BuildDefaultTrueRecursively,
-		"external/kotlinc":                         Bp2BuildDefaultTrueRecursively,
-		"external/kotlinx.coroutines":              Bp2BuildDefaultTrueRecursively,
-		"external/libaom":                          Bp2BuildDefaultTrueRecursively,
-		"external/libavc":                          Bp2BuildDefaultTrueRecursively,
-		"external/libcap":                          Bp2BuildDefaultTrueRecursively,
-		"external/libcxx":                          Bp2BuildDefaultTrueRecursively,
-		"external/libcxxabi":                       Bp2BuildDefaultTrueRecursively,
-		"external/libdivsufsort":                   Bp2BuildDefaultTrueRecursively,
-		"external/libdrm":                          Bp2BuildDefaultTrue,
-		"external/libevent":                        Bp2BuildDefaultTrueRecursively,
-		"external/libgav1":                         Bp2BuildDefaultTrueRecursively,
-		"external/libdav1d":                        Bp2BuildDefaultTrueRecursively,
-		"external/libhevc":                         Bp2BuildDefaultTrueRecursively,
-		"external/libjpeg-turbo":                   Bp2BuildDefaultTrueRecursively,
-		"external/libmpeg2":                        Bp2BuildDefaultTrueRecursively,
-		"external/libphonenumber":                  Bp2BuildDefaultTrueRecursively,
-		"external/libpng":                          Bp2BuildDefaultTrueRecursively,
-		"external/libvpx":                          Bp2BuildDefaultTrueRecursively,
-		"external/libyuv":                          Bp2BuildDefaultTrueRecursively,
-		"external/lz4/lib":                         Bp2BuildDefaultTrue,
-		"external/lz4/programs":                    Bp2BuildDefaultTrue,
-		"external/lzma/C":                          Bp2BuildDefaultTrueRecursively,
-		"external/mdnsresponder":                   Bp2BuildDefaultTrueRecursively,
-		"external/minijail":                        Bp2BuildDefaultTrueRecursively,
-		"external/mockito":                         Bp2BuildDefaultTrueRecursively,
-		"external/musl":                            Bp2BuildDefaultTrueRecursively,
-		"external/objenesis":                       Bp2BuildDefaultTrueRecursively,
-		"external/openscreen":                      Bp2BuildDefaultTrueRecursively,
-		"external/ow2-asm":                         Bp2BuildDefaultTrueRecursively,
-		"external/pcre":                            Bp2BuildDefaultTrueRecursively,
-		"external/perfmark/api":                    Bp2BuildDefaultTrueRecursively,
-		"external/perfetto":                        Bp2BuildDefaultTrue,
-		"external/protobuf":                        Bp2BuildDefaultTrueRecursively,
-		"external/python/jinja/src":                Bp2BuildDefaultTrueRecursively,
-		"external/python/markupsafe/src":           Bp2BuildDefaultTrueRecursively,
-		"external/python/pyfakefs/pyfakefs":        Bp2BuildDefaultTrueRecursively,
-		"external/python/pyyaml/lib/yaml":          Bp2BuildDefaultTrueRecursively,
-		"external/python/setuptools":               Bp2BuildDefaultTrueRecursively,
-		"external/python/six":                      Bp2BuildDefaultTrueRecursively,
-		"external/rappor":                          Bp2BuildDefaultTrueRecursively,
-		"external/rust/crates/rustc-demangle":      Bp2BuildDefaultTrueRecursively,
-		"external/rust/crates/rustc-demangle-capi": Bp2BuildDefaultTrueRecursively,
-		"external/scudo":                           Bp2BuildDefaultTrueRecursively,
-		"external/selinux/checkpolicy":             Bp2BuildDefaultTrueRecursively,
-		"external/selinux/libselinux":              Bp2BuildDefaultTrueRecursively,
-		"external/selinux/libsepol":                Bp2BuildDefaultTrueRecursively,
-		"external/speex":                           Bp2BuildDefaultTrueRecursively,
-		"external/sqlite":                          Bp2BuildDefaultTrueRecursively,
-		"external/tinyalsa":                        Bp2BuildDefaultTrueRecursively,
-		"external/tinyalsa_new":                    Bp2BuildDefaultTrueRecursively,
-		"external/toybox":                          Bp2BuildDefaultTrueRecursively,
-		"external/truth":                           Bp2BuildDefaultTrueRecursively,
-		"external/xz-java":                         Bp2BuildDefaultTrueRecursively,
-		"external/zlib":                            Bp2BuildDefaultTrueRecursively,
-		"external/zopfli":                          Bp2BuildDefaultTrueRecursively,
-		"external/zstd":                            Bp2BuildDefaultTrueRecursively,
-
-		"frameworks/av": Bp2BuildDefaultTrue,
-		"frameworks/av/media/audioaidlconversion":                              Bp2BuildDefaultTrueRecursively,
-		"frameworks/av/media/codec2/components/aom":                            Bp2BuildDefaultTrueRecursively,
-		"frameworks/av/media/codecs":                                           Bp2BuildDefaultTrueRecursively,
-		"frameworks/av/media/liberror":                                         Bp2BuildDefaultTrueRecursively,
-		"frameworks/av/media/libmediahelper":                                   Bp2BuildDefaultTrue,
-		"frameworks/av/media/libshmem":                                         Bp2BuildDefaultTrueRecursively,
-		"frameworks/av/media/module/codecs":                                    Bp2BuildDefaultTrueRecursively,
-		"frameworks/av/media/module/foundation":                                Bp2BuildDefaultTrueRecursively,
-		"frameworks/av/media/module/minijail":                                  Bp2BuildDefaultTrueRecursively,
-		"frameworks/av/services/minijail":                                      Bp2BuildDefaultTrueRecursively,
-		"frameworks/base/apex/jobscheduler/service/jni":                        Bp2BuildDefaultTrueRecursively,
-		"frameworks/base/core/java":                                            Bp2BuildDefaultTrue,
-		"frameworks/base/core/res":                                             Bp2BuildDefaultTrueRecursively,
-		"frameworks/base/errorprone":                                           Bp2BuildDefaultTrueRecursively,
-		"frameworks/base/libs/androidfw":                                       Bp2BuildDefaultTrue,
-		"frameworks/base/libs/services":                                        Bp2BuildDefaultTrue,
-		"frameworks/base/media/tests/MediaDump":                                Bp2BuildDefaultTrue,
-		"frameworks/base/mime":                                                 Bp2BuildDefaultTrueRecursively,
-		"frameworks/base/proto":                                                Bp2BuildDefaultTrue,
-		"frameworks/base/services/tests/servicestests/aidl":                    Bp2BuildDefaultTrue,
-		"frameworks/base/startop/apps/test":                                    Bp2BuildDefaultTrue,
-		"frameworks/base/tests/appwidgets/AppWidgetHostTest":                   Bp2BuildDefaultTrueRecursively,
-		"frameworks/base/tools/aapt":                                           Bp2BuildDefaultTrue,
-		"frameworks/base/tools/aapt2":                                          Bp2BuildDefaultTrue,
-		"frameworks/base/tools/codegen":                                        Bp2BuildDefaultTrueRecursively,
-		"frameworks/base/tools/locked_region_code_injection":                   Bp2BuildDefaultTrueRecursively,
-		"frameworks/base/tools/streaming_proto":                                Bp2BuildDefaultTrueRecursively,
-		"frameworks/hardware/interfaces":                                       Bp2BuildDefaultTrue,
-		"frameworks/hardware/interfaces/displayservice":                        Bp2BuildDefaultTrueRecursively,
-		"frameworks/hardware/interfaces/stats/aidl":                            Bp2BuildDefaultTrue,
-		"frameworks/libs/modules-utils/build":                                  Bp2BuildDefaultTrueRecursively,
-		"frameworks/libs/modules-utils/java":                                   Bp2BuildDefaultTrueRecursively,
-		"frameworks/libs/modules-utils/java/com/android/modules/utils/testing": Bp2BuildDefaultFalseRecursively,
-		"frameworks/native":                                                    Bp2BuildDefaultTrue,
-		"frameworks/native/libs/adbd_auth":                                     Bp2BuildDefaultTrueRecursively,
-		"frameworks/native/libs/arect":                                         Bp2BuildDefaultTrueRecursively,
-		"frameworks/native/libs/binder":                                        Bp2BuildDefaultTrue,
-		"frameworks/native/libs/gui":                                           Bp2BuildDefaultTrue,
-		"frameworks/native/libs/math":                                          Bp2BuildDefaultTrueRecursively,
-		"frameworks/native/libs/nativebase":                                    Bp2BuildDefaultTrueRecursively,
-		"frameworks/native/libs/permission":                                    Bp2BuildDefaultTrueRecursively,
-		"frameworks/native/libs/ui":                                            Bp2BuildDefaultTrue,
-		"frameworks/native/libs/vr":                                            Bp2BuildDefaultTrueRecursively,
-		"frameworks/native/opengl/tests/gl2_cameraeye":                         Bp2BuildDefaultTrue,
-		"frameworks/native/opengl/tests/gl2_java":                              Bp2BuildDefaultTrue,
-		"frameworks/native/opengl/tests/testLatency":                           Bp2BuildDefaultTrue,
-		"frameworks/native/opengl/tests/testPauseResume":                       Bp2BuildDefaultTrue,
-		"frameworks/native/opengl/tests/testViewport":                          Bp2BuildDefaultTrue,
-		"frameworks/native/services/batteryservice":                            Bp2BuildDefaultTrue,
-		"frameworks/proto_logging/stats":                                       Bp2BuildDefaultTrueRecursively,
-
-		"hardware/interfaces":                                     Bp2BuildDefaultTrue,
-		"hardware/interfaces/audio/aidl":                          Bp2BuildDefaultTrue,
-		"hardware/interfaces/audio/aidl/common":                   Bp2BuildDefaultTrue,
-		"hardware/interfaces/audio/aidl/default":                  Bp2BuildDefaultTrue,
-		"hardware/interfaces/audio/aidl/sounddose":                Bp2BuildDefaultTrue,
-		"hardware/interfaces/camera/metadata/aidl":                Bp2BuildDefaultTrueRecursively,
-		"hardware/interfaces/common/aidl":                         Bp2BuildDefaultTrue,
-		"hardware/interfaces/common/fmq/aidl":                     Bp2BuildDefaultTrue,
-		"hardware/interfaces/common/support":                      Bp2BuildDefaultTrue,
-		"hardware/interfaces/configstore/1.0":                     Bp2BuildDefaultTrue,
-		"hardware/interfaces/configstore/1.1":                     Bp2BuildDefaultTrue,
-		"hardware/interfaces/configstore/utils":                   Bp2BuildDefaultTrue,
-		"hardware/interfaces/contexthub/aidl":                     Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/allocator/2.0":              Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/allocator/3.0":              Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/allocator/4.0":              Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/allocator/aidl":             Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/bufferqueue/1.0":            Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/bufferqueue/2.0":            Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/common/1.0":                 Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/common/1.1":                 Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/common/1.2":                 Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/common/aidl":                Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/mapper/2.0":                 Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/mapper/2.1":                 Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/mapper/3.0":                 Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/mapper/4.0":                 Bp2BuildDefaultTrue,
-		"hardware/interfaces/health/1.0":                          Bp2BuildDefaultTrue,
-		"hardware/interfaces/health/1.0/default":                  Bp2BuildDefaultTrue,
-		"hardware/interfaces/health/2.0":                          Bp2BuildDefaultTrue,
-		"hardware/interfaces/health/2.0/default":                  Bp2BuildDefaultTrue,
-		"hardware/interfaces/health/2.0/utils":                    Bp2BuildDefaultTrueRecursively,
-		"hardware/interfaces/health/2.1":                          Bp2BuildDefaultTrue,
-		"hardware/interfaces/health/aidl":                         Bp2BuildDefaultTrue,
-		"hardware/interfaces/health/utils":                        Bp2BuildDefaultTrueRecursively,
-		"hardware/interfaces/media":                               Bp2BuildDefaultTrueRecursively,
-		"hardware/interfaces/media/bufferpool/aidl/default/tests": Bp2BuildDefaultFalseRecursively,
-		"hardware/interfaces/media/omx/1.0/vts":                   Bp2BuildDefaultFalseRecursively,
-		"hardware/interfaces/neuralnetworks":                      Bp2BuildDefaultTrueRecursively,
-		"hardware/interfaces/neuralnetworks/aidl/vts":             Bp2BuildDefaultFalseRecursively,
-		"hardware/interfaces/neuralnetworks/1.0/vts":              Bp2BuildDefaultFalseRecursively,
-		"hardware/interfaces/neuralnetworks/1.1/vts":              Bp2BuildDefaultFalseRecursively,
-		"hardware/interfaces/neuralnetworks/1.2/vts":              Bp2BuildDefaultFalseRecursively,
-		"hardware/interfaces/neuralnetworks/1.3/vts":              Bp2BuildDefaultFalseRecursively,
-		"hardware/interfaces/neuralnetworks/1.4/vts":              Bp2BuildDefaultFalseRecursively,
-		"hardware/interfaces/tests":                               Bp2BuildDefaultTrueRecursively,
-		"hardware/interfaces/tests/extension":                     Bp2BuildDefaultFalseRecursively, // missing deps
-		"hardware/interfaces/tests/msgq":                          Bp2BuildDefaultFalseRecursively, // missing deps
-
-		"libnativehelper": Bp2BuildDefaultTrueRecursively,
-
-		"packages/apps/DevCamera":                                    Bp2BuildDefaultTrue,
-		"packages/apps/HTMLViewer":                                   Bp2BuildDefaultTrue,
-		"packages/apps/Protips":                                      Bp2BuildDefaultTrue,
-		"packages/apps/SafetyRegulatoryInfo":                         Bp2BuildDefaultTrue,
-		"packages/apps/WallpaperPicker":                              Bp2BuildDefaultTrue,
-		"packages/modules/Connectivity/bpf_progs":                    Bp2BuildDefaultTrueRecursively,
-		"packages/modules/Connectivity/service-t":                    Bp2BuildDefaultTrueRecursively,
-		"packages/modules/Connectivity/service/native":               Bp2BuildDefaultTrueRecursively,
-		"packages/modules/Connectivity/staticlibs/native":            Bp2BuildDefaultTrueRecursively,
-		"packages/modules/Connectivity/staticlibs/netd":              Bp2BuildDefaultTrueRecursively,
-		"packages/modules/Connectivity/staticlibs/netd/libnetdutils": Bp2BuildDefaultTrueRecursively,
-		"packages/modules/Connectivity/tests/unit/jni":               Bp2BuildDefaultTrueRecursively,
-		"packages/modules/Gki/libkver":                               Bp2BuildDefaultTrue,
-		"packages/modules/NetworkStack/common/captiveportal":         Bp2BuildDefaultTrue,
-		"packages/modules/NeuralNetworks/apex":                       Bp2BuildDefaultTrue,
-		"packages/modules/NeuralNetworks/apex/testing":               Bp2BuildDefaultTrue,
-		"packages/modules/NeuralNetworks/driver/cache":               Bp2BuildDefaultTrueRecursively,
-		"packages/modules/SdkExtensions/gen_sdk":                     Bp2BuildDefaultTrue,
-		"packages/modules/StatsD/lib/libstatssocket":                 Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb":                                       Bp2BuildDefaultTrue,
-		"packages/modules/adb/apex":                                  Bp2BuildDefaultTrue,
-		"packages/modules/adb/crypto":                                Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/fastdeploy":                            Bp2BuildDefaultTrue,
-		"packages/modules/adb/libs":                                  Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/pairing_auth":                          Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/pairing_connection":                    Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/proto":                                 Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/tls":                                   Bp2BuildDefaultTrueRecursively,
-		"packages/modules/common/proto":                              Bp2BuildDefaultTrue,
-		"packages/providers/MediaProvider/tools/dialogs":             Bp2BuildDefaultFalse, // TODO(b/242834374)
-		"packages/screensavers/Basic":                                Bp2BuildDefaultTrue,
-		"packages/services/Car/tests/SampleRearViewCamera":           Bp2BuildDefaultFalse, // TODO(b/242834321)
-
-		"platform_testing/libraries/annotations":              Bp2BuildDefaultTrueRecursively,
-		"platform_testing/libraries/flag-helpers/libflagtest": Bp2BuildDefaultTrueRecursively,
-		"platform_testing/tests/example":                      Bp2BuildDefaultTrueRecursively,
-
-		"prebuilts/clang/host/linux-x86":                   Bp2BuildDefaultTrueRecursively,
-		"prebuilts/gradle-plugin":                          Bp2BuildDefaultTrueRecursively,
-		"prebuilts/module_sdk":                             Bp2BuildDefaultTrueRecursively,
-		"prebuilts/runtime/mainline/platform/sdk":          Bp2BuildDefaultTrueRecursively,
-		"prebuilts/sdk":                                    Bp2BuildDefaultTrue,
-		"prebuilts/sdk/current/androidx":                   Bp2BuildDefaultTrue,
-		"prebuilts/sdk/current/androidx-legacy":            Bp2BuildDefaultTrue,
-		"prebuilts/sdk/current/extras/app-toolkit":         Bp2BuildDefaultTrue,
-		"prebuilts/sdk/current/extras/constraint-layout-x": Bp2BuildDefaultTrue,
-		"prebuilts/sdk/current/extras/material-design-x":   Bp2BuildDefaultTrue,
-		"prebuilts/sdk/current/support":                    Bp2BuildDefaultTrue,
-		"prebuilts/tools":                                  Bp2BuildDefaultTrue,
-		"prebuilts/tools/common/m2":                        Bp2BuildDefaultTrue,
-		"prebuilts/r8":                                     Bp2BuildDefaultTrueRecursively,
-
-		"sdk/annotations":   Bp2BuildDefaultTrueRecursively,
-		"sdk/dumpeventlog":  Bp2BuildDefaultTrue,
-		"sdk/eventanalyzer": Bp2BuildDefaultTrue,
-
-		"system/apex":                                            Bp2BuildDefaultFalse, // TODO(b/207466993): flaky failures
-		"system/apex/apexer":                                     Bp2BuildDefaultTrue,
-		"system/apex/libs":                                       Bp2BuildDefaultTrueRecursively,
-		"system/apex/libs/libapexsupport":                        Bp2BuildDefaultFalseRecursively, // TODO(b/267572288): depends on rust
-		"system/apex/proto":                                      Bp2BuildDefaultTrueRecursively,
-		"system/apex/tools":                                      Bp2BuildDefaultTrueRecursively,
-		"system/core/debuggerd":                                  Bp2BuildDefaultTrueRecursively,
-		"system/core/diagnose_usb":                               Bp2BuildDefaultTrueRecursively,
-		"system/core/fs_mgr":                                     Bp2BuildDefaultTrueRecursively,
-		"system/core/healthd":                                    Bp2BuildDefaultTrue,
-		"system/core/healthd/testdata":                           Bp2BuildDefaultTrue,
-		"system/core/libasyncio":                                 Bp2BuildDefaultTrue,
-		"system/core/libcrypto_utils":                            Bp2BuildDefaultTrueRecursively,
-		"system/core/libcutils":                                  Bp2BuildDefaultTrueRecursively,
-		"system/core/libpackagelistparser":                       Bp2BuildDefaultTrueRecursively,
-		"system/core/libprocessgroup":                            Bp2BuildDefaultTrue,
-		"system/core/libprocessgroup/cgrouprc":                   Bp2BuildDefaultTrue,
-		"system/core/libprocessgroup/cgrouprc_format":            Bp2BuildDefaultTrue,
-		"system/core/libsparse":                                  Bp2BuildDefaultTrueRecursively,
-		"system/core/libstats/expresslog":                        Bp2BuildDefaultTrueRecursively,
-		"system/core/libsuspend":                                 Bp2BuildDefaultTrue,
-		"system/core/libsystem":                                  Bp2BuildDefaultTrueRecursively,
-		"system/core/libsysutils":                                Bp2BuildDefaultTrueRecursively,
-		"system/core/libutils":                                   Bp2BuildDefaultTrueRecursively,
-		"system/core/libvndksupport":                             Bp2BuildDefaultTrueRecursively,
-		"system/core/mkbootfs":                                   Bp2BuildDefaultTrueRecursively,
-		"system/core/property_service/libpropertyinfoparser":     Bp2BuildDefaultTrueRecursively,
-		"system/core/property_service/libpropertyinfoserializer": Bp2BuildDefaultTrueRecursively,
-		"system/core/trusty/libtrusty":                           Bp2BuildDefaultTrue,
-		"system/extras/f2fs_utils":                               Bp2BuildDefaultTrueRecursively,
-		"system/extras/toolchain-extras":                         Bp2BuildDefaultTrue,
-		"system/extras/verity":                                   Bp2BuildDefaultTrueRecursively,
-		"system/hardware/interfaces/media":                       Bp2BuildDefaultTrueRecursively,
-		"system/incremental_delivery/incfs":                      Bp2BuildDefaultTrue,
-		"system/libartpalette":                                   Bp2BuildDefaultTrueRecursively,
-		"system/libbase":                                         Bp2BuildDefaultTrueRecursively,
-		"system/libfmq":                                          Bp2BuildDefaultTrue,
-		"system/libhidl":                                         Bp2BuildDefaultTrue,
-		"system/libhidl/libhidlmemory":                           Bp2BuildDefaultTrue,
-		"system/libhidl/transport":                               Bp2BuildDefaultTrue,
-		"system/libhidl/transport/allocator/1.0":                 Bp2BuildDefaultTrue,
-		"system/libhidl/transport/base/1.0":                      Bp2BuildDefaultTrue,
-		"system/libhidl/transport/manager/1.0":                   Bp2BuildDefaultTrue,
-		"system/libhidl/transport/manager/1.1":                   Bp2BuildDefaultTrue,
-		"system/libhidl/transport/manager/1.2":                   Bp2BuildDefaultTrue,
-		"system/libhidl/transport/memory":                        Bp2BuildDefaultTrueRecursively,
-		"system/libhidl/transport/safe_union/1.0":                Bp2BuildDefaultTrue,
-		"system/libhidl/transport/token/1.0":                     Bp2BuildDefaultTrue,
-		"system/libhidl/transport/token/1.0/utils":               Bp2BuildDefaultTrue,
-		"system/libhwbinder":                                     Bp2BuildDefaultTrueRecursively,
-		"system/libprocinfo":                                     Bp2BuildDefaultTrue,
-		"system/libvintf":                                        Bp2BuildDefaultTrue,
-		"system/libziparchive":                                   Bp2BuildDefaultTrueRecursively,
-		"system/linkerconfig":                                    Bp2BuildDefaultTrueRecursively,
-		"system/logging":                                         Bp2BuildDefaultTrueRecursively,
-		"system/media":                                           Bp2BuildDefaultTrue,
-		"system/media/alsa_utils":                                Bp2BuildDefaultTrueRecursively,
-		"system/media/audio":                                     Bp2BuildDefaultTrueRecursively,
-		"system/media/audio_utils":                               Bp2BuildDefaultTrueRecursively,
-		"system/media/camera":                                    Bp2BuildDefaultTrueRecursively,
-		"system/memory/libion":                                   Bp2BuildDefaultTrueRecursively,
-		"system/memory/libmemunreachable":                        Bp2BuildDefaultTrueRecursively,
-		"system/netd":                                            Bp2BuildDefaultTrue,
-		"system/security/fsverity":                               Bp2BuildDefaultTrueRecursively,
-		"system/sepolicy/apex":                                   Bp2BuildDefaultTrueRecursively,
-		"system/testing/gtest_extras":                            Bp2BuildDefaultTrueRecursively,
-		"system/timezone/apex":                                   Bp2BuildDefaultTrueRecursively,
-		"system/timezone/output_data":                            Bp2BuildDefaultTrueRecursively,
-		"system/timezone/testdata":                               Bp2BuildDefaultTrueRecursively,
-		"system/timezone/testing":                                Bp2BuildDefaultTrueRecursively,
-		"system/tools/aidl/build/tests_bp2build":                 Bp2BuildDefaultTrue,
-		"system/tools/aidl/metadata":                             Bp2BuildDefaultTrue,
-		"system/tools/hidl":                                      Bp2BuildDefaultTrueRecursively,
-		"system/tools/mkbootimg":                                 Bp2BuildDefaultTrueRecursively,
-		"system/tools/sysprop":                                   Bp2BuildDefaultTrue,
-		"system/tools/xsdc/utils":                                Bp2BuildDefaultTrueRecursively,
-		"system/unwinding/libunwindstack":                        Bp2BuildDefaultTrueRecursively,
-
-		"test/vts/vts_hal_hidl_target": Bp2BuildDefaultTrueRecursively,
-
-		"toolchain/pgo-profiles":                      Bp2BuildDefaultTrueRecursively,
-		"tools/apifinder":                             Bp2BuildDefaultTrue,
-		"tools/apksig":                                Bp2BuildDefaultTrue,
-		"tools/dexter/slicer":                         Bp2BuildDefaultTrueRecursively,
-		"tools/external_updater":                      Bp2BuildDefaultTrueRecursively,
-		"tools/metalava":                              Bp2BuildDefaultTrueRecursively,
-		"tools/platform-compat/java/android/compat":   Bp2BuildDefaultTrueRecursively,
-		"tools/platform-compat/java/androidprocessor": Bp2BuildDefaultTrueRecursively,
-		"tools/tradefederation/core/util_apps":        Bp2BuildDefaultTrueRecursively,
-		"tools/tradefederation/prebuilts/filegroups":  Bp2BuildDefaultTrueRecursively,
-	}
-
-	Bp2buildKeepExistingBuildFile = map[string]bool{
-		// This is actually build/bazel/build.BAZEL symlinked to ./BUILD
-		".":/*recursive = */ false,
-
-		"build/bazel":/* recursive = */ true,
-		"build/make/core":/* recursive = */ false,
-		"build/bazel_common_rules":/* recursive = */ true,
-		"build/make/target/product/security":/* recursive = */ false,
-		// build/make/tools/signapk BUILD file is generated, so build/make/tools is not recursive.
-		"build/make/tools":/* recursive = */ false,
-		"build/pesto":/* recursive = */ true,
-		"build/soong":/* recursive = */ true,
-
-		// external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails
-		// e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed
-		"external/bazelbuild-rules_android":/* recursive = */ true,
-		"external/bazelbuild-rules_cc":/* recursive = */ true,
-		"external/bazelbuild-rules_java":/* recursive = */ true,
-		"external/bazelbuild-rules_license":/* recursive = */ true,
-		"external/bazelbuild-rules_go":/* recursive = */ true,
-		"external/bazelbuild-rules_python":/* recursive = */ true,
-		"external/bazelbuild-rules_rust":/* recursive = */ true,
-		"external/bazelbuild-rules_testing":/* recursive = */ true,
-		"external/bazelbuild-kotlin-rules":/* recursive = */ true,
-		"external/bazel-skylib":/* recursive = */ true,
-		"external/protobuf":/* recursive = */ false,
-		"external/python/absl-py":/* recursive = */ true,
-
-		"external/compiler-rt/lib/cfi":/* recursive = */ false,
-
-		// this BUILD file is globbed by //external/icu/icu4c/source:icu4c_test_data's "data/**/*".
-		"external/icu/icu4c/source/data/unidata/norm2":/* recursive = */ false,
-
-		// Building manually due to b/179889880: resource files cross package boundary
-		"packages/apps/Music":/* recursive = */ true,
-
-		"prebuilts/abi-dumps/platform":/* recursive = */ true,
-		"prebuilts/abi-dumps/ndk":/* recursive = */ true,
-		"prebuilts/bazel":/* recursive = */ true,
-		"prebuilts/bundletool":/* recursive = */ true,
-		"prebuilts/clang/host/linux-x86":/* recursive = */ false,
-		"prebuilts/clang-tools":/* recursive = */ true,
-		"prebuilts/gcc":/* recursive = */ true,
-		"prebuilts/build-tools":/* recursive = */ true,
-		"prebuilts/jdk/jdk8":/* recursive = */ true,
-		"prebuilts/jdk/jdk17":/* recursive = */ true,
-		"prebuilts/misc":/* recursive = */ false, // not recursive because we need bp2build converted build files in prebuilts/misc/common/asm
-		"prebuilts/sdk":/* recursive = */ false,
-		"prebuilts/sdk/tools":/* recursive = */ false,
-		"prebuilts/r8":/* recursive = */ false,
-		"prebuilts/runtime":/* recursive = */ false,
-		"prebuilts/rust":/* recursive = */ true,
-
-		// not recursive due to conflicting workspace paths in tools/atest/bazel/rules
-		"tools/asuite/atest":/* recursive = */ false,
-		"tools/asuite/atest/bazel/reporter":/* recursive = */ true,
-
-		// Used for testing purposes only. Should not actually exist in the real source tree.
-		"testpkg/keep_build_file":/* recursive = */ false,
-	}
-
-	Bp2buildModuleAlwaysConvertList = []string{
-		"aconfig.test.cpp",
-		"AconfigJavaHostTest",
-		// aconfig
-		"libonce_cell",
-		"libanyhow",
-		"libunicode_segmentation",
-		"libmemchr",
-		"libbitflags-1.3.2",
-		"libryu",
-		"libitoa",
-		"libos_str_bytes",
-		"libheck",
-		"libclap_lex",
-		"libsyn",
-		"libquote",
-		"libunicode_ident",
-		"libproc_macro2",
-		"libthiserror_impl",
-		"libserde_derive",
-		"libclap_derive",
-		"libthiserror",
-		"libserde",
-		"libclap",
-		"libbytes",
-		"libprotobuf_support",
-		"libtinytemplate",
-		"libserde_json",
-		"libprotobuf",
-
-		"protoc-gen-rust",
-		"libprotobuf_codegen",
-		"libprotobuf_parse",
-		"libregex",
-		"libtempfile",
-		"libwhich",
-		"libregex_syntax",
-		"libfastrand",
-		"libeither",
-		"libaho_corasick",
-		"liblibc",
-		"libcfg_if",
-		"liblog_rust",
-		"libgetrandom",
-		"libremove_dir_all",
-		"libahash",
-		"libhashbrown",
-		"libindexmap",
-		"libaconfig_protos",
-		"libpaste",
-		"aconfig",
-
-		// ext
-		"tagsoup",
-
-		// framework-minus-apex
-		"AndroidFrameworkLintChecker",
-		"ImmutabilityAnnotationProcessor",
-		"debian.mime.types.minimized",
-		"framework-javastream-protos",
-		"libview-inspector-annotation-processor",
-
-		// services
-		"apache-commons-math",
-		"cbor-java",
-		"icu4j_calendar_astronomer",
-		"statslog-art-java-gen",
-
-		"AndroidCommonLint",
-		"ImmutabilityAnnotation",
-		"ImmutabilityAnnotationProcessorHostLibrary",
-
-		"libidmap2_policies",
-		"libSurfaceFlingerProp",
-		"toolbox_input_labels",
-
-		// cc mainline modules
-
-		// com.android.media.swcodec
-		"com.android.media.swcodec",
-		"com.android.media.swcodec-androidManifest",
-		"com.android.media.swcodec-ld.config.txt",
-		"com.android.media.swcodec-mediaswcodec.32rc",
-		"com.android.media.swcodec-mediaswcodec.rc",
-		"com.android.media.swcodec.certificate",
-		"com.android.media.swcodec.key",
-		"test_com.android.media.swcodec",
-
-		// deps
-		"code_coverage.policy",
-		"code_coverage.policy.other",
-		"codec2_soft_exports",
-		"compatibility_matrix_schema",
-		"framework-connectivity-protos",
-		"framework-connectivity-javastream-protos",
-		"gemmlowp_headers",
-		"gl_headers",
-		"libandroid_runtime_lazy",
-		"libandroid_runtime_vm_headers",
-		"libaudioclient_aidl_conversion_util",
-		"libbinder_headers_platform_shared",
-		"libbinderthreadstateutils",
-		"libbluetooth-types-header",
-		"libcodec2",
-		"libcodec2_headers",
-		"libcodec2_internal",
-		"libdmabufheap",
-		"libgsm",
-		"libgrallocusage",
-		"libgralloctypes",
-		"libnativewindow",
-		"libneuralnetworks",
-		"libneuralnetworks_static",
-		"libgraphicsenv",
-		"libhardware_headers",
-		"libnativeloader-headers",
-		"libnativewindow_headers",
-		"libneuralnetworks_headers",
-		"libneuralnetworks_packageinfo",
-		"libopus",
-		"libprocpartition",
-		"libruy_static",
-		"libandroidio",
-		"libandroidio_srcs",
-		"libserviceutils",
-		"libsurfaceflinger_headers",
-		"libsync",
-		"libtextclassifier_hash_headers",
-		"libtextclassifier_hash_static",
-		"libtflite_kernel_utils",
-		"libtinyxml2",
-		"libvorbisidec",
-		"media_ndk_headers",
-		"media_plugin_headers",
-		"mediaswcodec.policy",
-		"mediaswcodec.xml",
-		"neuralnetworks_types",
-		"libneuralnetworks_common",
-		"philox_random",
-		"philox_random_headers",
-		"server_configurable_flags",
-		"service-permission-streaming-proto-sources",
-		"statslog_neuralnetworks.cpp",
-		"statslog_neuralnetworks.h",
-		"tensorflow_headers",
-
-		"libstagefright_bufferpool@2.0",
-		"libstagefright_bufferpool@2.0.1",
-		"libSurfaceFlingerProp",
-
-		// prebuilts
-		"prebuilt_stats-log-api-gen",
-		"prebuilt_aapt2",
-
-		// fastboot
-		"fastboot",
-		"libfastboot",
-
-		"PluginCoreLib",
-		"dagger2",
-		"dagger2-android-annotation-stubs",
-		"dagger2-bootstrap-compiler",
-		"dagger2-producers",
-		"okio-lib",
-		"setupdesign-strings",
-
-		//external/avb
-		"avbtool",
-		"libavb",
-		"avb_headers",
-
-		//external/libxml2
-		"xmllint",
-		"libxml2",
-
-		//external/fec
-		"libfec_rs",
-
-		//system/extras/ext4_utils
-		"libext4_utils",
-		"mke2fs_conf",
-		"mkuserimg_mke2fs",
-		"blk_alloc_to_base_fs",
-
-		//system/extras/libfec
-		"libfec",
-
-		//system/extras/squashfs_utils
-		"libsquashfs_utils",
-
-		//packages/apps/Car/libs/car-ui-lib/car-ui-androidx
-		// genrule dependencies for java_imports
-		"car-ui-androidx-annotation-nodeps",
-		"car-ui-androidx-collection-nodeps",
-		"car-ui-androidx-core-common-nodeps",
-		"car-ui-androidx-lifecycle-common-nodeps",
-		"car-ui-androidx-constraintlayout-solver-nodeps",
-
-		//frameworks/native/libs/input
-		"inputconstants_aidl",
-
-		// needed for aidl_interface's ndk backend
-		"libbinder_ndk",
-
-		"libusb",
-
-		//frameworks/native/cmds/cmd
-		"libcmd",
-
-		//system/chre
-		"chre_api",
-
-		//system/gsid
-		"libgsi",
-		"libgsi_headers",
-
-		//system/core/libkeyutils
-		"libkeyutils",
-
-		//bootable/recovery/otautil
-		"libotautil",
-
-		//system/vold
-		"libvold_headers",
-
-		//system/extras/libfscrypt
-		"libfscrypt",
-
-		//bootable/recovery/fuse_sideload
-		"libfusesideload",
-
-		"libcodec2_aidl",
-		"libcodec2_hidl@1.0",
-		"libcodec2_hidl@1.1",
-		"libcodec2_hidl@1.2",
-		"libcodec2_hidl_plugin_stub",
-		"libcodec2_hidl_plugin",
-		"libcodec2_hal_common",
-		"libstagefright_bufferqueue_helper_novndk",
-		"libGLESv2",
-		"libEGL",
-		"libcodec2_vndk",
-		"libnativeloader_lazy",
-		"libnativeloader",
-		"libEGL_getProcAddress",
-		"libEGL_blobCache",
-
-		"mediaswcodec",
-		"libmedia_headers",
-		"libmedia_codecserviceregistrant",
-		"libsfplugin_ccodec_utils",
-		"libcodec2_soft_aacenc",
-		"libcodec2_soft_amrnbdec",
-		"libcodec2_soft_amrnbenc",
-		"libcodec2_soft_amrwbdec",
-		"libcodec2_soft_amrwbenc",
-		"libcodec2_soft_hevcdec",
-		"libcodec2_soft_hevcenc",
-		"libcodec2_soft_g711alawdec",
-		"libcodec2_soft_g711mlawdec",
-		"libcodec2_soft_mpeg2dec",
-		"libcodec2_soft_h263dec",
-		"libcodec2_soft_h263enc",
-		"libcodec2_soft_mpeg4dec",
-		"libcodec2_soft_mpeg4enc",
-		"libcodec2_soft_mp3dec",
-		"libcodec2_soft_vorbisdec",
-		"libcodec2_soft_opusdec",
-		"libcodec2_soft_opusenc",
-		"libcodec2_soft_vp8dec",
-		"libcodec2_soft_vp9dec",
-		"libcodec2_soft_av1dec_gav1",
-		"libcodec2_soft_av1dec_dav1d",
-		"libcodec2_soft_vp8enc",
-		"libcodec2_soft_vp9enc",
-		"libcodec2_soft_rawdec",
-		"libcodec2_soft_flacdec",
-		"libcodec2_soft_flacenc",
-		"libcodec2_soft_gsmdec",
-		"libcodec2_soft_avcdec",
-		"libcodec2_soft_avcenc",
-		"libcodec2_soft_aacdec",
-		"libcodec2_soft_common",
-
-		// kotlin srcs in java libs
-		"kotlinx_atomicfu",
-
-		// kotlin srcs in java binary
-		"AnalyzerKt",
-		"trebuchet-core",
-
-		// kotlin srcs in android_library
-		"renderscript_toolkit",
-
-		//kotlin srcs in android_binary
-		"MusicKotlin",
-
-		// java_library with prebuilt sdk_version
-		"android-common",
-
-		// checked in current.txt for merged_txts
-		"non-updatable-current.txt",
-		"non-updatable-system-current.txt",
-		"non-updatable-module-lib-current.txt",
-		"non-updatable-system-server-current.txt",
-
-		// for api_fingerprint.txt generation
-		"api_fingerprint",
-
-		// for building com.android.neuralnetworks
-		"libimapper_stablec",
-		"libimapper_providerutils",
-
-		// min_sdk_version in android_app
-		"CtsShimUpgrade",
-
-		"art_cmdlineparser_headers",
-
-		// Mainline Module Apps
-		"CaptivePortalLogin",
-		"ModuleMetadata",
-
-		"libstagefright_headers",
-
-		// Apps with JNI libs
-		"SimpleJNI",
-		"libsimplejni",
-
-		// aidl
-		"aidl",
-		"libaidl-common",
-
-		// Used by xsd_config
-		"xsdc",
-
-		// cc_test that can be run by b test
-		"binderRpcWireProtocolTest",
-		"binderUnitTest",
-		"cpu_features-bit_utils_test",
-		"android.hardware.audio.common.test.utility_tests",
-		"HalAudioStreamWorkerTest",
-		"libjavacore-unit-tests",
-		"NeuralNetworksTest_utils",
-		"NeuralNetworksTest_logtag",
-		"NeuralNetworksTest_operations",
-		"nanoapp_chqts_shared_tests",
-		"fakeservicemanager_test",
-		"tristate_test",
-		"binderUtilsHostTest",
-		"run_dex2oat_test",
-		"bluetooth-address-unit-tests",
-
-		// for platform_compat_config
-		"process-compat-config",
-
-		// cc_* modules with rscript srcs
-		"rstest-latency",
-		"libRScpp_static",
-		"rs-headers",
-		"rs_script_api",
-		"libRSDispatch",
-
-		// hal_unit_tests and deps
-		"chre_flatbuffers",
-		"event_logger",
-		"hal_unit_tests",
-
-		"merge_annotation_zips_test",
-
-		// java_resources with multiple resource_dirs
-		"emma",
-
-		// NDK STL
-		"ndk_libc++abi",
-		"ndk_libunwind",
-		"ndk_libc++_static",
-		"ndk_libc++_shared",
-		"ndk_system",
-
-		// allowlist //prebuilts/common/misc/androidx-test/...
-		"androidx.test.runner",
-		"androidx.test.runner-nodeps",
-		"androidx.test.services.storage",
-		"androidx.test.services.storage-nodeps",
-		"androidx.test.monitor",
-		"androidx.test.monitor-nodeps",
-		"androidx.test.annotation",
-		"androidx.test.annotation-nodeps",
-
-		// jni deps of an internal android_test (b/297405812)
-		"libopenjdkjvmti_headers",
-
-		// tradefed deps
-		"apache-commons-compress",
-		"tradefed-protos",
-		"grpc-java",
-		"grpc-java-api",
-		"grpc-java-auth",
-		"grpc-java-context",
-		"grpc-java-core",
-		"grpc-java-core-inprocess",
-		"grpc-java-core-internal",
-		"grpc-java-core-util",
-		"grpc-java-protobuf",
-		"grpc-java-protobuf-lite",
-		"grpc-java-stub",
-		"grpc-java-annotation-stubs",
-		"grpc-java-annotation-stubs-srcjar",
-		"gen_annotations",
-		"opencensus-java-contrib-grpc-metrics",
-		"opencensus-java-api",
-		"gson",
-		"GsonBuildConfig.java",
-		"gson_version_generator",
-		"lab-resource-grpc",
-		"blueprint-deptools",
-		"protoc-gen-grpc-java-plugin",
-		"tf-remote-client",
-		"tradefed-lite",
-		"tradefed-isolation-protos",
-		"snakeyaml_patched_src_files",
-		"asuite_proto_java",
-		"tradefed-service-grpc-lib",
-		"tradefed-invocation-grpc",
-		"tradefed-external-dependencies",
-		"tradefed-dynamic-sharding-grpc",
-		"tradefed-device-manager-grpc",
-		"statsd_internal_protos",
-		"snakeyaml",
-		"loganalysis",
-		"junit-params",
-		"grpc-java-testing",
-		"grpc-java-netty-shaded",
-		"aoa-helper",
-		"test-services.apk",
-		"test-composers",
-		"py3-stdlib-prebuilt-srcs",
-		"platformprotos",
-		"test-services-normalized.apk",
-		"tradefed-common-util",
-		"tradefed-clearcut-client",
-		"tradefed-result-interfaces",
-		"tradefed-device-build-interfaces",
-		"tradefed-invocation-interfaces",
-		"tradefed-lib-core",
-
-		"libandroid_net_connectivity_com_android_net_module_util_jni",
-		"libservice-connectivity",
-
-		"mainline_modules_sdks_test",
-
-		"fake_device_config",
-	}
-
-	Bp2buildModuleTypeAlwaysConvertList = []string{
-		// go/keep-sorted start
-		"aconfig_declarations",
-		"aconfig_value_set",
-		"aconfig_values",
-		"aidl_interface_headers",
-		"bpf",
-		"cc_aconfig_library",
-		"cc_prebuilt_library",
-		"cc_prebuilt_library_headers",
-		"cc_prebuilt_library_shared",
-		"cc_prebuilt_library_static",
-		"combined_apis",
-		"droiddoc_exported_dir",
-		"java_aconfig_library",
-		"java_import",
-		"java_import_host",
-		"java_sdk_library",
-		"java_sdk_library_import",
-		"license",
-		"linker_config",
-		"ndk_headers",
-		"ndk_library",
-		"sysprop_library",
-		"versioned_ndk_headers",
-		"xsd_config",
-		// go/keep-sorted end
-	}
-
-	// Add the names of modules that bp2build should never convert, if it is
-	// in the package allowlist.  An error will be thrown if a module must
-	// not be here and in the alwaysConvert lists.
-	//
-	// For prebuilt modules (e.g. android_library_import), remember to add
-	// the "prebuilt_" prefix to the name, so that it's differentiable from
-	// the source versions within Soong's module graph.
-	Bp2buildModuleDoNotConvertList = []string{
-
-		// rust modules that have cc deps
-		"liblogger",
-		"libbssl_ffi",
-		"libbssl_ffi_nostd",
-		"pull_rust",
-		"libstatslog_rust",
-		"libstatslog_rust_header",
-		"libflatbuffers",
-		"liblog_event_list",
-		"libminijail_rust",
-		"libminijail_sys",
-		"libfsverity_rs",
-		"libtombstoned_client_rust",
-
-		// TODO(b/263326760): Failed already.
-		"minijail_compiler_unittest",
-		"minijail_parser_unittest",
-
-		// cc bugs
-
-		// TODO(b/198619163) module has same name as source
-		"logtagd.rc",
-
-		"libgtest_ndk_c++", "libgtest_main_ndk_c++", // TODO(b/201816222): Requires sdk_version support.
-
-		// TODO(b/202876379): has arch-variant static_executable
-		"linkerconfig",
-		"mdnsd",
-		"libcutils_test_static",
-		"KernelLibcutilsTest",
-
-		"linker",    // TODO(b/228316882): cc_binary uses link_crt
-		"versioner", // TODO(b/228313961):  depends on prebuilt shared library libclang-cpp_host as a shared library, which does not supply expected providers for a shared library
-
-		// requires host tools for apexer
-		"apexer_test", "apexer_test_host_tools", "host_apex_verifier", "host-apex-verifier",
-
-		// java bugs
-		"libbase_ndk",           // TODO(b/186826477): fails to link libctscamera2_jni for device (required for CtsCameraTestCases)
-		"bouncycastle",          // TODO(b/274474005): Need support for custom system_modules.
-		"bouncycastle-test-lib", // TODO(b/274474005): Reverse dependency of bouncycastle
-
-		// genrule incompatibilities
-		"brotli-fuzzer-corpus",                                       // TODO(b/202015218): outputs are in location incompatible with bazel genrule handling.
-		"platform_tools_properties", "build_tools_source_properties", // TODO(b/203369847): multiple genrules in the same package creating the same file
-
-		// aar support
-		"prebuilt_car-ui-androidx-core-common", // TODO(b/224773339), genrule dependency creates an .aar, not a .jar
-		// ERROR: The dependencies for the following 1 jar(s) are not complete.
-		// 1.bazel-out/android_target-fastbuild/bin/prebuilts/tools/common/m2/_aar/robolectric-monitor-1.0.2-alpha1/classes_and_libs_merged.jar
-		"prebuilt_robolectric-monitor-1.0.2-alpha1",
-
-		// path property for filegroups
-		"conscrypt",                        // TODO(b/210751803), we don't handle path property for filegroups
-		"conscrypt-for-host",               // TODO(b/210751803), we don't handle path property for filegroups
-		"host-libprotobuf-java-full",       // TODO(b/210751803), we don't handle path property for filegroups
-		"libprotobuf-internal-python-srcs", // TODO(b/210751803), we don't handle path property for filegroups
-
-		// go deps.
-		// TODO: b/305091740 - Rely on bp2build_deps to remove these dependencies.
-		"analyze_bcpf",              // depends on bpmodify a blueprint_go_binary.
-		"analyze_bcpf_test",         // depends on bpmodify a blueprint_go_binary.
-		"host_bionic_linker_asm",    // depends on extract_linker, a go binary.
-		"host_bionic_linker_script", // depends on extract_linker, a go binary.
-
-		// rust support
-		"libtombstoned_client_rust_bridge_code", "libtombstoned_client_wrapper", // rust conversions are not supported
-
-		// TODO: b/303474748 - aidl rules for java are incompatible with parcelable declarations
-		"modules-utils-list-slice",
-		"modules-utils-os",
-		"modules-utils-synchronous-result-receiver",
-
-		// aidl files not created
-		"overlayable_policy_aidl_interface",
-
-		// cc_test related.
-		// b/274164834 "Could not open Configuration file test.cfg"
-		"svcenc", "svcdec",
-
-		// Failing host cc_tests
-		"gtest_isolated_tests",
-		"libunwindstack_unit_test",
-		"power_tests", // failing test on server, but not on host
-
-		// reflect: call of reflect.Value.NumField on interface Value
-		// affects all cc_tests that depend on art_defaults
-		"libnativebridge-tests",
-		"libnativeloader_test",
-		"art_libnativebridge_cts_tests",
-		"art_standalone_libdexfile_external_tests",
-		"art_standalone_libdexfile_support_tests",
-		"libnativebridge-lazy-tests",
-		"libnativebridge-test-case",
-		"libnativebridge2-test-case",
-		"libnativebridge3-test-case",
-		"libnativebridge6-test-case",
-		"libnativebridge6prezygotefork",
-
-		"libandroidfw_tests", // failing due to data path issues
-
-		// error: overriding commands for target
-		// `out/host/linux-x86/nativetest64/gmock_tests/gmock_tests__cc_runner_test',
-		// previously defined at out/soong/installs-aosp_arm.mk:64919`
-		"gmock_tests",
-
-		// cc_test with unconverted deps, or are device-only (and not verified to pass yet)
-		"AMRWBEncTest",
-		"avcdec",
-		"avcenc",
-		"boringssl_test_support", //b/244431896
-		"cfi_test_helper",
-		"cintltst32",
-		"cintltst64",
-		"compare",
-		"cpuid",
-		"elftls_dlopen_ie_error_helper",
-		"fdtrack_test",
-		"google-benchmark-test",
-		"gtest-typed-test_test",
-		"gtest_tests",
-		"gtest_tests_no_main",
-		"gwp_asan_unittest",
-		"half_test",
-		"hashcombine_test",
-		"hevcdec",
-		"hevcenc",
-		"i444tonv12_eg",
-		"icu4c_sample_break",
-		"intltest32",
-		"intltest64",
-		"ion-unit-tests",
-		"jemalloc5_integrationtests",
-		"jemalloc5_unittests",
-		"jemalloc5_stresstests", // run by run_jemalloc_tests.sh and will be deleted after V
-		"libcutils_sockets_test",
-		"libhwbinder_latency",
-		"liblog-host-test", // failing tests
-		"libminijail_test",
-		"libminijail_unittest_gtest",
-		"libpackagelistparser_test",
-		"libprotobuf_vendor_suffix_test",
-		"libstagefright_amrnbenc_test",
-		"libstagefright_amrwbdec_test", // error: did not report any run
-		"libstagefright_m4vh263enc_test",
-		"libvndksupport-tests",
-		"libyuv_unittest",
-		"linker-unit-tests",
-		"malloc_debug_system_tests",
-		"malloc_debug_unit_tests",
-		"malloc_hooks_system_tests",
-		"mat_test",
-		"mathtest",
-		"memunreachable_test",
-		"metadata_tests",
-		"mpeg2dec",
-		"mvcdec",
-		"pngtest",
-		"preinit_getauxval_test_helper",
-		"preinit_syscall_test_helper",
-		"psnr",
-		"quat_test",
-		"scudo_unit_tests",
-		"thread_exit_cb_helper",
-		"ulp",
-		"vec_test",
-		"yuvconstants",
-		"yuvconvert",
-
-		// cc_test_library
-		"clang_diagnostic_tests",
-		"exec_linker_helper_lib",
-		"fortify_disabled_for_tidy",
-		"ld_config_test_helper_lib1",
-		"ld_config_test_helper_lib2",
-		"ld_config_test_helper_lib3",
-		"ld_preload_test_helper_lib1",
-		"ld_preload_test_helper_lib2",
-		"libBionicElfTlsLoaderTests",
-		"libBionicElfTlsTests",
-		"libBionicElfTlsTests",
-		"libBionicFramePointerTests",
-		"libBionicFramePointerTests",
-		"libBionicStandardTests",
-		"libBionicStandardTests",
-		"libBionicTests",
-		"libart-broken",
-		"libatest_simple_zip",
-		"libcfi-test",
-		"libcfi-test-bad",
-		"libcrash_test",
-		"libcrypto_fuzz_unsafe",
-		"libdl_preempt_test_1",
-		"libdl_preempt_test_2",
-		"libdl_test_df_1_global",
-		"libdlext_test",
-		"libdlext_test_different_soname",
-		"libdlext_test_fd",
-		"libdlext_test_norelro",
-		"libdlext_test_recursive",
-		"libdlext_test_zip",
-		"libdvrcommon_test",
-		"libfortify1-new-tests-clang",
-		"libfortify1-new-tests-clang",
-		"libfortify1-tests-clang",
-		"libfortify1-tests-clang",
-		"libfortify2-new-tests-clang",
-		"libfortify2-new-tests-clang",
-		"libfortify2-tests-clang",
-		"libfortify2-tests-clang",
-		"libgnu-hash-table-library",
-		"libicutest_static",
-		"liblinker_reloc_bench_000",
-		"liblinker_reloc_bench_001",
-		"liblinker_reloc_bench_002",
-		"liblinker_reloc_bench_003",
-		"liblinker_reloc_bench_004",
-		"liblinker_reloc_bench_005",
-		"liblinker_reloc_bench_006",
-		"liblinker_reloc_bench_007",
-		"liblinker_reloc_bench_008",
-		"liblinker_reloc_bench_009",
-		"liblinker_reloc_bench_010",
-		"liblinker_reloc_bench_011",
-		"liblinker_reloc_bench_012",
-		"liblinker_reloc_bench_013",
-		"liblinker_reloc_bench_014",
-		"liblinker_reloc_bench_015",
-		"liblinker_reloc_bench_016",
-		"liblinker_reloc_bench_017",
-		"liblinker_reloc_bench_018",
-		"liblinker_reloc_bench_019",
-		"liblinker_reloc_bench_020",
-		"liblinker_reloc_bench_021",
-		"liblinker_reloc_bench_022",
-		"liblinker_reloc_bench_023",
-		"liblinker_reloc_bench_024",
-		"liblinker_reloc_bench_025",
-		"liblinker_reloc_bench_026",
-		"liblinker_reloc_bench_027",
-		"liblinker_reloc_bench_028",
-		"liblinker_reloc_bench_029",
-		"liblinker_reloc_bench_030",
-		"liblinker_reloc_bench_031",
-		"liblinker_reloc_bench_032",
-		"liblinker_reloc_bench_033",
-		"liblinker_reloc_bench_034",
-		"liblinker_reloc_bench_035",
-		"liblinker_reloc_bench_036",
-		"liblinker_reloc_bench_037",
-		"liblinker_reloc_bench_038",
-		"liblinker_reloc_bench_039",
-		"liblinker_reloc_bench_040",
-		"liblinker_reloc_bench_041",
-		"liblinker_reloc_bench_042",
-		"liblinker_reloc_bench_043",
-		"liblinker_reloc_bench_044",
-		"liblinker_reloc_bench_045",
-		"liblinker_reloc_bench_046",
-		"liblinker_reloc_bench_047",
-		"liblinker_reloc_bench_048",
-		"liblinker_reloc_bench_049",
-		"liblinker_reloc_bench_050",
-		"liblinker_reloc_bench_051",
-		"liblinker_reloc_bench_052",
-		"liblinker_reloc_bench_053",
-		"liblinker_reloc_bench_054",
-		"liblinker_reloc_bench_055",
-		"liblinker_reloc_bench_056",
-		"liblinker_reloc_bench_057",
-		"liblinker_reloc_bench_058",
-		"liblinker_reloc_bench_059",
-		"liblinker_reloc_bench_060",
-		"liblinker_reloc_bench_061",
-		"liblinker_reloc_bench_062",
-		"liblinker_reloc_bench_063",
-		"liblinker_reloc_bench_064",
-		"liblinker_reloc_bench_065",
-		"liblinker_reloc_bench_066",
-		"liblinker_reloc_bench_067",
-		"liblinker_reloc_bench_068",
-		"liblinker_reloc_bench_069",
-		"liblinker_reloc_bench_070",
-		"liblinker_reloc_bench_071",
-		"liblinker_reloc_bench_072",
-		"liblinker_reloc_bench_073",
-		"liblinker_reloc_bench_074",
-		"liblinker_reloc_bench_075",
-		"liblinker_reloc_bench_076",
-		"liblinker_reloc_bench_077",
-		"liblinker_reloc_bench_078",
-		"liblinker_reloc_bench_079",
-		"liblinker_reloc_bench_080",
-		"liblinker_reloc_bench_081",
-		"liblinker_reloc_bench_082",
-		"liblinker_reloc_bench_083",
-		"liblinker_reloc_bench_084",
-		"liblinker_reloc_bench_085",
-		"liblinker_reloc_bench_086",
-		"liblinker_reloc_bench_087",
-		"liblinker_reloc_bench_088",
-		"liblinker_reloc_bench_089",
-		"liblinker_reloc_bench_090",
-		"liblinker_reloc_bench_091",
-		"liblinker_reloc_bench_092",
-		"liblinker_reloc_bench_093",
-		"liblinker_reloc_bench_094",
-		"liblinker_reloc_bench_095",
-		"liblinker_reloc_bench_096",
-		"liblinker_reloc_bench_097",
-		"liblinker_reloc_bench_098",
-		"liblinker_reloc_bench_099",
-		"liblinker_reloc_bench_100",
-		"liblinker_reloc_bench_101",
-		"liblinker_reloc_bench_102",
-		"liblinker_reloc_bench_103",
-		"liblinker_reloc_bench_104",
-		"liblinker_reloc_bench_105",
-		"liblinker_reloc_bench_106",
-		"liblinker_reloc_bench_107",
-		"liblinker_reloc_bench_108",
-		"liblinker_reloc_bench_109",
-		"liblinker_reloc_bench_110",
-		"liblinker_reloc_bench_111",
-		"liblinker_reloc_bench_112",
-		"liblinker_reloc_bench_113",
-		"liblinker_reloc_bench_114",
-		"liblinker_reloc_bench_115",
-		"liblinker_reloc_bench_116",
-		"liblinker_reloc_bench_117",
-		"liblinker_reloc_bench_118",
-		"liblinker_reloc_bench_119",
-		"liblinker_reloc_bench_120",
-		"liblinker_reloc_bench_121",
-		"liblinker_reloc_bench_122",
-		"liblinker_reloc_bench_123",
-		"liblinker_reloc_bench_124",
-		"liblinker_reloc_bench_125",
-		"liblinker_reloc_bench_126",
-		"liblinker_reloc_bench_127",
-		"liblinker_reloc_bench_128",
-		"liblinker_reloc_bench_129",
-		"liblinker_reloc_bench_130",
-		"liblinker_reloc_bench_131",
-		"liblinker_reloc_bench_132",
-		"liblinker_reloc_bench_133",
-		"liblinker_reloc_bench_134",
-		"liblinker_reloc_bench_135",
-		"liblinker_reloc_bench_136",
-		"liblinker_reloc_bench_137",
-		"liblinker_reloc_bench_138",
-		"liblinker_reloc_bench_139",
-		"liblinker_reloc_bench_140",
-		"liblinker_reloc_bench_141",
-		"liblinker_reloc_bench_142",
-		"liblinker_reloc_bench_143",
-		"liblinker_reloc_bench_144",
-		"liblinker_reloc_bench_145",
-		"liblinker_reloc_bench_146",
-		"liblinker_reloc_bench_147",
-		"liblinker_reloc_bench_148",
-		"liblinker_reloc_bench_149",
-		"liblinker_reloc_bench_150",
-		"liblinker_reloc_bench_151",
-		"liblinker_reloc_bench_152",
-		"liblinker_reloc_bench_153",
-		"liblinker_reloc_bench_154",
-		"liblinker_reloc_bench_155",
-		"liblinker_reloc_bench_156",
-		"liblinker_reloc_bench_157",
-		"liblinker_reloc_bench_158",
-		"liblinker_reloc_bench_159",
-		"liblinker_reloc_bench_160",
-		"liblinker_reloc_bench_161",
-		"liblinker_reloc_bench_162",
-		"liblinker_reloc_bench_163",
-		"liblinker_reloc_bench_164",
-		"liblinker_reloc_bench_165",
-		"liblinker_reloc_bench_166",
-		"liblinker_reloc_bench_167",
-		"liblinker_reloc_bench_168",
-		"libns_hidden_child_app",
-		"libns_hidden_child_global",
-		"libns_hidden_child_internal",
-		"libns_hidden_child_public",
-		"libnstest_dlopened",
-		"libnstest_ns_a_public1",
-		"libnstest_ns_a_public1_internal",
-		"libnstest_ns_b_public2",
-		"libnstest_ns_b_public3",
-		"libnstest_private",
-		"libnstest_private_external",
-		"libnstest_public",
-		"libnstest_public_internal",
-		"libnstest_root",
-		"libnstest_root_not_isolated",
-		"librelocations-ANDROID_REL",
-		"librelocations-ANDROID_RELR",
-		"librelocations-RELR",
-		"librelocations-fat",
-		"libsegment_gap_inner",
-		"libsegment_gap_outer",
-		"libssl_fuzz_unsafe",
-		"libstatssocket_private",
-		"libsysv-hash-table-library",
-		"libtest_atexit",
-		"libtest_check_order_dlsym",
-		"libtest_check_order_dlsym_1_left",
-		"libtest_check_order_dlsym_2_right",
-		"libtest_check_order_dlsym_3_c",
-		"libtest_check_order_dlsym_a",
-		"libtest_check_order_dlsym_b",
-		"libtest_check_order_dlsym_d",
-		"libtest_check_order_reloc_root",
-		"libtest_check_order_reloc_root_1",
-		"libtest_check_order_reloc_root_2",
-		"libtest_check_order_reloc_siblings",
-		"libtest_check_order_reloc_siblings_1",
-		"libtest_check_order_reloc_siblings_2",
-		"libtest_check_order_reloc_siblings_3",
-		"libtest_check_order_reloc_siblings_a",
-		"libtest_check_order_reloc_siblings_b",
-		"libtest_check_order_reloc_siblings_c",
-		"libtest_check_order_reloc_siblings_c_1",
-		"libtest_check_order_reloc_siblings_c_2",
-		"libtest_check_order_reloc_siblings_d",
-		"libtest_check_order_reloc_siblings_e",
-		"libtest_check_order_reloc_siblings_f",
-		"libtest_check_rtld_next_from_library",
-		"libtest_dlopen_df_1_global",
-		"libtest_dlopen_from_ctor",
-		"libtest_dlopen_from_ctor_main",
-		"libtest_dlopen_weak_undefined_func",
-		"libtest_dlsym_df_1_global",
-		"libtest_dlsym_from_this",
-		"libtest_dlsym_from_this_child",
-		"libtest_dlsym_from_this_grandchild",
-		"libtest_dlsym_weak_func",
-		"libtest_dt_runpath_a",
-		"libtest_dt_runpath_b",
-		"libtest_dt_runpath_c",
-		"libtest_dt_runpath_d",
-		"libtest_dt_runpath_d_zip",
-		"libtest_dt_runpath_x",
-		"libtest_dt_runpath_y",
-		"libtest_elftls_dynamic",
-		"libtest_elftls_dynamic_filler_1",
-		"libtest_elftls_dynamic_filler_2",
-		"libtest_elftls_dynamic_filler_3",
-		"libtest_elftls_shared_var",
-		"libtest_elftls_shared_var_ie",
-		"libtest_elftls_tprel",
-		"libtest_empty",
-		"libtest_ifunc",
-		"libtest_ifunc_variable",
-		"libtest_ifunc_variable_impl",
-		"libtest_indirect_thread_local_dtor",
-		"libtest_init_fini_order_child",
-		"libtest_init_fini_order_grand_child",
-		"libtest_init_fini_order_root",
-		"libtest_init_fini_order_root2",
-		"libtest_missing_symbol",
-		"libtest_missing_symbol_child_private",
-		"libtest_missing_symbol_child_public",
-		"libtest_missing_symbol_root",
-		"libtest_nodelete_1",
-		"libtest_nodelete_2",
-		"libtest_nodelete_dt_flags_1",
-		"libtest_pthread_atfork",
-		"libtest_relo_check_dt_needed_order",
-		"libtest_relo_check_dt_needed_order_1",
-		"libtest_relo_check_dt_needed_order_2",
-		"libtest_simple",
-		"libtest_thread_local_dtor",
-		"libtest_thread_local_dtor2",
-		"libtest_two_parents_child",
-		"libtest_two_parents_parent1",
-		"libtest_two_parents_parent2",
-		"libtest_versioned_lib",
-		"libtest_versioned_libv1",
-		"libtest_versioned_libv2",
-		"libtest_versioned_otherlib",
-		"libtest_versioned_otherlib_empty",
-		"libtest_versioned_uselibv1",
-		"libtest_versioned_uselibv2",
-		"libtest_versioned_uselibv2_other",
-		"libtest_versioned_uselibv3_other",
-		"libtest_with_dependency",
-		"libtest_with_dependency_loop",
-		"libtest_with_dependency_loop_a",
-		"libtest_with_dependency_loop_b",
-		"libtest_with_dependency_loop_b_tmp",
-		"libtest_with_dependency_loop_c",
-		"libtestshared",
-
-		// releasetools
-		"verity_utils",
-		"check_ota_package_signature",
-		"check_target_files_vintf",
-		"releasetools_check_target_files_vintf",
-		"ota_from_target_files",
-		"releasetools_ota_from_target_files",
-		"add_img_to_target_files",
-		"releasetools_add_img_to_target_files",
-		"fsverity_metadata_generator",
-		"sign_target_files_apks",
-
-		// depends on the support of yacc file
-		"libapplypatch",
-		"libapplypatch_modes",
-		"applypatch",
-
-		// TODO(b/254476335): disable the following due to this bug
-		"libapexinfo",
-		"libapexinfo_tests",
-
-		// uses glob in $(locations)
-		"libc_musl_sysroot",
-
-		// TODO(b/266459895): depends on libunwindstack
-		"libutils_test",
-
-		// Has dependencies on other tools like ziptool, bp2build'd data properties don't work with these tests atm
-		"ziparchive_tests_large",
-		"mkbootimg_test",
-		"certify_bootimg_test",
-
-		// Despite being _host module types, these require devices to run
-		"logd_integration_test",
-		"mobly-hello-world-test",
-		"mobly-multidevice-test",
-
-		// TODO(b/274805756): Support core_platform and current java APIs
-		"fake-framework",
-
-		// TODO(b/277616982): These modules depend on private java APIs, but maybe they don't need to.
-		"StreamingProtoTest",
-		"textclassifierprotoslite",
-		"styleprotoslite",
-		"CtsPkgInstallerConstants",
-		"guava-android-testlib",
-
-		// python_test_host with test data
-		"sbom_writers_test",
-		"hidl_test",
-
-		// TODO(B/283193845): Remove tradefed from this list.
-		"tradefed",
-
-		"libprotobuf-full-test", // TODO(b/246997908): cannot convert proto_libraries which implicitly include other srcs in the same directory
-		"libprotobuf-lite-test", // TODO(b/246997908): cannot convert proto_libraries which implicitly include other srcs in the same directory
-
-		"logcat", // TODO(b/246997908): cannot convert proto_libraries which implicitly include other srcs in the same directory
-
-		"expresscatalogvalidator", // TODO(b/246997908): cannot convert proto_libraries which implicitly include other srcs in the same directory
-
-		// r8 is a java_binary, which creates an implicit "r8.jar" target, but the
-		// same package contains a "r8.jar" file which gets overshadowed by the implicit target.
-		// We don't need this target as we're not using the Soong wrapper for now
-		"r8",
-
-		// TODO(b/299924782): Fix linking error
-		"libbinder_on_trusty_mock",
-
-		// TODO(b/299943581): Depends on aidl filegroups with implicit headers
-		"libdataloader_aidl-cpp",
-		"libincremental_manager_aidl-cpp",
-
-		// TODO(b/299974637) Fix linking error
-		"libbinder_rpc_unstable",
-
-		// TODO(b/297356704) sdk_version is unset.
-		"VendorAtomCodeGenJavaTest",
-
-		// TODO: b/305223367 - Missing dep on android.test.base-neverlink
-		"ObjenesisTck",
-
-		// TODO - b/306197073: Sets different STL for host and device variants
-		"trace_processor_shell",
-
-		// TODO - b/303713102: duplicate deps added by cc_lite_proto_library
-		"perfetto_unittests",
-		"perfetto_integrationtests",
-
-		// TODO - b/306194966: Depends on an empty filegroup
-		"libperfetto_c",
-	}
-
-	// Bazel prod-mode allowlist. Modules in this list are built by Bazel
-	// in either prod mode or staging mode.
-	ProdMixedBuildsEnabledList = []string{
-		// M5: tzdata launch
-		"com.android.tzdata",
-		"test1_com.android.tzdata",
-		"test3_com.android.tzdata",
-		// M7: adbd launch
-		"com.android.adbd",
-		"test_com.android.adbd",
-		"adbd_test",
-		"adb_crypto_test",
-		"adb_pairing_auth_test",
-		"adb_pairing_connection_test",
-		"adb_tls_connection_test",
-		// M9: mixed builds for mainline trains launch
-		"api_fingerprint",
-		// M11: neuralnetworks launch
-		"com.android.neuralnetworks",
-		"test_com.android.neuralnetworks",
-		"libneuralnetworks",
-		"libneuralnetworks_static",
-		// M13: media.swcodec launch
-		// TODO(b/307389608) Relaunch swcodec after fixing rust dependencies
-		// "com.android.media.swcodec",
-		// "test_com.android.media.swcodec",
-		// "libstagefright_foundation",
-		// "libcodec2_hidl@1.0",
-	}
-
-	// Staging-mode allowlist. Modules in this list are only built
-	// by Bazel with --bazel-mode-staging. This list should contain modules
-	// which will soon be added to the prod allowlist.
-	// It is implicit that all modules in ProdMixedBuildsEnabledList will
-	// also be built - do not add them to this list.
-	StagingMixedBuildsEnabledList = []string{}
-
-	// These should be the libs that are included by the apexes in the ProdMixedBuildsEnabledList
-	ProdDclaMixedBuildsEnabledList = []string{
-		"libbase",
-		"libc++",
-		"libcrypto",
-		"libcutils",
-		"libstagefright_flacdec",
-		"libutils",
-	}
-
-	// These should be the libs that are included by the apexes in the StagingMixedBuildsEnabledList
-	StagingDclaMixedBuildsEnabledList = []string{}
-
-	// TODO(b/269342245): Enable the rest of the DCLA libs
-	// "libssl",
-
 	// The list of module types which are expected to spend lots of build time.
 	// With `--ninja_weight_source=soong`, ninja builds these module types and deps first.
 	HugeModuleTypePrefixMap = map[string]int{
diff --git a/android/androidmk.go b/android/androidmk.go
index c4b93c7..07f7c58 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -30,6 +30,7 @@
 	"reflect"
 	"runtime"
 	"sort"
+	"strconv"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -159,7 +160,7 @@
 }
 
 type AndroidMkExtraEntriesContext interface {
-	Provider(provider blueprint.ProviderKey) interface{}
+	Provider(provider blueprint.AnyProviderKey) (any, bool)
 }
 
 type androidMkExtraEntriesContext struct {
@@ -167,8 +168,8 @@
 	mod blueprint.Module
 }
 
-func (a *androidMkExtraEntriesContext) Provider(provider blueprint.ProviderKey) interface{} {
-	return a.ctx.ModuleProvider(a.mod, provider)
+func (a *androidMkExtraEntriesContext) Provider(provider blueprint.AnyProviderKey) (any, bool) {
+	return a.ctx.moduleProvider(a.mod, provider)
 }
 
 type AndroidMkExtraEntriesFunc func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries)
@@ -275,14 +276,17 @@
 }
 
 // AddCompatibilityTestSuites adds the supplied test suites to the EntryMap, with special handling
-// for partial MTS test suites.
+// for partial MTS and MCTS test suites.
 func (a *AndroidMkEntries) AddCompatibilityTestSuites(suites ...string) {
-	// MTS supports a full test suite and partial per-module MTS test suites, with naming mts-${MODULE}.
-	// To reduce repetition, if we find a partial MTS test suite without an full MTS test suite,
+	// M(C)TS supports a full test suite and partial per-module MTS test suites, with naming mts-${MODULE}.
+	// To reduce repetition, if we find a partial M(C)TS test suite without an full M(C)TS test suite,
 	// we add the full test suite to our list.
 	if PrefixInList(suites, "mts-") && !InList("mts", suites) {
 		suites = append(suites, "mts")
 	}
+	if PrefixInList(suites, "mcts-") && !InList("mcts", suites) {
+		suites = append(suites, "mcts")
+	}
 	a.AddStrings("LOCAL_COMPATIBILITY_SUITE", suites...)
 }
 
@@ -492,8 +496,7 @@
 	ModuleDir(module blueprint.Module) string
 	ModuleSubDir(module blueprint.Module) string
 	Config() Config
-	ModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{}
-	ModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool
+	moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
 	ModuleType(module blueprint.Module) string
 }
 
@@ -623,11 +626,14 @@
 		}
 	}
 
-	if ctx.ModuleHasProvider(mod, LicenseMetadataProvider) {
-		licenseMetadata := ctx.ModuleProvider(mod, LicenseMetadataProvider).(*LicenseMetadataInfo)
+	if licenseMetadata, ok := SingletonModuleProvider(ctx, mod, LicenseMetadataProvider); ok {
 		a.SetPath("LOCAL_SOONG_LICENSE_METADATA", licenseMetadata.LicenseMetadataPath)
 	}
 
+	if _, ok := SingletonModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+		a.SetBool("LOCAL_SOONG_MODULE_INFO_JSON", true)
+	}
+
 	extraCtx := &androidMkExtraEntriesContext{
 		ctx: ctx,
 		mod: mod,
@@ -645,14 +651,14 @@
 	}
 }
 
+func (a *AndroidMkEntries) disabled() bool {
+	return a.Disabled || !a.OutputFile.Valid()
+}
+
 // write  flushes the AndroidMkEntries's in-struct data populated by AndroidMkEntries into the
 // given Writer object.
 func (a *AndroidMkEntries) write(w io.Writer) {
-	if a.Disabled {
-		return
-	}
-
-	if !a.OutputFile.Valid() {
+	if a.disabled() {
 		return
 	}
 
@@ -698,7 +704,9 @@
 		return
 	}
 
-	err := translateAndroidMk(ctx, absolutePath(transMk.String()), androidMkModulesList)
+	moduleInfoJSON := PathForOutput(ctx, "module-info"+String(ctx.Config().productVariables.Make_suffix)+".json")
+
+	err := translateAndroidMk(ctx, absolutePath(transMk.String()), moduleInfoJSON, androidMkModulesList)
 	if err != nil {
 		ctx.Errorf(err.Error())
 	}
@@ -709,14 +717,16 @@
 	})
 }
 
-func translateAndroidMk(ctx SingletonContext, absMkFile string, mods []blueprint.Module) error {
+func translateAndroidMk(ctx SingletonContext, absMkFile string, moduleInfoJSONPath WritablePath, mods []blueprint.Module) error {
 	buf := &bytes.Buffer{}
 
+	var moduleInfoJSONs []*ModuleInfoJSON
+
 	fmt.Fprintln(buf, "LOCAL_MODULE_MAKEFILE := $(lastword $(MAKEFILE_LIST))")
 
 	typeStats := make(map[string]int)
 	for _, mod := range mods {
-		err := translateAndroidMkModule(ctx, buf, mod)
+		err := translateAndroidMkModule(ctx, buf, &moduleInfoJSONs, mod)
 		if err != nil {
 			os.Remove(absMkFile)
 			return err
@@ -738,10 +748,36 @@
 		fmt.Fprintf(buf, "STATS.SOONG_MODULE_TYPE.%s := %d\n", mod_type, typeStats[mod_type])
 	}
 
-	return pathtools.WriteFileIfChanged(absMkFile, buf.Bytes(), 0666)
+	err := pathtools.WriteFileIfChanged(absMkFile, buf.Bytes(), 0666)
+	if err != nil {
+		return err
+	}
+
+	return writeModuleInfoJSON(ctx, moduleInfoJSONs, moduleInfoJSONPath)
 }
 
-func translateAndroidMkModule(ctx SingletonContext, w io.Writer, mod blueprint.Module) error {
+func writeModuleInfoJSON(ctx SingletonContext, moduleInfoJSONs []*ModuleInfoJSON, moduleInfoJSONPath WritablePath) error {
+	moduleInfoJSONBuf := &strings.Builder{}
+	moduleInfoJSONBuf.WriteString("[")
+	for i, moduleInfoJSON := range moduleInfoJSONs {
+		if i != 0 {
+			moduleInfoJSONBuf.WriteString(",\n")
+		}
+		moduleInfoJSONBuf.WriteString("{")
+		moduleInfoJSONBuf.WriteString(strconv.Quote(moduleInfoJSON.core.RegisterName))
+		moduleInfoJSONBuf.WriteString(":")
+		err := encodeModuleInfoJSON(moduleInfoJSONBuf, moduleInfoJSON)
+		moduleInfoJSONBuf.WriteString("}")
+		if err != nil {
+			return err
+		}
+	}
+	moduleInfoJSONBuf.WriteString("]")
+	WriteFileRule(ctx, moduleInfoJSONPath, moduleInfoJSONBuf.String())
+	return nil
+}
+
+func translateAndroidMkModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON, mod blueprint.Module) error {
 	defer func() {
 		if r := recover(); r != nil {
 			panic(fmt.Errorf("%s in translateAndroidMkModule for module %s variant %s",
@@ -750,17 +786,23 @@
 	}()
 
 	// Additional cases here require review for correct license propagation to make.
+	var err error
 	switch x := mod.(type) {
 	case AndroidMkDataProvider:
-		return translateAndroidModule(ctx, w, mod, x)
+		err = translateAndroidModule(ctx, w, moduleInfoJSONs, mod, x)
 	case bootstrap.GoBinaryTool:
-		return translateGoBinaryModule(ctx, w, mod, x)
+		err = translateGoBinaryModule(ctx, w, mod, x)
 	case AndroidMkEntriesProvider:
-		return translateAndroidMkEntriesModule(ctx, w, mod, x)
+		err = translateAndroidMkEntriesModule(ctx, w, moduleInfoJSONs, mod, x)
 	default:
 		// Not exported to make so no make variables to set.
-		return nil
 	}
+
+	if err != nil {
+		return err
+	}
+
+	return err
 }
 
 // A simple, special Android.mk entry output func to make it possible to build blueprint tools using
@@ -803,8 +845,8 @@
 
 // A support func for the deprecated AndroidMkDataProvider interface. Use AndroidMkEntryProvider
 // instead.
-func translateAndroidModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
-	provider AndroidMkDataProvider) error {
+func translateAndroidModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON,
+	mod blueprint.Module, provider AndroidMkDataProvider) error {
 
 	amod := mod.(Module).base()
 	if shouldSkipAndroidMkProcessing(amod) {
@@ -817,6 +859,7 @@
 	}
 
 	data.fillInData(ctx, mod)
+	aconfigUpdateAndroidMkData(ctx, mod.(Module), &data)
 
 	prefix := ""
 	if amod.ArchSpecific() {
@@ -854,6 +897,7 @@
 		case "*java.SystemModules": // doesn't go through base_rules
 		case "*java.systemModulesImport": // doesn't go through base_rules
 		case "*phony.phony": // license properties written
+		case "*phony.PhonyRule": // writes phony deps and acts like `.PHONY`
 		case "*selinux.selinuxContextsModule": // license properties written
 		case "*sysprop.syspropLibrary": // license properties written
 		default:
@@ -866,17 +910,19 @@
 		WriteAndroidMkData(w, data)
 	}
 
+	if !data.Entries.disabled() {
+		if moduleInfoJSON, ok := SingletonModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
+		}
+	}
+
 	return nil
 }
 
 // A support func for the deprecated AndroidMkDataProvider interface. Use AndroidMkEntryProvider
 // instead.
 func WriteAndroidMkData(w io.Writer, data AndroidMkData) {
-	if data.Disabled {
-		return
-	}
-
-	if !data.OutputFile.Valid() {
+	if data.Entries.disabled() {
 		return
 	}
 
@@ -891,18 +937,27 @@
 	fmt.Fprintln(w, "include "+data.Include)
 }
 
-func translateAndroidMkEntriesModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
-	provider AndroidMkEntriesProvider) error {
+func translateAndroidMkEntriesModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON,
+	mod blueprint.Module, provider AndroidMkEntriesProvider) error {
 	if shouldSkipAndroidMkProcessing(mod.(Module).base()) {
 		return nil
 	}
 
+	entriesList := provider.AndroidMkEntries()
+	aconfigUpdateAndroidMkEntries(ctx, mod.(Module), &entriesList)
+
 	// Any new or special cases here need review to verify correct propagation of license information.
-	for _, entries := range provider.AndroidMkEntries() {
+	for _, entries := range entriesList {
 		entries.fillInEntries(ctx, mod)
 		entries.write(w)
 	}
 
+	if len(entriesList) > 0 && !entriesList[0].disabled() {
+		if moduleInfoJSON, ok := SingletonModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
+		}
+	}
+
 	return nil
 }
 
diff --git a/android/apex.go b/android/apex.go
index c6d9940..4d36a93 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -89,7 +89,7 @@
 	TestApexes []string
 }
 
-var ApexInfoProvider = blueprint.NewMutatorProvider(ApexInfo{}, "apex")
+var ApexInfoProvider = blueprint.NewMutatorProvider[ApexInfo]("apex")
 
 func (i ApexInfo) AddJSONData(d *map[string]interface{}) {
 	(*d)["Apex"] = map[string]interface{}{
@@ -145,7 +145,14 @@
 	ApexContents []*ApexContents
 }
 
-var ApexTestForInfoProvider = blueprint.NewMutatorProvider(ApexTestForInfo{}, "apex_test_for")
+var ApexTestForInfoProvider = blueprint.NewMutatorProvider[ApexTestForInfo]("apex_test_for")
+
+// ApexBundleInfo contains information about the dependencies of an apex
+type ApexBundleInfo struct {
+	Contents *ApexContents
+}
+
+var ApexBundleInfoProvider = blueprint.NewMutatorProvider[ApexBundleInfo]("apex_info")
 
 // DepIsInSameApex defines an interface that should be used to determine whether a given dependency
 // should be considered as part of the same APEX as the current module or not. Note: this was
@@ -954,3 +961,18 @@
 	// Return true if the apex bundle is an apex_test
 	IsTestApex() bool
 }
+
+var ApexExportsInfoProvider = blueprint.NewProvider[ApexExportsInfo]()
+
+// ApexExportsInfo contains information about the artifacts provided by apexes to dexpreopt and hiddenapi
+type ApexExportsInfo struct {
+	// Canonical name of this APEX. Used to determine the path to the activated APEX on
+	// device (/apex/<apex_name>)
+	ApexName string
+
+	// Path to the image profile file on host (or empty, if profile is not generated).
+	ProfilePathOnHost Path
+
+	// Map from the apex library name (without prebuilt_ prefix) to the dex file path on host
+	LibraryNameToDexJarPathOnHost map[string]Path
+}
diff --git a/android/apex_contributions.go b/android/apex_contributions.go
index f13659a..c388aff 100644
--- a/android/apex_contributions.go
+++ b/android/apex_contributions.go
@@ -15,6 +15,8 @@
 package android
 
 import (
+	"strings"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -98,11 +100,23 @@
 func (a *allApexContributions) SetPrebuiltSelectionInfoProvider(ctx BaseModuleContext) {
 	addContentsToProvider := func(p *PrebuiltSelectionInfoMap, m *apexContributions) {
 		for _, content := range m.Contents() {
+			// Skip any apexes that have been added to the product specific ignore list
+			if InList(content, ctx.Config().BuildIgnoreApexContributionContents()) {
+				continue
+			}
+			// Coverage builds for TARGET_RELEASE=foo should always build from source,
+			// even if TARGET_RELEASE=foo uses prebuilt mainline modules.
+			// This is necessary because the checked-in prebuilts were generated with
+			// instrumentation turned off.
+			//
+			// Skip any prebuilt contents in coverage builds
+			if strings.HasPrefix(content, "prebuilt_") && (ctx.Config().JavaCoverageEnabled() || ctx.DeviceConfig().NativeCoverageEnabled()) {
+				continue
+			}
 			if !ctx.OtherModuleExists(content) && !ctx.Config().AllowMissingDependencies() {
 				ctx.ModuleErrorf("%s listed in apex_contributions %s does not exist\n", content, m.Name())
 			}
 			pi := &PrebuiltSelectionInfo{
-				baseModuleName:     RemoveOptionalPrebuiltPrefix(content),
 				selectedModuleName: content,
 				metadataModuleName: m.Name(),
 				apiDomain:          m.ApiDomain(),
@@ -119,14 +133,15 @@
 			ctx.ModuleErrorf("%s is not an apex_contributions module\n", child.Name())
 		}
 	})
-	ctx.SetProvider(PrebuiltSelectionInfoProvider, p)
+	SetProvider(ctx, PrebuiltSelectionInfoProvider, p)
 }
 
 // A provider containing metadata about whether source or prebuilt should be used
 // This provider will be used in prebuilt_select mutator to redirect deps
-var PrebuiltSelectionInfoProvider = blueprint.NewMutatorProvider(PrebuiltSelectionInfoMap{}, "prebuilt_select")
+var PrebuiltSelectionInfoProvider = blueprint.NewMutatorProvider[PrebuiltSelectionInfoMap]("prebuilt_select")
 
-// Map of baseModuleName to the selected source or prebuilt
+// Map of selected module names to a metadata object
+// The metadata contains information about the api_domain of the selected module
 type PrebuiltSelectionInfoMap map[string]PrebuiltSelectionInfo
 
 // Add a new entry to the map with some validations
@@ -134,18 +149,10 @@
 	if p == nil {
 		return
 	}
-	// Do not allow dups. If the base module (without the prebuilt_) has been added before, raise an exception.
-	if old, exists := (*pm)[p.baseModuleName]; exists {
-		ctx.ModuleErrorf("Cannot use Soong module: %s from apex_contributions: %s because it has been added previously as: %s from apex_contributions: %s\n",
-			p.selectedModuleName, p.metadataModuleName, old.selectedModuleName, old.metadataModuleName,
-		)
-	}
-	(*pm)[p.baseModuleName] = *p
+	(*pm)[p.selectedModuleName] = *p
 }
 
 type PrebuiltSelectionInfo struct {
-	// e.g. libc
-	baseModuleName string
 	// e.g. (libc|prebuilt_libc)
 	selectedModuleName string
 	// Name of the apex_contributions module
@@ -156,12 +163,21 @@
 
 // Returns true if `name` is explicitly requested using one of the selected
 // apex_contributions metadata modules.
-func (p *PrebuiltSelectionInfoMap) IsSelected(baseModuleName, name string) bool {
-	if i, exists := (*p)[baseModuleName]; exists {
-		return i.selectedModuleName == name
-	} else {
-		return false
+func (p *PrebuiltSelectionInfoMap) IsSelected(name string) bool {
+	_, exists := (*p)[name]
+	return exists
+}
+
+// Return the list of soong modules selected for this api domain
+// In the case of apexes, it is the canonical name of the apex on device (/apex/<apex_name>)
+func (p *PrebuiltSelectionInfoMap) GetSelectedModulesForApiDomain(apiDomain string) []string {
+	selected := []string{}
+	for _, entry := range *p {
+		if entry.apiDomain == apiDomain {
+			selected = append(selected, entry.selectedModuleName)
+		}
 	}
+	return selected
 }
 
 // This module type does not have any build actions.
diff --git a/android/api_levels.go b/android/api_levels.go
index 3f538c0..6fa4a0e 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -288,6 +288,8 @@
 // a core-for-system-modules.jar for the module-lib API scope.
 var LastWithoutModuleLibCoreSystemModules = uncheckedFinalApiLevel(31)
 
+var ApiLevelR = uncheckedFinalApiLevel(30)
+
 // ReplaceFinalizedCodenames returns the API level number associated with that API level
 // if the `raw` input is the codename of an API level has been finalized.
 // If the input is *not* a finalized codename, the input is returned unmodified.
@@ -336,7 +338,7 @@
 // ApiLevelFromUser for more details.
 func ApiLevelFromUserWithConfig(config Config, raw string) (ApiLevel, error) {
 	// This logic is replicated in starlark, if changing logic here update starlark code too
-	// https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/common/api.bzl;l=42;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061
+	// https://cs.android.com/android/platform/superproject/+/main:build/bazel/rules/common/api.bzl;l=42;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061
 	if raw == "" {
 		panic("API level string must be non-empty")
 	}
@@ -449,7 +451,7 @@
 		err    error
 	}
 	// This logic is replicated in starlark, if changing logic here update starlark code too
-	// https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/common/api.bzl;l=30;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061
+	// https://cs.android.com/android/platform/superproject/+/main:build/bazel/rules/common/api.bzl;l=30;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061
 	result := config.Once(finalCodenamesMapKey, func() interface{} {
 		apiLevelsMap, err := getApiLevelsMapReleasedVersions()
 
@@ -482,7 +484,7 @@
 		err    error
 	}
 	// This logic is replicated in starlark, if changing logic here update starlark code too
-	// https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/common/api.bzl;l=23;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061
+	// https://cs.android.com/android/platform/superproject/+/main:build/bazel/rules/common/api.bzl;l=23;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061
 	result := config.Once(apiLevelsMapKey, func() interface{} {
 		apiLevelsMap, err := getApiLevelsMapReleasedVersions()
 		if err == nil {
diff --git a/android/arch.go b/android/arch.go
index 7436660..4fe4345 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -426,6 +426,7 @@
 	// filters out non-Soong modules.  Now that we've handled them, create a
 	// normal android.BottomUpMutatorContext.
 	mctx := bottomUpMutatorContextFactory(bpctx, module, false)
+	defer bottomUpMutatorContextPool.Put(mctx)
 
 	base := module.base()
 
@@ -446,8 +447,10 @@
 		}
 	}
 
+	createCommonOSVariant := base.commonProperties.CreateCommonOSVariant
+
 	// If there are no supported OSes then disable the module.
-	if len(moduleOSList) == 0 {
+	if len(moduleOSList) == 0 && !createCommonOSVariant {
 		base.Disable()
 		return
 	}
@@ -458,7 +461,6 @@
 		osNames[i] = os.String()
 	}
 
-	createCommonOSVariant := base.commonProperties.CreateCommonOSVariant
 	if createCommonOSVariant {
 		// A CommonOS variant was requested so add it to the list of OS variants to
 		// create. It needs to be added to the end because it needs to depend on the
@@ -571,6 +573,7 @@
 	// filters out non-Soong modules.  Now that we've handled them, create a
 	// normal android.BottomUpMutatorContext.
 	mctx := bottomUpMutatorContextFactory(bpctx, module, false)
+	defer bottomUpMutatorContextPool.Put(mctx)
 
 	base := module.base()
 
@@ -1057,9 +1060,7 @@
 	// order checks the `android:"variant_prepend"` tag to handle properties where the
 	// arch-specific value needs to come before the generic value, for example for lists of
 	// include directories.
-	order := func(property string,
-		dstField, srcField reflect.StructField,
-		dstValue, srcValue interface{}) (proptools.Order, error) {
+	order := func(dstField, srcField reflect.StructField) (proptools.Order, error) {
 		if proptools.HasTag(dstField, "android", "variant_prepend") {
 			return proptools.Prepend, nil
 		} else {
diff --git a/android/arch_list.go b/android/arch_list.go
index ab644a4..f4409a9 100644
--- a/android/arch_list.go
+++ b/android/arch_list.go
@@ -34,6 +34,11 @@
 		"broadwell",
 		"goldmont",
 		"goldmont-plus",
+		// Target arch is goldmont, but without supporting SHA and XSAVES.
+		// This ensures efficient execution on a broad range of Intel/AMD CPUs used
+		// in Chromebooks, including those lacking SHA or XSAVES support.
+		// (e.g. Kaby Lake, Gemini Lake, Alder Lake and AMD Zen series)
+		"goldmont-without-sha-xsaves",
 		"haswell",
 		"icelake",
 		"ivybridge",
@@ -52,6 +57,7 @@
 		"broadwell",
 		"goldmont",
 		"goldmont-plus",
+		"goldmont-without-sha-xsaves",
 		"haswell",
 		"icelake",
 		"ivybridge",
@@ -197,6 +203,15 @@
 			"popcnt",
 			"movbe",
 		},
+		"goldmont-without-sha-xsaves": {
+			"ssse3",
+			"sse4",
+			"sse4_1",
+			"sse4_2",
+			"aes_ni",
+			"popcnt",
+			"movbe",
+		},
 		"haswell": {
 			"ssse3",
 			"sse4",
@@ -358,6 +373,14 @@
 			"aes_ni",
 			"popcnt",
 		},
+		"goldmont-without-sha-xsaves": {
+			"ssse3",
+			"sse4",
+			"sse4_1",
+			"sse4_2",
+			"aes_ni",
+			"popcnt",
+		},
 		"haswell": {
 			"ssse3",
 			"sse4",
diff --git a/android/arch_module_context.go b/android/arch_module_context.go
new file mode 100644
index 0000000..3cf4b41
--- /dev/null
+++ b/android/arch_module_context.go
@@ -0,0 +1,83 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+// ArchModuleContext can be embedded in other contexts to provide information about the module set by
+// the archMutator.
+type ArchModuleContext interface {
+	Target() Target
+	TargetPrimary() bool
+
+	// The additional arch specific targets (e.g. 32/64 bit) that this module variant is
+	// responsible for creating.
+	MultiTargets() []Target
+	Arch() Arch
+	Os() OsType
+	Host() bool
+	Device() bool
+	Darwin() bool
+	Windows() bool
+	PrimaryArch() bool
+}
+
+type archModuleContext struct {
+	// TODO: these should eventually go through a (possibly cached) provider like any other configuration instead
+	//  of being special cased.
+	os            OsType
+	target        Target
+	targetPrimary bool
+	multiTargets  []Target
+	primaryArch   bool
+}
+
+func (a *archModuleContext) Target() Target {
+	return a.target
+}
+
+func (a *archModuleContext) TargetPrimary() bool {
+	return a.targetPrimary
+}
+
+func (a *archModuleContext) MultiTargets() []Target {
+	return a.multiTargets
+}
+
+func (a *archModuleContext) Arch() Arch {
+	return a.target.Arch
+}
+
+func (a *archModuleContext) Os() OsType {
+	return a.os
+}
+
+func (a *archModuleContext) Host() bool {
+	return a.os.Class == Host
+}
+
+func (a *archModuleContext) Device() bool {
+	return a.os.Class == Device
+}
+
+func (a *archModuleContext) Darwin() bool {
+	return a.os == Darwin
+}
+
+func (a *archModuleContext) Windows() bool {
+	return a.os == Windows
+}
+
+func (b *archModuleContext) PrimaryArch() bool {
+	return b.primaryArch
+}
diff --git a/android/base_module_context.go b/android/base_module_context.go
index 4312e9b..b9c1153 100644
--- a/android/base_module_context.go
+++ b/android/base_module_context.go
@@ -26,6 +26,7 @@
 // instead of a blueprint.Module, plus some extra methods that return Android-specific information
 // about the current module.
 type BaseModuleContext interface {
+	ArchModuleContext
 	EarlyModuleContext
 
 	blueprintBaseModuleContext() blueprint.BaseModuleContext
@@ -75,30 +76,28 @@
 	// It is intended for use inside the visit functions of Visit* and WalkDeps.
 	OtherModuleType(m blueprint.Module) string
 
-	// OtherModuleProvider returns the value for a provider for the given module.  If the value is
-	// not set it returns the zero value of the type of the provider, so the return value can always
-	// be type asserted to the type of the provider.  The value returned may be a deep copy of the
-	// value originally passed to SetProvider.
-	OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{}
-
-	// OtherModuleHasProvider returns true if the provider for the given module has been set.
-	OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool
+	// otherModuleProvider returns the value for a provider for the given module.  If the value is
+	// not set it returns nil and false.  The value returned may be a deep copy of the value originally
+	// passed to SetProvider.
+	//
+	// This method shouldn't be used directly, prefer the type-safe android.OtherModuleProvider instead.
+	otherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
 
 	// Provider returns the value for a provider for the current module.  If the value is
-	// not set it returns the zero value of the type of the provider, so the return value can always
-	// be type asserted to the type of the provider.  It panics if called before the appropriate
+	// not set it returns nil and false.  It panics if called before the appropriate
 	// mutator or GenerateBuildActions pass for the provider.  The value returned may be a deep
 	// copy of the value originally passed to SetProvider.
-	Provider(provider blueprint.ProviderKey) interface{}
+	//
+	// This method shouldn't be used directly, prefer the type-safe android.ModuleProvider instead.
+	provider(provider blueprint.AnyProviderKey) (any, bool)
 
-	// HasProvider returns true if the provider for the current module has been set.
-	HasProvider(provider blueprint.ProviderKey) bool
-
-	// SetProvider sets the value for a provider for the current module.  It panics if not called
+	// setProvider sets the value for a provider for the current module.  It panics if not called
 	// during the appropriate mutator or GenerateBuildActions pass for the provider, if the value
 	// is not of the appropriate type, or if the value has already been set.  The value should not
 	// be modified after being passed to SetProvider.
-	SetProvider(provider blueprint.ProviderKey, value interface{})
+	//
+	// This method shouldn't be used directly, prefer the type-safe android.SetProvider instead.
+	setProvider(provider blueprint.AnyProviderKey, value any)
 
 	GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module
 
@@ -107,7 +106,7 @@
 	// dependencies that are not an android.Module.
 	GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module
 
-	// GetDirectDep returns the Module and DependencyTag for the  direct dependency with the specified
+	// GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified
 	// name, or nil if none exists.  If there are multiple dependencies on the same module it returns
 	// the first DependencyTag.
 	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
@@ -120,6 +119,15 @@
 	// function, it may be invalidated by future mutators.
 	VisitDirectDepsBlueprint(visit func(blueprint.Module))
 
+	// VisitDirectDepsIgnoreBlueprint calls visit for each direct dependency.  If there are multiple
+	// direct dependencies on the same module visit will be called multiple times on that module
+	// and OtherModuleDependencyTag will return a different tag for each.  It silently ignores any
+	// dependencies that are not an android.Module.
+	//
+	// The Module passed to the visit function should not be retained outside of the visit
+	// function, it may be invalidated by future mutators.
+	VisitDirectDepsIgnoreBlueprint(visit func(Module))
+
 	// VisitDirectDeps calls visit for each direct dependency.  If there are multiple
 	// direct dependencies on the same module visit will be called multiple times on that module
 	// and OtherModuleDependencyTag will return a different tag for each.  It raises an error if any of the
@@ -206,29 +214,12 @@
 	// getMissingDependencies returns the list of missing dependencies.
 	// Calling this function prevents adding new dependencies.
 	getMissingDependencies() []string
-
-	Target() Target
-	TargetPrimary() bool
-
-	// The additional arch specific targets (e.g. 32/64 bit) that this module variant is
-	// responsible for creating.
-	MultiTargets() []Target
-	Arch() Arch
-	Os() OsType
-	Host() bool
-	Device() bool
-	Darwin() bool
-	Windows() bool
-	PrimaryArch() bool
 }
 
 type baseModuleContext struct {
 	bp blueprint.BaseModuleContext
 	earlyModuleContext
-	os            OsType
-	target        Target
-	multiTargets  []Target
-	targetPrimary bool
+	archModuleContext
 
 	walkPath []Module
 	tagPath  []blueprint.DependencyTag
@@ -260,19 +251,16 @@
 func (b *baseModuleContext) OtherModuleType(m blueprint.Module) string {
 	return b.bp.OtherModuleType(m)
 }
-func (b *baseModuleContext) OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{} {
+
+func (b *baseModuleContext) otherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
 	return b.bp.OtherModuleProvider(m, provider)
 }
-func (b *baseModuleContext) OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool {
-	return b.bp.OtherModuleHasProvider(m, provider)
-}
-func (b *baseModuleContext) Provider(provider blueprint.ProviderKey) interface{} {
+
+func (b *baseModuleContext) provider(provider blueprint.AnyProviderKey) (any, bool) {
 	return b.bp.Provider(provider)
 }
-func (b *baseModuleContext) HasProvider(provider blueprint.ProviderKey) bool {
-	return b.bp.HasProvider(provider)
-}
-func (b *baseModuleContext) SetProvider(provider blueprint.ProviderKey, value interface{}) {
+
+func (b *baseModuleContext) setProvider(provider blueprint.AnyProviderKey, value any) {
 	b.bp.SetProvider(provider, value)
 }
 
@@ -311,7 +299,7 @@
 	AllowDisabledModuleDependency(target Module) bool
 }
 
-func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool) Module {
+func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool, ignoreBlueprint bool) Module {
 	aModule, _ := module.(Module)
 
 	if !strict {
@@ -319,7 +307,9 @@
 	}
 
 	if aModule == nil {
-		b.ModuleErrorf("module %q (%#v) not an android module", b.OtherModuleName(module), tag)
+		if !ignoreBlueprint {
+			b.ModuleErrorf("module %q (%#v) not an android module", b.OtherModuleName(module), tag)
+		}
 		return nil
 	}
 
@@ -416,8 +406,16 @@
 }
 
 func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) {
+	b.visitDirectDeps(visit, false)
+}
+
+func (b *baseModuleContext) VisitDirectDepsIgnoreBlueprint(visit func(Module)) {
+	b.visitDirectDeps(visit, true)
+}
+
+func (b *baseModuleContext) visitDirectDeps(visit func(Module), ignoreBlueprint bool) {
 	b.bp.VisitDirectDeps(func(module blueprint.Module) {
-		if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
+		if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, ignoreBlueprint); aModule != nil {
 			visit(aModule)
 		}
 	})
@@ -426,7 +424,7 @@
 func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) {
 	b.bp.VisitDirectDeps(func(module blueprint.Module) {
 		if b.bp.OtherModuleDependencyTag(module) == tag {
-			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
+			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil {
 				visit(aModule)
 			}
 		}
@@ -437,7 +435,7 @@
 	b.bp.VisitDirectDepsIf(
 		// pred
 		func(module blueprint.Module) bool {
-			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
+			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil {
 				return pred(aModule)
 			} else {
 				return false
@@ -451,7 +449,7 @@
 
 func (b *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) {
 	b.bp.VisitDepsDepthFirst(func(module blueprint.Module) {
-		if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
+		if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil {
 			visit(aModule)
 		}
 	})
@@ -461,7 +459,7 @@
 	b.bp.VisitDepsDepthFirstIf(
 		// pred
 		func(module blueprint.Module) bool {
-			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
+			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil {
 				return pred(aModule)
 			} else {
 				return false
@@ -566,46 +564,3 @@
 	}
 	return sb.String()
 }
-
-func (b *baseModuleContext) Target() Target {
-	return b.target
-}
-
-func (b *baseModuleContext) TargetPrimary() bool {
-	return b.targetPrimary
-}
-
-func (b *baseModuleContext) MultiTargets() []Target {
-	return b.multiTargets
-}
-
-func (b *baseModuleContext) Arch() Arch {
-	return b.target.Arch
-}
-
-func (b *baseModuleContext) Os() OsType {
-	return b.os
-}
-
-func (b *baseModuleContext) Host() bool {
-	return b.os.Class == Host
-}
-
-func (b *baseModuleContext) Device() bool {
-	return b.os.Class == Device
-}
-
-func (b *baseModuleContext) Darwin() bool {
-	return b.os == Darwin
-}
-
-func (b *baseModuleContext) Windows() bool {
-	return b.os == Windows
-}
-
-func (b *baseModuleContext) PrimaryArch() bool {
-	if len(b.config.Targets[b.target.Os]) <= 1 {
-		return true
-	}
-	return b.target.Arch.ArchType == b.config.Targets[b.target.Os][0].Arch.ArchType
-}
diff --git a/android/config.go b/android/config.go
index 4a08146..f72fc9c 100644
--- a/android/config.go
+++ b/android/config.go
@@ -18,6 +18,7 @@
 // product variables necessary for soong_build's operation.
 
 import (
+	"android/soong/shared"
 	"encoding/json"
 	"fmt"
 	"os"
@@ -27,6 +28,7 @@
 	"strconv"
 	"strings"
 	"sync"
+	"unicode"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
@@ -89,8 +91,6 @@
 	ModuleActionsFile string
 	DocFile           string
 
-	MultitreeBuild bool
-
 	BuildFromSourceStub bool
 
 	EnsureAllowlistIntegrity bool
@@ -118,6 +118,11 @@
 	return c.soongOutDir
 }
 
+// tempDir returns the path to out/soong/.temp, which is cleared at the beginning of every build.
+func (c Config) tempDir() string {
+	return shared.TempDirForOutDir(c.soongOutDir)
+}
+
 func (c Config) OutDir() string {
 	return c.outDir
 }
@@ -203,11 +208,22 @@
 		Bool(c.config.productVariables.ReleaseDefaultModuleBuildFromSource)
 }
 
+// Enables flagged apis annotated with READ_WRITE aconfig flags to be included in the stubs
+// and hiddenapi flags so that they are accessible at runtime
+func (c Config) ReleaseExportRuntimeApis() bool {
+	return Bool(c.config.productVariables.ExportRuntimeApis)
+}
+
 // Enables ABI monitoring of NDK libraries
 func (c Config) ReleaseNdkAbiMonitored() bool {
 	return c.config.productVariables.GetBuildFlagBool("RELEASE_NDK_ABI_MONITORED")
 }
 
+func (c Config) ReleaseHiddenApiExportableStubs() bool {
+	return c.config.productVariables.GetBuildFlagBool("RELEASE_HIDDEN_API_EXPORTABLE_STUBS") ||
+		Bool(c.config.productVariables.HiddenapiExportableStubs)
+}
+
 // A DeviceConfig object represents the configuration for a particular device
 // being built. For now there will only be one of these, but in the future there
 // may be multiple devices being built.
@@ -270,10 +286,6 @@
 
 	BuildMode SoongBuildMode
 
-	// If MultitreeBuild is true then this is one inner tree of a multitree
-	// build directed by the multitree orchestrator.
-	MultitreeBuild bool
-
 	// If testAllowNonExistentPaths is true then PathForSource and PathForModuleSrc won't error
 	// in tests when a path doesn't exist.
 	TestAllowNonExistentPaths bool
@@ -310,6 +322,18 @@
 	return loadFromConfigFile(&config.productVariables, absolutePath(config.ProductVariablesFileName))
 }
 
+// Checks if the string is a valid go identifier. This is equivalent to blueprint's definition
+// of an identifier, so it will match the same identifiers as those that can be used in bp files.
+func isGoIdentifier(ident string) bool {
+	for i, r := range ident {
+		valid := r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) && i > 0
+		if !valid {
+			return false
+		}
+	}
+	return len(ident) > 0
+}
+
 // loadFromConfigFile loads and decodes configuration options from a JSON file
 // in the current working directory.
 func loadFromConfigFile(configurable *ProductVariables, filename string) error {
@@ -345,6 +369,20 @@
 		Bool(configurable.GcovCoverage) ||
 			Bool(configurable.ClangCoverage))
 
+	// The go scanner's definition of identifiers is c-style identifiers, but allowing unicode's
+	// definition of letters and digits. This is the same scanner that blueprint uses, so it
+	// will allow the same identifiers as are valid in bp files.
+	for namespace := range configurable.VendorVars {
+		if !isGoIdentifier(namespace) {
+			return fmt.Errorf("soong config namespaces must be valid identifiers: %q", namespace)
+		}
+		for variable := range configurable.VendorVars[namespace] {
+			if !isGoIdentifier(variable) {
+				return fmt.Errorf("soong config variables must be valid identifiers: %q", variable)
+			}
+		}
+	}
+
 	// when Platform_sdk_final is true (or PLATFORM_VERSION_CODENAME is REL), use Platform_sdk_version;
 	// if false (pre-released version, for example), use Platform_sdk_codename.
 	if Bool(configurable.Platform_sdk_final) {
@@ -497,8 +535,6 @@
 		moduleListFile: cmdArgs.ModuleListFile,
 		fs:             pathtools.NewOsFs(absSrcDir),
 
-		MultitreeBuild: cmdArgs.MultitreeBuild,
-
 		buildFromSourceStub: cmdArgs.BuildFromSourceStub,
 	}
 
@@ -614,6 +650,7 @@
 		"framework-nfc":                     {},
 		"framework-ondevicepersonalization": {},
 		"framework-pdf":                     {},
+		"framework-pdf-v":                   {},
 		"framework-permission":              {},
 		"framework-permission-s":            {},
 		"framework-scheduling":              {},
@@ -1302,6 +1339,26 @@
 	return String(c.productVariables.VendorApiLevel)
 }
 
+func (c *config) PrevVendorApiLevel() string {
+	vendorApiLevel, err := strconv.Atoi(c.VendorApiLevel())
+	if err != nil {
+		panic(fmt.Errorf("Cannot parse vendor API level %s to an integer: %s",
+			c.VendorApiLevel(), err))
+	}
+	if vendorApiLevel < 202404 || vendorApiLevel%100 != 4 {
+		panic("Unknown vendor API level " + c.VendorApiLevel())
+	}
+	// The version before trunk stable is 34.
+	if vendorApiLevel == 202404 {
+		return "34"
+	}
+	return strconv.Itoa(vendorApiLevel - 100)
+}
+
+func (c *config) VendorApiLevelFrozen() bool {
+	return c.productVariables.GetBuildFlagBool("RELEASE_BOARD_API_LEVEL_FROZEN")
+}
+
 func (c *deviceConfig) Arches() []Arch {
 	var arches []Arch
 	for _, target := range c.config.Targets[Android] {
@@ -1459,18 +1516,18 @@
 }
 
 // AfdoProfile returns fully qualified path associated to the given module name
-func (c *deviceConfig) AfdoProfile(name string) (*string, error) {
+func (c *deviceConfig) AfdoProfile(name string) (string, error) {
 	for _, afdoProfile := range c.config.productVariables.AfdoProfiles {
 		split := strings.Split(afdoProfile, ":")
 		if len(split) != 3 {
-			return nil, fmt.Errorf("AFDO_PROFILES has invalid value: %s. "+
+			return "", fmt.Errorf("AFDO_PROFILES has invalid value: %s. "+
 				"The expected format is <module>:<fully-qualified-path-to-fdo_profile>", afdoProfile)
 		}
 		if split[0] == name {
-			return proptools.StringPtr(strings.Join([]string{split[1], split[2]}, ":")), nil
+			return strings.Join([]string{split[1], split[2]}, ":"), nil
 		}
 	}
-	return nil, nil
+	return "", nil
 }
 
 func (c *deviceConfig) VendorSepolicyDirs() []string {
@@ -1699,10 +1756,6 @@
 	return String(c.config.productVariables.PlatformSepolicyVersion)
 }
 
-func (c *deviceConfig) TotSepolicyVersion() string {
-	return String(c.config.productVariables.TotSepolicyVersion)
-}
-
 func (c *deviceConfig) PlatformSepolicyCompatVersions() []string {
 	return c.config.productVariables.PlatformSepolicyCompatVersions
 }
@@ -1850,6 +1903,10 @@
 	return InList(name, c.config.productVariables.BuildBrokenInputDirModules)
 }
 
+func (c *deviceConfig) BuildBrokenDontCheckSystemSdk() bool {
+	return c.config.productVariables.BuildBrokenDontCheckSystemSdk
+}
+
 func (c *config) BuildWarningBadOptionalUsesLibsAllowlist() []string {
 	return c.productVariables.BuildWarningBadOptionalUsesLibsAllowlist
 }
@@ -1958,44 +2015,46 @@
 	return Bool(c.config.productVariables.CheckVendorSeappViolations)
 }
 
-func (c *deviceConfig) NextReleaseHideFlaggedApi() bool {
-	return Bool(c.config.productVariables.NextReleaseHideFlaggedApi)
-}
-
-func (c *deviceConfig) ReleaseExposeFlaggedApi() bool {
-	return Bool(c.config.productVariables.Release_expose_flagged_api)
-}
-
-func (c *deviceConfig) HideFlaggedApis() bool {
-	return c.NextReleaseHideFlaggedApi() && !c.ReleaseExposeFlaggedApi()
-}
-
 func (c *config) GetBuildFlag(name string) (string, bool) {
 	val, ok := c.productVariables.BuildFlags[name]
 	return val, ok
 }
 
+func (c *config) UseResourceProcessorByDefault() bool {
+	return c.productVariables.GetBuildFlagBool("RELEASE_USE_RESOURCE_PROCESSOR_BY_DEFAULT")
+}
+
 var (
 	mainlineApexContributionBuildFlags = []string{
+		"RELEASE_APEX_CONTRIBUTIONS_ADBD",
 		"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES",
 		"RELEASE_APEX_CONTRIBUTIONS_APPSEARCH",
 		"RELEASE_APEX_CONTRIBUTIONS_ART",
 		"RELEASE_APEX_CONTRIBUTIONS_BLUETOOTH",
+		"RELEASE_APEX_CONTRIBUTIONS_CAPTIVEPORTALLOGIN",
+		"RELEASE_APEX_CONTRIBUTIONS_CELLBROADCAST",
 		"RELEASE_APEX_CONTRIBUTIONS_CONFIGINFRASTRUCTURE",
 		"RELEASE_APEX_CONTRIBUTIONS_CONNECTIVITY",
 		"RELEASE_APEX_CONTRIBUTIONS_CONSCRYPT",
 		"RELEASE_APEX_CONTRIBUTIONS_CRASHRECOVERY",
 		"RELEASE_APEX_CONTRIBUTIONS_DEVICELOCK",
+		"RELEASE_APEX_CONTRIBUTIONS_DOCUMENTSUIGOOGLE",
+		"RELEASE_APEX_CONTRIBUTIONS_EXTSERVICES",
 		"RELEASE_APEX_CONTRIBUTIONS_HEALTHFITNESS",
 		"RELEASE_APEX_CONTRIBUTIONS_IPSEC",
 		"RELEASE_APEX_CONTRIBUTIONS_MEDIA",
 		"RELEASE_APEX_CONTRIBUTIONS_MEDIAPROVIDER",
+		"RELEASE_APEX_CONTRIBUTIONS_NETWORKSTACKGOOGLE",
+		"RELEASE_APEX_CONTRIBUTIONS_NEURALNETWORKS",
 		"RELEASE_APEX_CONTRIBUTIONS_ONDEVICEPERSONALIZATION",
 		"RELEASE_APEX_CONTRIBUTIONS_PERMISSION",
 		"RELEASE_APEX_CONTRIBUTIONS_REMOTEKEYPROVISIONING",
+		"RELEASE_APEX_CONTRIBUTIONS_RESOLV",
 		"RELEASE_APEX_CONTRIBUTIONS_SCHEDULING",
 		"RELEASE_APEX_CONTRIBUTIONS_SDKEXTENSIONS",
+		"RELEASE_APEX_CONTRIBUTIONS_SWCODEC",
 		"RELEASE_APEX_CONTRIBUTIONS_STATSD",
+		"RELEASE_APEX_CONTRIBUTIONS_TZDATA",
 		"RELEASE_APEX_CONTRIBUTIONS_UWB",
 		"RELEASE_APEX_CONTRIBUTIONS_WIFI",
 	}
@@ -2012,3 +2071,7 @@
 	}
 	return ret
 }
+
+func (c *config) BuildIgnoreApexContributionContents() []string {
+	return c.productVariables.BuildIgnoreApexContributionContents
+}
diff --git a/android/config_bp2build.go b/android/config_bp2build.go
index b632e33..4c2fb5e 100644
--- a/android/config_bp2build.go
+++ b/android/config_bp2build.go
@@ -15,24 +15,11 @@
 package android
 
 import (
-	"fmt"
-	"reflect"
-	"regexp"
-	"sort"
 	"strings"
 
-	"android/soong/bazel"
-	"android/soong/starlark_fmt"
-
 	"github.com/google/blueprint"
 )
 
-// BazelVarExporter is a collection of configuration variables that can be exported for use in Bazel rules
-type BazelVarExporter interface {
-	// asBazel expands strings of configuration variables into their concrete values
-	asBazel(Config, ExportedStringVariables, ExportedStringListVariables, ExportedConfigDependingVariables) []bazelConstant
-}
-
 // ExportedVariables is a collection of interdependent configuration variables
 type ExportedVariables struct {
 	// Maps containing toolchain variables that are independent of the
@@ -61,18 +48,6 @@
 	}
 }
 
-func (ev ExportedVariables) asBazel(config Config,
-	stringVars ExportedStringVariables, stringListVars ExportedStringListVariables, cfgDepVars ExportedConfigDependingVariables) []bazelConstant {
-	ret := []bazelConstant{}
-	ret = append(ret, ev.exportedStringVars.asBazel(config, stringVars, stringListVars, cfgDepVars)...)
-	ret = append(ret, ev.exportedStringListVars.asBazel(config, stringVars, stringListVars, cfgDepVars)...)
-	ret = append(ret, ev.exportedStringListDictVars.asBazel(config, stringVars, stringListVars, cfgDepVars)...)
-	// Note: ExportedVariableReferenceDictVars collections can only contain references to other variables and must be printed last
-	ret = append(ret, ev.exportedVariableReferenceDictVars.asBazel(config, stringVars, stringListVars, cfgDepVars)...)
-	ret = append(ret, ev.exportedConfigDependingVars.asBazel(config, stringVars, stringListVars, cfgDepVars)...)
-	return ret
-}
-
 // ExportStringStaticVariable declares a static string variable and exports it to
 // Bazel's toolchain.
 func (ev ExportedVariables) ExportStringStaticVariable(name string, value string) {
@@ -142,49 +117,6 @@
 	m[k] = v
 }
 
-func (m ExportedConfigDependingVariables) asBazel(config Config,
-	stringVars ExportedStringVariables, stringListVars ExportedStringListVariables, cfgDepVars ExportedConfigDependingVariables) []bazelConstant {
-	ret := make([]bazelConstant, 0, len(m))
-	for variable, unevaluatedVar := range m {
-		evalFunc := reflect.ValueOf(unevaluatedVar)
-		validateVariableMethod(variable, evalFunc)
-		evaluatedResult := evalFunc.Call([]reflect.Value{reflect.ValueOf(config)})
-		evaluatedValue := evaluatedResult[0].Interface().(string)
-		expandedVars, err := expandVar(config, evaluatedValue, stringVars, stringListVars, cfgDepVars)
-		if err != nil {
-			panic(fmt.Errorf("error expanding config variable %s: %s", variable, err))
-		}
-		if len(expandedVars) > 1 {
-			ret = append(ret, bazelConstant{
-				variableName:       variable,
-				internalDefinition: starlark_fmt.PrintStringList(expandedVars, 0),
-			})
-		} else {
-			ret = append(ret, bazelConstant{
-				variableName:       variable,
-				internalDefinition: fmt.Sprintf(`"%s"`, validateCharacters(expandedVars[0])),
-			})
-		}
-	}
-	return ret
-}
-
-// Ensure that string s has no invalid characters to be generated into the bzl file.
-func validateCharacters(s string) string {
-	for _, c := range []string{`\n`, `"`, `\`} {
-		if strings.Contains(s, c) {
-			panic(fmt.Errorf("%s contains illegal character %s", s, c))
-		}
-	}
-	return s
-}
-
-type bazelConstant struct {
-	variableName       string
-	internalDefinition string
-	sortLast           bool
-}
-
 // ExportedStringVariables is a mapping of variable names to string values
 type ExportedStringVariables map[string]string
 
@@ -192,25 +124,6 @@
 	m[k] = v
 }
 
-func (m ExportedStringVariables) asBazel(config Config,
-	stringVars ExportedStringVariables, stringListVars ExportedStringListVariables, cfgDepVars ExportedConfigDependingVariables) []bazelConstant {
-	ret := make([]bazelConstant, 0, len(m))
-	for k, variableValue := range m {
-		expandedVar, err := expandVar(config, variableValue, stringVars, stringListVars, cfgDepVars)
-		if err != nil {
-			panic(fmt.Errorf("error expanding config variable %s: %s", k, err))
-		}
-		if len(expandedVar) > 1 {
-			panic(fmt.Errorf("%q expands to more than one string value: %q", variableValue, expandedVar))
-		}
-		ret = append(ret, bazelConstant{
-			variableName:       k,
-			internalDefinition: fmt.Sprintf(`"%s"`, validateCharacters(expandedVar[0])),
-		})
-	}
-	return ret
-}
-
 // ExportedStringListVariables is a mapping of variable names to a list of strings
 type ExportedStringListVariables map[string][]string
 
@@ -218,32 +131,6 @@
 	m[k] = v
 }
 
-func (m ExportedStringListVariables) asBazel(config Config,
-	stringScope ExportedStringVariables, stringListScope ExportedStringListVariables,
-	exportedVars ExportedConfigDependingVariables) []bazelConstant {
-	ret := make([]bazelConstant, 0, len(m))
-	// For each exported variable, recursively expand elements in the variableValue
-	// list to ensure that interpolated variables are expanded according to their values
-	// in the variable scope.
-	for k, variableValue := range m {
-		var expandedVars []string
-		for _, v := range variableValue {
-			expandedVar, err := expandVar(config, v, stringScope, stringListScope, exportedVars)
-			if err != nil {
-				panic(fmt.Errorf("Error expanding config variable %s=%s: %s", k, v, err))
-			}
-			expandedVars = append(expandedVars, expandedVar...)
-		}
-		// Assign the list as a bzl-private variable; this variable will be exported
-		// out through a constants struct later.
-		ret = append(ret, bazelConstant{
-			variableName:       k,
-			internalDefinition: starlark_fmt.PrintStringList(expandedVars, 0),
-		})
-	}
-	return ret
-}
-
 // ExportedStringListDictVariables is a mapping from variable names to a
 // dictionary which maps keys to lists of strings
 type ExportedStringListDictVariables map[string]map[string][]string
@@ -252,19 +139,6 @@
 	m[k] = v
 }
 
-// Since dictionaries are not supported in Ninja, we do not expand variables for dictionaries
-func (m ExportedStringListDictVariables) asBazel(_ Config, _ ExportedStringVariables,
-	_ ExportedStringListVariables, _ ExportedConfigDependingVariables) []bazelConstant {
-	ret := make([]bazelConstant, 0, len(m))
-	for k, dict := range m {
-		ret = append(ret, bazelConstant{
-			variableName:       k,
-			internalDefinition: starlark_fmt.PrintStringListDict(dict, 0),
-		})
-	}
-	return ret
-}
-
 // ExportedVariableReferenceDictVariables is a mapping from variable names to a
 // dictionary which references previously defined variables. This is used to
 // create a Starlark output such as:
@@ -281,237 +155,3 @@
 func (m ExportedVariableReferenceDictVariables) set(k string, v map[string]string) {
 	m[k] = v
 }
-
-func (m ExportedVariableReferenceDictVariables) asBazel(_ Config, _ ExportedStringVariables,
-	_ ExportedStringListVariables, _ ExportedConfigDependingVariables) []bazelConstant {
-	ret := make([]bazelConstant, 0, len(m))
-	for n, dict := range m {
-		for k, v := range dict {
-			matches, err := variableReference(v)
-			if err != nil {
-				panic(err)
-			} else if !matches.matches {
-				panic(fmt.Errorf("Expected a variable reference, got %q", v))
-			} else if len(matches.fullVariableReference) != len(v) {
-				panic(fmt.Errorf("Expected only a variable reference, got %q", v))
-			}
-			dict[k] = "_" + matches.variable
-		}
-		ret = append(ret, bazelConstant{
-			variableName:       n,
-			internalDefinition: starlark_fmt.PrintDict(dict, 0),
-			sortLast:           true,
-		})
-	}
-	return ret
-}
-
-// BazelToolchainVars expands an ExportedVariables collection and returns a string
-// of formatted Starlark variable definitions
-func BazelToolchainVars(config Config, exportedVars ExportedVariables) string {
-	results := exportedVars.asBazel(
-		config,
-		exportedVars.exportedStringVars,
-		exportedVars.exportedStringListVars,
-		exportedVars.exportedConfigDependingVars,
-	)
-
-	sort.Slice(results, func(i, j int) bool {
-		if results[i].sortLast != results[j].sortLast {
-			return !results[i].sortLast
-		}
-		return results[i].variableName < results[j].variableName
-	})
-
-	definitions := make([]string, 0, len(results))
-	constants := make([]string, 0, len(results))
-	for _, b := range results {
-		definitions = append(definitions,
-			fmt.Sprintf("_%s = %s", b.variableName, b.internalDefinition))
-		constants = append(constants,
-			fmt.Sprintf("%[1]s%[2]s = _%[2]s,", starlark_fmt.Indention(1), b.variableName))
-	}
-
-	// Build the exported constants struct.
-	ret := bazel.GeneratedBazelFileWarning
-	ret += "\n\n"
-	ret += strings.Join(definitions, "\n\n")
-	ret += "\n\n"
-	ret += "constants = struct(\n"
-	ret += strings.Join(constants, "\n")
-	ret += "\n)"
-
-	return ret
-}
-
-type match struct {
-	matches               bool
-	fullVariableReference string
-	variable              string
-}
-
-func variableReference(input string) (match, error) {
-	// e.g. "${ExternalCflags}"
-	r := regexp.MustCompile(`\${(?:config\.)?([a-zA-Z0-9_]+)}`)
-
-	matches := r.FindStringSubmatch(input)
-	if len(matches) == 0 {
-		return match{}, nil
-	}
-	if len(matches) != 2 {
-		return match{}, fmt.Errorf("Expected to only match 1 subexpression in %s, got %d", input, len(matches)-1)
-	}
-	return match{
-		matches:               true,
-		fullVariableReference: matches[0],
-		// Index 1 of FindStringSubmatch contains the subexpression match
-		// (variable name) of the capture group.
-		variable: matches[1],
-	}, nil
-}
-
-// expandVar recursively expand interpolated variables in the exportedVars scope.
-//
-// We're using a string slice to track the seen variables to avoid
-// stackoverflow errors with infinite recursion. it's simpler to use a
-// string slice than to handle a pass-by-referenced map, which would make it
-// quite complex to track depth-first interpolations. It's also unlikely the
-// interpolation stacks are deep (n > 1).
-func expandVar(config Config, toExpand string, stringScope ExportedStringVariables,
-	stringListScope ExportedStringListVariables, exportedVars ExportedConfigDependingVariables) ([]string, error) {
-
-	// Internal recursive function.
-	var expandVarInternal func(string, map[string]bool) (string, error)
-	expandVarInternal = func(toExpand string, seenVars map[string]bool) (string, error) {
-		var ret string
-		remainingString := toExpand
-		for len(remainingString) > 0 {
-			matches, err := variableReference(remainingString)
-			if err != nil {
-				panic(err)
-			}
-			if !matches.matches {
-				return ret + remainingString, nil
-			}
-			matchIndex := strings.Index(remainingString, matches.fullVariableReference)
-			ret += remainingString[:matchIndex]
-			remainingString = remainingString[matchIndex+len(matches.fullVariableReference):]
-
-			variable := matches.variable
-			// toExpand contains a variable.
-			if _, ok := seenVars[variable]; ok {
-				return ret, fmt.Errorf(
-					"Unbounded recursive interpolation of variable: %s", variable)
-			}
-			// A map is passed-by-reference. Create a new map for
-			// this scope to prevent variables seen in one depth-first expansion
-			// to be also treated as "seen" in other depth-first traversals.
-			newSeenVars := map[string]bool{}
-			for k := range seenVars {
-				newSeenVars[k] = true
-			}
-			newSeenVars[variable] = true
-			if unexpandedVars, ok := stringListScope[variable]; ok {
-				expandedVars := []string{}
-				for _, unexpandedVar := range unexpandedVars {
-					expandedVar, err := expandVarInternal(unexpandedVar, newSeenVars)
-					if err != nil {
-						return ret, err
-					}
-					expandedVars = append(expandedVars, expandedVar)
-				}
-				ret += strings.Join(expandedVars, " ")
-			} else if unexpandedVar, ok := stringScope[variable]; ok {
-				expandedVar, err := expandVarInternal(unexpandedVar, newSeenVars)
-				if err != nil {
-					return ret, err
-				}
-				ret += expandedVar
-			} else if unevaluatedVar, ok := exportedVars[variable]; ok {
-				evalFunc := reflect.ValueOf(unevaluatedVar)
-				validateVariableMethod(variable, evalFunc)
-				evaluatedResult := evalFunc.Call([]reflect.Value{reflect.ValueOf(config)})
-				evaluatedValue := evaluatedResult[0].Interface().(string)
-				expandedVar, err := expandVarInternal(evaluatedValue, newSeenVars)
-				if err != nil {
-					return ret, err
-				}
-				ret += expandedVar
-			} else {
-				return "", fmt.Errorf("Unbound config variable %s", variable)
-			}
-		}
-		return ret, nil
-	}
-	var ret []string
-	stringFields := splitStringKeepingQuotedSubstring(toExpand, ' ')
-	for _, v := range stringFields {
-		val, err := expandVarInternal(v, map[string]bool{})
-		if err != nil {
-			return ret, err
-		}
-		ret = append(ret, val)
-	}
-
-	return ret, nil
-}
-
-// splitStringKeepingQuotedSubstring splits a string on a provided separator,
-// but it will not split substrings inside unescaped double quotes. If the double
-// quotes are escaped, then the returned string will only include the quote, and
-// not the escape.
-func splitStringKeepingQuotedSubstring(s string, delimiter byte) []string {
-	var ret []string
-	quote := byte('"')
-
-	var substring []byte
-	quoted := false
-	escaped := false
-
-	for i := range s {
-		if !quoted && s[i] == delimiter {
-			ret = append(ret, string(substring))
-			substring = []byte{}
-			continue
-		}
-
-		characterIsEscape := i < len(s)-1 && s[i] == '\\' && s[i+1] == quote
-		if characterIsEscape {
-			escaped = true
-			continue
-		}
-
-		if s[i] == quote {
-			if !escaped {
-				quoted = !quoted
-			}
-			escaped = false
-		}
-
-		substring = append(substring, s[i])
-	}
-
-	ret = append(ret, string(substring))
-
-	return ret
-}
-
-func validateVariableMethod(name string, methodValue reflect.Value) {
-	methodType := methodValue.Type()
-	if methodType.Kind() != reflect.Func {
-		panic(fmt.Errorf("method given for variable %s is not a function",
-			name))
-	}
-	if n := methodType.NumIn(); n != 1 {
-		panic(fmt.Errorf("method for variable %s has %d inputs (should be 1)",
-			name, n))
-	}
-	if n := methodType.NumOut(); n != 1 {
-		panic(fmt.Errorf("method for variable %s has %d outputs (should be 1)",
-			name, n))
-	}
-	if kind := methodType.Out(0).Kind(); kind != reflect.String {
-		panic(fmt.Errorf("method for variable %s does not return a string",
-			name))
-	}
-}
diff --git a/android/config_bp2build_test.go b/android/config_bp2build_test.go
deleted file mode 100644
index 1a0ba7b..0000000
--- a/android/config_bp2build_test.go
+++ /dev/null
@@ -1,454 +0,0 @@
-// Copyright 2021 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package android
-
-import (
-	"android/soong/bazel"
-	"testing"
-)
-
-func TestExpandVars(t *testing.T) {
-	android_arm64_config := TestConfig("out", nil, "", nil)
-	android_arm64_config.BuildOS = Android
-	android_arm64_config.BuildArch = Arm64
-
-	testCases := []struct {
-		description     string
-		config          Config
-		stringScope     ExportedStringVariables
-		stringListScope ExportedStringListVariables
-		configVars      ExportedConfigDependingVariables
-		toExpand        string
-		expectedValues  []string
-	}{
-		{
-			description:    "no expansion for non-interpolated value",
-			toExpand:       "foo",
-			expectedValues: []string{"foo"},
-		},
-		{
-			description: "single level expansion for string var",
-			stringScope: ExportedStringVariables{
-				"foo": "bar",
-			},
-			toExpand:       "${foo}",
-			expectedValues: []string{"bar"},
-		},
-		{
-			description: "single level expansion with short-name for string var",
-			stringScope: ExportedStringVariables{
-				"foo": "bar",
-			},
-			toExpand:       "${config.foo}",
-			expectedValues: []string{"bar"},
-		},
-		{
-			description: "single level expansion string list var",
-			stringListScope: ExportedStringListVariables{
-				"foo": []string{"bar"},
-			},
-			toExpand:       "${foo}",
-			expectedValues: []string{"bar"},
-		},
-		{
-			description: "mixed level expansion for string list var",
-			stringScope: ExportedStringVariables{
-				"foo": "${bar}",
-				"qux": "hello",
-			},
-			stringListScope: ExportedStringListVariables{
-				"bar": []string{"baz", "${qux}"},
-			},
-			toExpand:       "${foo}",
-			expectedValues: []string{"baz hello"},
-		},
-		{
-			description: "double level expansion",
-			stringListScope: ExportedStringListVariables{
-				"foo": []string{"${bar}"},
-				"bar": []string{"baz"},
-			},
-			toExpand:       "${foo}",
-			expectedValues: []string{"baz"},
-		},
-		{
-			description: "double level expansion with a literal",
-			stringListScope: ExportedStringListVariables{
-				"a": []string{"${b}", "c"},
-				"b": []string{"d"},
-			},
-			toExpand:       "${a}",
-			expectedValues: []string{"d c"},
-		},
-		{
-			description: "double level expansion, with two variables in a string",
-			stringListScope: ExportedStringListVariables{
-				"a": []string{"${b} ${c}"},
-				"b": []string{"d"},
-				"c": []string{"e"},
-			},
-			toExpand:       "${a}",
-			expectedValues: []string{"d e"},
-		},
-		{
-			description: "triple level expansion with two variables in a string",
-			stringListScope: ExportedStringListVariables{
-				"a": []string{"${b} ${c}"},
-				"b": []string{"${c}", "${d}"},
-				"c": []string{"${d}"},
-				"d": []string{"foo"},
-			},
-			toExpand:       "${a}",
-			expectedValues: []string{"foo foo foo"},
-		},
-		{
-			description: "expansion with config depending vars",
-			configVars: ExportedConfigDependingVariables{
-				"a": func(c Config) string { return c.BuildOS.String() },
-				"b": func(c Config) string { return c.BuildArch.String() },
-			},
-			config:         android_arm64_config,
-			toExpand:       "${a}-${b}",
-			expectedValues: []string{"android-arm64"},
-		},
-		{
-			description: "double level multi type expansion",
-			stringListScope: ExportedStringListVariables{
-				"platform": []string{"${os}-${arch}"},
-				"const":    []string{"const"},
-			},
-			configVars: ExportedConfigDependingVariables{
-				"os":   func(c Config) string { return c.BuildOS.String() },
-				"arch": func(c Config) string { return c.BuildArch.String() },
-				"foo":  func(c Config) string { return "foo" },
-			},
-			config:         android_arm64_config,
-			toExpand:       "${const}/${platform}/${foo}",
-			expectedValues: []string{"const/android-arm64/foo"},
-		},
-	}
-
-	for _, testCase := range testCases {
-		t.Run(testCase.description, func(t *testing.T) {
-			output, _ := expandVar(testCase.config, testCase.toExpand, testCase.stringScope, testCase.stringListScope, testCase.configVars)
-			if len(output) != len(testCase.expectedValues) {
-				t.Errorf("Expected %d values, got %d", len(testCase.expectedValues), len(output))
-			}
-			for i, actual := range output {
-				expectedValue := testCase.expectedValues[i]
-				if actual != expectedValue {
-					t.Errorf("Actual value '%s' doesn't match expected value '%s'", actual, expectedValue)
-				}
-			}
-		})
-	}
-}
-
-func TestBazelToolchainVars(t *testing.T) {
-	testCases := []struct {
-		name        string
-		config      Config
-		vars        ExportedVariables
-		expectedOut string
-	}{
-		{
-			name: "exports strings",
-			vars: ExportedVariables{
-				exportedStringVars: ExportedStringVariables{
-					"a": "b",
-					"c": "d",
-				},
-			},
-			expectedOut: bazel.GeneratedBazelFileWarning + `
-
-_a = "b"
-
-_c = "d"
-
-constants = struct(
-    a = _a,
-    c = _c,
-)`,
-		},
-		{
-			name: "exports string lists",
-			vars: ExportedVariables{
-				exportedStringListVars: ExportedStringListVariables{
-					"a": []string{"b1", "b2"},
-					"c": []string{"d1", "d2"},
-				},
-			},
-			expectedOut: bazel.GeneratedBazelFileWarning + `
-
-_a = [
-    "b1",
-    "b2",
-]
-
-_c = [
-    "d1",
-    "d2",
-]
-
-constants = struct(
-    a = _a,
-    c = _c,
-)`,
-		},
-		{
-			name: "exports string lists dicts",
-			vars: ExportedVariables{
-				exportedStringListDictVars: ExportedStringListDictVariables{
-					"a": map[string][]string{"b1": {"b2"}},
-					"c": map[string][]string{"d1": {"d2"}},
-				},
-			},
-			expectedOut: bazel.GeneratedBazelFileWarning + `
-
-_a = {
-    "b1": ["b2"],
-}
-
-_c = {
-    "d1": ["d2"],
-}
-
-constants = struct(
-    a = _a,
-    c = _c,
-)`,
-		},
-		{
-			name: "exports dict with var refs",
-			vars: ExportedVariables{
-				exportedVariableReferenceDictVars: ExportedVariableReferenceDictVariables{
-					"a": map[string]string{"b1": "${b2}"},
-					"c": map[string]string{"d1": "${config.d2}"},
-				},
-			},
-			expectedOut: bazel.GeneratedBazelFileWarning + `
-
-_a = {
-    "b1": _b2,
-}
-
-_c = {
-    "d1": _d2,
-}
-
-constants = struct(
-    a = _a,
-    c = _c,
-)`,
-		},
-		{
-			name: "sorts across types with variable references last",
-			vars: ExportedVariables{
-				exportedStringVars: ExportedStringVariables{
-					"b": "b-val",
-					"d": "d-val",
-				},
-				exportedStringListVars: ExportedStringListVariables{
-					"c": []string{"c-val"},
-					"e": []string{"e-val"},
-				},
-				exportedStringListDictVars: ExportedStringListDictVariables{
-					"a": map[string][]string{"a1": {"a2"}},
-					"f": map[string][]string{"f1": {"f2"}},
-				},
-				exportedVariableReferenceDictVars: ExportedVariableReferenceDictVariables{
-					"aa": map[string]string{"b1": "${b}"},
-					"cc": map[string]string{"d1": "${config.d}"},
-				},
-			},
-			expectedOut: bazel.GeneratedBazelFileWarning + `
-
-_a = {
-    "a1": ["a2"],
-}
-
-_b = "b-val"
-
-_c = ["c-val"]
-
-_d = "d-val"
-
-_e = ["e-val"]
-
-_f = {
-    "f1": ["f2"],
-}
-
-_aa = {
-    "b1": _b,
-}
-
-_cc = {
-    "d1": _d,
-}
-
-constants = struct(
-    a = _a,
-    b = _b,
-    c = _c,
-    d = _d,
-    e = _e,
-    f = _f,
-    aa = _aa,
-    cc = _cc,
-)`,
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			out := BazelToolchainVars(tc.config, tc.vars)
-			if out != tc.expectedOut {
-				t.Errorf("Expected \n%s, got \n%s", tc.expectedOut, out)
-			}
-		})
-	}
-}
-
-func TestSplitStringKeepingQuotedSubstring(t *testing.T) {
-	testCases := []struct {
-		description string
-		s           string
-		delimiter   byte
-		split       []string
-	}{
-		{
-			description: "empty string returns single empty string",
-			s:           "",
-			delimiter:   ' ',
-			split: []string{
-				"",
-			},
-		},
-		{
-			description: "string with single space returns two empty strings",
-			s:           " ",
-			delimiter:   ' ',
-			split: []string{
-				"",
-				"",
-			},
-		},
-		{
-			description: "string with two spaces returns three empty strings",
-			s:           "  ",
-			delimiter:   ' ',
-			split: []string{
-				"",
-				"",
-				"",
-			},
-		},
-		{
-			description: "string with four words returns four word string",
-			s:           "hello world with words",
-			delimiter:   ' ',
-			split: []string{
-				"hello",
-				"world",
-				"with",
-				"words",
-			},
-		},
-		{
-			description: "string with words and nested quote returns word strings and quote string",
-			s:           `hello "world with" words`,
-			delimiter:   ' ',
-			split: []string{
-				"hello",
-				`"world with"`,
-				"words",
-			},
-		},
-		{
-			description: "string with escaped quote inside real quotes",
-			s:           `hello \"world "with\" words"`,
-			delimiter:   ' ',
-			split: []string{
-				"hello",
-				`"world`,
-				`"with" words"`,
-			},
-		},
-		{
-			description: "string with words and escaped quotes returns word strings",
-			s:           `hello \"world with\" words`,
-			delimiter:   ' ',
-			split: []string{
-				"hello",
-				`"world`,
-				`with"`,
-				"words",
-			},
-		},
-		{
-			description: "string which is single quoted substring returns only substring",
-			s:           `"hello world with words"`,
-			delimiter:   ' ',
-			split: []string{
-				`"hello world with words"`,
-			},
-		},
-		{
-			description: "string starting with quote returns quoted string",
-			s:           `"hello world with" words`,
-			delimiter:   ' ',
-			split: []string{
-				`"hello world with"`,
-				"words",
-			},
-		},
-		{
-			description: "string with starting quote and no ending quote returns quote to end of string",
-			s:           `hello "world with words`,
-			delimiter:   ' ',
-			split: []string{
-				"hello",
-				`"world with words`,
-			},
-		},
-		{
-			description: "quoted string is treated as a single \"word\" unless separated by delimiter",
-			s:           `hello "world"with words`,
-			delimiter:   ' ',
-			split: []string{
-				"hello",
-				`"world"with`,
-				"words",
-			},
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.description, func(t *testing.T) {
-			split := splitStringKeepingQuotedSubstring(tc.s, tc.delimiter)
-			if len(split) != len(tc.split) {
-				t.Fatalf("number of split string elements (%d) differs from expected (%d): split string (%v), expected (%v)",
-					len(split), len(tc.split), split, tc.split,
-				)
-			}
-			for i := range split {
-				if split[i] != tc.split[i] {
-					t.Errorf("split string element (%d), %v, differs from expected, %v", i, split[i], tc.split[i])
-				}
-			}
-		})
-	}
-}
diff --git a/android/deapexer.go b/android/deapexer.go
index 6a93f60..61ae64e 100644
--- a/android/deapexer.go
+++ b/android/deapexer.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"fmt"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -78,6 +79,14 @@
 	//
 	// See Prebuilt.ApexInfoMutator for more information.
 	exports map[string]WritablePath
+
+	// name of the java libraries exported from the apex
+	// e.g. core-libart
+	exportedModuleNames []string
+
+	// name of the java libraries exported from the apex that should be dexpreopt'd with the .prof
+	// file embedded in the apex
+	dexpreoptProfileGuidedExportedModuleNames []string
 }
 
 // ApexModuleName returns the name of the APEX module that provided the info.
@@ -96,21 +105,34 @@
 	return path
 }
 
+func (i DeapexerInfo) GetExportedModuleNames() []string {
+	return i.exportedModuleNames
+}
+
 // Provider that can be used from within the `GenerateAndroidBuildActions` of a module that depends
 // on a `deapexer` module to retrieve its `DeapexerInfo`.
-var DeapexerProvider = blueprint.NewProvider(DeapexerInfo{})
+var DeapexerProvider = blueprint.NewProvider[DeapexerInfo]()
 
 // NewDeapexerInfo creates and initializes a DeapexerInfo that is suitable
 // for use with a prebuilt_apex module.
 //
 // See apex/deapexer.go for more information.
-func NewDeapexerInfo(apexModuleName string, exports map[string]WritablePath) DeapexerInfo {
+func NewDeapexerInfo(apexModuleName string, exports map[string]WritablePath, moduleNames []string) DeapexerInfo {
 	return DeapexerInfo{
-		apexModuleName: apexModuleName,
-		exports:        exports,
+		apexModuleName:      apexModuleName,
+		exports:             exports,
+		exportedModuleNames: moduleNames,
 	}
 }
 
+func (i *DeapexerInfo) GetDexpreoptProfileGuidedExportedModuleNames() []string {
+	return i.dexpreoptProfileGuidedExportedModuleNames
+}
+
+func (i *DeapexerInfo) AddDexpreoptProfileGuidedExportedModuleNames(names ...string) {
+	i.dexpreoptProfileGuidedExportedModuleNames = append(i.dexpreoptProfileGuidedExportedModuleNames, names...)
+}
+
 type deapexerTagStruct struct {
 	blueprint.BaseDependencyTag
 }
@@ -133,6 +155,9 @@
 	// the path to the extracted file will be stored in the DeapexerInfo using the APEX relative file
 	// path as the key, The path can then be retrieved using the PrebuiltExportPath(key) method.
 	RequiredFilesFromPrebuiltApex(ctx BaseModuleContext) []string
+
+	// Returns true if a transitive dependency of an apex should use a .prof file to guide dexpreopt
+	UseProfileGuidedDexpreopt() bool
 }
 
 // Marker interface that identifies dependencies on modules that may require files from a prebuilt
@@ -146,11 +171,17 @@
 
 // FindDeapexerProviderForModule searches through the direct dependencies of the current context
 // module for a DeapexerTag dependency and returns its DeapexerInfo. If a single nonambiguous
-// deapexer module isn't found then errors are reported with ctx.ModuleErrorf and nil is returned.
-func FindDeapexerProviderForModule(ctx ModuleContext) *DeapexerInfo {
+// deapexer module isn't found then it returns it an error
+// clients should check the value of error and call ctx.ModuleErrof if a non nil error is received
+func FindDeapexerProviderForModule(ctx ModuleContext) (*DeapexerInfo, error) {
 	var di *DeapexerInfo
+	var err error
 	ctx.VisitDirectDepsWithTag(DeapexerTag, func(m Module) {
-		c := ctx.OtherModuleProvider(m, DeapexerProvider).(DeapexerInfo)
+		if err != nil {
+			// An err has been found. Do not visit further.
+			return
+		}
+		c, _ := OtherModuleProvider(ctx, m, DeapexerProvider)
 		p := &c
 		if di != nil {
 			// If two DeapexerInfo providers have been found then check if they are
@@ -159,17 +190,18 @@
 				di = selected
 				return
 			}
-			ctx.ModuleErrorf("Multiple installable prebuilt APEXes provide ambiguous deapexers: %s and %s",
-				di.ApexModuleName(), p.ApexModuleName())
+			err = fmt.Errorf("Multiple installable prebuilt APEXes provide ambiguous deapexers: %s and %s", di.ApexModuleName(), p.ApexModuleName())
 		}
 		di = p
 	})
-	if di != nil {
-		return di
+	if err != nil {
+		return nil, err
 	}
-	ai := ctx.Provider(ApexInfoProvider).(ApexInfo)
-	ctx.ModuleErrorf("No prebuilt APEX provides a deapexer module for APEX variant %s", ai.ApexVariationName)
-	return nil
+	if di != nil {
+		return di, nil
+	}
+	ai, _ := ModuleProvider(ctx, ApexInfoProvider)
+	return nil, fmt.Errorf("No prebuilt APEX provides a deapexer module for APEX variant %s", ai.ApexVariationName)
 }
 
 // removeCompressedApexSuffix removes the _compressed suffix from the name if present.
diff --git a/android/defaults_test.go b/android/defaults_test.go
index a7542ab..0ad0fb8 100644
--- a/android/defaults_test.go
+++ b/android/defaults_test.go
@@ -16,10 +16,13 @@
 
 import (
 	"testing"
+
+	"github.com/google/blueprint"
 )
 
 type defaultsTestProperties struct {
-	Foo []string
+	Foo       []string
+	Path_prop []string `android:"path"`
 }
 
 type defaultsTestModule struct {
@@ -130,3 +133,40 @@
 	// TODO: missing transitive defaults is currently not handled
 	_ = missingTransitiveDefaults
 }
+
+func TestDefaultsPathProperties(t *testing.T) {
+	bp := `
+		defaults {
+			name: "defaults",
+			path_prop: [":gen"],
+		}
+
+		test {
+			name: "foo",
+			defaults: ["defaults"],
+		}
+
+		test {
+			name: "gen",
+		}
+	`
+
+	result := GroupFixturePreparers(
+		prepareForDefaultsTest,
+		FixtureWithRootAndroidBp(bp),
+	).RunTest(t)
+
+	collectDeps := func(m Module) []string {
+		var deps []string
+		result.VisitDirectDeps(m, func(dep blueprint.Module) {
+			deps = append(deps, result.ModuleName(dep))
+		})
+		return deps
+	}
+
+	foo := result.Module("foo", "")
+	defaults := result.Module("defaults", "")
+
+	AssertStringListContains(t, "foo should depend on gen", collectDeps(foo), "gen")
+	AssertStringListDoesNotContain(t, "defaults should not depend on gen", collectDeps(defaults), "gen")
+}
diff --git a/android/defs.go b/android/defs.go
index 03968c1..dab012d 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -15,13 +15,8 @@
 package android
 
 import (
-	"fmt"
-	"strings"
-	"testing"
-
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
-	"github.com/google/blueprint/proptools"
 )
 
 var (
@@ -72,8 +67,7 @@
 			Command:     "if ! cmp -s $in $out; then cp $in $out; fi",
 			Description: "cp if changed $out",
 			Restat:      true,
-		},
-		"cpFlags")
+		})
 
 	CpExecutable = pctx.AndroidStaticRule("CpExecutable",
 		blueprint.RuleParams{
@@ -92,9 +86,8 @@
 	// A symlink rule.
 	Symlink = pctx.AndroidStaticRule("Symlink",
 		blueprint.RuleParams{
-			Command:        "rm -f $out && ln -f -s $fromPath $out",
-			Description:    "symlink $out",
-			SymlinkOutputs: []string{"$out"},
+			Command:     "rm -f $out && ln -f -s $fromPath $out",
+			Description: "symlink $out",
 		},
 		"fromPath")
 
@@ -142,110 +135,6 @@
 	exportedVars.ExportStringList("NeverAllowNoUseIncludeDir", neverallowNoUseIncludeDir)
 }
 
-func BazelCcToolchainVars(config Config) string {
-	return BazelToolchainVars(config, exportedVars)
-}
-
-var (
-	// echoEscaper escapes a string such that passing it to "echo -e" will produce the input value.
-	echoEscaper = strings.NewReplacer(
-		`\`, `\\`, // First escape existing backslashes so they aren't interpreted by `echo -e`.
-		"\n", `\n`, // Then replace newlines with \n
-	)
-
-	// echoEscaper reverses echoEscaper.
-	echoUnescaper = strings.NewReplacer(
-		`\n`, "\n",
-		`\\`, `\`,
-	)
-
-	// shellUnescaper reverses the replacer in proptools.ShellEscape
-	shellUnescaper = strings.NewReplacer(`'\''`, `'`)
-)
-
-func buildWriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) {
-	content = echoEscaper.Replace(content)
-	content = proptools.NinjaEscape(proptools.ShellEscapeIncludingSpaces(content))
-	if content == "" {
-		content = "''"
-	}
-	ctx.Build(pctx, BuildParams{
-		Rule:        writeFile,
-		Output:      outputFile,
-		Description: "write " + outputFile.Base(),
-		Args: map[string]string{
-			"content": content,
-		},
-	})
-}
-
-// WriteFileRule creates a ninja rule to write contents to a file.  The contents will be escaped
-// so that the file contains exactly the contents passed to the function, plus a trailing newline.
-func WriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) {
-	WriteFileRuleVerbatim(ctx, outputFile, content+"\n")
-}
-
-// WriteFileRuleVerbatim creates a ninja rule to write contents to a file.  The contents will be
-// escaped so that the file contains exactly the contents passed to the function.
-func WriteFileRuleVerbatim(ctx BuilderContext, outputFile WritablePath, content string) {
-	// This is MAX_ARG_STRLEN subtracted with some safety to account for shell escapes
-	const SHARD_SIZE = 131072 - 10000
-
-	if len(content) > SHARD_SIZE {
-		var chunks WritablePaths
-		for i, c := range ShardString(content, SHARD_SIZE) {
-			tempPath := outputFile.ReplaceExtension(ctx, fmt.Sprintf("%s.%d", outputFile.Ext(), i))
-			buildWriteFileRule(ctx, tempPath, c)
-			chunks = append(chunks, tempPath)
-		}
-		ctx.Build(pctx, BuildParams{
-			Rule:        Cat,
-			Inputs:      chunks.Paths(),
-			Output:      outputFile,
-			Description: "Merging to " + outputFile.Base(),
-		})
-		return
-	}
-	buildWriteFileRule(ctx, outputFile, content)
-}
-
-// WriteExecutableFileRuleVerbatim is the same as WriteFileRuleVerbatim, but runs chmod +x on the result
-func WriteExecutableFileRuleVerbatim(ctx BuilderContext, outputFile WritablePath, content string) {
-	intermediate := PathForIntermediates(ctx, "write_executable_file_intermediates").Join(ctx, outputFile.String())
-	WriteFileRuleVerbatim(ctx, intermediate, content)
-	ctx.Build(pctx, BuildParams{
-		Rule:   CpExecutable,
-		Output: outputFile,
-		Input:  intermediate,
-	})
-}
-
-// shellUnescape reverses proptools.ShellEscape
-func shellUnescape(s string) string {
-	// Remove leading and trailing quotes if present
-	if len(s) >= 2 && s[0] == '\'' {
-		s = s[1 : len(s)-1]
-	}
-	s = shellUnescaper.Replace(s)
-	return s
-}
-
-// ContentFromFileRuleForTests returns the content that was passed to a WriteFileRule for use
-// in tests.
-func ContentFromFileRuleForTests(t *testing.T, ctx *TestContext, params TestingBuildParams) string {
-	t.Helper()
-	if g, w := params.Rule, writeFile; g != w {
-		t.Errorf("expected params.Rule to be %q, was %q", w, g)
-		return ""
-	}
-
-	content := params.Args["content"]
-	content = shellUnescape(content)
-	content = echoUnescaper.Replace(content)
-
-	return content
-}
-
 // GlobToListFileRule creates a rule that writes a list of files matching a pattern to a file.
 func GlobToListFileRule(ctx ModuleContext, pattern string, excludes []string, file WritablePath) {
 	bootstrap.GlobFile(ctx.blueprintModuleContext(), pattern, excludes, file.String())
diff --git a/android/filegroup.go b/android/filegroup.go
index 04bd8a8..bc881ed 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -55,6 +55,9 @@
 	DefaultableModuleBase
 	properties fileGroupProperties
 	srcs       Paths
+
+	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
+	mergedAconfigFiles map[string]Paths
 }
 
 var _ SourceFileProducer = (*fileGroup)(nil)
@@ -92,7 +95,8 @@
 	if fg.properties.Path != nil {
 		fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
 	}
-	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: fg.srcs.Strings()})
+	SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: fg.srcs.Strings()})
+	CollectDependencyAconfigFiles(ctx, &fg.mergedAconfigFiles)
 }
 
 func (fg *fileGroup) Srcs() Paths {
diff --git a/android/license_metadata.go b/android/license_metadata.go
index 609ca79..463fd07 100644
--- a/android/license_metadata.go
+++ b/android/license_metadata.go
@@ -78,8 +78,7 @@
 			return
 		}
 
-		if ctx.OtherModuleHasProvider(dep, LicenseMetadataProvider) {
-			info := ctx.OtherModuleProvider(dep, LicenseMetadataProvider).(*LicenseMetadataInfo)
+		if info, ok := OtherModuleProvider(ctx, dep, LicenseMetadataProvider); ok {
 			allDepMetadataFiles = append(allDepMetadataFiles, info.LicenseMetadataPath)
 			if isContainer || isInstallDepNeeded(dep, ctx.OtherModuleDependencyTag(dep)) {
 				allDepMetadataDepSets = append(allDepMetadataDepSets, info.LicenseMetadataDepSet)
@@ -175,7 +174,7 @@
 		},
 	})
 
-	ctx.SetProvider(LicenseMetadataProvider, &LicenseMetadataInfo{
+	SetProvider(ctx, LicenseMetadataProvider, &LicenseMetadataInfo{
 		LicenseMetadataPath:   licenseMetadataFile,
 		LicenseMetadataDepSet: NewDepSet(TOPOLOGICAL, Paths{licenseMetadataFile}, allDepMetadataDepSets),
 	})
@@ -200,7 +199,7 @@
 }
 
 // LicenseMetadataProvider is used to propagate license metadata paths between modules.
-var LicenseMetadataProvider = blueprint.NewProvider(&LicenseMetadataInfo{})
+var LicenseMetadataProvider = blueprint.NewProvider[*LicenseMetadataInfo]()
 
 // LicenseMetadataInfo stores the license metadata path for a module.
 type LicenseMetadataInfo struct {
diff --git a/android/licenses.go b/android/licenses.go
index c6b3243..be1eede 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -230,7 +230,7 @@
 	licenseInfo := LicenseInfo{
 		Licenses: licenses,
 	}
-	ctx.SetProvider(LicenseInfoProvider, licenseInfo)
+	SetProvider(ctx, LicenseInfoProvider, licenseInfo)
 }
 
 // Update a property string array with a distinct union of its values and a list of new values.
@@ -322,7 +322,7 @@
 	Licenses []string
 }
 
-var LicenseInfoProvider = blueprint.NewProvider(LicenseInfo{})
+var LicenseInfoProvider = blueprint.NewProvider[LicenseInfo]()
 
 func init() {
 	RegisterMakeVarsProvider(pctx, licensesMakeVarsProvider)
diff --git a/android/makevars.go b/android/makevars.go
index 0800190..4039e7e 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -16,9 +16,11 @@
 
 import (
 	"bytes"
+	"cmp"
 	"fmt"
 	"path/filepath"
 	"runtime"
+	"slices"
 	"sort"
 	"strings"
 
@@ -92,7 +94,7 @@
 	ModuleDir(module blueprint.Module) string
 	ModuleSubDir(module blueprint.Module) string
 	ModuleType(module blueprint.Module) string
-	ModuleProvider(module blueprint.Module, key blueprint.ProviderKey) interface{}
+	moduleProvider(module blueprint.Module, key blueprint.AnyProviderKey) (any, bool)
 	BlueprintFile(module blueprint.Module) string
 
 	ModuleErrorf(module blueprint.Module, format string, args ...interface{})
@@ -242,6 +244,8 @@
 	var dists []dist
 	var phonies []phony
 	var katiInstalls []katiInstall
+	var katiInitRcInstalls []katiInstall
+	var katiVintfManifestInstalls []katiInstall
 	var katiSymlinks []katiInstall
 
 	providers := append([]makeVarsProvider(nil), makeVarsInitProviders...)
@@ -275,10 +279,33 @@
 
 		if m.ExportedToMake() {
 			katiInstalls = append(katiInstalls, m.base().katiInstalls...)
+			katiInitRcInstalls = append(katiInitRcInstalls, m.base().katiInitRcInstalls...)
+			katiVintfManifestInstalls = append(katiVintfManifestInstalls, m.base().katiVintfInstalls...)
 			katiSymlinks = append(katiSymlinks, m.base().katiSymlinks...)
 		}
 	})
 
+	compareKatiInstalls := func(a, b katiInstall) int {
+		aTo, bTo := a.to.String(), b.to.String()
+		if cmpTo := cmp.Compare(aTo, bTo); cmpTo != 0 {
+			return cmpTo
+		}
+
+		aFrom, bFrom := a.from.String(), b.from.String()
+		return cmp.Compare(aFrom, bFrom)
+	}
+
+	slices.SortFunc(katiInitRcInstalls, compareKatiInstalls)
+	katiInitRcInstalls = slices.CompactFunc(katiInitRcInstalls, func(a, b katiInstall) bool {
+		return compareKatiInstalls(a, b) == 0
+	})
+	katiInstalls = append(katiInstalls, katiInitRcInstalls...)
+
+	slices.SortFunc(katiVintfManifestInstalls, compareKatiInstalls)
+	katiVintfManifestInstalls = slices.CompactFunc(katiVintfManifestInstalls, func(a, b katiInstall) bool {
+		return compareKatiInstalls(a, b) == 0
+	})
+
 	if ctx.Failed() {
 		return
 	}
@@ -316,7 +343,7 @@
 		ctx.Errorf(err.Error())
 	}
 
-	installsBytes := s.writeInstalls(katiInstalls, katiSymlinks)
+	installsBytes := s.writeInstalls(katiInstalls, katiSymlinks, katiVintfManifestInstalls)
 	if err := pathtools.WriteFileIfChanged(installsFile, installsBytes, 0666); err != nil {
 		ctx.Errorf(err.Error())
 	}
@@ -438,7 +465,7 @@
 // writeInstalls writes the list of install rules generated by Soong to a makefile.  The rules
 // are exported to Make instead of written directly to the ninja file so that main.mk can add
 // the dependencies from the `required` property that are hard to resolve in Soong.
-func (s *makeVarsSingleton) writeInstalls(installs, symlinks []katiInstall) []byte {
+func (s *makeVarsSingleton) writeInstalls(installs, symlinks, katiVintfManifestInstalls []katiInstall) []byte {
 	buf := &bytes.Buffer{}
 
 	fmt.Fprint(buf, `# Autogenerated file
@@ -486,9 +513,9 @@
 	for _, symlink := range symlinks {
 		fmt.Fprintf(buf, "%s:", symlink.to.String())
 		if symlink.from != nil {
-			// The symlink doesn't need updating when the target is modified, but we sometimes
-			// have a dependency on a symlink to a binary instead of to the binary directly, and
-			// the mtime of the symlink must be updated when the binary is modified, so use a
+			// The katiVintfManifestInstall doesn't need updating when the target is modified, but we sometimes
+			// have a dependency on a katiVintfManifestInstall to a binary instead of to the binary directly, and
+			// the mtime of the katiVintfManifestInstall must be updated when the binary is modified, so use a
 			// normal dependency here instead of an order-only dependency.
 			fmt.Fprintf(buf, " %s", symlink.from.String())
 		}
@@ -507,7 +534,7 @@
 		if symlink.from != nil {
 			rel, err := filepath.Rel(filepath.Dir(symlink.to.String()), symlink.from.String())
 			if err != nil {
-				panic(fmt.Errorf("failed to find relative path for symlink from %q to %q: %w",
+				panic(fmt.Errorf("failed to find relative path for katiVintfManifestInstall from %q to %q: %w",
 					symlink.from.String(), symlink.to.String(), err))
 			}
 			fromStr = rel
@@ -521,6 +548,19 @@
 		fmt.Fprintln(buf)
 	}
 
+	for _, install := range katiVintfManifestInstalls {
+		// Write a rule for each vintf install request that calls the copy-vintf-manifest-chedk make function.
+		fmt.Fprintf(buf, "$(eval $(call copy-vintf-manifest-checked, %s, %s))\n", install.from.String(), install.to.String())
+
+		if len(install.implicitDeps) > 0 {
+			panic(fmt.Errorf("unsupported implicitDeps %q in vintf install rule %q", install.implicitDeps, install.to))
+		}
+		if len(install.orderOnlyDeps) > 0 {
+			panic(fmt.Errorf("unsupported orderOnlyDeps %q in vintf install rule %q", install.orderOnlyDeps, install.to))
+		}
+
+		fmt.Fprintln(buf)
+	}
 	return buf.Bytes()
 }
 
diff --git a/android/module.go b/android/module.go
index f571157..b615ff5 100644
--- a/android/module.go
+++ b/android/module.go
@@ -23,6 +23,7 @@
 	"net/url"
 	"path/filepath"
 	"reflect"
+	"slices"
 	"sort"
 	"strings"
 
@@ -33,6 +34,7 @@
 var (
 	DeviceSharedLibrary = "shared_library"
 	DeviceStaticLibrary = "static_library"
+	jarJarPrefixHandler func(ctx ModuleContext)
 )
 
 type Module interface {
@@ -77,6 +79,8 @@
 	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
+	InstallInOdm() bool
+	InstallInProduct() bool
 	InstallInVendor() bool
 	InstallForceOS() (*OsType, *ArchType)
 	PartitionTag(DeviceConfig) string
@@ -323,7 +327,7 @@
 	// defaults module, use the `defaults_visibility` property on the defaults module;
 	// not to be confused with the `default_visibility` property on the package module.
 	//
-	// See https://android.googlesource.com/platform/build/soong/+/master/README.md#visibility for
+	// See https://android.googlesource.com/platform/build/soong/+/main/README.md#visibility for
 	// more details.
 	Visibility []string
 
@@ -516,6 +520,9 @@
 	// trace, but influence modules among products.
 	SoongConfigTrace     soongConfigTrace `blueprint:"mutated"`
 	SoongConfigTraceHash string           `blueprint:"mutated"`
+
+	// The team (defined by the owner/vendor) who owns the property.
+	Team *string `android:"path"`
 }
 
 type distProperties struct {
@@ -528,6 +535,12 @@
 	Dists []Dist `android:"arch_variant"`
 }
 
+type TeamDepTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var teamDepTag = TeamDepTagType{}
+
 // CommonTestOptions represents the common `test_options` properties in
 // Android.bp.
 type CommonTestOptions struct {
@@ -837,8 +850,12 @@
 	// katiInstalls tracks the install rules that were created by Soong but are being exported
 	// to Make to convert to ninja rules so that Make can add additional dependencies.
 	katiInstalls katiInstalls
-	katiSymlinks katiInstalls
-	testData     []DataPath
+	// katiInitRcInstalls and katiVintfInstalls track the install rules created by Soong that are
+	// allowed to have duplicates across modules and variants.
+	katiInitRcInstalls katiInstalls
+	katiVintfInstalls  katiInstalls
+	katiSymlinks       katiInstalls
+	testData           []DataPath
 
 	// The files to copy to the dist as explicitly specified in the .bp file.
 	distFiles TaggedDistFiles
@@ -861,12 +878,19 @@
 	initRcPaths         Paths
 	vintfFragmentsPaths Paths
 
+	installedInitRcPaths         InstallPaths
+	installedVintfFragmentsPaths InstallPaths
+
 	// set of dependency module:location mappings used to populate the license metadata for
 	// apex containers.
 	licenseInstallMap []string
 
 	// The path to the generated license metadata file for the module.
 	licenseMetadataFile WritablePath
+
+	// moduleInfoJSON can be filled out by GenerateAndroidBuildActions to write a JSON file that will
+	// be included in the final module-info.json produced by Make.
+	moduleInfoJSON *ModuleInfoJSON
 }
 
 func (m *ModuleBase) AddJSONData(d *map[string]interface{}) {
@@ -978,6 +1002,12 @@
 
 func (m *ModuleBase) DepsMutator(BottomUpMutatorContext) {}
 
+func (m *ModuleBase) baseDepsMutator(ctx BottomUpMutatorContext) {
+	if m.Team() != "" {
+		ctx.AddDependency(ctx.Module(), teamDepTag, m.Team())
+	}
+}
+
 // AddProperties "registers" the provided props
 // each value in props MUST be a pointer to a struct
 func (m *ModuleBase) AddProperties(props ...interface{}) {
@@ -1399,6 +1429,14 @@
 	return Bool(m.commonProperties.Recovery)
 }
 
+func (m *ModuleBase) InstallInOdm() bool {
+	return false
+}
+
+func (m *ModuleBase) InstallInProduct() bool {
+	return false
+}
+
 func (m *ModuleBase) InstallInVendor() bool {
 	return Bool(m.commonProperties.Vendor) || Bool(m.commonProperties.Soc_specific) || Bool(m.commonProperties.Proprietary)
 }
@@ -1415,6 +1453,10 @@
 	return String(m.commonProperties.Owner)
 }
 
+func (m *ModuleBase) Team() string {
+	return String(m.commonProperties.Team)
+}
+
 func (m *ModuleBase) setImageVariation(variant string) {
 	m.commonProperties.ImageVariation = variant
 }
@@ -1600,14 +1642,31 @@
 func (m *ModuleBase) baseModuleContextFactory(ctx blueprint.BaseModuleContext) baseModuleContext {
 	return baseModuleContext{
 		bp:                 ctx,
+		archModuleContext:  m.archModuleContextFactory(ctx),
 		earlyModuleContext: m.earlyModuleContextFactory(ctx),
-		os:                 m.commonProperties.CompileOS,
-		target:             m.commonProperties.CompileTarget,
-		targetPrimary:      m.commonProperties.CompilePrimary,
-		multiTargets:       m.commonProperties.CompileMultiTargets,
 	}
 }
 
+func (m *ModuleBase) archModuleContextFactory(ctx blueprint.IncomingTransitionContext) archModuleContext {
+	config := ctx.Config().(Config)
+	target := m.Target()
+	primaryArch := false
+	if len(config.Targets[target.Os]) <= 1 {
+		primaryArch = true
+	} else {
+		primaryArch = target.Arch.ArchType == config.Targets[target.Os][0].Arch.ArchType
+	}
+
+	return archModuleContext{
+		os:            m.commonProperties.CompileOS,
+		target:        m.commonProperties.CompileTarget,
+		targetPrimary: m.commonProperties.CompilePrimary,
+		multiTargets:  m.commonProperties.CompileMultiTargets,
+		primaryArch:   primaryArch,
+	}
+
+}
+
 func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) {
 	ctx := &moduleContext{
 		module:            m.module,
@@ -1648,7 +1707,7 @@
 	if !ctx.PrimaryArch() {
 		suffix = append(suffix, ctx.Arch().ArchType.String())
 	}
-	if apexInfo := ctx.Provider(ApexInfoProvider).(ApexInfo); !apexInfo.IsForPlatform() {
+	if apexInfo, _ := ModuleProvider(ctx, ApexInfoProvider); !apexInfo.IsForPlatform() {
 		suffix = append(suffix, apexInfo.ApexVariationName)
 	}
 
@@ -1670,30 +1729,65 @@
 		// ensure all direct android.Module deps are enabled
 		ctx.VisitDirectDepsBlueprint(func(bm blueprint.Module) {
 			if m, ok := bm.(Module); ok {
-				ctx.validateAndroidModule(bm, ctx.OtherModuleDependencyTag(m), ctx.baseModuleContext.strictVisitDeps)
+				ctx.validateAndroidModule(bm, ctx.OtherModuleDependencyTag(m), ctx.baseModuleContext.strictVisitDeps, false)
 			}
 		})
 
+		if m.Device() {
+			// Handle any init.rc and vintf fragment files requested by the module.  All files installed by this
+			// module will automatically have a dependency on the installed init.rc or vintf fragment file.
+			// The same init.rc or vintf fragment file may be requested by multiple modules or variants,
+			// so instead of installing them now just compute the install path and store it for later.
+			// The full list of all init.rc and vintf fragment install rules will be deduplicated later
+			// so only a single rule is created for each init.rc or vintf fragment file.
+
+			if !m.InVendorRamdisk() {
+				m.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc)
+				rcDir := PathForModuleInstall(ctx, "etc", "init")
+				for _, src := range m.initRcPaths {
+					installedInitRc := rcDir.Join(ctx, src.Base())
+					m.katiInitRcInstalls = append(m.katiInitRcInstalls, katiInstall{
+						from: src,
+						to:   installedInitRc,
+					})
+					ctx.PackageFile(rcDir, src.Base(), src)
+					m.installedInitRcPaths = append(m.installedInitRcPaths, installedInitRc)
+				}
+			}
+
+			m.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments)
+			vintfDir := PathForModuleInstall(ctx, "etc", "vintf", "manifest")
+			for _, src := range m.vintfFragmentsPaths {
+				installedVintfFragment := vintfDir.Join(ctx, src.Base())
+				m.katiVintfInstalls = append(m.katiVintfInstalls, katiInstall{
+					from: src,
+					to:   installedVintfFragment,
+				})
+				ctx.PackageFile(vintfDir, src.Base(), src)
+				m.installedVintfFragmentsPaths = append(m.installedVintfFragmentsPaths, installedVintfFragment)
+			}
+		}
+
 		licensesPropertyFlattener(ctx)
 		if ctx.Failed() {
 			return
 		}
 
+		if jarJarPrefixHandler != nil {
+			jarJarPrefixHandler(ctx)
+			if ctx.Failed() {
+				return
+			}
+		}
+
 		m.module.GenerateAndroidBuildActions(ctx)
 		if ctx.Failed() {
 			return
 		}
 
-		m.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc)
-		rcDir := PathForModuleInstall(ctx, "etc", "init")
-		for _, src := range m.initRcPaths {
-			ctx.PackageFile(rcDir, filepath.Base(src.String()), src)
-		}
-
-		m.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments)
-		vintfDir := PathForModuleInstall(ctx, "etc", "vintf", "manifest")
-		for _, src := range m.vintfFragmentsPaths {
-			ctx.PackageFile(vintfDir, filepath.Base(src.String()), src)
+		aconfigUpdateAndroidBuildActions(ctx)
+		if ctx.Failed() {
+			return
 		}
 
 		// Create the set of tagged dist files after calling GenerateAndroidBuildActions
@@ -1731,11 +1825,97 @@
 
 	buildLicenseMetadata(ctx, m.licenseMetadataFile)
 
+	if m.moduleInfoJSON != nil {
+		var installed InstallPaths
+		installed = append(installed, m.katiInstalls.InstallPaths()...)
+		installed = append(installed, m.katiSymlinks.InstallPaths()...)
+		installed = append(installed, m.katiInitRcInstalls.InstallPaths()...)
+		installed = append(installed, m.katiVintfInstalls.InstallPaths()...)
+		installedStrings := installed.Strings()
+
+		var targetRequired, hostRequired []string
+		if ctx.Host() {
+			targetRequired = m.commonProperties.Target_required
+		} else {
+			hostRequired = m.commonProperties.Host_required
+		}
+
+		var data []string
+		for _, d := range m.testData {
+			data = append(data, d.ToRelativeInstallPath())
+		}
+
+		if m.moduleInfoJSON.Uninstallable {
+			installedStrings = nil
+			if len(m.moduleInfoJSON.CompatibilitySuites) == 1 && m.moduleInfoJSON.CompatibilitySuites[0] == "null-suite" {
+				m.moduleInfoJSON.CompatibilitySuites = nil
+				m.moduleInfoJSON.TestConfig = nil
+				m.moduleInfoJSON.AutoTestConfig = nil
+				data = nil
+			}
+		}
+
+		m.moduleInfoJSON.core = CoreModuleInfoJSON{
+			RegisterName:       m.moduleInfoRegisterName(ctx, m.moduleInfoJSON.SubName),
+			Path:               []string{ctx.ModuleDir()},
+			Installed:          installedStrings,
+			ModuleName:         m.BaseModuleName() + m.moduleInfoJSON.SubName,
+			SupportedVariants:  []string{m.moduleInfoVariant(ctx)},
+			TargetDependencies: targetRequired,
+			HostDependencies:   hostRequired,
+			Data:               data,
+		}
+		SetProvider(ctx, ModuleInfoJSONProvider, m.moduleInfoJSON)
+	}
+
 	m.buildParams = ctx.buildParams
 	m.ruleParams = ctx.ruleParams
 	m.variables = ctx.variables
 }
 
+func SetJarJarPrefixHandler(handler func(ModuleContext)) {
+	if jarJarPrefixHandler != nil {
+		panic("jarJarPrefixHandler already set")
+	}
+	jarJarPrefixHandler = handler
+}
+
+func (m *ModuleBase) moduleInfoRegisterName(ctx ModuleContext, subName string) string {
+	name := m.BaseModuleName()
+
+	prefix := ""
+	if ctx.Host() {
+		if ctx.Os() != ctx.Config().BuildOS {
+			prefix = "host_cross_"
+		}
+	}
+	suffix := ""
+	arches := slices.Clone(ctx.Config().Targets[ctx.Os()])
+	arches = slices.DeleteFunc(arches, func(target Target) bool {
+		return target.NativeBridge != ctx.Target().NativeBridge
+	})
+	if len(arches) > 0 && ctx.Arch().ArchType != arches[0].Arch.ArchType {
+		if ctx.Arch().ArchType.Multilib == "lib32" {
+			suffix = "_32"
+		} else {
+			suffix = "_64"
+		}
+	}
+	return prefix + name + subName + suffix
+}
+
+func (m *ModuleBase) moduleInfoVariant(ctx ModuleContext) string {
+	variant := "DEVICE"
+	if ctx.Host() {
+		if ctx.Os() != ctx.Config().BuildOS {
+			variant = "HOST_CROSS"
+		} else {
+			variant = "HOST"
+		}
+	}
+	return variant
+}
+
 // Check the supplied dist structure to make sure that it is valid.
 //
 // property - the base property, e.g. dist or dists[1], which is combined with the
diff --git a/android/module_context.go b/android/module_context.go
index 39986df..1cab630 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -16,11 +16,12 @@
 
 import (
 	"fmt"
-	"github.com/google/blueprint"
-	"github.com/google/blueprint/proptools"
 	"path"
 	"path/filepath"
 	"strings"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 // BuildParameters describes the set of potential parameters to build a Ninja rule.
@@ -44,10 +45,6 @@
 	// Outputs is a slice of output file of the action. When using this field, references to $out in
 	// the Ninja command will refer to these files.
 	Outputs WritablePaths
-	// SymlinkOutput is an output file specifically that is a symlink.
-	SymlinkOutput WritablePath
-	// SymlinkOutputs is a slice of output files specifically that is a symlink.
-	SymlinkOutputs WritablePaths
 	// ImplicitOutput is an output file generated by the action. Note: references to `$out` in the
 	// Ninja command will NOT include references to this file.
 	ImplicitOutput WritablePath
@@ -181,6 +178,8 @@
 	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
+	InstallInOdm() bool
+	InstallInProduct() bool
 	InstallInVendor() bool
 	InstallForceOS() (*OsType, *ArchType)
 
@@ -208,6 +207,11 @@
 	// LicenseMetadataFile returns the path where the license metadata for this module will be
 	// generated.
 	LicenseMetadataFile() Path
+
+	// ModuleInfoJSON returns a pointer to the ModuleInfoJSON struct that can be filled out by
+	// GenerateAndroidBuildActions.  If it is called then the struct will be written out and included in
+	// the module-info.json generated by Make, and Make will not generate its own data for this module.
+	ModuleInfoJSON() *ModuleInfoJSON
 }
 
 type moduleContext struct {
@@ -248,25 +252,6 @@
 	m.Build(pctx, BuildParams(params))
 }
 
-func validateBuildParams(params blueprint.BuildParams) error {
-	// Validate that the symlink outputs are declared outputs or implicit outputs
-	allOutputs := map[string]bool{}
-	for _, output := range params.Outputs {
-		allOutputs[output] = true
-	}
-	for _, output := range params.ImplicitOutputs {
-		allOutputs[output] = true
-	}
-	for _, symlinkOutput := range params.SymlinkOutputs {
-		if !allOutputs[symlinkOutput] {
-			return fmt.Errorf(
-				"Symlink output %s is not a declared output or implicit output",
-				symlinkOutput)
-		}
-	}
-	return nil
-}
-
 // Convert build parameters from their concrete Android types into their string representations,
 // and combine the singular and plural fields of the same type (e.g. Output and Outputs).
 func convertBuildParams(params BuildParams) blueprint.BuildParams {
@@ -276,7 +261,6 @@
 		Deps:            params.Deps,
 		Outputs:         params.Outputs.Strings(),
 		ImplicitOutputs: params.ImplicitOutputs.Strings(),
-		SymlinkOutputs:  params.SymlinkOutputs.Strings(),
 		Inputs:          params.Inputs.Strings(),
 		Implicits:       params.Implicits.Strings(),
 		OrderOnly:       params.OrderOnly.Strings(),
@@ -291,9 +275,6 @@
 	if params.Output != nil {
 		bparams.Outputs = append(bparams.Outputs, params.Output.String())
 	}
-	if params.SymlinkOutput != nil {
-		bparams.SymlinkOutputs = append(bparams.SymlinkOutputs, params.SymlinkOutput.String())
-	}
 	if params.ImplicitOutput != nil {
 		bparams.ImplicitOutputs = append(bparams.ImplicitOutputs, params.ImplicitOutput.String())
 	}
@@ -309,7 +290,6 @@
 
 	bparams.Outputs = proptools.NinjaEscapeList(bparams.Outputs)
 	bparams.ImplicitOutputs = proptools.NinjaEscapeList(bparams.ImplicitOutputs)
-	bparams.SymlinkOutputs = proptools.NinjaEscapeList(bparams.SymlinkOutputs)
 	bparams.Inputs = proptools.NinjaEscapeList(bparams.Inputs)
 	bparams.Implicits = proptools.NinjaEscapeList(bparams.Implicits)
 	bparams.OrderOnly = proptools.NinjaEscapeList(bparams.OrderOnly)
@@ -367,13 +347,6 @@
 	}
 
 	bparams := convertBuildParams(params)
-	err := validateBuildParams(bparams)
-	if err != nil {
-		m.ModuleErrorf(
-			"%s: build parameter validation failed: %s",
-			m.ModuleName(),
-			err.Error())
-	}
 	m.bp.Build(pctx.PackageContext, bparams)
 }
 
@@ -438,6 +411,14 @@
 	return m.module.InstallForceOS()
 }
 
+func (m *moduleContext) InstallInOdm() bool {
+	return m.module.InstallInOdm()
+}
+
+func (m *moduleContext) InstallInProduct() bool {
+	return m.module.InstallInProduct()
+}
+
 func (m *moduleContext) InstallInVendor() bool {
 	return m.module.InstallInVendor()
 }
@@ -508,6 +489,8 @@
 
 	if !m.skipInstall() {
 		deps = append(deps, InstallPaths(m.module.base().installFilesDepSet.ToList())...)
+		deps = append(deps, m.module.base().installedInitRcPaths...)
+		deps = append(deps, m.module.base().installedVintfFragmentsPaths...)
 
 		var implicitDeps, orderOnlyDeps Paths
 
@@ -685,6 +668,15 @@
 	return m.module.base().licenseMetadataFile
 }
 
+func (m *moduleContext) ModuleInfoJSON() *ModuleInfoJSON {
+	if moduleInfoJSON := m.module.base().moduleInfoJSON; moduleInfoJSON != nil {
+		return moduleInfoJSON
+	}
+	moduleInfoJSON := &ModuleInfoJSON{}
+	m.module.base().moduleInfoJSON = moduleInfoJSON
+	return moduleInfoJSON
+}
+
 // Returns a list of paths expanded from globs and modules referenced using ":module" syntax.  The property must
 // be tagged with `android:"path" to support automatic source module dependency resolution.
 //
diff --git a/android/module_info_json.go b/android/module_info_json.go
new file mode 100644
index 0000000..1c0a38e
--- /dev/null
+++ b/android/module_info_json.go
@@ -0,0 +1,103 @@
+package android
+
+import (
+	"encoding/json"
+	"io"
+	"slices"
+
+	"github.com/google/blueprint"
+)
+
+type CoreModuleInfoJSON struct {
+	RegisterName       string   `json:"-"`
+	Path               []string `json:"path,omitempty"`                // $(sort $(ALL_MODULES.$(m).PATH))
+	Installed          []string `json:"installed,omitempty"`           // $(sort $(ALL_MODULES.$(m).INSTALLED))
+	ModuleName         string   `json:"module_name,omitempty"`         // $(ALL_MODULES.$(m).MODULE_NAME)
+	SupportedVariants  []string `json:"supported_variants,omitempty"`  // $(sort $(ALL_MODULES.$(m).SUPPORTED_VARIANTS))
+	HostDependencies   []string `json:"host_dependencies,omitempty"`   // $(sort $(ALL_MODULES.$(m).HOST_REQUIRED_FROM_TARGET))
+	TargetDependencies []string `json:"target_dependencies,omitempty"` // $(sort $(ALL_MODULES.$(m).TARGET_REQUIRED_FROM_HOST))
+	Data               []string `json:"data,omitempty"`                // $(sort $(ALL_MODULES.$(m).TEST_DATA))
+}
+
+type ModuleInfoJSON struct {
+	core                CoreModuleInfoJSON
+	SubName             string   `json:"-"`
+	Uninstallable       bool     `json:"-"`
+	Class               []string `json:"class,omitempty"`                 // $(sort $(ALL_MODULES.$(m).CLASS))
+	Tags                []string `json:"tags,omitempty"`                  // $(sort $(ALL_MODULES.$(m).TAGS))
+	Dependencies        []string `json:"dependencies,omitempty"`          // $(sort $(ALL_DEPS.$(m).ALL_DEPS))
+	SharedLibs          []string `json:"shared_libs,omitempty"`           // $(sort $(ALL_MODULES.$(m).SHARED_LIBS))
+	StaticLibs          []string `json:"static_libs,omitempty"`           // $(sort $(ALL_MODULES.$(m).STATIC_LIBS))
+	SystemSharedLibs    []string `json:"system_shared_libs,omitempty"`    // $(sort $(ALL_MODULES.$(m).SYSTEM_SHARED_LIBS))
+	Srcs                []string `json:"srcs,omitempty"`                  // $(sort $(ALL_MODULES.$(m).SRCS))
+	SrcJars             []string `json:"srcjars,omitempty"`               // $(sort $(ALL_MODULES.$(m).SRCJARS))
+	ClassesJar          []string `json:"classes_jar,omitempty"`           // $(sort $(ALL_MODULES.$(m).CLASSES_JAR))
+	TestMainlineModules []string `json:"test_mainline_modules,omitempty"` // $(sort $(ALL_MODULES.$(m).TEST_MAINLINE_MODULES))
+	IsUnitTest          bool     `json:"is_unit_test,omitempty"`          // $(ALL_MODULES.$(m).IS_UNIT_TEST)
+	TestOptionsTags     []string `json:"test_options_tags,omitempty"`     // $(sort $(ALL_MODULES.$(m).TEST_OPTIONS_TAGS))
+	RuntimeDependencies []string `json:"runtime_dependencies,omitempty"`  // $(sort $(ALL_MODULES.$(m).LOCAL_RUNTIME_LIBRARIES))
+	StaticDependencies  []string `json:"static_dependencies,omitempty"`   // $(sort $(ALL_MODULES.$(m).LOCAL_STATIC_LIBRARIES))
+	DataDependencies    []string `json:"data_dependencies,omitempty"`     // $(sort $(ALL_MODULES.$(m).TEST_DATA_BINS))
+
+	CompatibilitySuites []string `json:"compatibility_suites,omitempty"` // $(sort $(ALL_MODULES.$(m).COMPATIBILITY_SUITES))
+	AutoTestConfig      []string `json:"auto_test_config,omitempty"`     // $(ALL_MODULES.$(m).auto_test_config)
+	TestConfig          []string `json:"test_config,omitempty"`          // $(strip $(ALL_MODULES.$(m).TEST_CONFIG) $(ALL_MODULES.$(m).EXTRA_TEST_CONFIGS)
+}
+
+//ALL_DEPS.$(LOCAL_MODULE).ALL_DEPS := $(sort \
+//$(ALL_DEPS.$(LOCAL_MODULE).ALL_DEPS) \
+//$(LOCAL_STATIC_LIBRARIES) \
+//$(LOCAL_WHOLE_STATIC_LIBRARIES) \
+//$(LOCAL_SHARED_LIBRARIES) \
+//$(LOCAL_DYLIB_LIBRARIES) \
+//$(LOCAL_RLIB_LIBRARIES) \
+//$(LOCAL_PROC_MACRO_LIBRARIES) \
+//$(LOCAL_HEADER_LIBRARIES) \
+//$(LOCAL_STATIC_JAVA_LIBRARIES) \
+//$(LOCAL_JAVA_LIBRARIES) \
+//$(LOCAL_JNI_SHARED_LIBRARIES))
+
+type combinedModuleInfoJSON struct {
+	*CoreModuleInfoJSON
+	*ModuleInfoJSON
+}
+
+func encodeModuleInfoJSON(w io.Writer, moduleInfoJSON *ModuleInfoJSON) error {
+	moduleInfoJSONCopy := *moduleInfoJSON
+
+	sortAndUnique := func(s *[]string) {
+		*s = slices.Clone(*s)
+		slices.Sort(*s)
+		*s = slices.Compact(*s)
+	}
+
+	sortAndUnique(&moduleInfoJSONCopy.core.Path)
+	sortAndUnique(&moduleInfoJSONCopy.core.Installed)
+	sortAndUnique(&moduleInfoJSONCopy.core.SupportedVariants)
+	sortAndUnique(&moduleInfoJSONCopy.core.HostDependencies)
+	sortAndUnique(&moduleInfoJSONCopy.core.TargetDependencies)
+	sortAndUnique(&moduleInfoJSONCopy.core.Data)
+
+	sortAndUnique(&moduleInfoJSONCopy.Class)
+	sortAndUnique(&moduleInfoJSONCopy.Tags)
+	sortAndUnique(&moduleInfoJSONCopy.Dependencies)
+	sortAndUnique(&moduleInfoJSONCopy.SharedLibs)
+	sortAndUnique(&moduleInfoJSONCopy.StaticLibs)
+	sortAndUnique(&moduleInfoJSONCopy.SystemSharedLibs)
+	sortAndUnique(&moduleInfoJSONCopy.Srcs)
+	sortAndUnique(&moduleInfoJSONCopy.SrcJars)
+	sortAndUnique(&moduleInfoJSONCopy.ClassesJar)
+	sortAndUnique(&moduleInfoJSONCopy.TestMainlineModules)
+	sortAndUnique(&moduleInfoJSONCopy.TestOptionsTags)
+	sortAndUnique(&moduleInfoJSONCopy.RuntimeDependencies)
+	sortAndUnique(&moduleInfoJSONCopy.StaticDependencies)
+	sortAndUnique(&moduleInfoJSONCopy.DataDependencies)
+	sortAndUnique(&moduleInfoJSONCopy.CompatibilitySuites)
+	sortAndUnique(&moduleInfoJSONCopy.AutoTestConfig)
+	sortAndUnique(&moduleInfoJSONCopy.TestConfig)
+
+	encoder := json.NewEncoder(w)
+	return encoder.Encode(combinedModuleInfoJSON{&moduleInfoJSONCopy.core, &moduleInfoJSONCopy})
+}
+
+var ModuleInfoJSONProvider = blueprint.NewProvider[*ModuleInfoJSON]()
diff --git a/android/module_test.go b/android/module_test.go
index 1ca7422..1f3db5c 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -15,10 +15,11 @@
 package android
 
 import (
-	"github.com/google/blueprint"
 	"path/filepath"
 	"runtime"
 	"testing"
+
+	"github.com/google/blueprint"
 )
 
 func TestSrcIsModule(t *testing.T) {
@@ -244,52 +245,6 @@
 		RunTestWithBp(t, bp)
 }
 
-func TestValidateCorrectBuildParams(t *testing.T) {
-	config := TestConfig(t.TempDir(), nil, "", nil)
-	pathContext := PathContextForTesting(config)
-	bparams := convertBuildParams(BuildParams{
-		// Test with Output
-		Output:        PathForOutput(pathContext, "undeclared_symlink"),
-		SymlinkOutput: PathForOutput(pathContext, "undeclared_symlink"),
-	})
-
-	err := validateBuildParams(bparams)
-	if err != nil {
-		t.Error(err)
-	}
-
-	bparams = convertBuildParams(BuildParams{
-		// Test with ImplicitOutput
-		ImplicitOutput: PathForOutput(pathContext, "undeclared_symlink"),
-		SymlinkOutput:  PathForOutput(pathContext, "undeclared_symlink"),
-	})
-
-	err = validateBuildParams(bparams)
-	if err != nil {
-		t.Error(err)
-	}
-}
-
-func TestValidateIncorrectBuildParams(t *testing.T) {
-	config := TestConfig(t.TempDir(), nil, "", nil)
-	pathContext := PathContextForTesting(config)
-	params := BuildParams{
-		Output:          PathForOutput(pathContext, "regular_output"),
-		Outputs:         PathsForOutput(pathContext, []string{"out1", "out2"}),
-		ImplicitOutput:  PathForOutput(pathContext, "implicit_output"),
-		ImplicitOutputs: PathsForOutput(pathContext, []string{"i_out1", "_out2"}),
-		SymlinkOutput:   PathForOutput(pathContext, "undeclared_symlink"),
-	}
-
-	bparams := convertBuildParams(params)
-	err := validateBuildParams(bparams)
-	if err != nil {
-		FailIfNoMatchingErrors(t, "undeclared_symlink is not a declared output or implicit output", []error{err})
-	} else {
-		t.Errorf("Expected build params to fail validation: %+v", bparams)
-	}
-}
-
 func TestDistErrorChecking(t *testing.T) {
 	bp := `
 		deps {
diff --git a/android/mutator.go b/android/mutator.go
index 0d391a4..0ff4f48 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -15,6 +15,8 @@
 package android
 
 import (
+	"sync"
+
 	"github.com/google/blueprint"
 )
 
@@ -325,32 +327,55 @@
 	// if the value is not of the appropriate type, or if the module is not a newly created
 	// variant of the current module.  The value should not be modified after being passed to
 	// SetVariationProvider.
-	SetVariationProvider(module blueprint.Module, provider blueprint.ProviderKey, value interface{})
+	SetVariationProvider(module blueprint.Module, provider blueprint.AnyProviderKey, value interface{})
 }
 
+// An outgoingTransitionContextImpl and incomingTransitionContextImpl is created for every dependency of every module
+// for each transition mutator.  bottomUpMutatorContext and topDownMutatorContext are created once for every module
+// for every BottomUp or TopDown mutator.  Use a global pool for each to avoid reallocating every time.
+var (
+	outgoingTransitionContextPool = sync.Pool{
+		New: func() any { return &outgoingTransitionContextImpl{} },
+	}
+	incomingTransitionContextPool = sync.Pool{
+		New: func() any { return &incomingTransitionContextImpl{} },
+	}
+	bottomUpMutatorContextPool = sync.Pool{
+		New: func() any { return &bottomUpMutatorContext{} },
+	}
+
+	topDownMutatorContextPool = sync.Pool{
+		New: func() any { return &topDownMutatorContext{} },
+	}
+)
+
 type bottomUpMutatorContext struct {
 	bp blueprint.BottomUpMutatorContext
 	baseModuleContext
 	finalPhase bool
 }
 
+// callers must immediately follow the call to this function with defer bottomUpMutatorContextPool.Put(mctx).
 func bottomUpMutatorContextFactory(ctx blueprint.BottomUpMutatorContext, a Module,
 	finalPhase bool) BottomUpMutatorContext {
 
 	moduleContext := a.base().baseModuleContextFactory(ctx)
-
-	return &bottomUpMutatorContext{
+	mctx := bottomUpMutatorContextPool.Get().(*bottomUpMutatorContext)
+	*mctx = bottomUpMutatorContext{
 		bp:                ctx,
 		baseModuleContext: moduleContext,
 		finalPhase:        finalPhase,
 	}
+	return mctx
 }
 
 func (x *registerMutatorsContext) BottomUp(name string, m BottomUpMutator) MutatorHandle {
 	finalPhase := x.finalPhase
 	f := func(ctx blueprint.BottomUpMutatorContext) {
 		if a, ok := ctx.Module().(Module); ok {
-			m(bottomUpMutatorContextFactory(ctx, a, finalPhase))
+			mctx := bottomUpMutatorContextFactory(ctx, a, finalPhase)
+			defer bottomUpMutatorContextPool.Put(mctx)
+			m(mctx)
 		}
 	}
 	mutator := &mutator{name: x.mutatorName(name), bottomUpMutator: f}
@@ -365,15 +390,21 @@
 }
 
 type IncomingTransitionContext interface {
+	ArchModuleContext
+
 	// Module returns the target of the dependency edge for which the transition
 	// is being computed
 	Module() Module
 
 	// Config returns the configuration for the build.
 	Config() Config
+
+	DeviceConfig() DeviceConfig
 }
 
 type OutgoingTransitionContext interface {
+	ArchModuleContext
+
 	// Module returns the target of the dependency edge for which the transition
 	// is being computed
 	Module() Module
@@ -381,9 +412,14 @@
 	// DepTag() Returns the dependency tag through which this dependency is
 	// reached
 	DepTag() blueprint.DependencyTag
+
+	// Config returns the configuration for the build.
+	Config() Config
+
+	DeviceConfig() DeviceConfig
 }
 
-// Transition mutators implement a top-down mechanism where a module tells its
+// TransitionMutator implements a top-down mechanism where a module tells its
 // direct dependencies what variation they should be built in but the dependency
 // has the final say.
 //
@@ -448,18 +484,18 @@
 	// called on.
 	Split(ctx BaseModuleContext) []string
 
-	// Called on a module to determine which variation it wants from its direct
-	// dependencies. The dependency itself can override this decision. This method
-	// should not mutate the module itself.
+	// OutgoingTransition is called on a module to determine which variation it wants
+	// from its direct dependencies. The dependency itself can override this decision.
+	// This method should not mutate the module itself.
 	OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string
 
-	// Called on a module to determine which variation it should be in based on
-	// the variation modules that depend on it want. This gives the module a final
-	// say about its own variations. This method should not mutate the module
+	// IncomingTransition is called on a module to determine which variation it should
+	// be in based on the variation modules that depend on it want. This gives the module
+	// a final say about its own variations. This method should not mutate the module
 	// itself.
 	IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string
 
-	// Called after a module was split into multiple variations on each variation.
+	// Mutate is called after a module was split into multiple variations on each variation.
 	// It should not split the module any further but adding new dependencies is
 	// fine. Unlike all the other methods on TransitionMutator, this method is
 	// allowed to mutate the module.
@@ -481,6 +517,7 @@
 }
 
 type outgoingTransitionContextImpl struct {
+	archModuleContext
 	bp blueprint.OutgoingTransitionContext
 }
 
@@ -492,15 +529,30 @@
 	return c.bp.DepTag()
 }
 
-func (a *androidTransitionMutator) OutgoingTransition(ctx blueprint.OutgoingTransitionContext, sourceVariation string) string {
-	if _, ok := ctx.Module().(Module); ok {
-		return a.mutator.OutgoingTransition(&outgoingTransitionContextImpl{bp: ctx}, sourceVariation)
+func (c *outgoingTransitionContextImpl) Config() Config {
+	return c.bp.Config().(Config)
+}
+
+func (c *outgoingTransitionContextImpl) DeviceConfig() DeviceConfig {
+	return DeviceConfig{c.bp.Config().(Config).deviceConfig}
+}
+
+func (a *androidTransitionMutator) OutgoingTransition(bpctx blueprint.OutgoingTransitionContext, sourceVariation string) string {
+	if m, ok := bpctx.Module().(Module); ok {
+		ctx := outgoingTransitionContextPool.Get().(*outgoingTransitionContextImpl)
+		defer outgoingTransitionContextPool.Put(ctx)
+		*ctx = outgoingTransitionContextImpl{
+			archModuleContext: m.base().archModuleContextFactory(bpctx),
+			bp:                bpctx,
+		}
+		return a.mutator.OutgoingTransition(ctx, sourceVariation)
 	} else {
 		return ""
 	}
 }
 
 type incomingTransitionContextImpl struct {
+	archModuleContext
 	bp blueprint.IncomingTransitionContext
 }
 
@@ -512,9 +564,19 @@
 	return c.bp.Config().(Config)
 }
 
-func (a *androidTransitionMutator) IncomingTransition(ctx blueprint.IncomingTransitionContext, incomingVariation string) string {
-	if _, ok := ctx.Module().(Module); ok {
-		return a.mutator.IncomingTransition(&incomingTransitionContextImpl{bp: ctx}, incomingVariation)
+func (c *incomingTransitionContextImpl) DeviceConfig() DeviceConfig {
+	return DeviceConfig{c.bp.Config().(Config).deviceConfig}
+}
+
+func (a *androidTransitionMutator) IncomingTransition(bpctx blueprint.IncomingTransitionContext, incomingVariation string) string {
+	if m, ok := bpctx.Module().(Module); ok {
+		ctx := incomingTransitionContextPool.Get().(*incomingTransitionContextImpl)
+		defer incomingTransitionContextPool.Put(ctx)
+		*ctx = incomingTransitionContextImpl{
+			archModuleContext: m.base().archModuleContextFactory(bpctx),
+			bp:                bpctx,
+		}
+		return a.mutator.IncomingTransition(ctx, incomingVariation)
 	} else {
 		return ""
 	}
@@ -522,7 +584,9 @@
 
 func (a *androidTransitionMutator) Mutate(ctx blueprint.BottomUpMutatorContext, variation string) {
 	if am, ok := ctx.Module().(Module); ok {
-		a.mutator.Mutate(bottomUpMutatorContextFactory(ctx, am, a.finalPhase), variation)
+		mctx := bottomUpMutatorContextFactory(ctx, am, a.finalPhase)
+		defer bottomUpMutatorContextPool.Put(mctx)
+		a.mutator.Mutate(mctx, variation)
 	}
 }
 
@@ -545,7 +609,9 @@
 	f := func(ctx blueprint.TopDownMutatorContext) {
 		if a, ok := ctx.Module().(Module); ok {
 			moduleContext := a.base().baseModuleContextFactory(ctx)
-			actx := &topDownMutatorContext{
+			actx := topDownMutatorContextPool.Get().(*topDownMutatorContext)
+			defer topDownMutatorContextPool.Put(actx)
+			*actx = topDownMutatorContext{
 				bp:                ctx,
 				baseModuleContext: moduleContext,
 			}
@@ -600,6 +666,7 @@
 
 func depsMutator(ctx BottomUpMutatorContext) {
 	if m := ctx.Module(); m.Enabled() {
+		m.base().baseDepsMutator(ctx)
 		m.DepsMutator(ctx)
 	}
 }
@@ -746,6 +813,6 @@
 	b.bp.CreateAliasVariation(fromVariationName, toVariationName)
 }
 
-func (b *bottomUpMutatorContext) SetVariationProvider(module blueprint.Module, provider blueprint.ProviderKey, value interface{}) {
+func (b *bottomUpMutatorContext) SetVariationProvider(module blueprint.Module, provider blueprint.AnyProviderKey, value interface{}) {
 	b.bp.SetVariationProvider(module, provider, value)
 }
diff --git a/android/neverallow.go b/android/neverallow.go
index f721b94..62c5e59 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -57,7 +57,6 @@
 	AddNeverAllowRules(createUncompressDexRules()...)
 	AddNeverAllowRules(createInitFirstStageRules()...)
 	AddNeverAllowRules(createProhibitFrameworkAccessRules()...)
-	AddNeverAllowRules(createBp2BuildRule())
 	AddNeverAllowRules(createCcStubsRule())
 	AddNeverAllowRules(createJavaExcludeStaticLibsRule())
 	AddNeverAllowRules(createProhibitHeaderOnlyRule())
@@ -68,14 +67,6 @@
 	neverallows = append(neverallows, rules...)
 }
 
-func createBp2BuildRule() Rule {
-	return NeverAllow().
-		With("bazel_module.bp2build_available", "true").
-		NotIn("soong_tests"). // only used in tests
-		Because("setting bp2build_available in Android.bp is not " +
-			"supported for custom conversion, use allowlists.go instead.")
-}
-
 var (
 	neverallowNotInIncludeDir = []string{
 		"art",
diff --git a/android/package.go b/android/package.go
index 878e4c4..eb76751 100644
--- a/android/package.go
+++ b/android/package.go
@@ -35,6 +35,7 @@
 	Default_visibility []string
 	// Specifies the default license terms for all modules defined in this package.
 	Default_applicable_licenses []string
+	Default_team                *string `android:"path"`
 }
 
 type packageModule struct {
@@ -47,6 +48,13 @@
 	// Nothing to do.
 }
 
+func (p *packageModule) DepsMutator(ctx BottomUpMutatorContext) {
+	// Add the dependency to do a validity check
+	if p.properties.Default_team != nil {
+		ctx.AddDependency(ctx.Module(), nil, *p.properties.Default_team)
+	}
+}
+
 func (p *packageModule) GenerateBuildActions(ctx blueprint.ModuleContext) {
 	// Nothing to do.
 }
diff --git a/android/packaging.go b/android/packaging.go
index 503bb97..a8fb28d 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"path/filepath"
+	"strings"
 
 	"github.com/google/blueprint"
 )
@@ -84,6 +85,7 @@
 
 	// GatherPackagingSpecs gathers PackagingSpecs of transitive dependencies.
 	GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec
+	GatherPackagingSpecsWithFilter(ctx ModuleContext, filter func(PackagingSpec) bool) map[string]PackagingSpec
 
 	// CopyDepsToZip zips the built artifacts of the dependencies into the given zip file and
 	// returns zip entries in it. This is expected to be called in GenerateAndroidBuildActions,
@@ -220,14 +222,18 @@
 	}
 }
 
-// See PackageModule.GatherPackagingSpecs
-func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec {
+func (p *PackagingBase) GatherPackagingSpecsWithFilter(ctx ModuleContext, filter func(PackagingSpec) bool) map[string]PackagingSpec {
 	m := make(map[string]PackagingSpec)
 	ctx.VisitDirectDeps(func(child Module) {
 		if pi, ok := ctx.OtherModuleDependencyTag(child).(PackagingItem); !ok || !pi.IsPackagingItem() {
 			return
 		}
 		for _, ps := range child.TransitivePackagingSpecs() {
+			if filter != nil {
+				if !filter(ps) {
+					continue
+				}
+			}
 			if _, ok := m[ps.relPathInPackage]; !ok {
 				m[ps.relPathInPackage] = ps
 			}
@@ -236,10 +242,22 @@
 	return m
 }
 
+// See PackageModule.GatherPackagingSpecs
+func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec {
+	return p.GatherPackagingSpecsWithFilter(ctx, nil)
+}
+
 // CopySpecsToDir is a helper that will add commands to the rule builder to copy the PackagingSpec
 // entries into the specified directory.
 func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir WritablePath) (entries []string) {
+	if len(specs) == 0 {
+		return entries
+	}
 	seenDir := make(map[string]bool)
+	preparerPath := PathForModuleOut(ctx, "preparer.sh")
+	cmd := builder.Command().Tool(preparerPath)
+	var sb strings.Builder
+	sb.WriteString("set -e\n")
 	for _, k := range SortedKeys(specs) {
 		ps := specs[k]
 		destPath := filepath.Join(dir.String(), ps.relPathInPackage)
@@ -247,18 +265,21 @@
 		entries = append(entries, ps.relPathInPackage)
 		if _, ok := seenDir[destDir]; !ok {
 			seenDir[destDir] = true
-			builder.Command().Text("mkdir").Flag("-p").Text(destDir)
+			sb.WriteString(fmt.Sprintf("mkdir -p %s\n", destDir))
 		}
 		if ps.symlinkTarget == "" {
-			builder.Command().Text("cp").Input(ps.srcPath).Text(destPath)
+			cmd.Implicit(ps.srcPath)
+			sb.WriteString(fmt.Sprintf("cp %s %s\n", ps.srcPath, destPath))
 		} else {
-			builder.Command().Text("ln").Flag("-sf").Text(ps.symlinkTarget).Text(destPath)
+			sb.WriteString(fmt.Sprintf("ln -sf %s %s\n", ps.symlinkTarget, destPath))
 		}
 		if ps.executable {
-			builder.Command().Text("chmod").Flag("a+x").Text(destPath)
+			sb.WriteString(fmt.Sprintf("chmod a+x %s\n", destPath))
 		}
 	}
 
+	WriteExecutableFileRuleVerbatim(ctx, preparerPath, sb.String())
+
 	return entries
 }
 
diff --git a/android/path_properties.go b/android/path_properties.go
index fdc4d91..bbfaa8c 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -33,6 +33,11 @@
 // The pathDepsMutator automatically adds dependencies on any module that is listed with the
 // ":module" module reference syntax in a property that is tagged with `android:"path"`.
 func pathDepsMutator(ctx BottomUpMutatorContext) {
+	if _, ok := ctx.Module().(DefaultsModule); ok {
+		// Defaults modules shouldn't have dependencies added for path properties, they have already been
+		// squashed into the real modules.
+		return
+	}
 	props := ctx.Module().base().GetProperties()
 	addPathDepsForProps(ctx, props)
 }
diff --git a/android/paths.go b/android/paths.go
index 3f35449..61c1258 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -111,6 +111,9 @@
 	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
+	InstallInOdm() bool
+	InstallInProduct() bool
+	InstallInVendor() bool
 	InstallForceOS() (*OsType, *ArchType)
 }
 
@@ -152,6 +155,18 @@
 	return ctx.Module().InstallInRoot()
 }
 
+func (ctx *baseModuleContextToModuleInstallPathContext) InstallInOdm() bool {
+	return ctx.Module().InstallInOdm()
+}
+
+func (ctx *baseModuleContextToModuleInstallPathContext) InstallInProduct() bool {
+	return ctx.Module().InstallInProduct()
+}
+
+func (ctx *baseModuleContextToModuleInstallPathContext) InstallInVendor() bool {
+	return ctx.Module().InstallInVendor()
+}
+
 func (ctx *baseModuleContextToModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) {
 	return ctx.Module().InstallForceOS()
 }
@@ -166,13 +181,13 @@
 
 var _ errorfContext = blueprint.SingletonContext(nil)
 
-// moduleErrorf is the interface containing the ModuleErrorf method matching
+// ModuleErrorfContext is the interface containing the ModuleErrorf method matching
 // the ModuleErrorf method in blueprint.ModuleContext.
-type moduleErrorf interface {
+type ModuleErrorfContext interface {
 	ModuleErrorf(format string, args ...interface{})
 }
 
-var _ moduleErrorf = blueprint.ModuleContext(nil)
+var _ ModuleErrorfContext = blueprint.ModuleContext(nil)
 
 // reportPathError will register an error with the attached context. It
 // attempts ctx.ModuleErrorf for a better error message first, then falls
@@ -185,7 +200,7 @@
 // attempts ctx.ModuleErrorf for a better error message first, then falls
 // back to ctx.Errorf.
 func ReportPathErrorf(ctx PathContext, format string, args ...interface{}) {
-	if mctx, ok := ctx.(moduleErrorf); ok {
+	if mctx, ok := ctx.(ModuleErrorfContext); ok {
 		mctx.ModuleErrorf(format, args...)
 	} else if ectx, ok := ctx.(errorfContext); ok {
 		ectx.Errorf(format, args...)
@@ -1646,6 +1661,8 @@
 
 	// makePath indicates whether this path is for Soong (false) or Make (true).
 	makePath bool
+
+	fullPath string
 }
 
 // Will panic if called from outside a test environment.
@@ -1658,7 +1675,12 @@
 
 func (p InstallPath) RelativeToTop() Path {
 	ensureTestOnly()
-	p.soongOutDir = OutSoongDir
+	if p.makePath {
+		p.soongOutDir = OutDir
+	} else {
+		p.soongOutDir = OutSoongDir
+	}
+	p.fullPath = filepath.Join(p.soongOutDir, p.path)
 	return p
 }
 
@@ -1676,12 +1698,7 @@
 func (p InstallPath) writablePath() {}
 
 func (p InstallPath) String() string {
-	if p.makePath {
-		// Make path starts with out/ instead of out/soong.
-		return filepath.Join(p.soongOutDir, "../", p.path)
-	} else {
-		return filepath.Join(p.soongOutDir, p.path)
-	}
+	return p.fullPath
 }
 
 // PartitionDir returns the path to the partition where the install path is rooted at. It is
@@ -1711,6 +1728,7 @@
 
 func (p InstallPath) withRel(rel string) InstallPath {
 	p.basePath = p.basePath.withRel(rel)
+	p.fullPath = filepath.Join(p.fullPath, rel)
 	return p
 }
 
@@ -1754,6 +1772,25 @@
 	return os, arch
 }
 
+func pathForPartitionInstallDir(ctx PathContext, partition, partitionPath string, makePath bool) InstallPath {
+	fullPath := ctx.Config().SoongOutDir()
+	if makePath {
+		// Make path starts with out/ instead of out/soong.
+		fullPath = filepath.Join(fullPath, "../", partitionPath)
+	} else {
+		fullPath = filepath.Join(fullPath, partitionPath)
+	}
+
+	return InstallPath{
+		basePath:     basePath{partitionPath, ""},
+		soongOutDir:  ctx.Config().soongOutDir,
+		partitionDir: partitionPath,
+		partition:    partition,
+		makePath:     makePath,
+		fullPath:     fullPath,
+	}
+}
+
 func pathForInstall(ctx PathContext, os OsType, arch ArchType, partition string,
 	pathComponents ...string) InstallPath {
 
@@ -1790,27 +1827,12 @@
 		reportPathError(ctx, err)
 	}
 
-	base := InstallPath{
-		basePath:     basePath{partitionPath, ""},
-		soongOutDir:  ctx.Config().soongOutDir,
-		partitionDir: partitionPath,
-		partition:    partition,
-	}
-
-	if ctx.Config().KatiEnabled() {
-		base.makePath = true
-	}
-
+	base := pathForPartitionInstallDir(ctx, partition, partitionPath, ctx.Config().KatiEnabled())
 	return base.Join(ctx, pathComponents...)
 }
 
 func pathForNdkOrSdkInstall(ctx PathContext, prefix string, paths []string) InstallPath {
-	base := InstallPath{
-		basePath:     basePath{prefix, ""},
-		soongOutDir:  ctx.Config().soongOutDir,
-		partitionDir: prefix,
-		makePath:     false,
-	}
+	base := pathForPartitionInstallDir(ctx, "", prefix, false)
 	return base.Join(ctx, paths...)
 }
 
@@ -1866,11 +1888,11 @@
 				// the layout of recovery partion is the same as that of system partition
 				partition = "recovery/root/system"
 			}
-		} else if ctx.SocSpecific() {
+		} else if ctx.SocSpecific() || ctx.InstallInVendor() {
 			partition = ctx.DeviceConfig().VendorPath()
-		} else if ctx.DeviceSpecific() {
+		} else if ctx.DeviceSpecific() || ctx.InstallInOdm() {
 			partition = ctx.DeviceConfig().OdmPath()
-		} else if ctx.ProductSpecific() {
+		} else if ctx.ProductSpecific() || ctx.InstallInProduct() {
 			partition = ctx.DeviceConfig().ProductPath()
 		} else if ctx.SystemExtSpecific() {
 			partition = ctx.DeviceConfig().SystemExtPath()
@@ -2066,6 +2088,9 @@
 	inDebugRamdisk  bool
 	inRecovery      bool
 	inRoot          bool
+	inOdm           bool
+	inProduct       bool
+	inVendor        bool
 	forceOS         *OsType
 	forceArch       *ArchType
 }
@@ -2108,6 +2133,18 @@
 	return m.inRoot
 }
 
+func (m testModuleInstallPathContext) InstallInOdm() bool {
+	return m.inOdm
+}
+
+func (m testModuleInstallPathContext) InstallInProduct() bool {
+	return m.inProduct
+}
+
+func (m testModuleInstallPathContext) InstallInVendor() bool {
+	return m.inVendor
+}
+
 func (m testModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) {
 	return m.forceOS, m.forceArch
 }
diff --git a/android/paths_test.go b/android/paths_test.go
index bf46c34..93b9b9a 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -268,8 +268,10 @@
 			name: "host binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     hostTarget.Os,
-					target: hostTarget,
+					archModuleContext: archModuleContext{
+						os:     hostTarget.Os,
+						target: hostTarget,
+					},
 				},
 			},
 			in:           []string{"bin", "my_test"},
@@ -281,8 +283,10 @@
 			name: "system binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 				},
 			},
 			in:           []string{"bin", "my_test"},
@@ -293,8 +297,10 @@
 			name: "vendor binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 					earlyModuleContext: earlyModuleContext{
 						kind: socSpecificModule,
 					},
@@ -308,8 +314,10 @@
 			name: "odm binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 					earlyModuleContext: earlyModuleContext{
 						kind: deviceSpecificModule,
 					},
@@ -323,8 +331,10 @@
 			name: "product binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 					earlyModuleContext: earlyModuleContext{
 						kind: productSpecificModule,
 					},
@@ -338,8 +348,10 @@
 			name: "system_ext binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 					earlyModuleContext: earlyModuleContext{
 						kind: systemExtSpecificModule,
 					},
@@ -353,8 +365,10 @@
 			name: "root binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 				},
 				inRoot: true,
 			},
@@ -366,8 +380,10 @@
 			name: "recovery binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 				},
 				inRecovery: true,
 			},
@@ -379,8 +395,10 @@
 			name: "recovery root binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 				},
 				inRecovery: true,
 				inRoot:     true,
@@ -394,8 +412,10 @@
 			name: "ramdisk binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 				},
 				inRamdisk: true,
 			},
@@ -407,8 +427,10 @@
 			name: "ramdisk root binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 				},
 				inRamdisk: true,
 				inRoot:    true,
@@ -421,8 +443,10 @@
 			name: "vendor_ramdisk binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 				},
 				inVendorRamdisk: true,
 			},
@@ -434,8 +458,10 @@
 			name: "vendor_ramdisk root binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 				},
 				inVendorRamdisk: true,
 				inRoot:          true,
@@ -448,8 +474,10 @@
 			name: "debug_ramdisk binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 				},
 				inDebugRamdisk: true,
 			},
@@ -461,8 +489,10 @@
 			name: "system native test binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 				},
 				inData: true,
 			},
@@ -474,8 +504,10 @@
 			name: "vendor native test binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 					earlyModuleContext: earlyModuleContext{
 						kind: socSpecificModule,
 					},
@@ -490,8 +522,10 @@
 			name: "odm native test binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 					earlyModuleContext: earlyModuleContext{
 						kind: deviceSpecificModule,
 					},
@@ -506,8 +540,10 @@
 			name: "product native test binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 					earlyModuleContext: earlyModuleContext{
 						kind: productSpecificModule,
 					},
@@ -523,8 +559,10 @@
 			name: "system_ext native test binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 					earlyModuleContext: earlyModuleContext{
 						kind: systemExtSpecificModule,
 					},
@@ -540,8 +578,10 @@
 			name: "sanitized system binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 				},
 				inSanitizerDir: true,
 			},
@@ -553,8 +593,10 @@
 			name: "sanitized vendor binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 					earlyModuleContext: earlyModuleContext{
 						kind: socSpecificModule,
 					},
@@ -569,8 +611,10 @@
 			name: "sanitized odm binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 					earlyModuleContext: earlyModuleContext{
 						kind: deviceSpecificModule,
 					},
@@ -585,8 +629,10 @@
 			name: "sanitized product binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 					earlyModuleContext: earlyModuleContext{
 						kind: productSpecificModule,
 					},
@@ -602,8 +648,10 @@
 			name: "sanitized system_ext binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 					earlyModuleContext: earlyModuleContext{
 						kind: systemExtSpecificModule,
 					},
@@ -619,8 +667,10 @@
 			name: "sanitized system native test binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 				},
 				inData:         true,
 				inSanitizerDir: true,
@@ -633,8 +683,10 @@
 			name: "sanitized vendor native test binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 					earlyModuleContext: earlyModuleContext{
 						kind: socSpecificModule,
 					},
@@ -650,8 +702,10 @@
 			name: "sanitized odm native test binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 					earlyModuleContext: earlyModuleContext{
 						kind: deviceSpecificModule,
 					},
@@ -667,8 +721,10 @@
 			name: "sanitized product native test binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 					earlyModuleContext: earlyModuleContext{
 						kind: productSpecificModule,
 					},
@@ -684,8 +740,10 @@
 			name: "sanitized system_ext native test binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 					earlyModuleContext: earlyModuleContext{
 						kind: systemExtSpecificModule,
 					},
@@ -700,8 +758,10 @@
 			name: "device testcases",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 				},
 				inTestcases: true,
 			},
@@ -712,8 +772,10 @@
 			name: "host testcases",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     hostTarget.Os,
-					target: hostTarget,
+					archModuleContext: archModuleContext{
+						os:     hostTarget.Os,
+						target: hostTarget,
+					},
 				},
 				inTestcases: true,
 			},
@@ -724,8 +786,10 @@
 			name: "forced host testcases",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 				},
 				inTestcases: true,
 				forceOS:     &Linux,
@@ -771,8 +835,10 @@
 			name: "ramdisk binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 				},
 				inRamdisk: true,
 				inRoot:    true,
@@ -786,8 +852,10 @@
 			name: "vendor_ramdisk binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
-					os:     deviceTarget.Os,
-					target: deviceTarget,
+					archModuleContext: archModuleContext{
+						os:     deviceTarget.Os,
+						target: deviceTarget,
+					},
 				},
 				inVendorRamdisk: true,
 				inRoot:          true,
@@ -821,8 +889,10 @@
 
 	ctx := &testModuleInstallPathContext{
 		baseModuleContext: baseModuleContext{
-			os:     deviceTarget.Os,
-			target: deviceTarget,
+			archModuleContext: archModuleContext{
+				os:     deviceTarget.Os,
+				target: deviceTarget,
+			},
 		},
 	}
 	ctx.baseModuleContext.config = testConfig
@@ -1491,8 +1561,10 @@
 
 	ctx := &testModuleInstallPathContext{
 		baseModuleContext: baseModuleContext{
-			os:     deviceTarget.Os,
-			target: deviceTarget,
+			archModuleContext: archModuleContext{
+				os:     deviceTarget.Os,
+				target: deviceTarget,
+			},
 		},
 	}
 	ctx.baseModuleContext.config = testConfig
diff --git a/android/prebuilt.go b/android/prebuilt.go
index a32a37d..5a94a0f 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -277,6 +277,9 @@
 		}
 		value := srcPropsValue.FieldByIndex(srcFieldIndex)
 		if value.Kind() == reflect.Ptr {
+			if value.IsNil() {
+				return nil
+			}
 			value = value.Elem()
 		}
 		if value.Kind() != reflect.String {
@@ -391,12 +394,19 @@
 	ctx.BottomUp("prebuilt_postdeps", PrebuiltPostDepsMutator).Parallel()
 }
 
+// Returns the name of the source module corresponding to a prebuilt module
+// For source modules, it returns its own name
+type baseModuleName interface {
+	BaseModuleName() string
+}
+
 // PrebuiltRenameMutator ensures that there always is a module with an
 // undecorated name.
 func PrebuiltRenameMutator(ctx BottomUpMutatorContext) {
 	m := ctx.Module()
 	if p := GetEmbeddedPrebuilt(m); p != nil {
-		name := m.base().BaseModuleName()
+		bmn, _ := m.(baseModuleName)
+		name := bmn.BaseModuleName()
 		if !ctx.OtherModuleExists(name) {
 			ctx.Rename(name)
 			p.properties.PrebuiltRenamedToSource = true
@@ -413,7 +423,8 @@
 	// If this module is a prebuilt, is enabled and has not been renamed to source then add a
 	// dependency onto the source if it is present.
 	if p := GetEmbeddedPrebuilt(m); p != nil && m.Enabled() && !p.properties.PrebuiltRenamedToSource {
-		name := m.base().BaseModuleName()
+		bmn, _ := m.(baseModuleName)
+		name := bmn.BaseModuleName()
 		if ctx.OtherModuleReverseDependencyVariantExists(name) {
 			ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name)
 			p.properties.SourceExists = true
@@ -461,11 +472,18 @@
 		// Propagate the provider received from `all_apex_contributions`
 		// to the source module
 		ctx.VisitDirectDepsWithTag(acDepTag, func(am Module) {
-			psi := ctx.OtherModuleProvider(am, PrebuiltSelectionInfoProvider).(PrebuiltSelectionInfoMap)
-			ctx.SetProvider(PrebuiltSelectionInfoProvider, psi)
+			psi, _ := OtherModuleProvider(ctx, am, PrebuiltSelectionInfoProvider)
+			SetProvider(ctx, PrebuiltSelectionInfoProvider, psi)
 		})
 
 	} else if s, ok := ctx.Module().(Module); ok {
+		// Use `all_apex_contributions` for source vs prebuilt selection.
+		psi := PrebuiltSelectionInfoMap{}
+		ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(am Module) {
+			// The value of psi gets overwritten with the provider from the last visited prebuilt.
+			// But all prebuilts have the same value of the provider, so this should be idempontent.
+			psi, _ = OtherModuleProvider(ctx, am, PrebuiltSelectionInfoProvider)
+		})
 		ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(prebuiltModule Module) {
 			p := GetEmbeddedPrebuilt(prebuiltModule)
 			if p.usePrebuilt(ctx, s, prebuiltModule) {
@@ -475,7 +493,18 @@
 				s.ReplacedByPrebuilt()
 			}
 		})
+
+		// If any module in this mainline module family has been flagged using apex_contributions, disable every other module in that family
+		// Add source
+		allModules := []Module{s}
+		// Add each prebuilt
+		ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(prebuiltModule Module) {
+			allModules = append(allModules, prebuiltModule)
+		})
+		hideUnflaggedModules(ctx, psi, allModules)
+
 	}
+
 	// If this is `all_apex_contributions`, set a provider containing
 	// metadata about source vs prebuilts selection
 	if am, ok := m.(*allApexContributions); ok {
@@ -483,20 +512,93 @@
 	}
 }
 
+// If any module in this mainline module family has been flagged using apex_contributions, disable every other module in that family
+func hideUnflaggedModules(ctx BottomUpMutatorContext, psi PrebuiltSelectionInfoMap, allModulesInFamily []Module) {
+	var selectedModuleInFamily Module
+	// query all_apex_contributions to see if any module in this family has been selected
+	for _, moduleInFamily := range allModulesInFamily {
+		// validate that are no duplicates
+		if isSelected(psi, moduleInFamily) {
+			if selectedModuleInFamily == nil {
+				// Store this so we can validate that there are no duplicates
+				selectedModuleInFamily = moduleInFamily
+			} else {
+				// There are duplicate modules from the same mainline module family
+				ctx.ModuleErrorf("Found duplicate variations of the same module in apex_contributions: %s and %s. Please remove one of these.\n", selectedModuleInFamily.Name(), moduleInFamily.Name())
+			}
+		}
+	}
+
+	// If a module has been selected, hide all other modules
+	if selectedModuleInFamily != nil {
+		for _, moduleInFamily := range allModulesInFamily {
+			if moduleInFamily.Name() != selectedModuleInFamily.Name() {
+				moduleInFamily.HideFromMake()
+				// If this is a prebuilt module, unset properties.UsePrebuilt
+				// properties.UsePrebuilt might evaluate to true via soong config var fallback mechanism
+				// Set it to false explicitly so that the following mutator does not replace rdeps to this unselected prebuilt
+				if p := GetEmbeddedPrebuilt(moduleInFamily); p != nil {
+					p.properties.UsePrebuilt = false
+				}
+			}
+		}
+	}
+	// Do a validation pass to make sure that multiple prebuilts of a specific module are not selected.
+	// This might happen if the prebuilts share the same soong config var namespace.
+	// This should be an error, unless one of the prebuilts has been explicitly declared in apex_contributions
+	var selectedPrebuilt Module
+	for _, moduleInFamily := range allModulesInFamily {
+		// Skip if this module is in a different namespace
+		if !moduleInFamily.ExportedToMake() {
+			continue
+		}
+		// Skip for the top-level java_sdk_library_(_import). This has some special cases that need to be addressed first.
+		// This does not run into non-determinism because PrebuiltPostDepsMutator also has the special case
+		if sdkLibrary, ok := moduleInFamily.(interface{ SdkLibraryName() *string }); ok && sdkLibrary.SdkLibraryName() != nil {
+			continue
+		}
+		if p := GetEmbeddedPrebuilt(moduleInFamily); p != nil && p.properties.UsePrebuilt {
+			if selectedPrebuilt == nil {
+				selectedPrebuilt = moduleInFamily
+			} else {
+				ctx.ModuleErrorf("Multiple prebuilt modules %v and %v have been marked as preferred for this source module. "+
+					"Please add the appropriate prebuilt module to apex_contributions for this release config.", selectedPrebuilt.Name(), moduleInFamily.Name())
+			}
+		}
+	}
+}
+
 // PrebuiltPostDepsMutator replaces dependencies on the source module with dependencies on the
 // prebuilt when both modules exist and the prebuilt should be used.  When the prebuilt should not
 // be used, disable installing it.
 func PrebuiltPostDepsMutator(ctx BottomUpMutatorContext) {
 	m := ctx.Module()
 	if p := GetEmbeddedPrebuilt(m); p != nil {
-		name := m.base().BaseModuleName()
+		bmn, _ := m.(baseModuleName)
+		name := bmn.BaseModuleName()
+		psi := PrebuiltSelectionInfoMap{}
+		ctx.VisitDirectDepsWithTag(acDepTag, func(am Module) {
+			psi, _ = OtherModuleProvider(ctx, am, PrebuiltSelectionInfoProvider)
+		})
+
 		if p.properties.UsePrebuilt {
 			if p.properties.SourceExists {
 				ctx.ReplaceDependenciesIf(name, func(from blueprint.Module, tag blueprint.DependencyTag, to blueprint.Module) bool {
+					if sdkLibrary, ok := m.(interface{ SdkLibraryName() *string }); ok && sdkLibrary.SdkLibraryName() != nil {
+						// Do not replace deps to the top-level prebuilt java_sdk_library hook.
+						// This hook has been special-cased in #isSelected to be _always_ active, even in next builds
+						// for dexpreopt and hiddenapi processing.
+						// If we do not special-case this here, rdeps referring to a java_sdk_library in next builds via libs
+						// will get prebuilt stubs
+						// TODO (b/308187268): Remove this after the apexes have been added to apex_contributions
+						if psi.IsSelected(name) {
+							return false
+						}
+					}
+
 					if t, ok := tag.(ReplaceSourceWithPrebuilt); ok {
 						return t.ReplaceSourceWithPrebuilt()
 					}
-
 					return true
 				})
 			}
@@ -525,21 +627,42 @@
 func isSelected(psi PrebuiltSelectionInfoMap, m Module) bool {
 	if sdkLibrary, ok := m.(interface{ SdkLibraryName() *string }); ok && sdkLibrary.SdkLibraryName() != nil {
 		sln := proptools.String(sdkLibrary.SdkLibraryName())
+
 		// This is the top-level library
 		// Do not supersede the existing prebuilts vs source selection mechanisms
-		if sln == m.base().BaseModuleName() {
+		// TODO (b/308187268): Remove this after the apexes have been added to apex_contributions
+		if bmn, ok := m.(baseModuleName); ok && sln == bmn.BaseModuleName() {
 			return false
 		}
 
 		// Stub library created by java_sdk_library_import
-		if p := GetEmbeddedPrebuilt(m); p != nil {
-			return psi.IsSelected(sln, PrebuiltNameFromSource(sln))
+		// java_sdk_library creates several child modules (java_import + prebuilt_stubs_sources) dynamically.
+		// This code block ensures that these child modules are selected if the top-level java_sdk_library_import is listed
+		// in the selected apex_contributions.
+		if javaImport, ok := m.(createdByJavaSdkLibraryName); ok && javaImport.CreatedByJavaSdkLibraryName() != nil {
+			return psi.IsSelected(PrebuiltNameFromSource(proptools.String(javaImport.CreatedByJavaSdkLibraryName())))
 		}
 
 		// Stub library created by java_sdk_library
-		return psi.IsSelected(sln, sln)
+		return psi.IsSelected(sln)
 	}
-	return psi.IsSelected(m.base().BaseModuleName(), m.Name())
+	return psi.IsSelected(m.Name())
+}
+
+// implemented by child modules of java_sdk_library_import
+type createdByJavaSdkLibraryName interface {
+	CreatedByJavaSdkLibraryName() *string
+}
+
+// Returns true if the prebuilt variant is disabled
+// e.g. for a cc_prebuilt_library_shared, this will return
+// - true for the static variant of the module
+// - false for the shared variant of the module
+//
+// Even though this is a cc_prebuilt_library_shared, we create both the variants today
+// https://source.corp.google.com/h/googleplex-android/platform/build/soong/+/e08e32b45a18a77bc3c3e751f730539b1b374f1b:cc/library.go;l=2113-2116;drc=2c4a9779cd1921d0397a12b3d3521f4c9b30d747;bpv=1;bpt=0
+func (p *Prebuilt) variantIsDisabled(ctx BaseMutatorContext, prebuilt Module) bool {
+	return p.srcsSupplier != nil && len(p.srcsSupplier(ctx, prebuilt)) == 0
 }
 
 // usePrebuilt returns true if a prebuilt should be used instead of the source module.  The prebuilt
@@ -548,9 +671,7 @@
 	// Use `all_apex_contributions` for source vs prebuilt selection.
 	psi := PrebuiltSelectionInfoMap{}
 	ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(am Module) {
-		if ctx.OtherModuleHasProvider(am, PrebuiltSelectionInfoProvider) {
-			psi = ctx.OtherModuleProvider(am, PrebuiltSelectionInfoProvider).(PrebuiltSelectionInfoMap)
-		}
+		psi, _ = OtherModuleProvider(ctx, am, PrebuiltSelectionInfoProvider)
 	})
 
 	// If the source module is explicitly listed in the metadata module, use that
@@ -558,7 +679,7 @@
 		return false
 	}
 	// If the prebuilt module is explicitly listed in the metadata module, use that
-	if isSelected(psi, prebuilt) {
+	if isSelected(psi, prebuilt) && !p.variantIsDisabled(ctx, prebuilt) {
 		return true
 	}
 
@@ -566,7 +687,7 @@
 	// fall back to the existing source vs prebuilt selection.
 	// TODO: Drop the fallback mechanisms
 
-	if p.srcsSupplier != nil && len(p.srcsSupplier(ctx, prebuilt)) == 0 {
+	if p.variantIsDisabled(ctx, prebuilt) {
 		return false
 	}
 
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 953258e..2241b08 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -741,7 +741,7 @@
 			}
 		}),
 	)
-	testPrebuiltErrorWithFixture(t, `Cannot use Soong module: prebuilt_foo from apex_contributions: my_apex_contributions because it has been added previously as: foo from apex_contributions: my_apex_contributions`, `
+	testPrebuiltErrorWithFixture(t, `Found duplicate variations of the same module in apex_contributions: foo and prebuilt_foo. Please remove one of these`, `
 		source {
 			name: "foo",
 		}
@@ -762,3 +762,45 @@
 		}
 		`, selectMainlineModuleContritbutions)
 }
+
+// Test that apex_contributions of prebuilt modules are ignored in coverage builds
+func TestSourceIsSelectedInCoverageBuilds(t *testing.T) {
+	prebuiltMainlineContributions := GroupFixturePreparers(
+		FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_prebuilt_apex_contributions",
+			}
+		}),
+		FixtureMergeEnv(map[string]string{
+			"EMMA_INSTRUMENT_FRAMEWORK": "true",
+		}),
+	)
+	bp := `
+		source {
+			name: "foo",
+		}
+		prebuilt {
+			name: "foo",
+			srcs: ["prebuilt_file"],
+		}
+		apex_contributions {
+			name: "my_prebuilt_apex_contributions",
+			api_domain: "my_mainline_module",
+			contents: [
+			  "prebuilt_foo",
+			],
+		}
+		all_apex_contributions {
+			name: "all_apex_contributions",
+		}
+		`
+	ctx := GroupFixturePreparers(
+		PrepareForTestWithArchMutator,
+		PrepareForTestWithPrebuilts,
+		FixtureRegisterWithContext(registerTestPrebuiltModules),
+		prebuiltMainlineContributions).RunTestWithBp(t, bp)
+	source := ctx.ModuleForTests("foo", "android_common").Module()
+	AssertBoolEquals(t, "Source should be preferred in coverage builds", true, !source.IsHideFromMake())
+	prebuilt := ctx.ModuleForTests("prebuilt_foo", "android_common").Module()
+	AssertBoolEquals(t, "Prebuilt should not be preferred in coverage builds", false, !prebuilt.IsHideFromMake())
+}
diff --git a/android/provider.go b/android/provider.go
new file mode 100644
index 0000000..3b9c5d2
--- /dev/null
+++ b/android/provider.go
@@ -0,0 +1,120 @@
+package android
+
+import (
+	"github.com/google/blueprint"
+)
+
+// OtherModuleProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
+// TopDownMutatorContext for use in OtherModuleProvider.
+type OtherModuleProviderContext interface {
+	otherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
+}
+
+var _ OtherModuleProviderContext = BaseModuleContext(nil)
+var _ OtherModuleProviderContext = ModuleContext(nil)
+var _ OtherModuleProviderContext = BottomUpMutatorContext(nil)
+var _ OtherModuleProviderContext = TopDownMutatorContext(nil)
+
+// OtherModuleProvider reads the provider for the given module.  If the provider has been set the value is
+// returned and the boolean is true.  If it has not been set the zero value of the provider's type  is returned
+// and the boolean is false.  The value returned may be a deep copy of the value originally passed to SetProvider.
+//
+// OtherModuleProviderContext is a helper interface that accepts ModuleContext, BottomUpMutatorContext, or
+// TopDownMutatorContext.
+func OtherModuleProvider[K any](ctx OtherModuleProviderContext, module blueprint.Module, provider blueprint.ProviderKey[K]) (K, bool) {
+	value, ok := ctx.otherModuleProvider(module, provider)
+	if !ok {
+		var k K
+		return k, false
+	}
+	return value.(K), ok
+}
+
+// ModuleProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
+// TopDownMutatorContext for use in ModuleProvider.
+type ModuleProviderContext interface {
+	provider(provider blueprint.AnyProviderKey) (any, bool)
+}
+
+var _ ModuleProviderContext = BaseModuleContext(nil)
+var _ ModuleProviderContext = ModuleContext(nil)
+var _ ModuleProviderContext = BottomUpMutatorContext(nil)
+var _ ModuleProviderContext = TopDownMutatorContext(nil)
+
+// ModuleProvider reads the provider for the current module.  If the provider has been set the value is
+// returned and the boolean is true.  If it has not been set the zero value of the provider's type  is returned
+// and the boolean is false.  The value returned may be a deep copy of the value originally passed to SetProvider.
+//
+// ModuleProviderContext is a helper interface that accepts ModuleContext, BottomUpMutatorContext, or
+// TopDownMutatorContext.
+func ModuleProvider[K any](ctx ModuleProviderContext, provider blueprint.ProviderKey[K]) (K, bool) {
+	value, ok := ctx.provider(provider)
+	if !ok {
+		var k K
+		return k, false
+	}
+	return value.(K), ok
+}
+
+type SingletonModuleProviderContext interface {
+	moduleProvider(blueprint.Module, blueprint.AnyProviderKey) (any, bool)
+}
+
+var _ SingletonModuleProviderContext = SingletonContext(nil)
+var _ SingletonModuleProviderContext = (*TestContext)(nil)
+
+// SingletonModuleProvider wraps blueprint.SingletonModuleProvider to provide a type-safe method to retrieve the value
+// of the given provider from a module using a SingletonContext.  If the provider has not been set the first return
+// value will be the zero value of the provider's type, and the second return value will be false.  If the provider has
+// been set the second return value will be true.
+func SingletonModuleProvider[K any](ctx SingletonModuleProviderContext, module blueprint.Module, provider blueprint.ProviderKey[K]) (K, bool) {
+	value, ok := ctx.moduleProvider(module, provider)
+	if !ok {
+		var k K
+		return k, false
+	}
+	return value.(K), ok
+}
+
+// SetProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
+// TopDownMutatorContext for use in SetProvider.
+type SetProviderContext interface {
+	setProvider(provider blueprint.AnyProviderKey, value any)
+}
+
+var _ SetProviderContext = BaseModuleContext(nil)
+var _ SetProviderContext = ModuleContext(nil)
+var _ SetProviderContext = BottomUpMutatorContext(nil)
+var _ SetProviderContext = TopDownMutatorContext(nil)
+
+// SetProvider sets the value for a provider for the current module.  It panics if not called
+// during the appropriate mutator or GenerateBuildActions pass for the provider, if the value
+// is not of the appropriate type, or if the value has already been set.  The value should not
+// be modified after being passed to SetProvider.
+//
+// SetProviderContext is a helper interface that accepts ModuleContext, BottomUpMutatorContext, or
+// TopDownMutatorContext.
+func SetProvider[K any](ctx SetProviderContext, provider blueprint.ProviderKey[K], value K) {
+	ctx.setProvider(provider, value)
+}
+
+var _ OtherModuleProviderContext = (*otherModuleProviderAdaptor)(nil)
+
+// An OtherModuleProviderFunc can be passed to NewOtherModuleProviderAdaptor to create an OtherModuleProviderContext
+// for use in tests.
+type OtherModuleProviderFunc func(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
+
+type otherModuleProviderAdaptor struct {
+	otherModuleProviderFunc OtherModuleProviderFunc
+}
+
+func (p *otherModuleProviderAdaptor) otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
+	return p.otherModuleProviderFunc(module, provider)
+}
+
+// NewOtherModuleProviderAdaptor returns an OtherModuleProviderContext that proxies calls to otherModuleProvider to
+// the provided OtherModuleProviderFunc.  It can be used in tests to unit test methods that need to call
+// android.OtherModuleProvider.
+func NewOtherModuleProviderAdaptor(otherModuleProviderFunc OtherModuleProviderFunc) OtherModuleProviderContext {
+	return &otherModuleProviderAdaptor{otherModuleProviderFunc}
+}
diff --git a/android/raw_files.go b/android/raw_files.go
new file mode 100644
index 0000000..9d7f5e8
--- /dev/null
+++ b/android/raw_files.go
@@ -0,0 +1,279 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+	"crypto/sha1"
+	"encoding/hex"
+	"fmt"
+	"github.com/google/blueprint"
+	"io"
+	"io/fs"
+	"os"
+	"path/filepath"
+	"strings"
+	"testing"
+
+	"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)
+}
+
+// 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)
+}
+
+// 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)
+}
+
+// tempFile provides a testable wrapper around a file in out/soong/.temp.  It writes to a temporary file when
+// not in tests, but writes to a buffer in memory when used in tests.
+type tempFile struct {
+	// tempFile contains wraps an io.Writer, which will be file if testMode is false, or testBuf if it is true.
+	io.Writer
+
+	file    *os.File
+	testBuf *strings.Builder
+}
+
+func newTempFile(ctx BuilderContext, pattern string, testMode bool) *tempFile {
+	if testMode {
+		testBuf := &strings.Builder{}
+		return &tempFile{
+			Writer:  testBuf,
+			testBuf: testBuf,
+		}
+	} else {
+		f, err := os.CreateTemp(absolutePath(ctx.Config().tempDir()), pattern)
+		if err != nil {
+			panic(fmt.Errorf("failed to open temporary raw file: %w", err))
+		}
+		return &tempFile{
+			Writer: f,
+			file:   f,
+		}
+	}
+}
+
+func (t *tempFile) close() error {
+	if t.file != nil {
+		return t.file.Close()
+	}
+	return nil
+}
+
+func (t *tempFile) name() string {
+	if t.file != nil {
+		return t.file.Name()
+	}
+	return "temp_file_in_test"
+}
+
+func (t *tempFile) rename(to string) {
+	if t.file != nil {
+		os.MkdirAll(filepath.Dir(to), 0777)
+		err := os.Rename(t.file.Name(), to)
+		if err != nil {
+			panic(fmt.Errorf("failed to rename %s to %s: %w", t.file.Name(), to, err))
+		}
+	}
+}
+
+func (t *tempFile) remove() error {
+	if t.file != nil {
+		return os.Remove(t.file.Name())
+	}
+	return nil
+}
+
+func writeContentToTempFileAndHash(ctx BuilderContext, content string, newline bool) (*tempFile, string) {
+	tempFile := newTempFile(ctx, "raw", ctx.Config().captureBuild)
+	defer tempFile.close()
+
+	hash := sha1.New()
+	w := io.MultiWriter(tempFile, hash)
+
+	_, err := io.WriteString(w, content)
+	if err == nil && newline {
+		_, err = io.WriteString(w, "\n")
+	}
+	if err != nil {
+		panic(fmt.Errorf("failed to write to temporary raw file %s: %w", tempFile.name(), err))
+	}
+	return tempFile, hex.EncodeToString(hash.Sum(nil))
+}
+
+func writeFileRule(ctx BuilderContext, outputFile WritablePath, content string, newline bool, executable bool) {
+	// Write the contents to a temporary file while computing its hash.
+	tempFile, hash := writeContentToTempFileAndHash(ctx, content, newline)
+
+	// Shard the final location of the raw file into a subdirectory based on the first two characters of the
+	// hash to avoid making the raw directory too large and slowing down accesses.
+	relPath := filepath.Join(hash[0:2], hash)
+
+	// These files are written during soong_build.  If something outside the build deleted them there would be no
+	// trigger to rerun soong_build, and the build would break with dependencies on missing files.  Writing them
+	// to their final locations would risk having them deleted when cleaning a module, and would also pollute the
+	// output directory with files for modules that have never been built.
+	// Instead, the files are written to a separate "raw" directory next to the build.ninja file, and a ninja
+	// rule is created to copy the files into their final location as needed.
+	// Obsolete files written by previous runs of soong_build must be cleaned up to avoid continually growing
+	// disk usage as the hashes of the files change over time.  The cleanup must not remove files that were
+	// created by previous runs of soong_build for other products, as the build.ninja files for those products
+	// may still exist and still reference those files.  The raw files from different products are kept
+	// separate by appending the Make_suffix to the directory name.
+	rawPath := PathForOutput(ctx, "raw"+proptools.String(ctx.Config().productVariables.Make_suffix), relPath)
+
+	rawFileInfo := rawFileInfo{
+		relPath: relPath,
+	}
+
+	if ctx.Config().captureBuild {
+		// When running tests tempFile won't write to disk, instead store the contents for later retrieval by
+		// ContentFromFileRuleForTests.
+		rawFileInfo.contentForTests = tempFile.testBuf.String()
+	}
+
+	rawFileSet := getRawFileSet(ctx.Config())
+	if _, exists := rawFileSet.LoadOrStore(hash, rawFileInfo); exists {
+		// If a raw file with this hash has already been created delete the temporary file.
+		tempFile.remove()
+	} else {
+		// If this is the first time this hash has been seen then move it from the temporary directory
+		// to the raw directory.  If the file already exists in the raw directory assume it has the correct
+		// contents.
+		absRawPath := absolutePath(rawPath.String())
+		_, err := os.Stat(absRawPath)
+		if os.IsNotExist(err) {
+			tempFile.rename(absRawPath)
+		} else if err != nil {
+			panic(fmt.Errorf("failed to stat %q: %w", absRawPath, err))
+		} else {
+			tempFile.remove()
+		}
+	}
+
+	// Emit a rule to copy the file from raw directory to the final requested location in the output tree.
+	// Restat is used to ensure that two different products that produce identical files copied from their
+	// own raw directories they don't cause everything downstream to rebuild.
+	rule := rawFileCopy
+	if executable {
+		rule = rawFileCopyExecutable
+	}
+	ctx.Build(pctx, BuildParams{
+		Rule:        rule,
+		Input:       rawPath,
+		Output:      outputFile,
+		Description: "raw " + outputFile.Base(),
+	})
+}
+
+var (
+	rawFileCopy = pctx.AndroidStaticRule("rawFileCopy",
+		blueprint.RuleParams{
+			Command:     "if ! cmp -s $in $out; then cp $in $out; fi",
+			Description: "copy raw file $out",
+			Restat:      true,
+		})
+	rawFileCopyExecutable = pctx.AndroidStaticRule("rawFileCopyExecutable",
+		blueprint.RuleParams{
+			Command:     "if ! cmp -s $in $out; then cp $in $out; fi && chmod +x $out",
+			Description: "copy raw exectuable file $out",
+			Restat:      true,
+		})
+)
+
+type rawFileInfo struct {
+	relPath         string
+	contentForTests string
+}
+
+var rawFileSetKey OnceKey = NewOnceKey("raw file set")
+
+func getRawFileSet(config Config) *SyncMap[string, rawFileInfo] {
+	return config.Once(rawFileSetKey, func() any {
+		return &SyncMap[string, rawFileInfo]{}
+	}).(*SyncMap[string, rawFileInfo])
+}
+
+// ContentFromFileRuleForTests returns the content that was passed to a WriteFileRule for use
+// in tests.
+func ContentFromFileRuleForTests(t *testing.T, ctx *TestContext, params TestingBuildParams) string {
+	t.Helper()
+	if params.Rule != rawFileCopy && params.Rule != rawFileCopyExecutable {
+		t.Errorf("expected params.Rule to be rawFileCopy or rawFileCopyExecutable, was %q", params.Rule)
+		return ""
+	}
+
+	key := filepath.Base(params.Input.String())
+	rawFileSet := getRawFileSet(ctx.Config())
+	rawFileInfo, _ := rawFileSet.Load(key)
+
+	return rawFileInfo.contentForTests
+}
+
+func rawFilesSingletonFactory() Singleton {
+	return &rawFilesSingleton{}
+}
+
+type rawFilesSingleton struct{}
+
+func (rawFilesSingleton) GenerateBuildActions(ctx SingletonContext) {
+	if ctx.Config().captureBuild {
+		// Nothing to do when running in tests, no temporary files were created.
+		return
+	}
+	rawFileSet := getRawFileSet(ctx.Config())
+	rawFilesDir := PathForOutput(ctx, "raw"+proptools.String(ctx.Config().productVariables.Make_suffix)).String()
+	absRawFilesDir := absolutePath(rawFilesDir)
+	err := filepath.WalkDir(absRawFilesDir, func(path string, d fs.DirEntry, err error) error {
+		if err != nil {
+			return err
+		}
+		if d.IsDir() {
+			// Ignore obsolete directories for now.
+			return nil
+		}
+
+		// Assume the basename of the file is a hash
+		key := filepath.Base(path)
+		relPath, err := filepath.Rel(absRawFilesDir, path)
+		if err != nil {
+			return err
+		}
+
+		// Check if a file with the same hash was written by this run of soong_build.  If the file was not written,
+		// or if a file with the same hash was written but to a different path in the raw directory, then delete it.
+		// Checking that the path matches allows changing the structure of the raw directory, for example to increase
+		// the sharding.
+		rawFileInfo, written := rawFileSet.Load(key)
+		if !written || rawFileInfo.relPath != relPath {
+			os.Remove(path)
+		}
+		return nil
+	})
+	if err != nil {
+		panic(fmt.Errorf("failed to clean %q: %w", rawFilesDir, err))
+	}
+}
diff --git a/android/register.go b/android/register.go
index cd968cd..d00c15f 100644
--- a/android/register.go
+++ b/android/register.go
@@ -191,8 +191,9 @@
 		// Register makevars after other singletons so they can export values through makevars
 		singleton{parallel: false, name: "makevars", factory: makeVarsSingletonFunc},
 
-		// Register env and ninjadeps last so that they can track all used environment variables and
+		// Register rawfiles and ninjadeps last so that they can track all used environment variables and
 		// Ninja file dependencies stored in the config.
+		singleton{parallel: false, name: "rawfiles", factory: rawFilesSingletonFactory},
 		singleton{parallel: false, name: "ninjadeps", factory: ninjaDepsSingletonFactory},
 	)
 
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 1a491f7..85e29bd 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -336,41 +336,6 @@
 	return outputList
 }
 
-func (r *RuleBuilder) symlinkOutputSet() map[string]WritablePath {
-	symlinkOutputs := make(map[string]WritablePath)
-	for _, c := range r.commands {
-		for _, symlinkOutput := range c.symlinkOutputs {
-			symlinkOutputs[symlinkOutput.String()] = symlinkOutput
-		}
-	}
-	return symlinkOutputs
-}
-
-// SymlinkOutputs returns the list of paths that the executor (Ninja) would
-// verify, after build edge completion, that:
-//
-// 1) Created output symlinks match the list of paths in this list exactly (no more, no fewer)
-// 2) Created output files are *not* declared in this list.
-//
-// These symlink outputs are expected to be a subset of outputs or implicit
-// outputs, or they would fail validation at build param construction time
-// later, to support other non-rule-builder approaches for constructing
-// statements.
-func (r *RuleBuilder) SymlinkOutputs() WritablePaths {
-	symlinkOutputs := r.symlinkOutputSet()
-
-	var symlinkOutputList WritablePaths
-	for _, symlinkOutput := range symlinkOutputs {
-		symlinkOutputList = append(symlinkOutputList, symlinkOutput)
-	}
-
-	sort.Slice(symlinkOutputList, func(i, j int) bool {
-		return symlinkOutputList[i].String() < symlinkOutputList[j].String()
-	})
-
-	return symlinkOutputList
-}
-
 func (r *RuleBuilder) depFileSet() map[string]WritablePath {
 	depFiles := make(map[string]WritablePath)
 	for _, c := range r.commands {
@@ -590,7 +555,7 @@
 							To:   proto.String(sboxOutSubDir),
 						},
 						{
-							From: proto.String(PathForOutput(r.ctx).String()),
+							From: proto.String(r.ctx.Config().OutDir()),
 							To:   proto.String(sboxOutSubDir),
 						},
 					},
@@ -629,8 +594,9 @@
 				name, r.sboxManifestPath.String(), r.outDir.String())
 		}
 
-		// Create a rule to write the manifest as textproto.
-		pbText, err := prototext.Marshal(&manifest)
+		// Create a rule to write the manifest as textproto. Pretty print it by indenting and
+		// splitting across multiple lines.
+		pbText, err := prototext.MarshalOptions{Indent: " "}.Marshal(&manifest)
 		if err != nil {
 			ReportPathErrorf(r.ctx, "sbox manifest failed to marshal: %q", err)
 		}
@@ -775,7 +741,6 @@
 		Validations:     r.Validations(),
 		Output:          output,
 		ImplicitOutputs: implicitOutputs,
-		SymlinkOutputs:  r.SymlinkOutputs(),
 		Depfile:         depFile,
 		Deps:            depFormat,
 		Description:     desc,
@@ -789,17 +754,16 @@
 type RuleBuilderCommand struct {
 	rule *RuleBuilder
 
-	buf            strings.Builder
-	inputs         Paths
-	implicits      Paths
-	orderOnlys     Paths
-	validations    Paths
-	outputs        WritablePaths
-	symlinkOutputs WritablePaths
-	depFiles       WritablePaths
-	tools          Paths
-	packagedTools  []PackagingSpec
-	rspFiles       []rspFileAndPaths
+	buf           strings.Builder
+	inputs        Paths
+	implicits     Paths
+	orderOnlys    Paths
+	validations   Paths
+	outputs       WritablePaths
+	depFiles      WritablePaths
+	tools         Paths
+	packagedTools []PackagingSpec
+	rspFiles      []rspFileAndPaths
 }
 
 type rspFileAndPaths struct {
@@ -891,7 +855,7 @@
 		// When sandboxing inputs all inputs have to be copied into the sandbox.  Input files that
 		// are outputs of other rules could be an arbitrary absolute path if OUT_DIR is set, so they
 		// will be copied to relative paths under __SBOX_OUT_DIR__/out.
-		rel, isRelOut, _ := maybeRelErr(PathForOutput(r.ctx).String(), path.String())
+		rel, isRelOut, _ := maybeRelErr(r.ctx.Config().OutDir(), path.String())
 		if isRelOut {
 			return filepath.Join(sboxOutSubDir, rel), true
 		}
@@ -1193,11 +1157,15 @@
 
 // OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
 // and will be the temporary output directory managed by sbox, not the final one.
-func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
+func (c *RuleBuilderCommand) OutputDir(subPathComponents ...string) *RuleBuilderCommand {
 	if !c.rule.sbox {
 		panic("OutputDir only valid with Sbox")
 	}
-	return c.Text(sboxOutDir)
+	path := sboxOutDir
+	if len(subPathComponents) > 0 {
+		path = filepath.Join(append([]string{sboxOutDir}, subPathComponents...)...)
+	}
+	return c.Text(path)
 }
 
 // DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
@@ -1223,42 +1191,6 @@
 	return c
 }
 
-// ImplicitSymlinkOutput declares the specified path as an implicit output that
-// will be a symlink instead of a regular file. Does not modify the command
-// line.
-func (c *RuleBuilderCommand) ImplicitSymlinkOutput(path WritablePath) *RuleBuilderCommand {
-	checkPathNotNil(path)
-	c.symlinkOutputs = append(c.symlinkOutputs, path)
-	return c.ImplicitOutput(path)
-}
-
-// ImplicitSymlinkOutputs declares the specified paths as implicit outputs that
-// will be a symlinks instead of regular files. Does not modify the command
-// line.
-func (c *RuleBuilderCommand) ImplicitSymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
-	for _, path := range paths {
-		c.ImplicitSymlinkOutput(path)
-	}
-	return c
-}
-
-// SymlinkOutput declares the specified path as an output that will be a symlink
-// instead of a regular file. Modifies the command line.
-func (c *RuleBuilderCommand) SymlinkOutput(path WritablePath) *RuleBuilderCommand {
-	checkPathNotNil(path)
-	c.symlinkOutputs = append(c.symlinkOutputs, path)
-	return c.Output(path)
-}
-
-// SymlinkOutputsl declares the specified paths as outputs that will be symlinks
-// instead of regular files. Modifies the command line.
-func (c *RuleBuilderCommand) SymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
-	for _, path := range paths {
-		c.SymlinkOutput(path)
-	}
-	return c
-}
-
 // ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
 // the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja.  If multiple depfiles
 // are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index 63c3527..6a8a964 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -48,7 +48,6 @@
 		"a":       nil,
 		"b":       nil,
 		"ls":      nil,
-		"ln":      nil,
 		"turbine": nil,
 		"java":    nil,
 		"javac":   nil,
@@ -81,32 +80,6 @@
 	// outputs: ["out/soong/linked"]
 }
 
-func ExampleRuleBuilder_SymlinkOutputs() {
-	ctx := builderContext()
-
-	rule := NewRuleBuilder(pctx, ctx)
-
-	rule.Command().
-		Tool(PathForSource(ctx, "ln")).
-		FlagWithInput("-s ", PathForTesting("a.o")).
-		SymlinkOutput(PathForOutput(ctx, "a"))
-	rule.Command().Text("cp out/soong/a out/soong/b").
-		ImplicitSymlinkOutput(PathForOutput(ctx, "b"))
-
-	fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
-	fmt.Printf("tools: %q\n", rule.Tools())
-	fmt.Printf("inputs: %q\n", rule.Inputs())
-	fmt.Printf("outputs: %q\n", rule.Outputs())
-	fmt.Printf("symlink_outputs: %q\n", rule.SymlinkOutputs())
-
-	// Output:
-	// commands: "ln -s a.o out/soong/a && cp out/soong/a out/soong/b"
-	// tools: ["ln"]
-	// inputs: ["a.o"]
-	// outputs: ["out/soong/a" "out/soong/b"]
-	// symlink_outputs: ["out/soong/a" "out/soong/b"]
-}
-
 func ExampleRuleBuilder_Temporary() {
 	ctx := builderContext()
 
@@ -334,8 +307,6 @@
 			Output(PathForOutput(ctx, "module/Output")).
 			OrderOnly(PathForSource(ctx, "OrderOnly")).
 			Validation(PathForSource(ctx, "Validation")).
-			SymlinkOutput(PathForOutput(ctx, "module/SymlinkOutput")).
-			ImplicitSymlinkOutput(PathForOutput(ctx, "module/ImplicitSymlinkOutput")).
 			Text("Text").
 			Tool(PathForSource(ctx, "Tool"))
 
@@ -367,15 +338,13 @@
 	wantRspFileInputs := Paths{PathForSource(ctx, "RspInput"),
 		PathForOutput(ctx, "other/RspOutput2")}
 	wantOutputs := PathsForOutput(ctx, []string{
-		"module/ImplicitOutput", "module/ImplicitSymlinkOutput", "module/Output", "module/SymlinkOutput",
-		"module/output", "module/output2", "module/output3"})
+		"module/ImplicitOutput", "module/Output", "module/output", "module/output2",
+		"module/output3"})
 	wantDepFiles := PathsForOutput(ctx, []string{
 		"module/DepFile", "module/depfile", "module/ImplicitDepFile", "module/depfile2"})
 	wantTools := PathsForSource(ctx, []string{"Tool", "tool2"})
 	wantOrderOnlys := PathsForSource(ctx, []string{"OrderOnly", "OrderOnlys"})
 	wantValidations := PathsForSource(ctx, []string{"Validation", "Validations"})
-	wantSymlinkOutputs := PathsForOutput(ctx, []string{
-		"module/ImplicitSymlinkOutput", "module/SymlinkOutput"})
 
 	t.Run("normal", func(t *testing.T) {
 		rule := NewRuleBuilder(pctx, ctx)
@@ -384,7 +353,7 @@
 		wantCommands := []string{
 			"out_local/soong/module/DepFile Flag FlagWithArg=arg FlagWithDepFile=out_local/soong/module/depfile " +
 				"FlagWithInput=input FlagWithOutput=out_local/soong/module/output FlagWithRspFileInputList=out_local/soong/rsp " +
-				"Input out_local/soong/module/Output out_local/soong/module/SymlinkOutput Text Tool after command2 old cmd",
+				"Input out_local/soong/module/Output Text Tool after command2 old cmd",
 			"command2 out_local/soong/module/depfile2 input2 out_local/soong/module/output2 tool2",
 			"command3 input3 out_local/soong/module/output2 out_local/soong/module/output3 input3 out_local/soong/module/output2",
 		}
@@ -397,7 +366,6 @@
 		AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs())
 		AssertDeepEquals(t, "rule.RspfileInputs()", wantRspFileInputs, rule.RspFileInputs())
 		AssertDeepEquals(t, "rule.Outputs()", wantOutputs, rule.Outputs())
-		AssertDeepEquals(t, "rule.SymlinkOutputs()", wantSymlinkOutputs, rule.SymlinkOutputs())
 		AssertDeepEquals(t, "rule.DepFiles()", wantDepFiles, rule.DepFiles())
 		AssertDeepEquals(t, "rule.Tools()", wantTools, rule.Tools())
 		AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
@@ -415,7 +383,7 @@
 			"__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile " +
 				"FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output " +
 				"FlagWithRspFileInputList=out_local/soong/rsp Input __SBOX_SANDBOX_DIR__/out/Output " +
-				"__SBOX_SANDBOX_DIR__/out/SymlinkOutput Text Tool after command2 old cmd",
+				"Text Tool after command2 old cmd",
 			"command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 tool2",
 			"command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3 input3 __SBOX_SANDBOX_DIR__/out/output2",
 		}
@@ -427,7 +395,6 @@
 		AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs())
 		AssertDeepEquals(t, "rule.RspfileInputs()", wantRspFileInputs, rule.RspFileInputs())
 		AssertDeepEquals(t, "rule.Outputs()", wantOutputs, rule.Outputs())
-		AssertDeepEquals(t, "rule.SymlinkOutputs()", wantSymlinkOutputs, rule.SymlinkOutputs())
 		AssertDeepEquals(t, "rule.DepFiles()", wantDepFiles, rule.DepFiles())
 		AssertDeepEquals(t, "rule.Tools()", wantTools, rule.Tools())
 		AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
@@ -445,7 +412,7 @@
 			"__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile " +
 				"FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output " +
 				"FlagWithRspFileInputList=out_local/soong/rsp Input __SBOX_SANDBOX_DIR__/out/Output " +
-				"__SBOX_SANDBOX_DIR__/out/SymlinkOutput Text __SBOX_SANDBOX_DIR__/tools/src/Tool after command2 old cmd",
+				"Text __SBOX_SANDBOX_DIR__/tools/src/Tool after command2 old cmd",
 			"command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/tools/src/tool2",
 			"command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3 input3 __SBOX_SANDBOX_DIR__/out/output2",
 		}
@@ -457,7 +424,6 @@
 		AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs())
 		AssertDeepEquals(t, "rule.RspfileInputs()", wantRspFileInputs, rule.RspFileInputs())
 		AssertDeepEquals(t, "rule.Outputs()", wantOutputs, rule.Outputs())
-		AssertDeepEquals(t, "rule.SymlinkOutputs()", wantSymlinkOutputs, rule.SymlinkOutputs())
 		AssertDeepEquals(t, "rule.DepFiles()", wantDepFiles, rule.DepFiles())
 		AssertDeepEquals(t, "rule.Tools()", wantTools, rule.Tools())
 		AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
@@ -474,8 +440,8 @@
 		wantCommands := []string{
 			"__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile " +
 				"FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output " +
-				"FlagWithRspFileInputList=__SBOX_SANDBOX_DIR__/out/rsp Input __SBOX_SANDBOX_DIR__/out/Output " +
-				"__SBOX_SANDBOX_DIR__/out/SymlinkOutput Text __SBOX_SANDBOX_DIR__/tools/src/Tool after command2 old cmd",
+				"FlagWithRspFileInputList=__SBOX_SANDBOX_DIR__/out/soong/rsp Input __SBOX_SANDBOX_DIR__/out/Output " +
+				"Text __SBOX_SANDBOX_DIR__/tools/src/Tool after command2 old cmd",
 			"command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/tools/src/tool2",
 			"command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3 input3 __SBOX_SANDBOX_DIR__/out/output2",
 		}
@@ -487,7 +453,6 @@
 		AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs())
 		AssertDeepEquals(t, "rule.RspfileInputs()", wantRspFileInputs, rule.RspFileInputs())
 		AssertDeepEquals(t, "rule.Outputs()", wantOutputs, rule.Outputs())
-		AssertDeepEquals(t, "rule.SymlinkOutputs()", wantSymlinkOutputs, rule.SymlinkOutputs())
 		AssertDeepEquals(t, "rule.DepFiles()", wantDepFiles, rule.DepFiles())
 		AssertDeepEquals(t, "rule.Tools()", wantTools, rule.Tools())
 		AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
@@ -816,13 +781,13 @@
 func TestRuleBuilderWithNinjaVarEscaping(t *testing.T) {
 	bp := `
 		rule_builder_test {
-			name: "foo_sbox_escaped_ninja",
+			name: "foo_sbox_escaped",
 			flags: ["${cmdFlags}"],
 			sbox: true,
 			sbox_inputs: true,
 		}
 		rule_builder_test {
-			name: "foo_sbox",
+			name: "foo_sbox_unescaped",
 			flags: ["${cmdFlags}"],
 			sbox: true,
 			sbox_inputs: true,
@@ -834,15 +799,16 @@
 		FixtureWithRootAndroidBp(bp),
 	).RunTest(t)
 
-	escapedNinjaMod := result.ModuleForTests("foo_sbox_escaped_ninja", "").Rule("writeFile")
+	escapedNinjaMod := result.ModuleForTests("foo_sbox_escaped", "").Output("sbox.textproto")
+	AssertStringEquals(t, "expected rule", "android/soong/android.rawFileCopy", escapedNinjaMod.Rule.String())
 	AssertStringDoesContain(
 		t,
 		"",
-		escapedNinjaMod.BuildParams.Args["content"],
-		"$${cmdFlags}",
+		ContentFromFileRuleForTests(t, result.TestContext, escapedNinjaMod),
+		"${cmdFlags}",
 	)
 
-	unescapedNinjaMod := result.ModuleForTests("foo_sbox", "").Rule("unescapedWriteFile")
+	unescapedNinjaMod := result.ModuleForTests("foo_sbox_unescaped", "").Rule("unescapedWriteFile")
 	AssertStringDoesContain(
 		t,
 		"",
diff --git a/android/sdk.go b/android/sdk.go
index 6b598ab..6d5293e 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -860,11 +860,11 @@
 	Components []string
 }
 
-var ExportedComponentsInfoProvider = blueprint.NewProvider(ExportedComponentsInfo{})
+var ExportedComponentsInfoProvider = blueprint.NewProvider[ExportedComponentsInfo]()
 
 // AdditionalSdkInfo contains additional properties to add to the generated SDK info file.
 type AdditionalSdkInfo struct {
 	Properties map[string]interface{}
 }
 
-var AdditionalSdkInfoProvider = blueprint.NewProvider(AdditionalSdkInfo{})
+var AdditionalSdkInfoProvider = blueprint.NewProvider[AdditionalSdkInfo]()
diff --git a/android/sdk_version.go b/android/sdk_version.go
index aafcee7..b2ff960 100644
--- a/android/sdk_version.go
+++ b/android/sdk_version.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"reflect"
 	"strconv"
 	"strings"
 )
@@ -108,6 +109,17 @@
 	}
 }
 
+func (k SdkKind) DefaultExportableJavaLibraryName() string {
+	switch k {
+	case SdkPublic, SdkSystem, SdkTest, SdkModule, SdkSystemServer:
+		return k.DefaultJavaLibraryName() + "_exportable"
+	case SdkCore:
+		return k.DefaultJavaLibraryName() + ".exportable"
+	default:
+		panic(fmt.Errorf("API surface %v does not provide exportable stubs", k))
+	}
+}
+
 // SdkSpec represents the kind and the version of an SDK for a module to build against
 type SdkSpec struct {
 	Kind     SdkKind
@@ -162,6 +174,17 @@
 	// If BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES has a numeric value,
 	// use it instead of "current" for the vendor partition.
 	currentSdkVersion := ctx.DeviceConfig().CurrentApiLevelForVendorModules()
+	// b/314011075: special case for Java modules in vendor partition. They can no longer use
+	// SDK 35 or later. Their maximum API level is limited to 34 (Android U). This is to
+	// discourage the use of Java APIs in the vendor partition which hasn't been officially
+	// supported since the Project Treble back in Android 10. We would like to eventually
+	// evacuate all Java modules from the partition, but that shall be done progressively.
+	// Note that the check for the availability of SDK 34 is to not break existing tests where
+	// any of the frozen SDK version is unavailable.
+	if isJava(ctx.Module()) && isSdkVersion34AvailableIn(ctx.Config()) {
+		currentSdkVersion = "34"
+	}
+
 	if currentSdkVersion == "current" {
 		return s
 	}
@@ -290,28 +313,79 @@
 	}
 }
 
+// Checks if the use of this SDK `s` is valid for the given module context `ctx`.
 func (s SdkSpec) ValidateSystemSdk(ctx EarlyModuleContext) bool {
-	// Ensures that the specified system SDK version is one of BOARD_SYSTEMSDK_VERSIONS (for vendor/product Java module)
-	// Assuming that BOARD_SYSTEMSDK_VERSIONS := 28 29,
-	// sdk_version of the modules in vendor/product that use system sdk must be either system_28, system_29 or system_current
-	if s.Kind != SdkSystem || s.ApiLevel.IsPreview() {
+	// Do some early checks. This check is currently only for Java modules. And our only concern
+	// is the use of "system" SDKs.
+	if !isJava(ctx.Module()) || s.Kind != SdkSystem || ctx.DeviceConfig().BuildBrokenDontCheckSystemSdk() {
 		return true
 	}
-	allowedVersions := ctx.DeviceConfig().PlatformSystemSdkVersions()
-	if ctx.DeviceSpecific() || ctx.SocSpecific() || (ctx.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
-		systemSdkVersions := ctx.DeviceConfig().SystemSdkVersions()
-		if len(systemSdkVersions) > 0 {
-			allowedVersions = systemSdkVersions
+
+	inVendor := ctx.DeviceSpecific() || ctx.SocSpecific()
+	inProduct := ctx.ProductSpecific()
+	isProductUnbundled := ctx.Config().EnforceProductPartitionInterface()
+	inApex := false
+	if am, ok := ctx.Module().(ApexModule); ok {
+		inApex = am.InAnyApex()
+	}
+	isUnbundled := inVendor || (inProduct && isProductUnbundled) || inApex
+
+	// Bundled modules can use any SDK
+	if !isUnbundled {
+		return true
+	}
+
+	// Unbundled modules are allowed to use BOARD_SYSTEMSDK_VERSIONS
+	supportedVersions := ctx.DeviceConfig().SystemSdkVersions()
+
+	// b/314011075: special case for vendor modules. Java modules in the vendor partition can
+	// not use SDK 35 or later. This is to discourage the use of Java APIs in the vendor
+	// partition which hasn't been officially supported since the Project Treble back in Android
+	// 10. We would like to eventually evacuate all Java modules from the partition, but that
+	// shall be done progressively.
+	if inVendor {
+		// 28 was the API when BOARD_SYSTEMSDK_VERSIONS was introduced, so that's the oldest
+		// we should allow.
+		supportedVersions = []string{}
+		for v := 28; v <= 34; v++ {
+			supportedVersions = append(supportedVersions, strconv.Itoa(v))
 		}
 	}
-	if len(allowedVersions) > 0 && !InList(s.ApiLevel.String(), allowedVersions) {
+
+	// APEXes in the system partition are still considered as part of the platform, thus can use
+	// more SDKs from PLATFORM_SYSTEMSDK_VERSIONS
+	if inApex && !inVendor {
+		supportedVersions = ctx.DeviceConfig().PlatformSystemSdkVersions()
+	}
+
+	thisVer, err := s.EffectiveVersion(ctx)
+	if err != nil {
+		ctx.PropertyErrorf("sdk_version", "invalid sdk version %q", s.Raw)
+		return false
+	}
+
+	thisVerString := strconv.Itoa(thisVer.FinalOrPreviewInt())
+	if thisVer.IsPreview() {
+		thisVerString = *ctx.Config().productVariables.Platform_sdk_version_or_codename
+	}
+
+	if !InList(thisVerString, supportedVersions) {
 		ctx.PropertyErrorf("sdk_version", "incompatible sdk version %q. System SDK version should be one of %q",
-			s.Raw, allowedVersions)
+			s.Raw, supportedVersions)
 		return false
 	}
 	return true
 }
 
+func isJava(m Module) bool {
+	moduleType := reflect.TypeOf(m).String()
+	return strings.HasPrefix(moduleType, "*java.")
+}
+
+func isSdkVersion34AvailableIn(c Config) bool {
+	return c.PlatformSdkVersion().FinalInt() >= 34
+}
+
 func init() {
 	RegisterMakeVarsProvider(pctx, javaSdkMakeVars)
 }
@@ -319,6 +393,7 @@
 // Export the name of the soong modules representing the various Java API surfaces.
 func javaSdkMakeVars(ctx MakeVarsContext) {
 	ctx.Strict("ANDROID_PUBLIC_STUBS", SdkPublic.DefaultJavaLibraryName())
+	ctx.Strict("ANDROID_PUBLIC_EXPORTABLE_STUBS", SdkPublic.DefaultExportableJavaLibraryName())
 	ctx.Strict("ANDROID_SYSTEM_STUBS", SdkSystem.DefaultJavaLibraryName())
 	ctx.Strict("ANDROID_TEST_STUBS", SdkTest.DefaultJavaLibraryName())
 	ctx.Strict("ANDROID_MODULE_LIB_STUBS", SdkModule.DefaultJavaLibraryName())
diff --git a/android/singleton.go b/android/singleton.go
index 7c6cf4f..ccddeaf 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -20,6 +20,8 @@
 
 // SingletonContext
 type SingletonContext interface {
+	blueprintSingletonContext() blueprint.SingletonContext
+
 	Config() Config
 	DeviceConfig() DeviceConfig
 
@@ -33,15 +35,7 @@
 	// Allows generating build actions for `referer` based on the metadata for `name` deferred until the singleton context.
 	ModuleVariantsFromName(referer Module, name string) []Module
 
-	// ModuleProvider returns the value, if any, for the provider for a module.  If the value for the
-	// provider was not set it returns the zero value of the type of the provider, which means the
-	// return value can always be type-asserted to the type of the provider.  The return value should
-	// always be considered read-only.  It panics if called before the appropriate mutator or
-	// GenerateBuildActions pass for the provider on the module.
-	ModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{}
-
-	// ModuleHasProvider returns true if the provider for the given module has been set.
-	ModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool
+	moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
 
 	ModuleErrorf(module blueprint.Module, format string, args ...interface{})
 	Errorf(format string, args ...interface{})
@@ -135,6 +129,10 @@
 	ruleParams  map[blueprint.Rule]blueprint.RuleParams
 }
 
+func (s *singletonContextAdaptor) blueprintSingletonContext() blueprint.SingletonContext {
+	return s.SingletonContext
+}
+
 func (s *singletonContextAdaptor) Config() Config {
 	return s.SingletonContext.Config().(Config)
 }
@@ -172,12 +170,7 @@
 		s.buildParams = append(s.buildParams, params)
 	}
 	bparams := convertBuildParams(params)
-	err := validateBuildParams(bparams)
-	if err != nil {
-		s.Errorf("%s: build parameter validation failed: %s", s.Name(), err.Error())
-	}
 	s.SingletonContext.Build(pctx.PackageContext, bparams)
-
 }
 
 func (s *singletonContextAdaptor) Phony(name string, deps ...Path) {
@@ -257,8 +250,8 @@
 }
 
 func (s *singletonContextAdaptor) ModuleVariantsFromName(referer Module, name string) []Module {
-	// get qualified module name for visibility enforcement
-	qualified := createQualifiedModuleName(s.ModuleName(referer), s.ModuleDir(referer))
+	// get module reference for visibility enforcement
+	qualified := createVisibilityModuleReference(s.ModuleName(referer), s.ModuleDir(referer), s.ModuleType(referer))
 
 	modules := s.SingletonContext.ModuleVariantsFromName(referer, name)
 	result := make([]Module, 0, len(modules))
@@ -269,7 +262,7 @@
 			depDir := s.ModuleDir(module)
 			depQualified := qualifiedModuleName{depDir, depName}
 			// Targets are always visible to other targets in their own package.
-			if depQualified.pkg != qualified.pkg {
+			if depQualified.pkg != qualified.name.pkg {
 				rule := effectiveVisibilityRules(s.Config(), depQualified)
 				if !rule.matches(qualified) {
 					s.ModuleErrorf(referer, "module %q references %q which is not visible to this module\nYou may need to add %q to its visibility",
@@ -282,3 +275,7 @@
 	}
 	return result
 }
+
+func (s *singletonContextAdaptor) moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
+	return s.SingletonContext.ModuleProvider(module, provider)
+}
diff --git a/android/singleton_module_test.go b/android/singleton_module_test.go
index 9d98478..3b1bf39 100644
--- a/android/singleton_module_test.go
+++ b/android/singleton_module_test.go
@@ -103,6 +103,9 @@
 }
 
 func TestVariantSingletonModule(t *testing.T) {
+	if testing.Short() {
+		t.Skip("test fails with data race enabled")
+	}
 	bp := `
 		test_singleton_module {
 			name: "test_singleton_module",
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index ec0edfd..90b49eb 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -41,6 +41,7 @@
 	ctx.RegisterModuleType("soong_config_module_type", SoongConfigModuleTypeFactory)
 	ctx.RegisterModuleType("soong_config_string_variable", SoongConfigStringVariableDummyFactory)
 	ctx.RegisterModuleType("soong_config_bool_variable", SoongConfigBoolVariableDummyFactory)
+	ctx.RegisterModuleType("soong_config_value_variable", SoongConfigValueVariableDummyFactory)
 }
 
 var PrepareForTestWithSoongConfigModuleBuildComponents = FixtureRegisterWithContext(RegisterSoongConfigModuleBuildComponents)
@@ -303,6 +304,11 @@
 	properties soongconfig.VariableProperties
 }
 
+type soongConfigValueVariableDummyModule struct {
+	ModuleBase
+	properties soongconfig.VariableProperties
+}
+
 // soong_config_string_variable defines a variable and a set of possible string values for use
 // in a soong_config_module_type definition.
 func SoongConfigStringVariableDummyFactory() Module {
@@ -321,6 +327,15 @@
 	return module
 }
 
+// soong_config_value_variable defines a variable whose value can be expanded into
+// the value of a string property.
+func SoongConfigValueVariableDummyFactory() Module {
+	module := &soongConfigValueVariableDummyModule{}
+	module.AddProperties(&module.properties)
+	initAndroidModuleBase(module)
+	return module
+}
+
 func (m *soongConfigStringVariableDummyModule) Name() string {
 	return m.properties.Name + fmt.Sprintf("%p", m)
 }
@@ -333,6 +348,12 @@
 func (*soongConfigBoolVariableDummyModule) Namespaceless()                                {}
 func (*soongConfigBoolVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
 
+func (m *soongConfigValueVariableDummyModule) Name() string {
+	return m.properties.Name + fmt.Sprintf("%p", m)
+}
+func (*soongConfigValueVariableDummyModule) Namespaceless()                                {}
+func (*soongConfigValueVariableDummyModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
+
 // importModuleTypes registers the module factories for a list of module types defined
 // in an Android.bp file. These module factories are scoped for the current Android.bp
 // file only.
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index 79bdeb8..a6b2c51 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -376,8 +376,7 @@
 		prepareForSoongConfigTestModule,
 		FixtureWithRootAndroidBp(bp),
 	).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
-		// TODO(b/171232169): improve the error message for non-existent properties
-		`unrecognized property "soong_config_variables`,
+		`unrecognized property "soong_config_variables.feature1.made_up_property`,
 	})).RunTest(t)
 }
 
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 23c8afa..c78b726 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -20,12 +20,9 @@
 	"reflect"
 	"sort"
 	"strings"
-	"sync"
 
 	"github.com/google/blueprint/parser"
 	"github.com/google/blueprint/proptools"
-
-	"android/soong/starlark_fmt"
 )
 
 const conditionsDefault = "conditions_default"
@@ -236,110 +233,6 @@
 	variables map[string]soongConfigVariable
 }
 
-// Bp2BuildSoongConfigDefinition keeps a global record of all soong config
-// string vars, bool vars and value vars created by every
-// soong_config_module_type in this build.
-type Bp2BuildSoongConfigDefinitions struct {
-	StringVars map[string]map[string]bool
-	BoolVars   map[string]bool
-	ValueVars  map[string]bool
-}
-
-var bp2buildSoongConfigVarsLock sync.Mutex
-
-// SoongConfigVariablesForBp2build extracts information from a
-// SoongConfigDefinition that bp2build needs to generate constraint settings and
-// values for, in order to migrate soong_config_module_type usages to Bazel.
-func (defs *Bp2BuildSoongConfigDefinitions) AddVars(mtDef *SoongConfigDefinition) {
-	// In bp2build mode, this method is called concurrently in goroutines from
-	// loadhooks while parsing soong_config_module_type, so add a mutex to
-	// prevent concurrent map writes. See b/207572723
-	bp2buildSoongConfigVarsLock.Lock()
-	defer bp2buildSoongConfigVarsLock.Unlock()
-
-	if defs.StringVars == nil {
-		defs.StringVars = make(map[string]map[string]bool)
-	}
-	if defs.BoolVars == nil {
-		defs.BoolVars = make(map[string]bool)
-	}
-	if defs.ValueVars == nil {
-		defs.ValueVars = make(map[string]bool)
-	}
-	// varCache contains a cache of string variables namespace + property
-	// The same variable may be used in multiple module types (for example, if need support
-	// for cc_default and java_default), only need to process once
-	varCache := map[string]bool{}
-
-	for _, moduleType := range mtDef.ModuleTypes {
-		for _, v := range moduleType.Variables {
-			key := strings.Join([]string{moduleType.ConfigNamespace, v.variableProperty()}, "__")
-
-			// The same variable may be used in multiple module types (for example, if need support
-			// for cc_default and java_default), only need to process once
-			if _, keyInCache := varCache[key]; keyInCache {
-				continue
-			} else {
-				varCache[key] = true
-			}
-
-			if strVar, ok := v.(*stringVariable); ok {
-				if _, ok := defs.StringVars[key]; !ok {
-					defs.StringVars[key] = make(map[string]bool, len(strVar.values))
-				}
-				for _, value := range strVar.values {
-					defs.StringVars[key][value] = true
-				}
-			} else if _, ok := v.(*boolVariable); ok {
-				defs.BoolVars[key] = true
-			} else if _, ok := v.(*valueVariable); ok {
-				defs.ValueVars[key] = true
-			} else {
-				panic(fmt.Errorf("Unsupported variable type: %+v", v))
-			}
-		}
-	}
-}
-
-// This is a copy of the one available in soong/android/util.go, but depending
-// on the android package causes a cyclic dependency. A refactoring here is to
-// extract common utils out from android/utils.go for other packages like this.
-func sortedStringKeys(m interface{}) []string {
-	v := reflect.ValueOf(m)
-	if v.Kind() != reflect.Map {
-		panic(fmt.Sprintf("%#v is not a map", m))
-	}
-	keys := v.MapKeys()
-	s := make([]string, 0, len(keys))
-	for _, key := range keys {
-		s = append(s, key.String())
-	}
-	sort.Strings(s)
-	return s
-}
-
-// String emits the Soong config variable definitions as Starlark dictionaries.
-func (defs Bp2BuildSoongConfigDefinitions) String() string {
-	ret := ""
-	ret += "soong_config_bool_variables = "
-	ret += starlark_fmt.PrintBoolDict(defs.BoolVars, 0)
-	ret += "\n\n"
-
-	ret += "soong_config_value_variables = "
-	ret += starlark_fmt.PrintBoolDict(defs.ValueVars, 0)
-	ret += "\n\n"
-
-	stringVars := make(map[string][]string, len(defs.StringVars))
-	for k, v := range defs.StringVars {
-		stringVars[k] = sortedStringKeys(v)
-	}
-
-	ret += "soong_config_string_variables = "
-	ret += starlark_fmt.PrintStringListDict(stringVars, 0)
-
-	return ret
-}
-
 // CreateProperties returns a reflect.Value of a newly constructed type that contains the desired
 // property layout for the Soong config variables, with each possible value an interface{} that
 // contains a nil pointer to another newly constructed type that contains the affectable properties.
@@ -788,6 +681,14 @@
 	if !propStruct.IsValid() {
 		return nil, nil
 	}
+	if err := s.printfIntoPropertyRecursive(nil, propStruct, configValue); err != nil {
+		return nil, err
+	}
+
+	return values.Interface(), nil
+}
+
+func (s *valueVariable) printfIntoPropertyRecursive(fieldName []string, propStruct reflect.Value, configValue string) error {
 	for i := 0; i < propStruct.NumField(); i++ {
 		field := propStruct.Field(i)
 		kind := field.Kind()
@@ -796,28 +697,37 @@
 				continue
 			}
 			field = field.Elem()
+			kind = field.Kind()
 		}
 		switch kind {
 		case reflect.String:
 			err := printfIntoProperty(field, configValue)
 			if err != nil {
-				return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
+				fieldName = append(fieldName, propStruct.Type().Field(i).Name)
+				return fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, strings.Join(fieldName, "."), err)
 			}
 		case reflect.Slice:
 			for j := 0; j < field.Len(); j++ {
 				err := printfIntoProperty(field.Index(j), configValue)
 				if err != nil {
-					return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
+					fieldName = append(fieldName, propStruct.Type().Field(i).Name)
+					return fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, strings.Join(fieldName, "."), err)
 				}
 			}
 		case reflect.Bool:
 			// Nothing to do
+		case reflect.Struct:
+			fieldName = append(fieldName, propStruct.Type().Field(i).Name)
+			if err := s.printfIntoPropertyRecursive(fieldName, field, configValue); err != nil {
+				return err
+			}
+			fieldName = fieldName[:len(fieldName)-1]
 		default:
-			return nil, fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, propStruct.Type().Field(i).Name, kind)
+			fieldName = append(fieldName, propStruct.Type().Field(i).Name)
+			return fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, strings.Join(fieldName, "."), kind)
 		}
 	}
-
-	return values.Interface(), nil
+	return nil
 }
 
 func printfIntoProperty(propertyValue reflect.Value, configValue string) error {
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index a5fa349..1da0b49 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -299,7 +299,7 @@
 	Conditions_default *properties
 }
 
-type soongConfigVars struct {
+type boolSoongConfigVars struct {
 	Bool_var interface{}
 }
 
@@ -307,7 +307,11 @@
 	String_var interface{}
 }
 
-func Test_PropertiesToApply(t *testing.T) {
+type valueSoongConfigVars struct {
+	My_value_var interface{}
+}
+
+func Test_PropertiesToApply_Bool(t *testing.T) {
 	mt, _ := newModuleType(&ModuleTypeProperties{
 		Module_type:      "foo",
 		Config_namespace: "bar",
@@ -323,9 +327,9 @@
 		B: false,
 	}
 	actualProps := &struct {
-		Soong_config_variables soongConfigVars
+		Soong_config_variables boolSoongConfigVars
 	}{
-		Soong_config_variables: soongConfigVars{
+		Soong_config_variables: boolSoongConfigVars{
 			Bool_var: &boolVarProps{
 				A:                  boolVarPositive.A,
 				B:                  boolVarPositive.B,
@@ -369,6 +373,132 @@
 	}
 }
 
+func Test_PropertiesToApply_Value(t *testing.T) {
+	mt, _ := newModuleType(&ModuleTypeProperties{
+		Module_type:      "foo",
+		Config_namespace: "bar",
+		Value_variables:  []string{"my_value_var"},
+		Properties:       []string{"a", "b"},
+	})
+	conditionsDefault := &properties{
+		A: proptools.StringPtr("default"),
+		B: false,
+	}
+	actualProps := &struct {
+		Soong_config_variables valueSoongConfigVars
+	}{
+		Soong_config_variables: valueSoongConfigVars{
+			My_value_var: &boolVarProps{
+				A:                  proptools.StringPtr("A=%s"),
+				B:                  true,
+				Conditions_default: conditionsDefault,
+			},
+		},
+	}
+	props := reflect.ValueOf(actualProps)
+
+	testCases := []struct {
+		name      string
+		config    SoongConfig
+		wantProps []interface{}
+	}{
+		{
+			name:      "no_vendor_config",
+			config:    Config(map[string]string{}),
+			wantProps: []interface{}{conditionsDefault},
+		},
+		{
+			name:   "value_var_set",
+			config: Config(map[string]string{"my_value_var": "Hello"}),
+			wantProps: []interface{}{&properties{
+				A: proptools.StringPtr("A=Hello"),
+				B: true,
+			}},
+		},
+	}
+
+	for _, tc := range testCases {
+		gotProps, err := PropertiesToApply(mt, props, tc.config)
+		if err != nil {
+			t.Errorf("%s: Unexpected error in PropertiesToApply: %s", tc.name, err)
+		}
+
+		if !reflect.DeepEqual(gotProps, tc.wantProps) {
+			t.Errorf("%s: Expected %s, got %s", tc.name, tc.wantProps, gotProps)
+		}
+	}
+}
+
+func Test_PropertiesToApply_Value_Nested(t *testing.T) {
+	mt, _ := newModuleType(&ModuleTypeProperties{
+		Module_type:      "foo",
+		Config_namespace: "bar",
+		Value_variables:  []string{"my_value_var"},
+		Properties:       []string{"a.b"},
+	})
+	type properties struct {
+		A struct {
+			B string
+		}
+	}
+	conditionsDefault := &properties{
+		A: struct{ B string }{
+			B: "default",
+		},
+	}
+	type valueVarProps struct {
+		A struct {
+			B string
+		}
+		Conditions_default *properties
+	}
+	actualProps := &struct {
+		Soong_config_variables valueSoongConfigVars
+	}{
+		Soong_config_variables: valueSoongConfigVars{
+			My_value_var: &valueVarProps{
+				A: struct{ B string }{
+					B: "A.B=%s",
+				},
+				Conditions_default: conditionsDefault,
+			},
+		},
+	}
+	props := reflect.ValueOf(actualProps)
+
+	testCases := []struct {
+		name      string
+		config    SoongConfig
+		wantProps []interface{}
+	}{
+		{
+			name:      "no_vendor_config",
+			config:    Config(map[string]string{}),
+			wantProps: []interface{}{conditionsDefault},
+		},
+		{
+			name:   "value_var_set",
+			config: Config(map[string]string{"my_value_var": "Hello"}),
+			wantProps: []interface{}{&properties{
+				A: struct{ B string }{
+					B: "A.B=Hello",
+				},
+			}},
+		},
+	}
+
+	for _, tc := range testCases {
+		gotProps, err := PropertiesToApply(mt, props, tc.config)
+		if err != nil {
+			t.Errorf("%s: Unexpected error in PropertiesToApply: %s", tc.name, err)
+		}
+
+		if !reflect.DeepEqual(gotProps, tc.wantProps) {
+			t.Errorf("%s: Expected %s, got %s", tc.name, tc.wantProps, gotProps)
+		}
+	}
+}
+
 func Test_PropertiesToApply_String_Error(t *testing.T) {
 	mt, _ := newModuleType(&ModuleTypeProperties{
 		Module_type:      "foo",
@@ -413,220 +543,3 @@
 		t.Fatalf("Error message was not correct, expected %q, got %q", expected, err.Error())
 	}
 }
-
-func Test_Bp2BuildSoongConfigDefinitionsAddVars(t *testing.T) {
-	testCases := []struct {
-		desc     string
-		defs     []*SoongConfigDefinition
-		expected Bp2BuildSoongConfigDefinitions
-	}{
-		{
-			desc: "non-overlapping",
-			defs: []*SoongConfigDefinition{
-				&SoongConfigDefinition{
-					ModuleTypes: map[string]*ModuleType{
-						"a": &ModuleType{
-							ConfigNamespace: "foo",
-							Variables: []soongConfigVariable{
-								&stringVariable{
-									baseVariable: baseVariable{"string_var"},
-									values:       []string{"a", "b", "c"},
-								},
-							},
-						},
-					},
-				},
-				&SoongConfigDefinition{
-					ModuleTypes: map[string]*ModuleType{
-						"b": &ModuleType{
-							ConfigNamespace: "foo",
-							Variables: []soongConfigVariable{
-								&stringVariable{
-									baseVariable: baseVariable{"string_var"},
-									values:       []string{"a", "b", "c"},
-								},
-								&boolVariable{baseVariable: baseVariable{"bool_var"}},
-								&valueVariable{baseVariable: baseVariable{"variable_var"}},
-							},
-						},
-					},
-				},
-			},
-			expected: Bp2BuildSoongConfigDefinitions{
-				StringVars: map[string]map[string]bool{
-					"foo__string_var": map[string]bool{"a": true, "b": true, "c": true},
-				},
-				BoolVars:  map[string]bool{"foo__bool_var": true},
-				ValueVars: map[string]bool{"foo__variable_var": true},
-			},
-		},
-		{
-			desc: "overlapping",
-			defs: []*SoongConfigDefinition{
-				&SoongConfigDefinition{
-					ModuleTypes: map[string]*ModuleType{
-						"a": &ModuleType{
-							ConfigNamespace: "foo",
-							Variables: []soongConfigVariable{
-								&stringVariable{
-									baseVariable: baseVariable{"string_var"},
-									values:       []string{"a", "b", "c"},
-								},
-							},
-						},
-					},
-				},
-				&SoongConfigDefinition{
-					ModuleTypes: map[string]*ModuleType{
-						"b": &ModuleType{
-							ConfigNamespace: "foo",
-							Variables: []soongConfigVariable{
-								&stringVariable{
-									baseVariable: baseVariable{"string_var"},
-									values:       []string{"b", "c", "d"},
-								},
-								&boolVariable{baseVariable: baseVariable{"bool_var"}},
-								&valueVariable{baseVariable: baseVariable{"variable_var"}},
-							},
-						},
-					},
-				},
-			},
-			expected: Bp2BuildSoongConfigDefinitions{
-				StringVars: map[string]map[string]bool{
-					"foo__string_var": map[string]bool{"a": true, "b": true, "c": true, "d": true},
-				},
-				BoolVars:  map[string]bool{"foo__bool_var": true},
-				ValueVars: map[string]bool{"foo__variable_var": true},
-			},
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.desc, func(t *testing.T) {
-			actual := &Bp2BuildSoongConfigDefinitions{}
-			for _, d := range tc.defs {
-				func(def *SoongConfigDefinition) {
-					actual.AddVars(def)
-				}(d)
-			}
-			if !reflect.DeepEqual(*actual, tc.expected) {
-				t.Errorf("Expected %#v, got %#v", tc.expected, *actual)
-			}
-		})
-	}
-
-}
-
-func Test_Bp2BuildSoongConfigDefinitions(t *testing.T) {
-	testCases := []struct {
-		desc     string
-		defs     Bp2BuildSoongConfigDefinitions
-		expected string
-	}{
-		{
-			desc: "all empty",
-			defs: Bp2BuildSoongConfigDefinitions{},
-			expected: `soong_config_bool_variables = {}
-
-soong_config_value_variables = {}
-
-soong_config_string_variables = {}`}, {
-			desc: "only bool",
-			defs: Bp2BuildSoongConfigDefinitions{
-				BoolVars: map[string]bool{
-					"bool_var": true,
-				},
-			},
-			expected: `soong_config_bool_variables = {
-    "bool_var": True,
-}
-
-soong_config_value_variables = {}
-
-soong_config_string_variables = {}`}, {
-			desc: "only value vars",
-			defs: Bp2BuildSoongConfigDefinitions{
-				ValueVars: map[string]bool{
-					"value_var": true,
-				},
-			},
-			expected: `soong_config_bool_variables = {}
-
-soong_config_value_variables = {
-    "value_var": True,
-}
-
-soong_config_string_variables = {}`}, {
-			desc: "only string vars",
-			defs: Bp2BuildSoongConfigDefinitions{
-				StringVars: map[string]map[string]bool{
-					"string_var": map[string]bool{
-						"choice1": true,
-						"choice2": true,
-						"choice3": true,
-					},
-				},
-			},
-			expected: `soong_config_bool_variables = {}
-
-soong_config_value_variables = {}
-
-soong_config_string_variables = {
-    "string_var": [
-        "choice1",
-        "choice2",
-        "choice3",
-    ],
-}`}, {
-			desc: "all vars",
-			defs: Bp2BuildSoongConfigDefinitions{
-				BoolVars: map[string]bool{
-					"bool_var_one": true,
-				},
-				ValueVars: map[string]bool{
-					"value_var_one": true,
-					"value_var_two": true,
-				},
-				StringVars: map[string]map[string]bool{
-					"string_var_one": map[string]bool{
-						"choice1": true,
-						"choice2": true,
-						"choice3": true,
-					},
-					"string_var_two": map[string]bool{
-						"foo": true,
-						"bar": true,
-					},
-				},
-			},
-			expected: `soong_config_bool_variables = {
-    "bool_var_one": True,
-}
-
-soong_config_value_variables = {
-    "value_var_one": True,
-    "value_var_two": True,
-}
-
-soong_config_string_variables = {
-    "string_var_one": [
-        "choice1",
-        "choice2",
-        "choice3",
-    ],
-    "string_var_two": [
-        "bar",
-        "foo",
-    ],
-}`},
-	}
-	for _, test := range testCases {
-		t.Run(test.desc, func(t *testing.T) {
-			actual := test.defs.String()
-			if actual != test.expected {
-				t.Errorf("Expected:\n%s\nbut got:\n%s", test.expected, actual)
-			}
-		})
-	}
-}
diff --git a/android/team.go b/android/team.go
new file mode 100644
index 0000000..df61f40
--- /dev/null
+++ b/android/team.go
@@ -0,0 +1,58 @@
+// Copyright 2020 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() {
+	RegisterTeamBuildComponents(InitRegistrationContext)
+}
+
+func RegisterTeamBuildComponents(ctx RegistrationContext) {
+	ctx.RegisterModuleType("team", TeamFactory)
+}
+
+var PrepareForTestWithTeamBuildComponents = GroupFixturePreparers(
+	FixtureRegisterWithContext(RegisterTeamBuildComponents),
+)
+
+type teamProperties struct {
+	Trendy_team_id *string `json:"trendy_team_id"`
+}
+
+type teamModule struct {
+	ModuleBase
+	DefaultableModuleBase
+
+	properties teamProperties
+}
+
+// Real work is done for the module that depends on us.
+// If needed, the team can serialize the config to json/proto file as well.
+func (t *teamModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
+
+func (t *teamModule) TrendyTeamId(ctx ModuleContext) string {
+	return *t.properties.Trendy_team_id
+}
+
+func TeamFactory() Module {
+	module := &teamModule{}
+
+	base := module.base()
+	module.AddProperties(&base.nameProperties, &module.properties)
+
+	InitAndroidModule(module)
+	InitDefaultableModule(module)
+
+	return module
+}
diff --git a/android/team_proto/Android.bp b/android/team_proto/Android.bp
new file mode 100644
index 0000000..7e2a4c1
--- /dev/null
+++ b/android/team_proto/Android.bp
@@ -0,0 +1,43 @@
+// Copyright 2022 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-android_team_proto",
+    pkgPath: "android/soong/android/team_proto",
+    deps: [
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+    ],
+    srcs: [
+        "team.pb.go",
+    ],
+}
+
+python_library_host {
+    name: "teams-proto-py",
+    pkg_path: "teams",
+    srcs: [
+        "team.proto",
+    ],
+    libs: [
+        "libprotobuf-python",
+    ],
+    proto: {
+        canonical_path_from_root: false,
+    },
+}
diff --git a/android/team_proto/OWNERS b/android/team_proto/OWNERS
new file mode 100644
index 0000000..2beb4f4
--- /dev/null
+++ b/android/team_proto/OWNERS
@@ -0,0 +1,5 @@
+dariofreni@google.com
+joeo@google.com
+ronish@google.com
+caditya@google.com
+rbraunstein@google.com
diff --git a/android/team_proto/regen.sh b/android/team_proto/regen.sh
new file mode 100755
index 0000000..63b2016
--- /dev/null
+++ b/android/team_proto/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. team.proto
diff --git a/android/team_proto/team.pb.go b/android/team_proto/team.pb.go
new file mode 100644
index 0000000..61260cf
--- /dev/null
+++ b/android/team_proto/team.pb.go
@@ -0,0 +1,253 @@
+// 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.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.30.0
+// 	protoc        v3.21.12
+// source: team.proto
+
+package team_proto
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type Team struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// REQUIRED: Name of the build target
+	TargetName *string `protobuf:"bytes,1,opt,name=target_name,json=targetName" json:"target_name,omitempty"`
+	// REQUIRED: Code location of the target.
+	// To be used to support legacy/backup systems that use OWNERS file and is
+	// also required for our dashboard to support per code location basis UI
+	Path *string `protobuf:"bytes,2,opt,name=path" json:"path,omitempty"`
+	// REQUIRED: Team ID of the team that owns this target.
+	TrendyTeamId *string `protobuf:"bytes,3,opt,name=trendy_team_id,json=trendyTeamId" json:"trendy_team_id,omitempty"`
+	// OPTIONAL: Files directly owned by this module.
+	File []string `protobuf:"bytes,4,rep,name=file" json:"file,omitempty"`
+}
+
+func (x *Team) Reset() {
+	*x = Team{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_team_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Team) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Team) ProtoMessage() {}
+
+func (x *Team) ProtoReflect() protoreflect.Message {
+	mi := &file_team_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Team.ProtoReflect.Descriptor instead.
+func (*Team) Descriptor() ([]byte, []int) {
+	return file_team_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Team) GetTargetName() string {
+	if x != nil && x.TargetName != nil {
+		return *x.TargetName
+	}
+	return ""
+}
+
+func (x *Team) GetPath() string {
+	if x != nil && x.Path != nil {
+		return *x.Path
+	}
+	return ""
+}
+
+func (x *Team) GetTrendyTeamId() string {
+	if x != nil && x.TrendyTeamId != nil {
+		return *x.TrendyTeamId
+	}
+	return ""
+}
+
+func (x *Team) GetFile() []string {
+	if x != nil {
+		return x.File
+	}
+	return nil
+}
+
+type AllTeams struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Teams []*Team `protobuf:"bytes,1,rep,name=teams" json:"teams,omitempty"`
+}
+
+func (x *AllTeams) Reset() {
+	*x = AllTeams{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_team_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *AllTeams) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AllTeams) ProtoMessage() {}
+
+func (x *AllTeams) ProtoReflect() protoreflect.Message {
+	mi := &file_team_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use AllTeams.ProtoReflect.Descriptor instead.
+func (*AllTeams) Descriptor() ([]byte, []int) {
+	return file_team_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *AllTeams) GetTeams() []*Team {
+	if x != nil {
+		return x.Teams
+	}
+	return nil
+}
+
+var File_team_proto protoreflect.FileDescriptor
+
+var file_team_proto_rawDesc = []byte{
+	0x0a, 0x0a, 0x74, 0x65, 0x61, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x74, 0x65,
+	0x61, 0x6d, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x75, 0x0a, 0x04, 0x54, 0x65, 0x61, 0x6d,
+	0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d,
+	0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x79, 0x5f,
+	0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74,
+	0x72, 0x65, 0x6e, 0x64, 0x79, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66,
+	0x69, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x22,
+	0x32, 0x0a, 0x08, 0x41, 0x6c, 0x6c, 0x54, 0x65, 0x61, 0x6d, 0x73, 0x12, 0x26, 0x0a, 0x05, 0x74,
+	0x65, 0x61, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x61,
+	0x6d, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x05, 0x74, 0x65,
+	0x61, 0x6d, 0x73, 0x42, 0x22, 0x5a, 0x20, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73,
+	0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x74, 0x65, 0x61,
+	0x6d, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+}
+
+var (
+	file_team_proto_rawDescOnce sync.Once
+	file_team_proto_rawDescData = file_team_proto_rawDesc
+)
+
+func file_team_proto_rawDescGZIP() []byte {
+	file_team_proto_rawDescOnce.Do(func() {
+		file_team_proto_rawDescData = protoimpl.X.CompressGZIP(file_team_proto_rawDescData)
+	})
+	return file_team_proto_rawDescData
+}
+
+var file_team_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_team_proto_goTypes = []interface{}{
+	(*Team)(nil),     // 0: team_proto.Team
+	(*AllTeams)(nil), // 1: team_proto.AllTeams
+}
+var file_team_proto_depIdxs = []int32{
+	0, // 0: team_proto.AllTeams.teams:type_name -> team_proto.Team
+	1, // [1:1] is the sub-list for method output_type
+	1, // [1:1] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_team_proto_init() }
+func file_team_proto_init() {
+	if File_team_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_team_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Team); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_team_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*AllTeams); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_team_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_team_proto_goTypes,
+		DependencyIndexes: file_team_proto_depIdxs,
+		MessageInfos:      file_team_proto_msgTypes,
+	}.Build()
+	File_team_proto = out.File
+	file_team_proto_rawDesc = nil
+	file_team_proto_goTypes = nil
+	file_team_proto_depIdxs = nil
+}
diff --git a/android/team_proto/team.proto b/android/team_proto/team.proto
new file mode 100644
index 0000000..401eccc
--- /dev/null
+++ b/android/team_proto/team.proto
@@ -0,0 +1,34 @@
+// 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.
+
+syntax = "proto2";
+package team_proto;
+option go_package = "android/soong/android/team_proto";
+
+message Team {
+  // REQUIRED: Name of the build target
+  optional string target_name = 1;
+
+  // REQUIRED: Code location of the target.
+  // To be used to support legacy/backup systems that use OWNERS file and is
+  // also required for our dashboard to support per code location basis UI
+  optional string path = 2;
+
+  // REQUIRED: Team ID of the team that owns this target.
+  optional string trendy_team_id = 3;
+
+  // OPTIONAL: Files directly owned by this module.
+  repeated string file = 4;
+}
+
+message AllTeams {
+  repeated Team teams = 1;
+}
diff --git a/android/team_test.go b/android/team_test.go
new file mode 100644
index 0000000..75b3e9f
--- /dev/null
+++ b/android/team_test.go
@@ -0,0 +1,99 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package android
+
+import (
+	"testing"
+)
+
+type fakeModuleForTests struct {
+	ModuleBase
+}
+
+func fakeModuleFactory() Module {
+	module := &fakeModuleForTests{}
+	InitAndroidModule(module)
+	return module
+}
+
+func (*fakeModuleForTests) GenerateAndroidBuildActions(ModuleContext) {}
+
+func TestTeam(t *testing.T) {
+	t.Parallel()
+	ctx := GroupFixturePreparers(
+		PrepareForTestWithTeamBuildComponents,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterModuleType("fake", fakeModuleFactory)
+		}),
+	).RunTestWithBp(t, `
+		fake {
+			name: "main_test",
+			team: "someteam",
+		}
+		team {
+			name: "someteam",
+			trendy_team_id: "cool_team",
+		}
+
+		team {
+			name: "team2",
+			trendy_team_id: "22222",
+		}
+
+		fake {
+			name: "tool",
+			team: "team2",
+		}
+	`)
+
+	// Assert the rule from GenerateAndroidBuildActions exists.
+	m := ctx.ModuleForTests("main_test", "")
+	AssertStringEquals(t, "msg", m.Module().base().Team(), "someteam")
+	m = ctx.ModuleForTests("tool", "")
+	AssertStringEquals(t, "msg", m.Module().base().Team(), "team2")
+}
+
+func TestMissingTeamFails(t *testing.T) {
+	t.Parallel()
+	GroupFixturePreparers(
+		PrepareForTestWithTeamBuildComponents,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterModuleType("fake", fakeModuleFactory)
+		}),
+	).
+		ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern("depends on undefined module \"ring-bearer")).
+		RunTestWithBp(t, `
+		fake {
+			name: "you_cannot_pass",
+			team: "ring-bearer",
+		}
+	`)
+}
+
+func TestPackageBadTeamNameFails(t *testing.T) {
+	t.Parallel()
+	GroupFixturePreparers(
+		PrepareForTestWithTeamBuildComponents,
+		PrepareForTestWithPackageModule,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterModuleType("fake", fakeModuleFactory)
+		}),
+	).
+		ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern("depends on undefined module \"ring-bearer")).
+		RunTestWithBp(t, `
+		package {
+			default_team: "ring-bearer",
+		}
+	`)
+}
diff --git a/android/test_config.go b/android/test_config.go
index 9e1ac70..a15343a 100644
--- a/android/test_config.go
+++ b/android/test_config.go
@@ -39,11 +39,12 @@
 			DeviceName:                          stringPtr("test_device"),
 			DeviceProduct:                       stringPtr("test_product"),
 			Platform_sdk_version:                intPtr(30),
+			Platform_sdk_version_or_codename:    stringPtr("S"),
 			Platform_sdk_codename:               stringPtr("S"),
 			Platform_base_sdk_extension_version: intPtr(1),
 			Platform_version_active_codenames:   []string{"S", "Tiramisu"},
-			DeviceSystemSdkVersions:             []string{"14", "15"},
-			Platform_systemsdk_versions:         []string{"29", "30"},
+			DeviceSystemSdkVersions:             []string{"29", "30", "S"},
+			Platform_systemsdk_versions:         []string{"29", "30", "S", "Tiramisu"},
 			AAPTConfig:                          []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
 			AAPTPreferredConfig:                 stringPtr("xhdpi"),
 			AAPTCharacteristics:                 stringPtr("nosdcard"),
diff --git a/android/testing.go b/android/testing.go
index fa4dffd..7b4411e 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -183,7 +183,6 @@
 type TestContext struct {
 	*Context
 	preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc
-	bp2buildPreArch, bp2buildMutators     []RegisterMutatorFunc
 	NameResolver                          *NameResolver
 
 	// The list of singletons registered for the test.
@@ -203,7 +202,7 @@
 	ctx.PreArchMutators(f)
 }
 
-func (ctx *TestContext) ModuleProvider(m blueprint.Module, p blueprint.ProviderKey) interface{} {
+func (ctx *TestContext) moduleProvider(m blueprint.Module, p blueprint.AnyProviderKey) (any, bool) {
 	return ctx.Context.ModuleProvider(m, p)
 }
 
@@ -219,10 +218,10 @@
 	ctx.finalDeps = append(ctx.finalDeps, f)
 }
 
-// PreArchBp2BuildMutators adds mutators to be register for converting Android Blueprint modules
-// into Bazel BUILD targets that should run prior to deps and conversion.
-func (ctx *TestContext) PreArchBp2BuildMutators(f RegisterMutatorFunc) {
-	ctx.bp2buildPreArch = append(ctx.bp2buildPreArch, f)
+func (ctx *TestContext) OtherModuleProviderAdaptor() OtherModuleProviderContext {
+	return NewOtherModuleProviderAdaptor(func(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
+		return ctx.moduleProvider(module, provider)
+	})
 }
 
 // registeredComponentOrder defines the order in which a sortableComponent type is registered at
@@ -726,7 +725,6 @@
 //   - Depfile
 //   - Rspfile
 //   - RspfileContent
-//   - SymlinkOutputs
 //   - CommandDeps
 //   - CommandOrderOnly
 //
@@ -748,8 +746,6 @@
 	bparams.Depfile = normalizeWritablePathRelativeToTop(bparams.Depfile)
 	bparams.Output = normalizeWritablePathRelativeToTop(bparams.Output)
 	bparams.Outputs = bparams.Outputs.RelativeToTop()
-	bparams.SymlinkOutput = normalizeWritablePathRelativeToTop(bparams.SymlinkOutput)
-	bparams.SymlinkOutputs = bparams.SymlinkOutputs.RelativeToTop()
 	bparams.ImplicitOutput = normalizeWritablePathRelativeToTop(bparams.ImplicitOutput)
 	bparams.ImplicitOutputs = bparams.ImplicitOutputs.RelativeToTop()
 	bparams.Input = normalizePathRelativeToTop(bparams.Input)
@@ -767,7 +763,6 @@
 	rparams.Depfile = normalizeStringRelativeToTop(p.config, rparams.Depfile)
 	rparams.Rspfile = normalizeStringRelativeToTop(p.config, rparams.Rspfile)
 	rparams.RspfileContent = normalizeStringRelativeToTop(p.config, rparams.RspfileContent)
-	rparams.SymlinkOutputs = normalizeStringArrayRelativeToTop(p.config, rparams.SymlinkOutputs)
 	rparams.CommandDeps = normalizeStringArrayRelativeToTop(p.config, rparams.CommandDeps)
 	rparams.CommandOrderOnly = normalizeStringArrayRelativeToTop(p.config, rparams.CommandOrderOnly)
 
@@ -1126,6 +1121,7 @@
 	}
 
 	entriesList := p.AndroidMkEntries()
+	aconfigUpdateAndroidMkEntries(ctx, mod.(Module), &entriesList)
 	for i, _ := range entriesList {
 		entriesList[i].fillInEntries(ctx, mod)
 	}
@@ -1141,6 +1137,7 @@
 	}
 	data := p.AndroidMk()
 	data.fillInData(ctx, mod)
+	aconfigUpdateAndroidMkData(ctx, mod.(Module), &data)
 	return data
 }
 
diff --git a/android/updatable_modules.go b/android/updatable_modules.go
index 1548170..dd7dc2c 100644
--- a/android/updatable_modules.go
+++ b/android/updatable_modules.go
@@ -33,4 +33,4 @@
 // * AOSP            - xx9990000
 // * x-mainline-prod - xx9990000
 // * master          - 990090000
-const DefaultUpdatableModuleVersion = "990090000"
+const DefaultUpdatableModuleVersion = "350090000"
diff --git a/android/util.go b/android/util.go
index ae1c657..363b31c 100644
--- a/android/util.go
+++ b/android/util.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"cmp"
 	"fmt"
 	"path/filepath"
 	"reflect"
@@ -22,6 +23,7 @@
 	"runtime"
 	"sort"
 	"strings"
+	"sync"
 )
 
 // CopyOf returns a new slice that has the same contents as s.
@@ -105,15 +107,8 @@
 	return SortedKeys(m)
 }
 
-type Ordered interface {
-	~string |
-		~float32 | ~float64 |
-		~int | ~int8 | ~int16 | ~int32 | ~int64 |
-		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
-}
-
 // SortedKeys returns the keys of the given map in the ascending order.
-func SortedKeys[T Ordered, V any](m map[T]V) []T {
+func SortedKeys[T cmp.Ordered, V any](m map[T]V) []T {
 	if len(m) == 0 {
 		return nil
 	}
@@ -597,3 +592,32 @@
 		set[item] = true
 	}
 }
+
+// SyncMap is a wrapper around sync.Map that provides type safety via generics.
+type SyncMap[K comparable, V any] struct {
+	sync.Map
+}
+
+// Load returns the value stored in the map for a key, or the zero value if no
+// value is present.
+// The ok result indicates whether value was found in the map.
+func (m *SyncMap[K, V]) Load(key K) (value V, ok bool) {
+	v, ok := m.Map.Load(key)
+	if !ok {
+		return *new(V), false
+	}
+	return v.(V), true
+}
+
+// Store sets the value for a key.
+func (m *SyncMap[K, V]) Store(key K, value V) {
+	m.Map.Store(key, value)
+}
+
+// LoadOrStore returns the existing value for the key if present.
+// Otherwise, it stores and returns the given value.
+// The loaded result is true if the value was loaded, false if stored.
+func (m *SyncMap[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
+	v, loaded := m.Map.LoadOrStore(key, value)
+	return v.(V), loaded
+}
diff --git a/android/util_test.go b/android/util_test.go
index 699135b..8e73d83 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"cmp"
 	"fmt"
 	"reflect"
 	"strconv"
@@ -650,7 +651,7 @@
 	}
 }
 
-func testSortedKeysHelper[K Ordered, V any](t *testing.T, name string, input map[K]V, expected []K) {
+func testSortedKeysHelper[K cmp.Ordered, V any](t *testing.T, name string, input map[K]V, expected []K) {
 	t.Helper()
 	t.Run(name, func(t *testing.T) {
 		actual := SortedKeys(input)
diff --git a/android/variable.go b/android/variable.go
index 3228daf..47c6029 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -73,6 +73,7 @@
 			Cflags              []string `android:"arch_variant"`
 			Shared_libs         []string `android:"arch_variant"`
 			Whole_static_libs   []string `android:"arch_variant"`
+			Static_libs         []string `android:"arch_variant"`
 			Exclude_static_libs []string `android:"arch_variant"`
 			Srcs                []string `android:"arch_variant"`
 			Header_libs         []string `android:"arch_variant"`
@@ -181,8 +182,9 @@
 		// release_aidl_use_unfrozen is "true" when a device can
 		// use the unfrozen versions of AIDL interfaces.
 		Release_aidl_use_unfrozen struct {
-			Cflags []string
-			Cmd    *string
+			Cflags          []string
+			Cmd             *string
+			Vintf_fragments []string
 		}
 	} `android:"arch_variant"`
 }
@@ -385,7 +387,6 @@
 
 	BoardSepolicyVers       *string `json:",omitempty"`
 	PlatformSepolicyVersion *string `json:",omitempty"`
-	TotSepolicyVersion      *string `json:",omitempty"`
 
 	SystemExtSepolicyPrebuiltApiDir *string `json:",omitempty"`
 	ProductSepolicyPrebuiltApiDir   *string `json:",omitempty"`
@@ -451,6 +452,7 @@
 	BuildBrokenVendorPropertyNamespace  bool     `json:",omitempty"`
 	BuildBrokenIncorrectPartitionImages bool     `json:",omitempty"`
 	BuildBrokenInputDirModules          []string `json:",omitempty"`
+	BuildBrokenDontCheckSystemSdk       bool     `json:",omitempty"`
 
 	BuildWarningBadOptionalUsesLibsAllowlist []string `json:",omitempty"`
 
@@ -493,13 +495,15 @@
 	// partition images. They should not be read from soong modules.
 	PartitionVarsForBazelMigrationOnlyDoNotUse PartitionVariables `json:",omitempty"`
 
-	NextReleaseHideFlaggedApi *bool `json:",omitempty"`
-
-	Release_expose_flagged_api *bool `json:",omitempty"`
-
 	BuildFlags map[string]string `json:",omitempty"`
 
 	BuildFromSourceStub *bool `json:",omitempty"`
+
+	BuildIgnoreApexContributionContents []string `json:",omitempty"`
+
+	HiddenapiExportableStubs *bool `json:",omitempty"`
+
+	ExportRuntimeApis *bool `json:",omitempty"`
 }
 
 type PartitionQualifiedVariablesType struct {
diff --git a/android/visibility.go b/android/visibility.go
index 3130135..b387562 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -57,12 +57,29 @@
 
 var visibilityRuleRegexp = regexp.MustCompile(visibilityRulePattern)
 
+type visibilityModuleReference struct {
+	name              qualifiedModuleName
+	isPartitionModule bool
+}
+
+func createVisibilityModuleReference(name, dir, typ string) visibilityModuleReference {
+	isPartitionModule := false
+	switch typ {
+	case "android_filesystem", "android_system_image":
+		isPartitionModule = true
+	}
+	return visibilityModuleReference{
+		name:              createQualifiedModuleName(name, dir),
+		isPartitionModule: isPartitionModule,
+	}
+}
+
 // A visibility rule is associated with a module and determines which other modules it is visible
 // to, i.e. which other modules can depend on the rule's module.
 type visibilityRule interface {
 	// Check to see whether this rules matches m.
 	// Returns true if it does, false otherwise.
-	matches(m qualifiedModuleName) bool
+	matches(m visibilityModuleReference) bool
 
 	String() string
 }
@@ -108,8 +125,10 @@
 // ["//visibility:private"].
 type compositeRule []visibilityRule
 
+var _ visibilityRule = compositeRule{}
+
 // A compositeRule matches if and only if any of its rules matches.
-func (c compositeRule) matches(m qualifiedModuleName) bool {
+func (c compositeRule) matches(m visibilityModuleReference) bool {
 	for _, r := range c {
 		if r.matches(m) {
 			return true
@@ -135,8 +154,10 @@
 	pkg string
 }
 
-func (r packageRule) matches(m qualifiedModuleName) bool {
-	return m.pkg == r.pkg
+var _ visibilityRule = packageRule{}
+
+func (r packageRule) matches(m visibilityModuleReference) bool {
+	return m.name.pkg == r.pkg
 }
 
 func (r packageRule) String() string {
@@ -149,8 +170,10 @@
 	pkgPrefix string
 }
 
-func (r subpackagesRule) matches(m qualifiedModuleName) bool {
-	return isAncestor(r.pkgPrefix, m.pkg)
+var _ visibilityRule = subpackagesRule{}
+
+func (r subpackagesRule) matches(m visibilityModuleReference) bool {
+	return isAncestor(r.pkgPrefix, m.name.pkg)
 }
 
 func isAncestor(p1 string, p2 string) bool {
@@ -168,7 +191,9 @@
 // visibilityRule for //visibility:public
 type publicRule struct{}
 
-func (r publicRule) matches(_ qualifiedModuleName) bool {
+var _ visibilityRule = publicRule{}
+
+func (r publicRule) matches(_ visibilityModuleReference) bool {
 	return true
 }
 
@@ -179,7 +204,9 @@
 // visibilityRule for //visibility:private
 type privateRule struct{}
 
-func (r privateRule) matches(_ qualifiedModuleName) bool {
+var _ visibilityRule = privateRule{}
+
+func (r privateRule) matches(_ visibilityModuleReference) bool {
 	return false
 }
 
@@ -187,6 +214,19 @@
 	return "//visibility:private"
 }
 
+// visibilityRule for //visibility:any_partition
+type anyPartitionRule struct{}
+
+var _ visibilityRule = anyPartitionRule{}
+
+func (r anyPartitionRule) matches(m visibilityModuleReference) bool {
+	return m.isPartitionModule
+}
+
+func (r anyPartitionRule) String() string {
+	return "//visibility:any_partition"
+}
+
 var visibilityRuleMap = NewOnceKey("visibilityRuleMap")
 
 // The map from qualifiedModuleName to visibilityRule.
@@ -237,13 +277,10 @@
 
 // Checks the per-module visibility rule lists before defaults expansion.
 func visibilityRuleChecker(ctx BottomUpMutatorContext) {
-	qualified := createQualifiedModuleName(ctx.ModuleName(), ctx.ModuleDir())
-	if m, ok := ctx.Module().(Module); ok {
-		visibilityProperties := m.visibilityProperties()
-		for _, p := range visibilityProperties {
-			if visibility := p.getStrings(); visibility != nil {
-				checkRules(ctx, qualified.pkg, p.getName(), visibility)
-			}
+	visibilityProperties := ctx.Module().visibilityProperties()
+	for _, p := range visibilityProperties {
+		if visibility := p.getStrings(); visibility != nil {
+			checkRules(ctx, ctx.ModuleDir(), p.getName(), visibility)
 		}
 	}
 }
@@ -266,7 +303,7 @@
 
 		if pkg == "visibility" {
 			switch name {
-			case "private", "public":
+			case "private", "public", "any_partition":
 			case "legacy_public":
 				ctx.PropertyErrorf(property, "//visibility:legacy_public must not be used")
 				continue
@@ -305,10 +342,7 @@
 //
 // See ../README.md#Visibility for information on the format of the visibility rules.
 func visibilityRuleGatherer(ctx BottomUpMutatorContext) {
-	m, ok := ctx.Module().(Module)
-	if !ok {
-		return
-	}
+	m := ctx.Module()
 
 	qualifiedModuleId := m.qualifiedModuleId(ctx)
 	currentPkg := qualifiedModuleId.pkg
@@ -355,6 +389,8 @@
 				hasNonPrivateRule = false
 				// This does not actually create a rule so continue onto the next rule.
 				continue
+			case "any_partition":
+				r = anyPartitionRule{}
 			}
 		} else {
 			switch name {
@@ -395,10 +431,7 @@
 
 func isAllowedFromOutsideVendor(pkg string, name string) bool {
 	if pkg == "vendor" {
-		if name == "__subpackages__" {
-			return true
-		}
-		return false
+		return name == "__subpackages__"
 	}
 
 	return !isAncestor("vendor", pkg)
@@ -434,11 +467,7 @@
 }
 
 func visibilityRuleEnforcer(ctx TopDownMutatorContext) {
-	if _, ok := ctx.Module().(Module); !ok {
-		return
-	}
-
-	qualified := createQualifiedModuleName(ctx.ModuleName(), ctx.ModuleDir())
+	qualified := createVisibilityModuleReference(ctx.ModuleName(), ctx.ModuleDir(), ctx.ModuleType())
 
 	// Visit all the dependencies making sure that this module has access to them all.
 	ctx.VisitDirectDeps(func(dep Module) {
@@ -453,7 +482,7 @@
 		depQualified := qualifiedModuleName{depDir, depName}
 
 		// Targets are always visible to other targets in their own package.
-		if depQualified.pkg == qualified.pkg {
+		if depQualified.pkg == qualified.name.pkg {
 			return
 		}
 
@@ -478,7 +507,7 @@
 	if ok {
 		rule = value.(compositeRule)
 	} else {
-		rule = packageDefaultVisibility(config, qualified)
+		rule = packageDefaultVisibility(moduleToVisibilityRule, qualified)
 	}
 
 	// If no rule is specified then return the default visibility rule to avoid
@@ -494,8 +523,7 @@
 	return qualified
 }
 
-func packageDefaultVisibility(config Config, moduleId qualifiedModuleName) compositeRule {
-	moduleToVisibilityRule := moduleToVisibilityRuleMap(config)
+func packageDefaultVisibility(moduleToVisibilityRule *sync.Map, moduleId qualifiedModuleName) compositeRule {
 	packageQualifiedId := moduleId.getContainingPackageId()
 	for {
 		value, ok := moduleToVisibilityRule.Load(packageQualifiedId)
@@ -574,10 +602,12 @@
 
 	rule := effectiveVisibilityRules(ctx.Config(), qualified)
 
+	currentModule := createVisibilityModuleReference(moduleName, dir, ctx.OtherModuleType(module))
+
 	// Modules are implicitly visible to other modules in the same package,
 	// without checking the visibility rules. Here we need to add that visibility
 	// explicitly.
-	if !rule.matches(qualified) {
+	if !rule.matches(currentModule) {
 		if len(rule) == 1 {
 			if _, ok := rule[0].(privateRule); ok {
 				// If the rule is //visibility:private we can't append another
diff --git a/android/visibility_test.go b/android/visibility_test.go
index a66f0b6..d4add7d 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -1904,6 +1904,38 @@
 				}`),
 		},
 	},
+	{
+		name: "any_partition visibility works",
+		fs: MockFS{
+			"top/Android.bp": []byte(`
+				android_filesystem {
+					name: "foo",
+					deps: ["bar"],
+				}`),
+			"top/nested/Android.bp": []byte(`
+				package(default_visibility=["//visibility:private"])
+				mock_library {
+					name: "bar",
+					visibility: ["//visibility:any_partition"],
+				}`),
+		},
+	},
+	{
+		name: "any_partition visibility doesn't work for non-partitions",
+		fs: MockFS{
+			"top/Android.bp": []byte(`
+				mock_library {
+					name: "foo",
+					deps: ["bar"],
+				}`),
+			"top/nested/Android.bp": []byte(`
+				mock_library {
+					name: "bar",
+					visibility: ["//visibility:any_partition"],
+				}`),
+		},
+		expectedErrors: []string{`module "foo" variant "android_common": depends on //top/nested:bar which is not visible to this module`},
+	},
 }
 
 func TestVisibility(t *testing.T) {
@@ -1925,6 +1957,8 @@
 					ctx.RegisterModuleType("mock_library", newMockLibraryModule)
 					ctx.RegisterModuleType("mock_parent", newMockParentFactory)
 					ctx.RegisterModuleType("mock_defaults", defaultsFactory)
+					// For testing //visibility:any_partition. The module type doesn't matter, just that it's registered under the name "android_filesystem"
+					ctx.RegisterModuleType("android_filesystem", newMockLibraryModule)
 				}),
 				prepareForTestWithFakePrebuiltModules,
 				// Add additional files to the mock filesystem
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index a8f7947..0572ee8 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -64,6 +64,8 @@
 	"LOCAL_SANITIZE_DIAG":                  sanitize("diag."),
 	"LOCAL_STRIP_MODULE":                   strip(),
 	"LOCAL_CFLAGS":                         cflags,
+	"LOCAL_PROTOC_FLAGS":                   protoLocalIncludeDirs,
+	"LOCAL_PROTO_JAVA_OUTPUT_PARAMS":       protoOutputParams,
 	"LOCAL_UNINSTALLABLE_MODULE":           invert("installable"),
 	"LOCAL_PROGUARD_ENABLED":               proguardEnabled,
 	"LOCAL_MODULE_PATH":                    prebuiltModulePath,
@@ -86,6 +88,7 @@
 
 	"LOCAL_ANNOTATION_PROCESSOR_CLASSES": skip, // Soong gets the processor classes from the plugin
 	"LOCAL_CTS_TEST_PACKAGE":             skip, // Obsolete
+	"LOCAL_XTS_TEST_PACKAGE":             skip, // Obsolete
 	"LOCAL_JACK_ENABLED":                 skip, // Obselete
 	"LOCAL_JACK_FLAGS":                   skip, // Obselete
 }
@@ -766,6 +769,27 @@
 	return includeVariableNow(bpVariable{"export_cflags", bpparser.ListType}, ctx)
 }
 
+func protoOutputParams(ctx variableAssignmentContext) error {
+	// The Soong replacement for LOCAL_PROTO_JAVA_OUTPUT_PARAMS doesn't need ","
+	ctx.mkvalue = ctx.mkvalue.Clone()
+	ctx.mkvalue.ReplaceLiteral(`, `, ` `)
+	return includeVariableNow(bpVariable{"proto.output_params", bpparser.ListType}, ctx)
+}
+
+func protoLocalIncludeDirs(ctx variableAssignmentContext) error {
+	// The Soong replacement for LOCAL_PROTOC_FLAGS includes "--proto_path=$(LOCAL_PATH)/..."
+	ctx.mkvalue = ctx.mkvalue.Clone()
+	if len(ctx.mkvalue.Strings) >= 1 && strings.Contains(ctx.mkvalue.Strings[0], "--proto_path=") {
+		ctx.mkvalue.Strings[0] = strings.Replace(ctx.mkvalue.Strings[0], "--proto_path=", "", 1)
+		paths, err := localizePaths(ctx)
+		if err == nil {
+			err = setVariable(ctx.file, ctx.append, ctx.prefix, "proto.local_include_dirs", paths, true)
+		}
+		return err
+	}
+	return fmt.Errorf("Currently LOCAL_PROTOC_FLAGS only support with value '--proto_path=$(LOCAL_PATH)/...'")
+}
+
 func proguardEnabled(ctx variableAssignmentContext) error {
 	val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
 	if err != nil {
diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
index 0580ae5..460f1ff 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -823,6 +823,26 @@
 `,
 	},
 	{
+		desc: "IGNORE_LOCAL_XTS_TEST_PACKAGE",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := FooTest
+LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_XTS_TEST_PACKAGE := foo.bar
+LOCAL_COMPATIBILITY_SUPPORT_FILES := file1
+include $(BUILD_CTS_PACKAGE)
+`,
+		expected: `
+android_test {
+    name: "FooTest",
+    defaults: ["cts_defaults"],
+    test_suites: ["cts"],
+
+    data: ["file1"],
+}
+`,
+	},
+	{
 		desc: "BUILD_CTS_*_JAVA_LIBRARY",
 		in: `
 include $(CLEAR_VARS)
@@ -1707,9 +1727,34 @@
 }
 `,
 	},
+	{
+		desc: "LOCAL_PROTO_JAVA_OUTPUT_PARAMS",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_PROTO_JAVA_OUTPUT_PARAMS := enum_style=java, parcelable_messages=true
+LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos
+include $(BUILD_PACKAGE)
+		`,
+		expected: `
+android_app {
+	name: "foo",
+	proto: {
+        output_params: [
+            "enum_style=java",
+            "parcelable_messages=true",
+        ],
+		local_include_dirs: ["protos"],
+    },
+}
+`,
+	},
 }
 
 func TestEndToEnd(t *testing.T) {
+	// Skip checking Android.mk path with cleaning "ANDROID_BUILD_TOP"
+	t.Setenv("ANDROID_BUILD_TOP", "")
+
 	for i, test := range testCases {
 		expected, err := bpfix.Reformat(test.expected)
 		if err != nil {
diff --git a/apex/androidmk.go b/apex/androidmk.go
index bc68ad3..619be8d 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -124,6 +124,10 @@
 		pathForSymbol := filepath.Join("$(PRODUCT_OUT)", "apex", apexBundleName, fi.installDir)
 		modulePath := pathForSymbol
 		fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath)
+		// AconfigUpdateAndroidMkData may have added elements to Extra.  Process them here.
+		for _, extra := range apexAndroidMkData.Extra {
+			extra(w, fi.builtFile)
+		}
 
 		// For non-flattend APEXes, the merged notice file is attached to the APEX itself.
 		// We don't need to have notice file for the individual modules in it. Otherwise,
@@ -229,6 +233,7 @@
 
 func (a *apexBundle) androidMkForType() android.AndroidMkData {
 	return android.AndroidMkData{
+		// While we do not provide a value for `Extra`, AconfigUpdateAndroidMkData may add some, which we must honor.
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
 			moduleNames := []string{}
 			if a.installable() {
@@ -269,6 +274,10 @@
 
 			android.AndroidMkEmitAssignList(w, "LOCAL_OVERRIDES_MODULES", a.overridableProperties.Overrides)
 			a.writeRequiredModules(w, moduleNames)
+			// AconfigUpdateAndroidMkData may have added elements to Extra.  Process them here.
+			for _, extra := range data.Extra {
+				extra(w, a.outputFile)
+			}
 
 			fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
 			fmt.Fprintln(w, "ALL_MODULES.$(my_register_name).BUNDLE :=", a.bundleModuleFile.String())
diff --git a/apex/apex.go b/apex/apex.go
index 38a166e..4b37d0f 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -18,13 +18,11 @@
 
 import (
 	"fmt"
-	"log"
 	"path/filepath"
 	"regexp"
 	"sort"
 	"strings"
 
-	"android/soong/aconfig"
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
@@ -236,6 +234,9 @@
 	// List of filesystem images that are embedded inside this APEX bundle.
 	Filesystems []string
 
+	// List of prebuilt_etcs that are embedded inside this APEX bundle.
+	Prebuilts []string
+
 	// List of native libraries to exclude from this APEX.
 	Exclude_native_shared_libs []string
 
@@ -253,6 +254,9 @@
 
 	// List of filesystem images to exclude from this APEX bundle.
 	Exclude_filesystems []string
+
+	// List of prebuilt_etcs to exclude from this APEX bundle.
+	Exclude_prebuilts []string
 }
 
 // Merge combines another ApexNativeDependencies into this one
@@ -263,6 +267,7 @@
 	a.Binaries = append(a.Binaries, b.Binaries...)
 	a.Tests = append(a.Tests, b.Tests...)
 	a.Filesystems = append(a.Filesystems, b.Filesystems...)
+	a.Prebuilts = append(a.Prebuilts, b.Prebuilts...)
 
 	a.Exclude_native_shared_libs = append(a.Exclude_native_shared_libs, b.Exclude_native_shared_libs...)
 	a.Exclude_jni_libs = append(a.Exclude_jni_libs, b.Exclude_jni_libs...)
@@ -270,6 +275,7 @@
 	a.Exclude_binaries = append(a.Exclude_binaries, b.Exclude_binaries...)
 	a.Exclude_tests = append(a.Exclude_tests, b.Exclude_tests...)
 	a.Exclude_filesystems = append(a.Exclude_filesystems, b.Exclude_filesystems...)
+	a.Exclude_prebuilts = append(a.Exclude_prebuilts, b.Exclude_prebuilts...)
 }
 
 type apexMultilibProperties struct {
@@ -481,6 +487,9 @@
 	javaApisUsedByModuleFile     android.ModuleOutPath
 
 	aconfigFiles []android.Path
+
+	// Single aconfig "cache file" merged from this module and all dependencies.
+	mergedAconfigFiles map[string]android.Paths
 }
 
 // apexFileClass represents a type of file that can be included in APEX.
@@ -712,6 +721,8 @@
 		android.RemoveListFromList(nativeModules.Rust_dyn_libs, nativeModules.Exclude_rust_dyn_libs)...)
 	ctx.AddFarVariationDependencies(target.Variations(), fsTag,
 		android.RemoveListFromList(nativeModules.Filesystems, nativeModules.Exclude_filesystems)...)
+	ctx.AddFarVariationDependencies(target.Variations(), prebuiltTag,
+		android.RemoveListFromList(nativeModules.Prebuilts, nativeModules.Exclude_prebuilts)...)
 }
 
 func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) {
@@ -720,7 +731,7 @@
 
 // getImageVariationPair returns a pair for the image variation name as its
 // prefix and suffix. The prefix indicates whether it's core/vendor/product and the
-// suffix indicates the vndk version when it's vendor or product.
+// suffix indicates the vndk version for vendor/product if vndk is enabled.
 // getImageVariation can simply join the result of this function to get the
 // image variation name.
 func (a *apexBundle) getImageVariationPair(deviceConfig android.DeviceConfig) (string, string) {
@@ -728,8 +739,8 @@
 		return cc.VendorVariationPrefix, a.vndkVersion(deviceConfig)
 	}
 
-	var prefix string
-	var vndkVersion string
+	prefix := android.CoreVariation
+	vndkVersion := ""
 	if deviceConfig.VndkVersion() != "" {
 		if a.SocSpecific() || a.DeviceSpecific() {
 			prefix = cc.VendorVariationPrefix
@@ -738,15 +749,18 @@
 			prefix = cc.ProductVariationPrefix
 			vndkVersion = deviceConfig.PlatformVndkVersion()
 		}
+	} else {
+		if a.SocSpecific() || a.DeviceSpecific() {
+			prefix = cc.VendorVariation
+		} else if a.ProductSpecific() {
+			prefix = cc.ProductVariation
+		}
 	}
 	if vndkVersion == "current" {
 		vndkVersion = deviceConfig.PlatformVndkVersion()
 	}
-	if vndkVersion != "" {
-		return prefix, vndkVersion
-	}
 
-	return android.CoreVariation, "" // The usual case
+	return prefix, vndkVersion
 }
 
 // getImageVariation returns the image variant name for this apexBundle. In most cases, it's simply
@@ -827,10 +841,12 @@
 		}
 
 		addDependenciesForNativeModules(ctx, deps, target, imageVariation)
-		ctx.AddFarVariationDependencies([]blueprint.Variation{
-			{Mutator: "os", Variation: target.OsVariation()},
-			{Mutator: "arch", Variation: target.ArchVariation()},
-		}, shBinaryTag, a.properties.Sh_binaries...)
+		if isPrimaryAbi {
+			ctx.AddFarVariationDependencies([]blueprint.Variation{
+				{Mutator: "os", Variation: target.OsVariation()},
+				{Mutator: "arch", Variation: target.ArchVariation()},
+			}, shBinaryTag, a.properties.Sh_binaries...)
+		}
 	}
 
 	// Common-arch dependencies come next
@@ -912,13 +928,7 @@
 	ProvidedLibs []string
 }
 
-var DCLAInfoProvider = blueprint.NewMutatorProvider(DCLAInfo{}, "apex_info")
-
-type ApexBundleInfo struct {
-	Contents *android.ApexContents
-}
-
-var ApexBundleInfoProvider = blueprint.NewMutatorProvider(ApexBundleInfo{}, "apex_info")
+var DCLAInfoProvider = blueprint.NewMutatorProvider[DCLAInfo]("apex_info")
 
 var _ ApexInfoMutator = (*apexBundle)(nil)
 
@@ -993,10 +1003,8 @@
 			}
 		}
 
-		//TODO: b/296491928 Vendor APEX should use libbinder.ndk instead of libbinder once VNDK is fully deprecated.
 		if useVndk && mctx.Config().IsVndkDeprecated() && child.Name() == "libbinder" {
-			log.Print("Libbinder is linked from Vendor APEX ", a.Name(), " with module ", parent.Name())
-			return false
+			mctx.ModuleErrorf("Module %s in the vendor APEX %s should not use libbinder. Use libbinder_ndk instead.", parent.Name(), a.Name())
 		}
 
 		// By default, all the transitive dependencies are collected, unless filtered out
@@ -1020,7 +1028,7 @@
 
 	// The membership information is saved for later access
 	apexContents := android.NewApexContents(contents)
-	mctx.SetProvider(ApexBundleInfoProvider, ApexBundleInfo{
+	android.SetProvider(mctx, android.ApexBundleInfoProvider, android.ApexBundleInfo{
 		Contents: apexContents,
 	})
 
@@ -1058,7 +1066,7 @@
 	})
 
 	if a.dynamic_common_lib_apex() {
-		mctx.SetProvider(DCLAInfoProvider, DCLAInfo{
+		android.SetProvider(mctx, DCLAInfoProvider, DCLAInfo{
 			ProvidedLibs: a.properties.Native_shared_libs,
 		})
 	}
@@ -1137,15 +1145,43 @@
 // Skip these mainline modules for now
 var (
 	skipStrictUpdatabilityLintAllowlist = []string{
+		// go/keep-sorted start
+		"PackageManagerTestApex",
+		"com.android.adservices",
+		"com.android.appsearch",
 		"com.android.art",
 		"com.android.art.debug",
+		"com.android.btservices",
+		"com.android.cellbroadcast",
+		"com.android.configinfrastructure",
 		"com.android.conscrypt",
+		"com.android.extservices",
+		"com.android.extservices_tplus",
+		"com.android.healthfitness",
+		"com.android.ipsec",
 		"com.android.media",
-		// test apexes
+		"com.android.mediaprovider",
+		"com.android.ondevicepersonalization",
+		"com.android.os.statsd",
+		"com.android.permission",
+		"com.android.profiling",
+		"com.android.rkpd",
+		"com.android.scheduling",
+		"com.android.tethering",
+		"com.android.uwb",
+		"com.android.wifi",
 		"test_com.android.art",
+		"test_com.android.cellbroadcast",
 		"test_com.android.conscrypt",
+		"test_com.android.extservices",
+		"test_com.android.ipsec",
 		"test_com.android.media",
+		"test_com.android.mediaprovider",
+		"test_com.android.os.statsd",
+		"test_com.android.permission",
+		"test_com.android.wifi",
 		"test_jitzygote_com.android.art",
+		// go/keep-sorted end
 	}
 
 	// TODO: b/215736885 Remove this list
@@ -1201,10 +1237,10 @@
 	if _, ok := mctx.Module().(android.ApexModule); ok {
 		var contents []*android.ApexContents
 		for _, testFor := range mctx.GetDirectDepsWithTag(testForTag) {
-			abInfo := mctx.OtherModuleProvider(testFor, ApexBundleInfoProvider).(ApexBundleInfo)
+			abInfo, _ := android.OtherModuleProvider(mctx, testFor, android.ApexBundleInfoProvider)
 			contents = append(contents, abInfo.Contents)
 		}
-		mctx.SetProvider(android.ApexTestForInfoProvider, android.ApexTestForInfo{
+		android.SetProvider(mctx, android.ApexTestForInfoProvider, android.ApexTestForInfo{
 			ApexContents: contents,
 		})
 	}
@@ -1376,7 +1412,7 @@
 var _ cc.Coverage = (*apexBundle)(nil)
 
 // Implements cc.Coverage
-func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
+func (a *apexBundle) IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool {
 	return ctx.DeviceConfig().NativeCoverageEnabled()
 }
 
@@ -1465,7 +1501,7 @@
 		panic(fmt.Errorf("expected exactly at most one dcla dependency, got %d", len(dclaModules)))
 	}
 	if len(dclaModules) > 0 {
-		DCLAInfo := ctx.OtherModuleProvider(dclaModules[0], DCLAInfoProvider).(DCLAInfo)
+		DCLAInfo, _ := android.OtherModuleProvider(ctx, dclaModules[0], DCLAInfoProvider)
 		return DCLAInfo.ProvidedLibs
 	}
 	return []string{}
@@ -1626,7 +1662,7 @@
 type javaModule interface {
 	android.Module
 	BaseModuleName() string
-	DexJarBuildPath() java.OptionalDexJarPath
+	DexJarBuildPath(ctx android.ModuleErrorfContext) java.OptionalDexJarPath
 	JacocoReportClassesFile() android.Path
 	LintDepSets() java.LintDepSets
 	Stem() string
@@ -1640,7 +1676,7 @@
 
 // apexFileForJavaModule creates an apexFile for a java module's dex implementation jar.
 func apexFileForJavaModule(ctx android.BaseModuleContext, module javaModule) apexFile {
-	return apexFileForJavaModuleWithFile(ctx, module, module.DexJarBuildPath().PathOrNil())
+	return apexFileForJavaModuleWithFile(ctx, module, module.DexJarBuildPath(ctx).PathOrNil())
 }
 
 // apexFileForJavaModuleWithFile creates an apexFile for a java module with the supplied file.
@@ -1783,7 +1819,7 @@
 			return false
 		}
 
-		ai := ctx.OtherModuleProvider(child, android.ApexInfoProvider).(android.ApexInfo)
+		ai, _ := android.OtherModuleProvider(ctx, child, android.ApexInfoProvider)
 		externalDep := !android.InList(ctx.ModuleName(), ai.InApexVariants)
 
 		// Visit actually
@@ -2036,8 +2072,10 @@
 				return true // track transitive dependencies
 			case *java.AndroidAppImport:
 				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
+				addAconfigFiles(vctx, ctx, child)
 			case *java.AndroidTestHelperApp:
 				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
+				addAconfigFiles(vctx, ctx, child)
 			case *java.AndroidAppSet:
 				appDir := "app"
 				if ap.Privileged() {
@@ -2051,6 +2089,7 @@
 				af := newApexFile(ctx, ap.OutputFile(), ap.BaseModuleName(), appDirName, appSet, ap)
 				af.certificate = java.PresignedCertificate
 				vctx.filesInfo = append(vctx.filesInfo, af)
+				addAconfigFiles(vctx, ctx, child)
 			default:
 				ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
 			}
@@ -2079,6 +2118,7 @@
 		case prebuiltTag:
 			if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
 				vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
+				addAconfigFiles(vctx, ctx, child)
 			} else {
 				ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
 			}
@@ -2102,6 +2142,7 @@
 					af := apexFileForExecutable(ctx, ccTest)
 					af.class = nativeTest
 					vctx.filesInfo = append(vctx.filesInfo, af)
+					addAconfigFiles(vctx, ctx, child)
 				}
 				return true // track transitive dependencies
 			} else {
@@ -2143,14 +2184,10 @@
 				return false
 			}
 
-			//TODO: b/296491928 Vendor APEX should use libbinder.ndk instead of libbinder once VNDK is fully deprecated.
-			if ch.UseVndk() && ctx.Config().IsVndkDeprecated() && child.Name() == "libbinder" {
-				return false
-			}
 			af := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs)
 			af.transitiveDep = true
 
-			abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
+			abInfo, _ := android.ModuleProvider(ctx, android.ApexBundleInfoProvider)
 			if !abInfo.Contents.DirectlyInApex(depName) && (ch.IsStubs() || ch.HasStubsVariants()) {
 				// If the dependency is a stubs lib, don't include it in this APEX,
 				// but make sure that the lib is installed on the device.
@@ -2191,11 +2228,13 @@
 			}
 
 			vctx.filesInfo = append(vctx.filesInfo, af)
+			addAconfigFiles(vctx, ctx, child)
 			return true // track transitive dependencies
 		} else if rm, ok := child.(*rust.Module); ok {
 			af := apexFileForRustLibrary(ctx, rm)
 			af.transitiveDep = true
 			vctx.filesInfo = append(vctx.filesInfo, af)
+			addAconfigFiles(vctx, ctx, child)
 			return true // track transitive dependencies
 		}
 	} else if cc.IsTestPerSrcDepTag(depTag) {
@@ -2224,6 +2263,7 @@
 			af := apexFileForRustLibrary(ctx, rustm)
 			af.transitiveDep = true
 			vctx.filesInfo = append(vctx.filesInfo, af)
+			addAconfigFiles(vctx, ctx, child)
 			return true // track transitive dependencies
 		}
 	} else if rust.IsRlibDepTag(depTag) {
@@ -2242,6 +2282,7 @@
 				return false
 			}
 			vctx.filesInfo = append(vctx.filesInfo, af)
+			addAconfigFiles(vctx, ctx, child)
 			return true // track transitive dependencies
 		default:
 			ctx.PropertyErrorf("bootclasspath_fragments",
@@ -2256,6 +2297,7 @@
 			if profileAf := apexFileForJavaModuleProfile(ctx, child.(javaModule)); profileAf != nil {
 				vctx.filesInfo = append(vctx.filesInfo, *profileAf)
 			}
+			addAconfigFiles(vctx, ctx, child)
 			return true // track transitive dependencies
 		default:
 			ctx.PropertyErrorf("systemserverclasspath_fragments",
@@ -2272,7 +2314,7 @@
 }
 
 func addAconfigFiles(vctx *visitorContext, ctx android.ModuleContext, module blueprint.Module) {
-	dep := ctx.OtherModuleProvider(module, aconfig.TransitiveDeclarationsInfoProvider).(aconfig.TransitiveDeclarationsInfo)
+	dep, _ := android.OtherModuleProvider(ctx, module, android.AconfigTransitiveDeclarationsInfoProvider)
 	if len(dep.AconfigFiles) > 0 && dep.AconfigFiles[ctx.ModuleName()] != nil {
 		vctx.aconfigFiles = append(vctx.aconfigFiles, dep.AconfigFiles[ctx.ModuleName()]...)
 	}
@@ -2354,6 +2396,7 @@
 			return
 		}
 	}
+	android.CollectDependencyAconfigFiles(ctx, &a.mergedAconfigFiles)
 
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 3) some fields in apexBundle struct are configured
@@ -2371,12 +2414,31 @@
 	a.buildApex(ctx)
 	a.buildApexDependencyInfo(ctx)
 	a.buildLintReports(ctx)
+
+	// Set a provider for dexpreopt of bootjars
+	a.provideApexExportsInfo(ctx)
+}
+
+// Set a provider containing information about the jars and .prof provided by the apex
+// Apexes built from source retrieve this information by visiting `bootclasspath_fragments`
+// Used by dex_bootjars to generate the boot image
+func (a *apexBundle) provideApexExportsInfo(ctx android.ModuleContext) {
+	ctx.VisitDirectDepsWithTag(bcpfTag, func(child android.Module) {
+		if info, ok := android.OtherModuleProvider(ctx, child, java.BootclasspathFragmentApexContentInfoProvider); ok {
+			exports := android.ApexExportsInfo{
+				ApexName:                      a.ApexVariationName(),
+				ProfilePathOnHost:             info.ProfilePathOnHost(),
+				LibraryNameToDexJarPathOnHost: info.DexBootJarPathMap(),
+			}
+			android.SetProvider(ctx, android.ApexExportsInfoProvider, exports)
+		}
+	})
 }
 
 // apexBootclasspathFragmentFiles returns the list of apexFile structures defining the files that
 // the bootclasspath_fragment contributes to the apex.
 func apexBootclasspathFragmentFiles(ctx android.ModuleContext, module blueprint.Module) []apexFile {
-	bootclasspathFragmentInfo := ctx.OtherModuleProvider(module, java.BootclasspathFragmentApexContentInfoProvider).(java.BootclasspathFragmentApexContentInfo)
+	bootclasspathFragmentInfo, _ := android.OtherModuleProvider(ctx, module, java.BootclasspathFragmentApexContentInfoProvider)
 	var filesToAdd []apexFile
 
 	// Add classpaths.proto config.
@@ -2425,7 +2487,7 @@
 // apexClasspathFragmentProtoFile returns *apexFile structure defining the classpath.proto config that
 // the module contributes to the apex; or nil if the proto config was not generated.
 func apexClasspathFragmentProtoFile(ctx android.ModuleContext, module blueprint.Module) *apexFile {
-	info := ctx.OtherModuleProvider(module, java.ClasspathFragmentProtoContentInfoProvider).(java.ClasspathFragmentProtoContentInfo)
+	info, _ := android.OtherModuleProvider(ctx, module, java.ClasspathFragmentProtoContentInfoProvider)
 	if !info.ClasspathFragmentProtoGenerated {
 		return nil
 	}
@@ -2437,7 +2499,7 @@
 // apexFileForBootclasspathFragmentContentModule creates an apexFile for a bootclasspath_fragment
 // content module, i.e. a library that is part of the bootclasspath.
 func apexFileForBootclasspathFragmentContentModule(ctx android.ModuleContext, fragmentModule blueprint.Module, javaModule javaModule) apexFile {
-	bootclasspathFragmentInfo := ctx.OtherModuleProvider(fragmentModule, java.BootclasspathFragmentApexContentInfoProvider).(java.BootclasspathFragmentApexContentInfo)
+	bootclasspathFragmentInfo, _ := android.OtherModuleProvider(ctx, fragmentModule, java.BootclasspathFragmentApexContentInfoProvider)
 
 	// Get the dexBootJar from the bootclasspath_fragment as that is responsible for performing the
 	// hidden API encpding.
@@ -2494,6 +2556,9 @@
 type Defaults struct {
 	android.ModuleBase
 	android.DefaultsModuleBase
+
+	// Single aconfig "cache file" merged from this module and all dependencies.
+	mergedAconfigFiles map[string]android.Paths
 }
 
 // apex_defaults provides defaultable properties to other apex modules.
@@ -2516,6 +2581,10 @@
 	android.OverrideModuleBase
 }
 
+func (d *Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	android.CollectDependencyAconfigFiles(ctx, &d.mergedAconfigFiles)
+}
+
 func (o *OverrideApex) GenerateAndroidBuildActions(_ android.ModuleContext) {
 	// All the overrides happen in the base module.
 }
@@ -2589,7 +2658,7 @@
 		return
 	}
 
-	abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
+	abInfo, _ := android.ModuleProvider(ctx, android.ApexBundleInfoProvider)
 
 	a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
 		if ccm, ok := to.(*cc.Module); ok {
@@ -2650,7 +2719,7 @@
 func (a *apexBundle) checkClasspathFragments(ctx android.ModuleContext) {
 	ctx.VisitDirectDeps(func(module android.Module) {
 		if tag := ctx.OtherModuleDependencyTag(module); tag == bcpfTag || tag == sscpfTag {
-			info := ctx.OtherModuleProvider(module, java.ClasspathFragmentProtoContentInfoProvider).(java.ClasspathFragmentProtoContentInfo)
+			info, _ := android.OtherModuleProvider(ctx, module, java.ClasspathFragmentProtoContentInfoProvider)
 			if !info.ClasspathFragmentProtoGenerated {
 				ctx.OtherModuleErrorf(module, "is included in updatable apex %v, it must not set generate_classpaths_proto to false", ctx.ModuleName())
 			}
@@ -2835,77 +2904,8 @@
 	//
 	// Module separator
 	//
-	m["com.android.mediaprovider"] = []string{
-		"MediaProvider",
-		"MediaProviderGoogle",
-		"fmtlib_ndk",
-		"libbase_ndk",
-		"libfuse",
-		"libfuse_jni",
-	}
-	//
-	// Module separator
-	//
 	m["com.android.runtime"] = []string{
-		"libdebuggerd",
-		"libdebuggerd_common_headers",
-		"libdebuggerd_handler_core",
-		"libdl_static",
-		"libjemalloc5",
-		"liblinker_main",
-		"liblinker_malloc",
-		"liblzma",
-		"libprocinfo",
-		"libpropertyinfoparser",
-		"libscudo",
-		"libsystemproperties",
-		"libtombstoned_client_static",
-		"libunwindstack",
 		"libz",
-		"libziparchive",
-	}
-	//
-	// Module separator
-	//
-	m["com.android.tethering"] = []string{
-		"android.hardware.tetheroffload.config-V1.0-java",
-		"android.hardware.tetheroffload.control-V1.0-java",
-		"net-utils-framework-common",
-	}
-	//
-	// Module separator
-	//
-	m["com.android.wifi"] = []string{
-		"PlatformProperties",
-		"android.hardware.wifi-V1.0-java",
-		"android.hardware.wifi-V1.0-java-constants",
-		"android.hardware.wifi-V1.1-java",
-		"android.hardware.wifi-V1.2-java",
-		"android.hardware.wifi-V1.3-java",
-		"android.hardware.wifi-V1.4-java",
-		"android.hardware.wifi.hostapd-V1.0-java",
-		"android.hardware.wifi.hostapd-V1.1-java",
-		"android.hardware.wifi.hostapd-V1.2-java",
-		"android.hardware.wifi.supplicant-V1.0-java",
-		"android.hardware.wifi.supplicant-V1.1-java",
-		"android.hardware.wifi.supplicant-V1.2-java",
-		"android.hardware.wifi.supplicant-V1.3-java",
-		"bouncycastle-unbundled",
-		"framework-wifi-util-lib",
-		"ksoap2",
-		"libnanohttpd",
-		"wifi-lite-protos",
-		"wifi-nano-protos",
-		"wifi-service-pre-jarjar",
-	}
-	//
-	// Module separator
-	//
-	m[android.AvailableToAnyApex] = []string{
-		"libprofile-clang-extras",
-		"libprofile-clang-extras_ndk",
-		"libprofile-extras",
-		"libprofile-extras_ndk",
 	}
 	return m
 }
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index a63344f..25c0cc4 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -83,7 +83,7 @@
 	updatableFlatLists := android.Paths{}
 	ctx.VisitAllModules(func(module android.Module) {
 		if binaryInfo, ok := module.(android.ApexBundleDepsInfoIntf); ok {
-			apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
+			apexInfo, _ := android.SingletonModuleProvider(ctx, module, android.ApexInfoProvider)
 			if path := binaryInfo.FlatListPath(); path != nil {
 				if binaryInfo.Updatable() || apexInfo.Updatable {
 					updatableFlatLists = append(updatableFlatLists, path)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index abf6b15..9dc77c2 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -790,6 +790,79 @@
 	}
 }
 
+func TestApexWithDessertSha(t *testing.T) {
+	ctx := testApex(t, `
+		apex_defaults {
+			name: "my_defaults",
+			key: "myapex.key",
+			product_specific: true,
+			file_contexts: ":my-file-contexts",
+			updatable: false,
+		}
+		apex {
+			name: "myapex_30",
+			min_sdk_version: "30",
+			defaults: ["my_defaults"],
+		}
+
+		apex {
+			name: "myapex_current",
+			min_sdk_version: "current",
+			defaults: ["my_defaults"],
+		}
+
+		apex {
+			name: "myapex_none",
+			defaults: ["my_defaults"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		filegroup {
+			name: "my-file-contexts",
+			srcs: ["product_specific_file_contexts"],
+		}
+	`, withFiles(map[string][]byte{
+		"product_specific_file_contexts": nil,
+	}), android.FixtureModifyProductVariables(
+		func(variables android.FixtureProductVariables) {
+			variables.Unbundled_build = proptools.BoolPtr(true)
+			variables.Always_use_prebuilt_sdks = proptools.BoolPtr(false)
+		}), android.FixtureMergeEnv(map[string]string{
+		"UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA": "UpsideDownCake.abcdefghijklmnopqrstuvwxyz123456",
+	}))
+
+	testCases := []struct {
+		module        string
+		minSdkVersion string
+	}{
+		{
+			module:        "myapex_30",
+			minSdkVersion: "30",
+		},
+		{
+			module:        "myapex_current",
+			minSdkVersion: "UpsideDownCake.abcdefghijklmnopqrstuvwxyz123456",
+		},
+		{
+			module:        "myapex_none",
+			minSdkVersion: "UpsideDownCake.abcdefghijklmnopqrstuvwxyz123456",
+		},
+	}
+	for _, tc := range testCases {
+		module := ctx.ModuleForTests(tc.module, "android_common_"+tc.module)
+		args := module.Rule("apexRule").Args
+		optFlags := args["opt_flags"]
+		if !strings.Contains(optFlags, "--min_sdk_version "+tc.minSdkVersion) {
+			t.Errorf("%s: Expected min_sdk_version=%s, got: %s", tc.module, tc.minSdkVersion, optFlags)
+		}
+	}
+}
+
 func TestFileContexts(t *testing.T) {
 	for _, vendor := range []bool{true, false} {
 		prop := ""
@@ -1455,6 +1528,7 @@
 			name: "libc",
 			no_libcrt: true,
 			nocrt: true,
+			no_crt_pad_segment: true,
 			stl: "none",
 			system_shared_libs: [],
 			stubs: { versions: ["1"] },
@@ -1469,6 +1543,7 @@
 			name: "libclang_rt.hwasan",
 			no_libcrt: true,
 			nocrt: true,
+			no_crt_pad_segment: true,
 			stl: "none",
 			system_shared_libs: [],
 			srcs: [""],
@@ -1511,6 +1586,7 @@
 			name: "libc",
 			no_libcrt: true,
 			nocrt: true,
+			no_crt_pad_segment: true,
 			stl: "none",
 			system_shared_libs: [],
 			stubs: { versions: ["1"] },
@@ -1521,6 +1597,7 @@
 			name: "libclang_rt.hwasan",
 			no_libcrt: true,
 			nocrt: true,
+			no_crt_pad_segment: true,
 			stl: "none",
 			system_shared_libs: [],
 			srcs: [""],
@@ -3725,7 +3802,7 @@
 }
 
 func ensureExactDeapexedContents(t *testing.T, ctx *android.TestContext, moduleName string, variant string, files []string) {
-	deapexer := ctx.ModuleForTests(moduleName+".deapexer", variant).Rule("deapexer")
+	deapexer := ctx.ModuleForTests(moduleName+".deapexer", variant).Description("deapex")
 	outputs := make([]string, 0, len(deapexer.ImplicitOutputs)+1)
 	if deapexer.Output != nil {
 		outputs = append(outputs, deapexer.Output.String())
@@ -3768,13 +3845,6 @@
 				"lib64/libvndk.so",
 				"lib64/libvndksp.so"),
 		},
-		{
-			vndkVersion: "",
-			expectedFiles: append(commonFiles,
-				// Legacy VNDK APEX contains only VNDK-SP files (of core variant)
-				"lib/libvndksp.so",
-				"lib64/libvndksp.so"),
-		},
 	}
 	for _, tc := range testCases {
 		t.Run("VNDK.current with DeviceVndkVersion="+tc.vndkVersion, func(t *testing.T) {
@@ -4976,6 +5046,7 @@
 			key: "myapex.key",
 			sh_binaries: ["myscript"],
 			updatable: false,
+			compile_multilib: "both",
 		}
 
 		apex_key {
@@ -5355,6 +5426,13 @@
 	).RunTest(t)
 }
 
+// A minimal context object for use with DexJarBuildPath
+type moduleErrorfTestCtx struct {
+}
+
+func (ctx moduleErrorfTestCtx) ModuleErrorf(format string, args ...interface{}) {
+}
+
 // These tests verify that the prebuilt_apex/deapexer to java_import wiring allows for the
 // propagation of paths to dex implementation jars from the former to the latter.
 func TestPrebuiltExportDexImplementationJars(t *testing.T) {
@@ -5364,10 +5442,10 @@
 		t.Helper()
 		// Make sure the import has been given the correct path to the dex jar.
 		p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency)
-		dexJarBuildPath := p.DexJarBuildPath().PathOrNil()
+		dexJarBuildPath := p.DexJarBuildPath(moduleErrorfTestCtx{}).PathOrNil()
 		stem := android.RemoveOptionalPrebuiltPrefix(name)
 		android.AssertStringEquals(t, "DexJarBuildPath should be apex-related path.",
-			".intermediates/myapex.deapexer/android_common/deapexer/javalib/"+stem+".jar",
+			".intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/"+stem+".jar",
 			android.NormalizePathForTesting(dexJarBuildPath))
 	}
 
@@ -5421,8 +5499,8 @@
 		// Make sure that dexpreopt can access dex implementation files from the prebuilt.
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
 
-		deapexerName := deapexerModuleName("myapex")
-		android.AssertStringEquals(t, "APEX module name from deapexer name", "myapex", apexModuleName(deapexerName))
+		deapexerName := deapexerModuleName("prebuilt_myapex")
+		android.AssertStringEquals(t, "APEX module name from deapexer name", "prebuilt_myapex", apexModuleName(deapexerName))
 
 		// Make sure that the deapexer has the correct input APEX.
 		deapexer := ctx.ModuleForTests(deapexerName, "android_common")
@@ -5645,8 +5723,8 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
-		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
+		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
@@ -5723,8 +5801,8 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
-		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
+		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
@@ -5737,7 +5815,7 @@
 		myApex := ctx.ModuleForTests("myapex", "android_common_myapex").Module()
 
 		overrideNames := []string{
-			"",
+			"myapex",
 			"myjavalib.myapex",
 			"libfoo.myapex",
 			"libbar.myapex",
@@ -5912,8 +5990,8 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
-		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
+		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
@@ -6109,8 +6187,8 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
-		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
+		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
@@ -6120,6 +6198,84 @@
 			out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv
 		`)
 	})
+
+	t.Run("Co-existing unflagged apexes should create a duplicate module error", func(t *testing.T) {
+		bp := `
+		// Source
+		apex {
+			name: "myapex",
+			enabled: false,
+			key: "myapex.key",
+			bootclasspath_fragments: ["my-bootclasspath-fragment"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		// Prebuilt
+		prebuilt_apex {
+			name: "myapex.v1",
+			source_apex_name: "myapex",
+			arch: {
+				arm64: {
+					src: "myapex-arm64.apex",
+				},
+				arm: {
+					src: "myapex-arm.apex",
+				},
+			},
+			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+			prefer: true,
+		}
+		prebuilt_apex {
+			name: "myapex.v2",
+			source_apex_name: "myapex",
+			arch: {
+				arm64: {
+					src: "myapex-arm64.apex",
+				},
+				arm: {
+					src: "myapex-arm.apex",
+				},
+			},
+			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+			prefer: true,
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "my-bootclasspath-fragment",
+			contents: ["libfoo", "libbar"],
+			apex_available: ["myapex"],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
+			prefer: true,
+		}
+
+		java_import {
+			name: "libfoo",
+			jars: ["libfoo.jar"],
+			apex_available: ["myapex"],
+			prefer: true,
+		}
+		java_import {
+			name: "libbar",
+			jars: ["libbar.jar"],
+			apex_available: ["myapex"],
+			prefer: true,
+		}
+	`
+
+		testDexpreoptWithApexes(t, bp, "Multiple prebuilt modules prebuilt_myapex.v1 and prebuilt_myapex.v2 have been marked as preferred for this source module", preparer, fragment)
+	})
+
 }
 
 func TestApexWithTests(t *testing.T) {
@@ -8313,9 +8469,9 @@
 	ctx := testApex(t, bp, prepareForTestWithSantitizeHwaddress)
 
 	// Check that the extractor produces the correct output file from the correct input file.
-	extractorOutput := "out/soong/.intermediates/myapex.apex.extractor/android_common/extracted/myapex.hwasan.apks"
+	extractorOutput := "out/soong/.intermediates/prebuilt_myapex.apex.extractor/android_common/extracted/myapex.hwasan.apks"
 
-	m := ctx.ModuleForTests("myapex.apex.extractor", "android_common")
+	m := ctx.ModuleForTests("prebuilt_myapex.apex.extractor", "android_common")
 	extractedApex := m.Output(extractorOutput)
 
 	android.AssertArrayString(t, "extractor input", []string{"myapex.hwasan.apks"}, extractedApex.Inputs.Strings())
@@ -8340,10 +8496,10 @@
 		}
 	`)
 
-	m := ctx.ModuleForTests("myapex.apex.extractor", "android_common")
+	m := ctx.ModuleForTests("prebuilt_myapex.apex.extractor", "android_common")
 
 	// Check that the extractor produces the correct apks file from the input module
-	extractorOutput := "out/soong/.intermediates/myapex.apex.extractor/android_common/extracted/myapex.apks"
+	extractorOutput := "out/soong/.intermediates/prebuilt_myapex.apex.extractor/android_common/extracted/myapex.apks"
 	extractedApex := m.Output(extractorOutput)
 
 	android.AssertArrayString(t, "extractor input", []string{"myapex.apks"}, extractedApex.Inputs.Strings())
@@ -8408,30 +8564,39 @@
 func TestDuplicateDeapexersFromPrebuiltApexes(t *testing.T) {
 	preparers := android.GroupFixturePreparers(
 		java.PrepareForTestWithJavaDefaultModules,
+		prepareForTestWithBootclasspathFragment,
+		dexpreopt.FixtureSetTestOnlyArtBootImageJars("com.android.art:libfoo"),
 		PrepareForTestWithApexBuildComponents,
 	).
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
-			"Multiple installable prebuilt APEXes provide ambiguous deapexers: com.android.myapex and com.mycompany.android.myapex"))
+			"Multiple installable prebuilt APEXes provide ambiguous deapexers: prebuilt_com.android.art and prebuilt_com.mycompany.android.art"))
 
 	bpBase := `
 		apex_set {
-			name: "com.android.myapex",
+			name: "com.android.art",
 			installable: true,
-			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+			exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
 			set: "myapex.apks",
 		}
 
 		apex_set {
-			name: "com.mycompany.android.myapex",
-			apex_name: "com.android.myapex",
+			name: "com.mycompany.android.art",
+			apex_name: "com.android.art",
 			installable: true,
-			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+			exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
 			set: "company-myapex.apks",
 		}
 
 		prebuilt_bootclasspath_fragment {
-			name: "my-bootclasspath-fragment",
-			apex_available: ["com.android.myapex"],
+			name: "art-bootclasspath-fragment",
+			apex_available: ["com.android.art"],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
 			%s
 		}
 	`
@@ -8441,7 +8606,7 @@
 			java_import {
 				name: "libfoo",
 				jars: ["libfoo.jar"],
-				apex_available: ["com.android.myapex"],
+				apex_available: ["com.android.art"],
 			}
 		`)
 	})
@@ -8453,7 +8618,8 @@
 				public: {
 					jars: ["libbar.jar"],
 				},
-				apex_available: ["com.android.myapex"],
+				shared_library: false,
+				apex_available: ["com.android.art"],
 			}
 		`)
 	})
@@ -8468,7 +8634,8 @@
 				public: {
 					jars: ["libbar.jar"],
 				},
-				apex_available: ["com.android.myapex"],
+				shared_library: false,
+				apex_available: ["com.android.art"],
 			}
 		`)
 	})
@@ -8480,6 +8647,8 @@
 		PrepareForTestWithApexBuildComponents,
 	)
 
+	errCtx := moduleErrorfTestCtx{}
+
 	bpBase := `
 		apex_set {
 			name: "com.android.myapex",
@@ -8528,8 +8697,8 @@
 		module := result.Module("libfoo", "android_common_com.android.myapex")
 		usesLibraryDep := module.(java.UsesLibraryDependency)
 		android.AssertPathRelativeToTopEquals(t, "dex jar path",
-			"out/soong/.intermediates/com.android.myapex.deapexer/android_common/deapexer/javalib/libfoo.jar",
-			usesLibraryDep.DexJarBuildPath().Path())
+			"out/soong/.intermediates/prebuilt_com.android.myapex.deapexer/android_common/deapexer/javalib/libfoo.jar",
+			usesLibraryDep.DexJarBuildPath(errCtx).Path())
 	})
 
 	t.Run("java_sdk_library_import", func(t *testing.T) {
@@ -8551,8 +8720,8 @@
 		module := result.Module("libfoo", "android_common_com.android.myapex")
 		usesLibraryDep := module.(java.UsesLibraryDependency)
 		android.AssertPathRelativeToTopEquals(t, "dex jar path",
-			"out/soong/.intermediates/com.android.myapex.deapexer/android_common/deapexer/javalib/libfoo.jar",
-			usesLibraryDep.DexJarBuildPath().Path())
+			"out/soong/.intermediates/prebuilt_com.android.myapex.deapexer/android_common/deapexer/javalib/libfoo.jar",
+			usesLibraryDep.DexJarBuildPath(errCtx).Path())
 	})
 
 	t.Run("prebuilt_bootclasspath_fragment", func(t *testing.T) {
@@ -9124,7 +9293,7 @@
 		}),
 	)
 
-	m := ctx.ModuleForTests("myapex.apex.extractor", "android_common")
+	m := ctx.ModuleForTests("prebuilt_myapex.apex.extractor", "android_common")
 
 	// Check extract_apks tool parameters.
 	extractedApex := m.Output("extracted/myapex.apks")
@@ -9165,7 +9334,7 @@
 		}),
 	)
 
-	m := ctx.ModuleForTests("myapex.apex.extractor", "android_common")
+	m := ctx.ModuleForTests("prebuilt_myapex.apex.extractor", "android_common")
 
 	// Check extract_apks tool parameters. No native bridge arch expected
 	extractedApex := m.Output("extracted/myapex.apks")
@@ -10175,188 +10344,196 @@
 	}
 }
 
-// TODO(b/193460475): Re-enable this test
-//func TestApexStrictUpdtabilityLint(t *testing.T) {
-//	bpTemplate := `
-//		apex {
-//			name: "myapex",
-//			key: "myapex.key",
-//			java_libs: ["myjavalib"],
-//			updatable: %v,
-//			min_sdk_version: "29",
-//		}
-//		apex_key {
-//			name: "myapex.key",
-//		}
-//		java_library {
-//			name: "myjavalib",
-//			srcs: ["MyClass.java"],
-//			apex_available: [ "myapex" ],
-//			lint: {
-//				strict_updatability_linting: %v,
-//			},
-//			sdk_version: "current",
-//			min_sdk_version: "29",
-//		}
-//		`
-//	fs := android.MockFS{
-//		"lint-baseline.xml": nil,
-//	}
-//
-//	testCases := []struct {
-//		testCaseName              string
-//		apexUpdatable             bool
-//		javaStrictUpdtabilityLint bool
-//		lintFileExists            bool
-//		disallowedFlagExpected    bool
-//	}{
-//		{
-//			testCaseName:              "lint-baseline.xml does not exist, no disallowed flag necessary in lint cmd",
-//			apexUpdatable:             true,
-//			javaStrictUpdtabilityLint: true,
-//			lintFileExists:            false,
-//			disallowedFlagExpected:    false,
-//		},
-//		{
-//			testCaseName:              "non-updatable apex respects strict_updatability of javalib",
-//			apexUpdatable:             false,
-//			javaStrictUpdtabilityLint: false,
-//			lintFileExists:            true,
-//			disallowedFlagExpected:    false,
-//		},
-//		{
-//			testCaseName:              "non-updatable apex respects strict updatability of javalib",
-//			apexUpdatable:             false,
-//			javaStrictUpdtabilityLint: true,
-//			lintFileExists:            true,
-//			disallowedFlagExpected:    true,
-//		},
-//		{
-//			testCaseName:              "updatable apex sets strict updatability of javalib to true",
-//			apexUpdatable:             true,
-//			javaStrictUpdtabilityLint: false, // will be set to true by mutator
-//			lintFileExists:            true,
-//			disallowedFlagExpected:    true,
-//		},
-//	}
-//
-//	for _, testCase := range testCases {
-//		bp := fmt.Sprintf(bpTemplate, testCase.apexUpdatable, testCase.javaStrictUpdtabilityLint)
-//		fixtures := []android.FixturePreparer{}
-//		if testCase.lintFileExists {
-//			fixtures = append(fixtures, fs.AddToFixture())
-//		}
-//
-//		result := testApex(t, bp, fixtures...)
-//		myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
-//		sboxProto := android.RuleBuilderSboxProtoForTests(t, myjavalib.Output("lint.sbox.textproto"))
-//		disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi")
-//
-//		if disallowedFlagActual != testCase.disallowedFlagExpected {
-//			t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command)
-//		}
-//	}
-//}
-//
-//func TestUpdatabilityLintSkipLibcore(t *testing.T) {
-//	bp := `
-//		apex {
-//			name: "myapex",
-//			key: "myapex.key",
-//			java_libs: ["myjavalib"],
-//			updatable: true,
-//			min_sdk_version: "29",
-//		}
-//		apex_key {
-//			name: "myapex.key",
-//		}
-//		java_library {
-//			name: "myjavalib",
-//			srcs: ["MyClass.java"],
-//			apex_available: [ "myapex" ],
-//			sdk_version: "current",
-//			min_sdk_version: "29",
-//		}
-//		`
-//
-//	testCases := []struct {
-//		testCaseName           string
-//		moduleDirectory        string
-//		disallowedFlagExpected bool
-//	}{
-//		{
-//			testCaseName:           "lintable module defined outside libcore",
-//			moduleDirectory:        "",
-//			disallowedFlagExpected: true,
-//		},
-//		{
-//			testCaseName:           "lintable module defined in libcore root directory",
-//			moduleDirectory:        "libcore/",
-//			disallowedFlagExpected: false,
-//		},
-//		{
-//			testCaseName:           "lintable module defined in libcore child directory",
-//			moduleDirectory:        "libcore/childdir/",
-//			disallowedFlagExpected: true,
-//		},
-//	}
-//
-//	for _, testCase := range testCases {
-//		lintFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"lint-baseline.xml", "")
-//		bpFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"Android.bp", bp)
-//		result := testApex(t, "", lintFileCreator, bpFileCreator)
-//		myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
-//		sboxProto := android.RuleBuilderSboxProtoForTests(t, myjavalib.Output("lint.sbox.textproto"))
-//		cmdFlags := fmt.Sprintf("--baseline %vlint-baseline.xml --disallowed_issues NewApi", testCase.moduleDirectory)
-//		disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, cmdFlags)
-//
-//		if disallowedFlagActual != testCase.disallowedFlagExpected {
-//			t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command)
-//		}
-//	}
-//}
-//
-//// checks transtive deps of an apex coming from bootclasspath_fragment
-//func TestApexStrictUpdtabilityLintBcpFragmentDeps(t *testing.T) {
-//	bp := `
-//		apex {
-//			name: "myapex",
-//			key: "myapex.key",
-//			bootclasspath_fragments: ["mybootclasspathfragment"],
-//			updatable: true,
-//			min_sdk_version: "29",
-//		}
-//		apex_key {
-//			name: "myapex.key",
-//		}
-//		bootclasspath_fragment {
-//			name: "mybootclasspathfragment",
-//			contents: ["myjavalib"],
-//			apex_available: ["myapex"],
-//			hidden_api: {
-//				split_packages: ["*"],
-//			},
-//		}
-//		java_library {
-//			name: "myjavalib",
-//			srcs: ["MyClass.java"],
-//			apex_available: [ "myapex" ],
-//			sdk_version: "current",
-//			min_sdk_version: "29",
-//			compile_dex: true,
-//		}
-//		`
-//	fs := android.MockFS{
-//		"lint-baseline.xml": nil,
-//	}
-//
-//	result := testApex(t, bp, dexpreopt.FixtureSetApexBootJars("myapex:myjavalib"), fs.AddToFixture())
-//	myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
-//	sboxProto := android.RuleBuilderSboxProtoForTests(t, myjavalib.Output("lint.sbox.textproto"))
-//	if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi") {
-//		t.Errorf("Strict updabality lint missing in myjavalib coming from bootclasspath_fragment mybootclasspath-fragment\nActual lint cmd: %v", *sboxProto.Commands[0].Command)
-//	}
-//}
+func TestApexStrictUpdtabilityLint(t *testing.T) {
+	bpTemplate := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			java_libs: ["myjavalib"],
+			updatable: %v,
+			min_sdk_version: "29",
+		}
+		apex_key {
+			name: "myapex.key",
+		}
+		java_library {
+			name: "myjavalib",
+			srcs: ["MyClass.java"],
+			apex_available: [ "myapex" ],
+			lint: {
+				strict_updatability_linting: %v,
+				%s
+			},
+			sdk_version: "current",
+			min_sdk_version: "29",
+		}
+		`
+	fs := android.MockFS{
+		"lint-baseline.xml": nil,
+	}
+
+	testCases := []struct {
+		testCaseName              string
+		apexUpdatable             bool
+		javaStrictUpdtabilityLint bool
+		lintFileExists            bool
+		disallowedFlagExpected    bool
+	}{
+		{
+			testCaseName:              "lint-baseline.xml does not exist, no disallowed flag necessary in lint cmd",
+			apexUpdatable:             true,
+			javaStrictUpdtabilityLint: true,
+			lintFileExists:            false,
+			disallowedFlagExpected:    false,
+		},
+		{
+			testCaseName:              "non-updatable apex respects strict_updatability of javalib",
+			apexUpdatable:             false,
+			javaStrictUpdtabilityLint: false,
+			lintFileExists:            true,
+			disallowedFlagExpected:    false,
+		},
+		{
+			testCaseName:              "non-updatable apex respects strict updatability of javalib",
+			apexUpdatable:             false,
+			javaStrictUpdtabilityLint: true,
+			lintFileExists:            true,
+			disallowedFlagExpected:    true,
+		},
+		{
+			testCaseName:              "updatable apex sets strict updatability of javalib to true",
+			apexUpdatable:             true,
+			javaStrictUpdtabilityLint: false, // will be set to true by mutator
+			lintFileExists:            true,
+			disallowedFlagExpected:    true,
+		},
+	}
+
+	for _, testCase := range testCases {
+		fixtures := []android.FixturePreparer{}
+		baselineProperty := ""
+		if testCase.lintFileExists {
+			fixtures = append(fixtures, fs.AddToFixture())
+			baselineProperty = "baseline_filename: \"lint-baseline.xml\""
+		}
+		bp := fmt.Sprintf(bpTemplate, testCase.apexUpdatable, testCase.javaStrictUpdtabilityLint, baselineProperty)
+
+		result := testApex(t, bp, fixtures...)
+		myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
+		sboxProto := android.RuleBuilderSboxProtoForTests(t, result, myjavalib.Output("lint.sbox.textproto"))
+		disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi")
+
+		if disallowedFlagActual != testCase.disallowedFlagExpected {
+			t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command)
+		}
+	}
+}
+
+func TestUpdatabilityLintSkipLibcore(t *testing.T) {
+	bp := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			java_libs: ["myjavalib"],
+			updatable: true,
+			min_sdk_version: "29",
+		}
+		apex_key {
+			name: "myapex.key",
+		}
+		java_library {
+			name: "myjavalib",
+			srcs: ["MyClass.java"],
+			apex_available: [ "myapex" ],
+			sdk_version: "current",
+			min_sdk_version: "29",
+			lint: {
+				baseline_filename: "lint-baseline.xml",
+			}
+		}
+		`
+
+	testCases := []struct {
+		testCaseName           string
+		moduleDirectory        string
+		disallowedFlagExpected bool
+	}{
+		{
+			testCaseName:           "lintable module defined outside libcore",
+			moduleDirectory:        "",
+			disallowedFlagExpected: true,
+		},
+		{
+			testCaseName:           "lintable module defined in libcore root directory",
+			moduleDirectory:        "libcore/",
+			disallowedFlagExpected: false,
+		},
+		{
+			testCaseName:           "lintable module defined in libcore child directory",
+			moduleDirectory:        "libcore/childdir/",
+			disallowedFlagExpected: true,
+		},
+	}
+
+	for _, testCase := range testCases {
+		lintFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"lint-baseline.xml", "")
+		bpFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"Android.bp", bp)
+		result := testApex(t, "", lintFileCreator, bpFileCreator)
+		myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
+		sboxProto := android.RuleBuilderSboxProtoForTests(t, result, myjavalib.Output("lint.sbox.textproto"))
+		cmdFlags := fmt.Sprintf("--baseline %vlint-baseline.xml --disallowed_issues NewApi", testCase.moduleDirectory)
+		disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, cmdFlags)
+
+		if disallowedFlagActual != testCase.disallowedFlagExpected {
+			t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command)
+		}
+	}
+}
+
+// checks transtive deps of an apex coming from bootclasspath_fragment
+func TestApexStrictUpdtabilityLintBcpFragmentDeps(t *testing.T) {
+	bp := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			bootclasspath_fragments: ["mybootclasspathfragment"],
+			updatable: true,
+			min_sdk_version: "29",
+		}
+		apex_key {
+			name: "myapex.key",
+		}
+		bootclasspath_fragment {
+			name: "mybootclasspathfragment",
+			contents: ["myjavalib"],
+			apex_available: ["myapex"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+		java_library {
+			name: "myjavalib",
+			srcs: ["MyClass.java"],
+			apex_available: [ "myapex" ],
+			sdk_version: "current",
+			min_sdk_version: "29",
+			compile_dex: true,
+			lint: {
+				baseline_filename: "lint-baseline.xml",
+			}
+		}
+		`
+	fs := android.MockFS{
+		"lint-baseline.xml": nil,
+	}
+
+	result := testApex(t, bp, dexpreopt.FixtureSetApexBootJars("myapex:myjavalib"), fs.AddToFixture())
+	myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, result, myjavalib.Output("lint.sbox.textproto"))
+	if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi") {
+		t.Errorf("Strict updabality lint missing in myjavalib coming from bootclasspath_fragment mybootclasspath-fragment\nActual lint cmd: %v", *sboxProto.Commands[0].Command)
+	}
+}
 
 // updatable apexes should propagate updatable=true to its apps
 func TestUpdatableApexEnforcesAppUpdatability(t *testing.T) {
@@ -10947,25 +11124,23 @@
 	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
 	s := mod.Rule("apexRule").Args["copy_commands"]
 	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
-	if len(copyCmds) != 5 {
+	if len(copyCmds) != 8 {
 		t.Fatalf("Expected 5 commands, got %d in:\n%s", len(copyCmds), s)
 	}
 
-	ensureMatches(t, copyCmds[4], "^cp -f .*/aconfig_flags.pb .*/image.apex$")
+	ensureMatches(t, copyCmds[4], "^cp -f .*/aconfig_flags.pb .*/image.apex/etc$")
+	ensureMatches(t, copyCmds[5], "^cp -f .*/package.map .*/image.apex/etc$")
+	ensureMatches(t, copyCmds[6], "^cp -f .*/flag.map .*/image.apex/etc$")
+	ensureMatches(t, copyCmds[7], "^cp -f .*/flag.val .*/image.apex/etc$")
 
-	combineAconfigRule := mod.Rule("All_aconfig_declarations_dump")
-	s = " " + combineAconfigRule.Args["cache_files"]
-	aconfigArgs := regexp.MustCompile(" --cache ").Split(s, -1)[1:]
-	if len(aconfigArgs) != 2 {
-		t.Fatalf("Expected 2 commands, got %d in:\n%s", len(aconfigArgs), s)
+	inputs := []string{
+		"my_aconfig_declarations_foo/intermediate.pb",
+		"my_aconfig_declarations_bar/intermediate.pb",
 	}
-	android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_foo/intermediate.pb")
-	android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_bar/intermediate.pb")
-
-	buildParams := combineAconfigRule.BuildParams
-	android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_foo/intermediate.pb")
-	android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_bar/intermediate.pb")
-	ensureContains(t, buildParams.Output.String(), "android_common_myapex/aconfig_flags.pb")
+	VerifyAconfigRule(t, &mod, "combine_aconfig_declarations", inputs, "android_common_myapex/aconfig_flags.pb", "", "")
+	VerifyAconfigRule(t, &mod, "create_aconfig_package_map_file", inputs, "android_common_myapex/package.map", "myapex", "package_map")
+	VerifyAconfigRule(t, &mod, "create_aconfig_flag_map_file", inputs, "android_common_myapex/flag.map", "myapex", "flag_map")
+	VerifyAconfigRule(t, &mod, "create_aconfig_flag_val_file", inputs, "android_common_myapex/flag.val", "myapex", "flag_val")
 }
 
 func TestAconfigFilesJavaAndCcDeps(t *testing.T) {
@@ -11073,30 +11248,24 @@
 	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
 	s := mod.Rule("apexRule").Args["copy_commands"]
 	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
-	if len(copyCmds) != 9 {
-		t.Fatalf("Expected 9 commands, got %d in:\n%s", len(copyCmds), s)
+	if len(copyCmds) != 12 {
+		t.Fatalf("Expected 12 commands, got %d in:\n%s", len(copyCmds), s)
 	}
 
-	ensureMatches(t, copyCmds[8], "^cp -f .*/aconfig_flags.pb .*/image.apex$")
+	ensureMatches(t, copyCmds[8], "^cp -f .*/aconfig_flags.pb .*/image.apex/etc$")
+	ensureMatches(t, copyCmds[9], "^cp -f .*/package.map .*/image.apex/etc$")
+	ensureMatches(t, copyCmds[10], "^cp -f .*/flag.map .*/image.apex/etc$")
+	ensureMatches(t, copyCmds[11], "^cp -f .*/flag.val .*/image.apex/etc$")
 
-	combineAconfigRule := mod.Rule("All_aconfig_declarations_dump")
-	s = " " + combineAconfigRule.Args["cache_files"]
-	aconfigArgs := regexp.MustCompile(" --cache ").Split(s, -1)[1:]
-	if len(aconfigArgs) != 3 {
-		t.Fatalf("Expected 3 commands, got %d in:\n%s", len(aconfigArgs), s)
+	inputs := []string{
+		"my_aconfig_declarations_foo/intermediate.pb",
+		"my_cc_library_bar/android_arm64_armv8-a_shared_apex10000/myapex/aconfig_merged.pb",
+		"my_aconfig_declarations_baz/intermediate.pb",
 	}
-	android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_foo/intermediate.pb")
-	android.EnsureListContainsSuffix(t, aconfigArgs, "my_cc_library_bar/android_arm64_armv8-a_shared_apex10000/aconfig_merged.pb")
-	android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_baz/intermediate.pb")
-
-	buildParams := combineAconfigRule.BuildParams
-	if len(buildParams.Inputs) != 3 {
-		t.Fatalf("Expected 3 input, got %d", len(buildParams.Inputs))
-	}
-	android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_foo/intermediate.pb")
-	android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_cc_library_bar/android_arm64_armv8-a_shared_apex10000/aconfig_merged.pb")
-	android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_baz/intermediate.pb")
-	ensureContains(t, buildParams.Output.String(), "android_common_myapex/aconfig_flags.pb")
+	VerifyAconfigRule(t, &mod, "combine_aconfig_declarations", inputs, "android_common_myapex/aconfig_flags.pb", "", "")
+	VerifyAconfigRule(t, &mod, "create_aconfig_package_map_file", inputs, "android_common_myapex/package.map", "myapex", "package_map")
+	VerifyAconfigRule(t, &mod, "create_aconfig_flag_map_file", inputs, "android_common_myapex/flag.map", "myapex", "flag_map")
+	VerifyAconfigRule(t, &mod, "create_aconfig_flag_val_file", inputs, "android_common_myapex/flag.val", "myapex", "flag_val")
 }
 
 func TestAconfigFilesRustDeps(t *testing.T) {
@@ -11220,28 +11389,45 @@
 	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
 	s := mod.Rule("apexRule").Args["copy_commands"]
 	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
-	if len(copyCmds) != 23 {
-		t.Fatalf("Expected 23 commands, got %d in:\n%s", len(copyCmds), s)
+	if len(copyCmds) != 26 {
+		t.Fatalf("Expected 26 commands, got %d in:\n%s", len(copyCmds), s)
 	}
 
-	ensureMatches(t, copyCmds[22], "^cp -f .*/aconfig_flags.pb .*/image.apex$")
+	ensureMatches(t, copyCmds[22], "^cp -f .*/aconfig_flags.pb .*/image.apex/etc$")
+	ensureMatches(t, copyCmds[23], "^cp -f .*/package.map .*/image.apex/etc$")
+	ensureMatches(t, copyCmds[24], "^cp -f .*/flag.map .*/image.apex/etc$")
+	ensureMatches(t, copyCmds[25], "^cp -f .*/flag.val .*/image.apex/etc$")
 
-	combineAconfigRule := mod.Rule("All_aconfig_declarations_dump")
-	s = " " + combineAconfigRule.Args["cache_files"]
+	inputs := []string{
+		"my_aconfig_declarations_foo/intermediate.pb",
+		"my_aconfig_declarations_bar/intermediate.pb",
+		"my_aconfig_declarations_baz/intermediate.pb",
+		"my_rust_binary/android_arm64_armv8-a_apex10000/myapex/aconfig_merged.pb",
+	}
+	VerifyAconfigRule(t, &mod, "combine_aconfig_declarations", inputs, "android_common_myapex/aconfig_flags.pb", "", "")
+	VerifyAconfigRule(t, &mod, "create_aconfig_package_map_file", inputs, "android_common_myapex/package.map", "myapex", "package_map")
+	VerifyAconfigRule(t, &mod, "create_aconfig_flag_map_file", inputs, "android_common_myapex/flag.map", "myapex", "flag_map")
+	VerifyAconfigRule(t, &mod, "create_aconfig_flag_val_file", inputs, "android_common_myapex/flag.val", "myapex", "flag_val")
+}
+
+func VerifyAconfigRule(t *testing.T, mod *android.TestingModule, desc string, inputs []string, output string, container string, file_type string) {
+	aconfigRule := mod.Description(desc)
+	s := " " + aconfigRule.Args["cache_files"]
 	aconfigArgs := regexp.MustCompile(" --cache ").Split(s, -1)[1:]
-	if len(aconfigArgs) != 2 {
-		t.Fatalf("Expected 2 commands, got %d in:\n%s", len(aconfigArgs), s)
+	if len(aconfigArgs) != len(inputs) {
+		t.Fatalf("Expected %d commands, got %d in:\n%s", len(inputs), len(aconfigArgs), s)
 	}
-	android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_foo/intermediate.pb")
-	android.EnsureListContainsSuffix(t, aconfigArgs, "my_rust_binary/android_arm64_armv8-a_apex10000/aconfig_merged.pb")
 
-	buildParams := combineAconfigRule.BuildParams
-	if len(buildParams.Inputs) != 2 {
-		t.Fatalf("Expected 3 input, got %d", len(buildParams.Inputs))
+	ensureEquals(t, container, aconfigRule.Args["container"])
+	ensureEquals(t, file_type, aconfigRule.Args["file_type"])
+
+	buildParams := aconfigRule.BuildParams
+	for _, input := range inputs {
+		android.EnsureListContainsSuffix(t, aconfigArgs, input)
+		android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), input)
 	}
-	android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_foo/intermediate.pb")
-	android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_rust_binary/android_arm64_armv8-a_apex10000/aconfig_merged.pb")
-	ensureContains(t, buildParams.Output.String(), "android_common_myapex/aconfig_flags.pb")
+
+	ensureContains(t, buildParams.Output.String(), output)
 }
 
 func TestAconfigFilesOnlyMatchCurrentApex(t *testing.T) {
@@ -11404,3 +11590,343 @@
 	android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_foo/intermediate.pb")
 	ensureContains(t, buildParams.Output.String(), "android_common_myapex/aconfig_flags.pb")
 }
+
+// Test that the boot jars come from the _selected_ apex prebuilt
+// RELEASE_APEX_CONTIRBUTIONS_* build flags will be used to select the correct prebuilt for a specific release config
+func TestBootDexJarsMultipleApexPrebuilts(t *testing.T) {
+	checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, stem string, bootDexJarPath string) {
+		t.Helper()
+		s := ctx.ModuleForTests("dex_bootjars", "android_common")
+		foundLibfooJar := false
+		base := stem + ".jar"
+		for _, output := range s.AllOutputs() {
+			if filepath.Base(output) == base {
+				foundLibfooJar = true
+				buildRule := s.Output(output)
+				android.AssertStringEquals(t, "boot dex jar path", bootDexJarPath, buildRule.Input.String())
+			}
+		}
+		if !foundLibfooJar {
+			t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs %q", android.StringPathsRelativeToTop(ctx.Config().SoongOutDir(), s.AllOutputs()))
+		}
+	}
+
+	// Check that the boot jars of the selected apex are run through boot_jars_package_check
+	// This validates that the jars on the bootclasspath do not contain packages outside an allowlist
+	checkBootJarsPackageCheck := func(t *testing.T, ctx *android.TestContext, expectedBootJar string) {
+		platformBcp := ctx.ModuleForTests("platform-bootclasspath", "android_common")
+		bootJarsCheckRule := platformBcp.Rule("boot_jars_package_check")
+		android.AssertStringMatches(t, "Could not find the correct boot dex jar in package check rule", bootJarsCheckRule.RuleParams.Command, "build/soong/scripts/check_boot_jars/package_allowed_list.txt.*"+expectedBootJar)
+	}
+
+	// Check that the boot jars used to generate the monolithic hiddenapi flags come from the selected apex
+	checkBootJarsForMonolithicHiddenapi := func(t *testing.T, ctx *android.TestContext, expectedBootJar string) {
+		monolithicHiddenapiFlagsCmd := ctx.ModuleForTests("platform-bootclasspath", "android_common").Output("out/soong/hiddenapi/hiddenapi-stub-flags.txt").RuleParams.Command
+		android.AssertStringMatches(t, "Could not find the correct boot dex jar in monolithic hiddenapi flags generation command", monolithicHiddenapiFlagsCmd, "--boot-dex="+expectedBootJar)
+	}
+
+	bp := `
+		// Source APEX.
+
+		java_library {
+			name: "framework-foo",
+			srcs: ["foo.java"],
+			installable: true,
+			apex_available: [
+				"com.android.foo",
+			],
+		}
+
+		bootclasspath_fragment {
+			name: "foo-bootclasspath-fragment",
+			contents: ["framework-foo"],
+			apex_available: [
+				"com.android.foo",
+			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+
+		apex_key {
+			name: "com.android.foo.key",
+			public_key: "com.android.foo.avbpubkey",
+			private_key: "com.android.foo.pem",
+		}
+
+		apex {
+			name: "com.android.foo",
+			key: "com.android.foo.key",
+			bootclasspath_fragments: ["foo-bootclasspath-fragment"],
+			updatable: false,
+		}
+
+		// Prebuilt APEX.
+
+		java_sdk_library_import {
+			name: "framework-foo",
+			public: {
+				jars: ["foo.jar"],
+			},
+			apex_available: ["com.android.foo"],
+			shared_library: false,
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "foo-bootclasspath-fragment",
+			contents: ["framework-foo"],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
+			apex_available: [
+				"com.android.foo",
+			],
+		}
+
+		prebuilt_apex {
+			name: "com.android.foo",
+			apex_name: "com.android.foo",
+			src: "com.android.foo-arm.apex",
+			exported_bootclasspath_fragments: ["foo-bootclasspath-fragment"],
+		}
+
+		// Another Prebuilt ART APEX
+		prebuilt_apex {
+			name: "com.android.foo.v2",
+			apex_name: "com.android.foo", // Used to determine the API domain
+			src: "com.android.foo-arm.apex",
+			exported_bootclasspath_fragments: ["foo-bootclasspath-fragment"],
+		}
+
+		// APEX contribution modules
+
+		apex_contributions {
+			name: "foo.source.contributions",
+			api_domain: "com.android.foo",
+			contents: ["com.android.foo"],
+		}
+
+		apex_contributions {
+			name: "foo.prebuilt.contributions",
+			api_domain: "com.android.foo",
+			contents: ["prebuilt_com.android.foo"],
+		}
+
+		apex_contributions {
+			name: "foo.prebuilt.v2.contributions",
+			api_domain: "com.android.foo",
+			contents: ["com.android.foo.v2"], // prebuilt_ prefix is missing because of prebuilt_rename mutator
+		}
+	`
+
+	testCases := []struct {
+		desc                      string
+		selectedApexContributions string
+		expectedBootJar           string
+	}{
+		{
+			desc:                      "Source apex com.android.foo is selected, bootjar should come from source java library",
+			selectedApexContributions: "foo.source.contributions",
+			expectedBootJar:           "out/soong/.intermediates/foo-bootclasspath-fragment/android_common_apex10000/hiddenapi-modular/encoded/framework-foo.jar",
+		},
+		{
+			desc:                      "Prebuilt apex prebuilt_com.android.foo is selected, profile should come from .prof deapexed from the prebuilt",
+			selectedApexContributions: "foo.prebuilt.contributions",
+			expectedBootJar:           "out/soong/.intermediates/prebuilt_com.android.foo.deapexer/android_common/deapexer/javalib/framework-foo.jar",
+		},
+		{
+			desc:                      "Prebuilt apex prebuilt_com.android.foo.v2 is selected, profile should come from .prof deapexed from the prebuilt",
+			selectedApexContributions: "foo.prebuilt.v2.contributions",
+			expectedBootJar:           "out/soong/.intermediates/prebuilt_com.android.foo.v2.deapexer/android_common/deapexer/javalib/framework-foo.jar",
+		},
+	}
+
+	fragment := java.ApexVariantReference{
+		Apex:   proptools.StringPtr("com.android.foo"),
+		Module: proptools.StringPtr("foo-bootclasspath-fragment"),
+	}
+
+	for _, tc := range testCases {
+		preparer := android.GroupFixturePreparers(
+			java.FixtureConfigureApexBootJars("com.android.foo:framework-foo"),
+			android.FixtureMergeMockFs(map[string][]byte{
+				"system/sepolicy/apex/com.android.foo-file_contexts": nil,
+			}),
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.BuildFlags = map[string]string{
+					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": tc.selectedApexContributions,
+				}
+			}),
+		)
+		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
+		checkBootDexJarPath(t, ctx, "framework-foo", tc.expectedBootJar)
+		checkBootJarsPackageCheck(t, ctx, tc.expectedBootJar)
+		checkBootJarsForMonolithicHiddenapi(t, ctx, tc.expectedBootJar)
+	}
+}
+
+// Test that product packaging installs the selected mainline module (either source or a specific prebuilt)
+// RELEASE_APEX_CONTIRBUTIONS_* build flags will be used to select the correct prebuilt for a specific release config
+func TestInstallationRulesForMultipleApexPrebuilts(t *testing.T) {
+	// check that the LOCAL_MODULE in the generated mk file matches the name used in PRODUCT_PACKAGES
+	// Since the name used in PRODUCT_PACKAGES does not contain prebuilt_ prefix, LOCAL_MODULE should not contain any prefix either
+	checkLocalModuleName := func(t *testing.T, ctx *android.TestContext, soongApexModuleName string, expectedLocalModuleName string) {
+		// Variations are created based on apex_name
+		entries := android.AndroidMkEntriesForTest(t, ctx, ctx.ModuleForTests(soongApexModuleName, "android_common_com.android.foo").Module())
+		android.AssertStringEquals(t, "LOCAL_MODULE of the prebuilt apex must match the name listed in PRODUCT_PACKAGES", expectedLocalModuleName, entries[0].EntryMap["LOCAL_MODULE"][0])
+	}
+	// for a mainline module family, check that only the flagged soong module is visible to make
+	checkHideFromMake := func(t *testing.T, ctx *android.TestContext, visibleModuleName string, hiddenModuleNames []string) {
+		variation := func(moduleName string) string {
+			ret := "android_common_com.android.foo"
+			if moduleName == "com.google.android.foo" {
+				ret = "android_common_com.google.android.foo_com.android.foo"
+			}
+			return ret
+		}
+
+		visibleModule := ctx.ModuleForTests(visibleModuleName, variation(visibleModuleName)).Module()
+		android.AssertBoolEquals(t, "Apex "+visibleModuleName+" selected using apex_contributions should be visible to make", false, visibleModule.IsHideFromMake())
+
+		for _, hiddenModuleName := range hiddenModuleNames {
+			hiddenModule := ctx.ModuleForTests(hiddenModuleName, variation(hiddenModuleName)).Module()
+			android.AssertBoolEquals(t, "Apex "+hiddenModuleName+" not selected using apex_contributions should be hidden from make", true, hiddenModule.IsHideFromMake())
+
+		}
+	}
+
+	bp := `
+		apex_key {
+			name: "com.android.foo.key",
+			public_key: "com.android.foo.avbpubkey",
+			private_key: "com.android.foo.pem",
+		}
+
+		// AOSP source apex
+		apex {
+			name: "com.android.foo",
+			key: "com.android.foo.key",
+			updatable: false,
+		}
+
+		// Google source apex
+		override_apex {
+			name: "com.google.android.foo",
+			base: "com.android.foo",
+			key: "com.android.foo.key",
+		}
+
+		// Prebuilt Google APEX.
+
+		prebuilt_apex {
+			name: "com.google.android.foo",
+			apex_name: "com.android.foo",
+			src: "com.android.foo-arm.apex",
+			prefer: true, // prefer is set to true on both the prebuilts to induce an error if flagging is not present
+		}
+
+		// Another Prebuilt Google APEX
+		prebuilt_apex {
+			name: "com.google.android.foo.v2",
+			apex_name: "com.android.foo",
+			source_apex_name: "com.google.android.foo", // source_apex_name becomes LOCAL_MODULE in the generated mk file
+			src: "com.android.foo-arm.apex",
+			prefer: true, // prefer is set to true on both the prebuilts to induce an error if flagging is not present
+		}
+
+		// APEX contribution modules
+
+		apex_contributions {
+			name: "foo.source.contributions",
+			api_domain: "com.android.foo",
+			contents: ["com.google.android.foo"],
+		}
+
+		apex_contributions {
+			name: "foo.prebuilt.contributions",
+			api_domain: "com.android.foo",
+			contents: ["prebuilt_com.google.android.foo"],
+		}
+
+		apex_contributions {
+			name: "foo.prebuilt.v2.contributions",
+			api_domain: "com.android.foo",
+			contents: ["prebuilt_com.google.android.foo.v2"],
+		}
+
+		// This is an incompatible module because it selects multiple versions of the same mainline module
+		apex_contributions {
+			name: "foo.prebuilt.duplicate.contributions",
+			api_domain: "com.android.foo",
+			contents: [
+			    "prebuilt_com.google.android.foo",
+			    "prebuilt_com.google.android.foo.v2",
+			],
+		}
+	`
+
+	testCases := []struct {
+		desc                      string
+		selectedApexContributions string
+		expectedVisibleModuleName string
+		expectedHiddenModuleNames []string
+		expectedError             string
+	}{
+		{
+			desc:                      "Source apex is selected, prebuilts should be hidden from make",
+			selectedApexContributions: "foo.source.contributions",
+			expectedVisibleModuleName: "com.google.android.foo",
+			expectedHiddenModuleNames: []string{"prebuilt_com.google.android.foo", "prebuilt_com.google.android.foo.v2"},
+		},
+		{
+			desc:                      "Prebuilt apex prebuilt_com.android.foo is selected, source and the other prebuilt should be hidden from make",
+			selectedApexContributions: "foo.prebuilt.contributions",
+			expectedVisibleModuleName: "prebuilt_com.google.android.foo",
+			expectedHiddenModuleNames: []string{"com.google.android.foo", "prebuilt_com.google.android.foo.v2"},
+		},
+		{
+			desc:                      "Prebuilt apex prebuilt_com.android.fooi.v2 is selected, source and the other prebuilt should be hidden from make",
+			selectedApexContributions: "foo.prebuilt.v2.contributions",
+			expectedVisibleModuleName: "prebuilt_com.google.android.foo.v2",
+			expectedHiddenModuleNames: []string{"com.google.android.foo", "prebuilt_com.google.android.foo"},
+		},
+		{
+			desc:                      "Multiple versions of a prebuilt apex is selected in the same release config",
+			selectedApexContributions: "foo.prebuilt.duplicate.contributions",
+			expectedError:             "Found duplicate variations of the same module in apex_contributions: prebuilt_com.google.android.foo and prebuilt_com.google.android.foo.v2",
+		},
+	}
+
+	for _, tc := range testCases {
+		preparer := android.GroupFixturePreparers(
+			android.FixtureMergeMockFs(map[string][]byte{
+				"system/sepolicy/apex/com.android.foo-file_contexts": nil,
+			}),
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.BuildFlags = map[string]string{
+					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": tc.selectedApexContributions,
+				}
+			}),
+		)
+		if tc.expectedError != "" {
+			preparer = preparer.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(tc.expectedError))
+			testApex(t, bp, preparer)
+			return
+		}
+		ctx := testApex(t, bp, preparer)
+
+		// Check that the LOCAL_MODULE of the two prebuilts is com.android.foo
+		// This ensures that product packaging can pick them for installation if it has been flagged by apex_contributions
+		checkLocalModuleName(t, ctx, "prebuilt_com.google.android.foo", "com.google.android.foo")
+		checkLocalModuleName(t, ctx, "prebuilt_com.google.android.foo.v2", "com.google.android.foo")
+
+		// Check that
+		// 1. The contents of the selected apex_contributions are visible to make
+		// 2. The rest of the apexes in the mainline module family (source or other prebuilt) is hidden from make
+		checkHideFromMake(t, ctx, tc.expectedVisibleModuleName, tc.expectedHiddenModuleNames)
+	}
+}
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 43be310..778c20a 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -53,7 +53,11 @@
 		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz"),
 		java.FixtureConfigureApexBootJars("someapex:foo", "someapex:bar"),
 		prepareForTestWithArtApex,
-
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+			}
+		}),
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo", "baz"),
 	).RunTestWithBp(t, `
@@ -152,11 +156,11 @@
 
 	// Check stub dex paths exported by art.
 	artFragment := result.Module("art-bootclasspath-fragment", "android_common")
-	artInfo := result.ModuleProvider(artFragment, java.HiddenAPIInfoProvider).(java.HiddenAPIInfo)
+	artInfo, _ := android.SingletonModuleProvider(result, artFragment, java.HiddenAPIInfoProvider)
 
-	bazPublicStubs := "out/soong/.intermediates/baz.stubs/android_common/dex/baz.stubs.jar"
-	bazSystemStubs := "out/soong/.intermediates/baz.stubs.system/android_common/dex/baz.stubs.system.jar"
-	bazTestStubs := "out/soong/.intermediates/baz.stubs.test/android_common/dex/baz.stubs.test.jar"
+	bazPublicStubs := "out/soong/.intermediates/baz.stubs.exportable/android_common/dex/baz.stubs.exportable.jar"
+	bazSystemStubs := "out/soong/.intermediates/baz.stubs.exportable.system/android_common/dex/baz.stubs.exportable.system.jar"
+	bazTestStubs := "out/soong/.intermediates/baz.stubs.exportable.test/android_common/dex/baz.stubs.exportable.test.jar"
 
 	checkAPIScopeStubs("art", artInfo, java.PublicHiddenAPIScope, bazPublicStubs)
 	checkAPIScopeStubs("art", artInfo, java.SystemHiddenAPIScope, bazSystemStubs)
@@ -165,10 +169,10 @@
 
 	// Check stub dex paths exported by other.
 	otherFragment := result.Module("other-bootclasspath-fragment", "android_common")
-	otherInfo := result.ModuleProvider(otherFragment, java.HiddenAPIInfoProvider).(java.HiddenAPIInfo)
+	otherInfo, _ := android.SingletonModuleProvider(result, otherFragment, java.HiddenAPIInfoProvider)
 
-	fooPublicStubs := "out/soong/.intermediates/foo.stubs/android_common/dex/foo.stubs.jar"
-	fooSystemStubs := "out/soong/.intermediates/foo.stubs.system/android_common/dex/foo.stubs.system.jar"
+	fooPublicStubs := "out/soong/.intermediates/foo.stubs.exportable/android_common/dex/foo.stubs.exportable.jar"
+	fooSystemStubs := "out/soong/.intermediates/foo.stubs.exportable.system/android_common/dex/foo.stubs.exportable.system.jar"
 
 	checkAPIScopeStubs("other", otherInfo, java.PublicHiddenAPIScope, bazPublicStubs, fooPublicStubs)
 	checkAPIScopeStubs("other", otherInfo, java.SystemHiddenAPIScope, bazSystemStubs, fooSystemStubs)
@@ -312,6 +316,7 @@
 		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
 			`art-bootclasspath-fragment`,
 			`com.android.art.key`,
+			`dex2oatd`,
 		})
 
 		// Make sure that the source bootclasspath_fragment copies its dex files to the predefined
@@ -374,7 +379,7 @@
 			java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
 		).RunTest(t)
 
-		ensureExactDeapexedContents(t, result.TestContext, "com.android.art", "android_common", []string{
+		ensureExactDeapexedContents(t, result.TestContext, "prebuilt_com.android.art", "android_common", []string{
 			"etc/boot-image.prof",
 			"javalib/bar.jar",
 			"javalib/foo.jar",
@@ -383,6 +388,7 @@
 		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
 			`art-bootclasspath-fragment`,
 			`com.android.art.key`,
+			`dex2oatd`,
 			`prebuilt_com.android.art`,
 		})
 
@@ -529,14 +535,16 @@
 		result := preparers.RunTestWithBp(t, fmt.Sprintf(bp, "enabled: false,"))
 
 		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
-			`com.android.art.apex.selector`,
+			`dex2oatd`,
 			`prebuilt_art-bootclasspath-fragment`,
+			`prebuilt_com.android.art.apex.selector`,
+			`prebuilt_com.android.art.deapexer`,
 		})
 
 		java.CheckModuleDependencies(t, result.TestContext, "art-bootclasspath-fragment", "android_common_com.android.art", []string{
-			`com.android.art.deapexer`,
 			`dex2oatd`,
 			`prebuilt_bar`,
+			`prebuilt_com.android.art.deapexer`,
 			`prebuilt_foo`,
 		})
 
@@ -546,7 +554,7 @@
 
 	t.Run("enabled alternative APEX", func(t *testing.T) {
 		preparers.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
-			"Multiple installable prebuilt APEXes provide ambiguous deapexers: com.android.art and com.mycompany.android.art")).
+			"Multiple installable prebuilt APEXes provide ambiguous deapexers: prebuilt_com.android.art and prebuilt_com.mycompany.android.art")).
 			RunTestWithBp(t, fmt.Sprintf(bp, ""))
 	})
 }
@@ -644,6 +652,7 @@
 	})
 
 	java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex", []string{
+		`dex2oatd`,
 		`myapex.key`,
 		`mybootclasspathfragment`,
 	})
@@ -655,7 +664,7 @@
 	// Make sure that the fragment provides the hidden API encoded dex jars to the APEX.
 	fragment := result.Module("mybootclasspathfragment", "android_common_apex10000")
 
-	info := result.ModuleProvider(fragment, java.BootclasspathFragmentApexContentInfoProvider).(java.BootclasspathFragmentApexContentInfo)
+	info, _ := android.SingletonModuleProvider(result, fragment, java.BootclasspathFragmentApexContentInfoProvider)
 
 	checkFragmentExportedDexJar := func(name string, expectedDexJar string) {
 		module := result.Module(name, "android_common_apex10000")
@@ -675,7 +684,7 @@
 
 func getDexJarPath(result *android.TestResult, name string) string {
 	module := result.Module(name, "android_common")
-	return module.(java.UsesLibraryDependency).DexJarBuildPath().Path().RelativeToTop().String()
+	return module.(java.UsesLibraryDependency).DexJarBuildPath(moduleErrorfTestCtx{}).Path().RelativeToTop().String()
 }
 
 // TestBootclasspathFragment_HiddenAPIList checks to make sure that the correct parameters are
@@ -694,6 +703,11 @@
 
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo", "quuz"),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+			}
+		}),
 	).RunTestWithBp(t, `
 		apex {
 			name: "com.android.art",
@@ -805,11 +819,11 @@
 		"foo",
 	})
 
-	fooStubs := getDexJarPath(result, "foo.stubs")
-	quuzPublicStubs := getDexJarPath(result, "quuz.stubs")
-	quuzSystemStubs := getDexJarPath(result, "quuz.stubs.system")
-	quuzTestStubs := getDexJarPath(result, "quuz.stubs.test")
-	quuzModuleLibStubs := getDexJarPath(result, "quuz.stubs.module_lib")
+	fooStubs := getDexJarPath(result, "foo.stubs.exportable")
+	quuzPublicStubs := getDexJarPath(result, "quuz.stubs.exportable")
+	quuzSystemStubs := getDexJarPath(result, "quuz.stubs.exportable.system")
+	quuzTestStubs := getDexJarPath(result, "quuz.stubs.exportable.test")
+	quuzModuleLibStubs := getDexJarPath(result, "quuz.stubs.exportable.module_lib")
 
 	// Make sure that the fragment uses the quuz stub dex jars when generating the hidden API flags.
 	fragment := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
diff --git a/apex/builder.go b/apex/builder.go
index 3078863..6ad282a 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -85,6 +85,18 @@
 	pctx.HostBinToolVariable("aconfig", "aconfig")
 }
 
+type createStorageStruct struct {
+	Output_file string
+	Desc        string
+	File_type   string
+}
+
+var createStorageInfo = []createStorageStruct{
+	{"package.map", "create_aconfig_package_map_file", "package_map"},
+	{"flag.map", "create_aconfig_flag_map_file", "flag_map"},
+	{"flag.val", "create_aconfig_flag_val_file", "flag_val"},
+}
+
 var (
 	apexManifestRule = pctx.StaticRule("apexManifestRule", blueprint.RuleParams{
 		Command: `rm -f $out && ${jsonmodify} $in ` +
@@ -633,6 +645,7 @@
 	prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
 
 	defaultReadOnlyFiles := []string{"apex_manifest.json", "apex_manifest.pb"}
+	aconfigDest := imageDir.Join(ctx, "etc").String()
 	if len(a.aconfigFiles) > 0 {
 		apexAconfigFile := android.PathForModuleOut(ctx, "aconfig_flags.pb")
 		ctx.Build(pctx, android.BuildParams{
@@ -645,9 +658,28 @@
 			},
 		})
 
-		copyCommands = append(copyCommands, "cp -f "+apexAconfigFile.String()+" "+imageDir.String())
+		copyCommands = append(copyCommands, "cp -f "+apexAconfigFile.String()+" "+aconfigDest)
 		implicitInputs = append(implicitInputs, apexAconfigFile)
-		defaultReadOnlyFiles = append(defaultReadOnlyFiles, apexAconfigFile.Base())
+		defaultReadOnlyFiles = append(defaultReadOnlyFiles, "etc/"+apexAconfigFile.Base())
+
+		for _, info := range createStorageInfo {
+			outputFile := android.PathForModuleOut(ctx, info.Output_file)
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        aconfig.CreateStorageRule,
+				Inputs:      a.aconfigFiles,
+				Output:      outputFile,
+				Description: info.Desc,
+				Args: map[string]string{
+					"container":   ctx.ModuleName(),
+					"file_type":   info.File_type,
+					"cache_files": android.JoinPathsWithPrefix(a.aconfigFiles, "--cache "),
+				},
+			})
+
+			copyCommands = append(copyCommands, "cp -f "+outputFile.String()+" "+aconfigDest)
+			implicitInputs = append(implicitInputs, outputFile)
+			defaultReadOnlyFiles = append(defaultReadOnlyFiles, "etc/"+outputFile.Base())
+		}
 	}
 
 	////////////////////////////////////////////////////////////////////////////////////
@@ -695,18 +727,20 @@
 	if moduleMinSdkVersion.IsCurrent() || moduleMinSdkVersion.IsNone() {
 		minSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String()
 
-		if java.UseApiFingerprint(ctx) {
-			minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", java.ApiFingerprintPath(ctx).String())
-			implicitInputs = append(implicitInputs, java.ApiFingerprintPath(ctx))
+		if useApiFingerprint, fingerprintMinSdkVersion, fingerprintDeps :=
+			java.UseApiFingerprint(ctx); useApiFingerprint {
+			minSdkVersion = fingerprintMinSdkVersion
+			implicitInputs = append(implicitInputs, fingerprintDeps)
 		}
 	}
 	// apex module doesn't have a concept of target_sdk_version, hence for the time
 	// being targetSdkVersion == default targetSdkVersion of the branch.
 	targetSdkVersion := strconv.Itoa(ctx.Config().DefaultAppTargetSdk(ctx).FinalOrFutureInt())
 
-	if java.UseApiFingerprint(ctx) {
-		targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", java.ApiFingerprintPath(ctx).String())
-		implicitInputs = append(implicitInputs, java.ApiFingerprintPath(ctx))
+	if useApiFingerprint, fingerprintTargetSdkVersion, fingerprintDeps :=
+		java.UseApiFingerprint(ctx); useApiFingerprint {
+		targetSdkVersion = fingerprintTargetSdkVersion
+		implicitInputs = append(implicitInputs, fingerprintDeps)
 	}
 	optFlags = append(optFlags, "--target_sdk_version "+targetSdkVersion)
 	optFlags = append(optFlags, "--min_sdk_version "+minSdkVersion)
@@ -910,7 +944,7 @@
 	var validations android.Paths
 	validations = append(validations, runApexLinkerconfigValidation(ctx, unsignedOutputFile.OutputPath, imageDir.OutputPath))
 	// TODO(b/279688635) deapexer supports [ext4]
-	if suffix == imageApexSuffix && ext4 == a.payloadFsType {
+	if !a.testApex && suffix == imageApexSuffix && ext4 == a.payloadFsType {
 		validations = append(validations, runApexSepolicyTests(ctx, unsignedOutputFile.OutputPath))
 	}
 	if !a.testApex && len(a.properties.Unwanted_transitive_deps) > 0 {
diff --git a/apex/classpath_element_test.go b/apex/classpath_element_test.go
index 9142eed..b9a9198 100644
--- a/apex/classpath_element_test.go
+++ b/apex/classpath_element_test.go
@@ -20,7 +20,6 @@
 
 	"android/soong/android"
 	"android/soong/java"
-	"github.com/google/blueprint"
 )
 
 // Contains tests for java.CreateClasspathElements logic from java/classpath_element.go that
@@ -28,19 +27,12 @@
 
 // testClasspathElementContext is a ClasspathElementContext suitable for use in tests.
 type testClasspathElementContext struct {
+	android.OtherModuleProviderContext
 	testContext *android.TestContext
 	module      android.Module
 	errs        []error
 }
 
-func (t *testClasspathElementContext) OtherModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool {
-	return t.testContext.ModuleHasProvider(module, provider)
-}
-
-func (t *testClasspathElementContext) OtherModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{} {
-	return t.testContext.ModuleProvider(module, provider)
-}
-
 func (t *testClasspathElementContext) ModuleErrorf(fmt string, args ...interface{}) {
 	t.errs = append(t.errs, t.testContext.ModuleErrorf(t.module, fmt, args...))
 }
@@ -238,7 +230,11 @@
 	}
 
 	newCtx := func() *testClasspathElementContext {
-		return &testClasspathElementContext{testContext: result.TestContext, module: bootclasspath}
+		return &testClasspathElementContext{
+			OtherModuleProviderContext: result.TestContext.OtherModuleProviderAdaptor(),
+			testContext:                result.TestContext,
+			module:                     bootclasspath,
+		}
 	}
 
 	// Verify that CreateClasspathElements works when given valid input.
diff --git a/apex/deapexer.go b/apex/deapexer.go
index 3b7c77d..a673108 100644
--- a/apex/deapexer.go
+++ b/apex/deapexer.go
@@ -53,6 +53,10 @@
 	// all architectures, e.g. java.
 	CommonModules []string
 
+	// List of modules that use an embedded .prof to guide optimization of the equivalent dexpreopt artifact
+	// This is a subset of CommonModules
+	DexpreoptProfileGuidedModules []string
+
 	// List of files exported from the .apex file by this module
 	//
 	// Each entry is a path from the apex root, e.g. javalib/core-libart.jar.
@@ -98,6 +102,7 @@
 func (p *Deapexer) DepsMutator(ctx android.BottomUpMutatorContext) {
 	// Add dependencies from the java modules to which this exports files from the `.apex` file onto
 	// this module so that they can access the `DeapexerInfo` object that this provides.
+	// TODO: b/308174306 - Once all the mainline modules have been flagged, drop this dependency edge
 	for _, lib := range p.properties.CommonModules {
 		dep := prebuiltApexExportedModuleName(ctx, lib)
 		ctx.AddReverseDependency(ctx.Module(), android.DeapexerTag, dep)
@@ -126,8 +131,9 @@
 	// apex relative path to extracted file path available for other modules.
 	if len(exports) > 0 {
 		// Make the information available for other modules.
-		di := android.NewDeapexerInfo(apexModuleName(ctx.ModuleName()), exports)
-		ctx.SetProvider(android.DeapexerProvider, di)
+		di := android.NewDeapexerInfo(apexModuleName(ctx.ModuleName()), exports, p.properties.CommonModules)
+		di.AddDexpreoptProfileGuidedExportedModuleNames(p.properties.DexpreoptProfileGuidedModules...)
+		android.SetProvider(ctx, android.DeapexerProvider, di)
 
 		// Create a sorted list of the files that this exports.
 		exportedPaths = android.SortedUniquePaths(exportedPaths)
diff --git a/apex/dexpreopt_bootjars_test.go b/apex/dexpreopt_bootjars_test.go
index 2e828ca..7a17f50 100644
--- a/apex/dexpreopt_bootjars_test.go
+++ b/apex/dexpreopt_bootjars_test.go
@@ -164,6 +164,7 @@
 		"out/soong/dexpreopt_arm64/dex_bootjars_input/baz.jar",
 		"out/soong/.intermediates/art-bootclasspath-fragment/android_common_apex10000/art-bootclasspath-fragment/boot.prof",
 		"out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof",
+		"out/soong/dexpreopt/uffd_gc_flag.txt",
 	}
 
 	expectedOutputs := []string{
@@ -199,8 +200,9 @@
 		"out/soong/dexpreopt_arm64/dex_bootjars_input/foo.jar",
 		"out/soong/dexpreopt_arm64/dex_bootjars_input/bar.jar",
 		"out/soong/dexpreopt_arm64/dex_bootjars_input/baz.jar",
-		"out/soong/.intermediates/com.android.art.deapexer/android_common/deapexer/etc/boot-image.prof",
+		"out/soong/.intermediates/prebuilt_com.android.art.deapexer/android_common/deapexer/etc/boot-image.prof",
 		"out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof",
+		"out/soong/dexpreopt/uffd_gc_flag.txt",
 	}
 
 	expectedOutputs := []string{
@@ -252,3 +254,162 @@
 
 	testDexpreoptBoot(t, ruleFile, expectedInputs, expectedOutputs, false)
 }
+
+// Multiple ART apexes might exist in the tree.
+// The profile should correspond to the apex selected using release build flags
+func TestDexpreoptProfileWithMultiplePrebuiltArtApexes(t *testing.T) {
+	ruleFile := "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art"
+	bp := `
+		// Platform.
+
+		platform_bootclasspath {
+			name: "platform-bootclasspath",
+			fragments: [
+				{
+					apex: "com.android.art",
+					module: "art-bootclasspath-fragment",
+				},
+			],
+		}
+
+		// Source ART APEX.
+
+		java_library {
+			name: "core-oj",
+			srcs: ["core-oj.java"],
+			installable: true,
+			apex_available: [
+				"com.android.art",
+			],
+		}
+
+		bootclasspath_fragment {
+			name: "art-bootclasspath-fragment",
+			image_name: "art",
+			contents: ["core-oj"],
+			apex_available: [
+				"com.android.art",
+			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+
+		apex_key {
+			name: "com.android.art.key",
+			public_key: "com.android.art.avbpubkey",
+			private_key: "com.android.art.pem",
+		}
+
+		apex {
+			name: "com.android.art",
+			key: "com.android.art.key",
+			bootclasspath_fragments: ["art-bootclasspath-fragment"],
+			updatable: false,
+		}
+
+		// Prebuilt ART APEX.
+
+		java_import {
+			name: "core-oj",
+			jars: ["core-oj.jar"],
+			apex_available: [
+				"com.android.art",
+			],
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "art-bootclasspath-fragment",
+			image_name: "art",
+			contents: ["core-oj"],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
+			apex_available: [
+				"com.android.art",
+			],
+		}
+
+		prebuilt_apex {
+			name: "com.android.art",
+			apex_name: "com.android.art",
+			src: "com.android.art-arm.apex",
+			exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
+		}
+
+		// Another Prebuilt ART APEX
+		prebuilt_apex {
+			name: "com.android.art.v2",
+			apex_name: "com.android.art", // Used to determine the API domain
+			src: "com.android.art-arm.apex",
+			exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
+		}
+
+		// APEX contribution modules
+
+		apex_contributions {
+			name: "art.source.contributions",
+			api_domain: "com.android.art",
+			contents: ["com.android.art"],
+		}
+
+		apex_contributions {
+			name: "art.prebuilt.contributions",
+			api_domain: "com.android.art",
+			contents: ["prebuilt_com.android.art"],
+		}
+
+		apex_contributions {
+			name: "art.prebuilt.v2.contributions",
+			api_domain: "com.android.art",
+			contents: ["com.android.art.v2"], // prebuilt_ prefix is missing because of prebuilt_rename mutator
+		}
+
+	`
+
+	testCases := []struct {
+		desc                         string
+		selectedArtApexContributions string
+		expectedProfile              string
+	}{
+		{
+			desc:                         "Source apex com.android.art is selected, profile should come from source java library",
+			selectedArtApexContributions: "art.source.contributions",
+			expectedProfile:              "out/soong/.intermediates/art-bootclasspath-fragment/android_common_apex10000/art-bootclasspath-fragment/boot.prof",
+		},
+		{
+			desc:                         "Prebuilt apex prebuilt_com.android.art is selected, profile should come from .prof deapexed from the prebuilt",
+			selectedArtApexContributions: "art.prebuilt.contributions",
+			expectedProfile:              "out/soong/.intermediates/prebuilt_com.android.art.deapexer/android_common/deapexer/etc/boot-image.prof",
+		},
+		{
+			desc:                         "Prebuilt apex prebuilt_com.android.art.v2 is selected, profile should come from .prof deapexed from the prebuilt",
+			selectedArtApexContributions: "art.prebuilt.v2.contributions",
+			expectedProfile:              "out/soong/.intermediates/prebuilt_com.android.art.v2.deapexer/android_common/deapexer/etc/boot-image.prof",
+		},
+	}
+	for _, tc := range testCases {
+		result := android.GroupFixturePreparers(
+			java.PrepareForTestWithDexpreopt,
+			java.PrepareForTestWithJavaSdkLibraryFiles,
+			java.FixtureConfigureBootJars("com.android.art:core-oj"),
+			PrepareForTestWithApexBuildComponents,
+			prepareForTestWithArtApex,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.BuildFlags = map[string]string{
+					"RELEASE_APEX_CONTRIBUTIONS_ART": tc.selectedArtApexContributions,
+				}
+			}),
+		).RunTestWithBp(t, bp)
+
+		dexBootJars := result.ModuleForTests("dex_bootjars", "android_common")
+		rule := dexBootJars.Output(ruleFile)
+
+		inputs := rule.Implicits.Strings()
+		android.AssertStringListContains(t, tc.desc, inputs, tc.expectedProfile)
+	}
+}
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 05bb136..2be9c10 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -86,6 +86,7 @@
 					"bar-fragment",
 				],
 				updatable: false,
+				min_sdk_version: "30", // R
 			}
 
 			apex_key {
@@ -138,6 +139,7 @@
 				sdk_version: "none",
 				compile_dex: true,
 				permitted_packages: ["bar"],
+				min_sdk_version: "30", // R
 			}
 
 			java_sdk_library {
@@ -152,22 +154,22 @@
 	).RunTest(t)
 
 	pbcp := result.Module("platform-bootclasspath", "android_common")
-	info := result.ModuleProvider(pbcp, java.MonolithicHiddenAPIInfoProvider).(java.MonolithicHiddenAPIInfo)
+	info, _ := android.SingletonModuleProvider(result, pbcp, java.MonolithicHiddenAPIInfoProvider)
 
 	for _, category := range java.HiddenAPIFlagFileCategories {
-		name := category.PropertyName
+		name := category.PropertyName()
 		message := fmt.Sprintf("category %s", name)
 		filename := strings.ReplaceAll(name, "_", "-")
 		expected := []string{fmt.Sprintf("%s.txt", filename), fmt.Sprintf("bar-%s.txt", filename)}
 		android.AssertPathsRelativeToTopEquals(t, message, expected, info.FlagsFilesByCategory[category])
 	}
 
-	android.AssertPathsRelativeToTopEquals(t, "annotation flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/annotation-flags.csv"}, info.AnnotationFlagsPaths)
-	android.AssertPathsRelativeToTopEquals(t, "metadata flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/metadata.csv"}, info.MetadataPaths)
-	android.AssertPathsRelativeToTopEquals(t, "index flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/index.csv"}, info.IndexPaths)
+	android.AssertPathsRelativeToTopEquals(t, "annotation flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex30/modular-hiddenapi/annotation-flags.csv"}, info.AnnotationFlagsPaths)
+	android.AssertPathsRelativeToTopEquals(t, "metadata flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex30/modular-hiddenapi/metadata.csv"}, info.MetadataPaths)
+	android.AssertPathsRelativeToTopEquals(t, "index flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex30/modular-hiddenapi/index.csv"}, info.IndexPaths)
 
-	android.AssertArrayString(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/filtered-stub-flags.csv:out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/signature-patterns.csv"}, info.StubFlagSubsets.RelativeToTop())
-	android.AssertArrayString(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/filtered-flags.csv:out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/signature-patterns.csv"}, info.FlagSubsets.RelativeToTop())
+	android.AssertArrayString(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex30/modular-hiddenapi/filtered-stub-flags.csv:out/soong/.intermediates/bar-fragment/android_common_apex30/modular-hiddenapi/signature-patterns.csv"}, info.StubFlagSubsets.RelativeToTop())
+	android.AssertArrayString(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex30/modular-hiddenapi/filtered-flags.csv:out/soong/.intermediates/bar-fragment/android_common_apex30/modular-hiddenapi/signature-patterns.csv"}, info.FlagSubsets.RelativeToTop())
 }
 
 // TestPlatformBootclasspath_LegacyPrebuiltFragment verifies that the
@@ -234,7 +236,7 @@
 	)
 
 	pbcp := result.Module("myplatform-bootclasspath", "android_common")
-	info := result.ModuleProvider(pbcp, java.MonolithicHiddenAPIInfoProvider).(java.MonolithicHiddenAPIInfo)
+	info, _ := android.SingletonModuleProvider(result, pbcp, java.MonolithicHiddenAPIInfoProvider)
 
 	android.AssertArrayString(t, "stub flags", []string{"prebuilt-stub-flags.csv:out/soong/.intermediates/mybootclasspath-fragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv"}, info.StubFlagSubsets.RelativeToTop())
 	android.AssertArrayString(t, "all flags", []string{"prebuilt-all-flags.csv:out/soong/.intermediates/mybootclasspath-fragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv"}, info.FlagSubsets.RelativeToTop())
@@ -252,6 +254,11 @@
 		java.FixtureWithLastReleaseApis("foo"),
 		java.PrepareForTestWithDexpreopt,
 		dexpreopt.FixtureDisableDexpreoptBootImages(false),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+			}
+		}),
 	).RunTestWithBp(t, `
 		apex {
 			name: "com.android.art",
@@ -382,11 +389,14 @@
 
 	// Make sure that the myplatform-bootclasspath has the correct dependencies.
 	CheckModuleDependencies(t, result.TestContext, "myplatform-bootclasspath", "android_common", []string{
+		// source vs prebuilt selection metadata module
+		`platform:all_apex_contributions`,
+
 		// The following are stubs.
-		`platform:android_stubs_current`,
-		`platform:android_system_stubs_current`,
-		`platform:android_test_stubs_current`,
-		`platform:legacy.core.platform.api.stubs`,
+		`platform:android_stubs_current_exportable`,
+		`platform:android_system_stubs_current_exportable`,
+		`platform:android_test_stubs_current_exportable`,
+		`platform:legacy.core.platform.api.stubs.exportable`,
 
 		// Needed for generating the boot image.
 		`platform:dex2oatd`,
@@ -419,6 +429,9 @@
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 			variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
+			variables.BuildFlags = map[string]string{
+				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+			}
 		}),
 		java.FixtureWithPrebuiltApis(map[string][]string{
 			"current": {},
@@ -534,13 +547,16 @@
 
 	// Make sure that the myplatform-bootclasspath has the correct dependencies.
 	CheckModuleDependencies(t, result.TestContext, "myplatform-bootclasspath", "android_common", []string{
+		// source vs prebuilt selection metadata module
+		`platform:all_apex_contributions`,
+
 		// The following are stubs.
 		"platform:prebuilt_sdk_public_current_android",
 		"platform:prebuilt_sdk_system_current_android",
 		"platform:prebuilt_sdk_test_current_android",
 
 		// Not a prebuilt as no prebuilt existed when it was added.
-		"platform:legacy.core.platform.api.stubs",
+		"platform:legacy.core.platform.api.stubs.exportable",
 
 		// The platform_bootclasspath intentionally adds dependencies on both source and prebuilt
 		// modules when available as it does not know which one will be preferred.
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 7d339d5..cebbae9 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -22,6 +22,7 @@
 	"strings"
 
 	"android/soong/android"
+	"android/soong/dexpreopt"
 	"android/soong/java"
 	"android/soong/provenance"
 
@@ -50,6 +51,7 @@
 
 type prebuiltCommon struct {
 	android.ModuleBase
+	java.Dexpreopter
 	prebuilt android.Prebuilt
 
 	// Properties common to both prebuilt_apex and apex_set.
@@ -85,6 +87,12 @@
 	// device (/apex/<apex_name>). If unspecified, follows the name property.
 	Apex_name *string
 
+	// Name of the source APEX that gets shadowed by this prebuilt
+	// e.g. com.mycompany.android.myapex
+	// If unspecified, follows the naming convention that the source apex of
+	// the prebuilt is Name() without "prebuilt_" prefix
+	Source_apex_name *string
+
 	ForceDisable bool `blueprint:"mutated"`
 
 	// whether the extracted apex file is installable.
@@ -124,7 +132,11 @@
 }
 
 func (p *prebuiltCommon) ApexVariationName() string {
-	return proptools.StringDefault(p.prebuiltCommonProperties.Apex_name, p.ModuleBase.BaseModuleName())
+	return proptools.StringDefault(p.prebuiltCommonProperties.Apex_name, p.BaseModuleName())
+}
+
+func (p *prebuiltCommon) BaseModuleName() string {
+	return proptools.StringDefault(p.prebuiltCommonProperties.Source_apex_name, p.ModuleBase.BaseModuleName())
 }
 
 func (p *prebuiltCommon) Prebuilt() *android.Prebuilt {
@@ -170,50 +182,46 @@
 	return proptools.BoolDefault(p.prebuiltCommonProperties.Installable, true)
 }
 
-// initApexFilesForAndroidMk initializes the prebuiltCommon.apexFilesForAndroidMk field from the
-// modules that this depends upon.
+// To satisfy java.DexpreopterInterface
+func (p *prebuiltCommon) IsInstallable() bool {
+	return p.installable()
+}
+
+// initApexFilesForAndroidMk initializes the prebuiltCommon.requiredModuleNames field with the install only deps of the prebuilt apex
 func (p *prebuiltCommon) initApexFilesForAndroidMk(ctx android.ModuleContext) {
-	// Walk the dependencies of this module looking for the java modules that it exports.
-	ctx.WalkDeps(func(child, parent android.Module) bool {
-		tag := ctx.OtherModuleDependencyTag(child)
+	// If this apex contains a system server jar, then the dexpreopt artifacts should be added as required
+	for _, install := range p.Dexpreopter.DexpreoptBuiltInstalledForApex() {
+		p.requiredModuleNames = append(p.requiredModuleNames, install.FullModuleName())
+	}
+}
 
-		name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(child))
-		if java.IsBootclasspathFragmentContentDepTag(tag) ||
-			java.IsSystemServerClasspathFragmentContentDepTag(tag) || tag == exportedJavaLibTag {
-			// If the exported java module provides a dex jar path then add it to the list of apexFiles.
-			path := child.(interface {
-				DexJarBuildPath() java.OptionalDexJarPath
-			}).DexJarBuildPath()
-			if path.IsSet() {
-				af := apexFile{
-					module:              child,
-					moduleDir:           ctx.OtherModuleDir(child),
-					androidMkModuleName: name,
-					builtFile:           path.Path(),
-					class:               javaSharedLib,
-				}
-				if module, ok := child.(java.DexpreopterInterface); ok {
-					for _, install := range module.DexpreoptBuiltInstalledForApex() {
-						af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName())
-					}
-				}
-				p.apexFilesForAndroidMk = append(p.apexFilesForAndroidMk, af)
-			}
-		} else if tag == exportedBootclasspathFragmentTag {
-			_, ok := child.(*java.PrebuiltBootclasspathFragmentModule)
-			if !ok {
-				ctx.PropertyErrorf("exported_bootclasspath_fragments", "%q is not a prebuilt_bootclasspath_fragment module", name)
-				return false
-			}
-			// Visit the children of the bootclasspath_fragment.
-			return true
-		} else if tag == exportedSystemserverclasspathFragmentTag {
-			// Visit the children of the systemserver_fragment.
-			return true
+// If this prebuilt has system server jar, create the rules to dexpreopt it and install it alongside the prebuilt apex
+func (p *prebuiltCommon) dexpreoptSystemServerJars(ctx android.ModuleContext) {
+	// If this apex does not export anything, return
+	if !p.hasExportedDeps() {
+		return
+	}
+	// If this prebuilt apex has not been selected, return
+	if p.IsHideFromMake() {
+		return
+	}
+	// Use apex_name to determine the api domain of this prebuilt apex
+	apexName := p.ApexVariationName()
+	di, err := android.FindDeapexerProviderForModule(ctx)
+	if err != nil {
+		ctx.ModuleErrorf(err.Error())
+	}
+	dc := dexpreopt.GetGlobalConfig(ctx)
+	systemServerJarList := dc.AllApexSystemServerJars(ctx)
+
+	for i := 0; i < systemServerJarList.Len(); i++ {
+		sscpApex := systemServerJarList.Apex(i)
+		sscpJar := systemServerJarList.Jar(i)
+		if apexName != sscpApex {
+			continue
 		}
-
-		return false
-	})
+		p.Dexpreopter.DexpreoptPrebuiltApexSystemServerJars(ctx, sscpJar, di)
+	}
 }
 
 func (p *prebuiltCommon) addRequiredModules(entries *android.AndroidMkEntries) {
@@ -232,6 +240,7 @@
 			OutputFile:    android.OptionalPathForPath(p.outputApex),
 			Include:       "$(BUILD_PREBUILT)",
 			Host_required: p.hostRequired,
+			OverrideName:  p.BaseModuleName(),
 			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 					entries.SetString("LOCAL_MODULE_PATH", p.installDir.String())
@@ -248,6 +257,11 @@
 		},
 	}
 
+	// Add the dexpreopt artifacts to androidmk
+	for _, install := range p.Dexpreopter.DexpreoptBuiltInstalledForApex() {
+		entriesList = append(entriesList, install.ToMakeEntries())
+	}
+
 	// Iterate over the apexFilesForAndroidMk list and create an AndroidMkEntries struct for each
 	// file. This provides similar behavior to that provided in apexBundle.AndroidMk() as it makes the
 	// apex specific variants of the exported java modules available for use from within make.
@@ -428,7 +442,7 @@
 
 	// Create contents for the prebuilt_apex and store it away for later use.
 	apexContents := android.NewApexContents(contents)
-	mctx.SetProvider(ApexBundleInfoProvider, ApexBundleInfo{
+	android.SetProvider(mctx, android.ApexBundleInfoProvider, android.ApexBundleInfo{
 		Contents: apexContents,
 	})
 
@@ -437,7 +451,7 @@
 	apexInfo := android.ApexInfo{
 		ApexVariationName: apexVariationName,
 		InApexVariants:    []string{apexVariationName},
-		InApexModules:     []string{p.ModuleBase.BaseModuleName()}, // BaseModuleName() to avoid the prebuilt_ prefix.
+		InApexModules:     []string{p.BaseModuleName()}, // BaseModuleName() to avoid the prebuilt_ prefix.
 		ApexContents:      []*android.ApexContents{apexContents},
 		ForPrebuiltApex:   true,
 	}
@@ -484,6 +498,9 @@
 	inputApex android.Path
 
 	provenanceMetaDataFile android.OutputPath
+
+	// Single aconfig "cache file" merged from this module and all dependencies.
+	mergedAconfigFiles map[string]android.Paths
 }
 
 type ApexFileProperties struct {
@@ -612,6 +629,7 @@
 
 	// Compute the deapexer properties from the transitive dependencies of this module.
 	commonModules := []string{}
+	dexpreoptProfileGuidedModules := []string{}
 	exportedFiles := []string{}
 	ctx.WalkDeps(func(child, parent android.Module) bool {
 		tag := ctx.OtherModuleDependencyTag(child)
@@ -621,13 +639,18 @@
 			return false
 		}
 
-		name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(child))
+		name := java.ModuleStemForDeapexing(child)
 		if _, ok := tag.(android.RequiresFilesFromPrebuiltApexTag); ok {
 			commonModules = append(commonModules, name)
 
-			requiredFiles := child.(android.RequiredFilesFromPrebuiltApex).RequiredFilesFromPrebuiltApex(ctx)
+			extract := child.(android.RequiredFilesFromPrebuiltApex)
+			requiredFiles := extract.RequiredFilesFromPrebuiltApex(ctx)
 			exportedFiles = append(exportedFiles, requiredFiles...)
 
+			if extract.UseProfileGuidedDexpreopt() {
+				dexpreoptProfileGuidedModules = append(dexpreoptProfileGuidedModules, name)
+			}
+
 			// Visit the dependencies of this module just in case they also require files from the
 			// prebuilt apex.
 			return true
@@ -640,7 +663,8 @@
 	deapexerProperties := &DeapexerProperties{
 		// Remove any duplicates from the common modules lists as a module may be included via a direct
 		// dependency as well as transitive ones.
-		CommonModules: android.SortedUniqueStrings(commonModules),
+		CommonModules:                 android.SortedUniqueStrings(commonModules),
+		DexpreoptProfileGuidedModules: android.SortedUniqueStrings(dexpreoptProfileGuidedModules),
 	}
 
 	// Populate the exported files property in a fixed order.
@@ -740,13 +764,11 @@
 //     V            V            V
 //     selector  <---  deapexer  <---  exported java lib
 func (p *Prebuilt) createPrebuiltApexModules(ctx android.TopDownMutatorContext) {
-	baseModuleName := p.BaseModuleName()
-
-	apexSelectorModuleName := apexSelectorModuleName(baseModuleName)
+	apexSelectorModuleName := apexSelectorModuleName(p.Name())
 	createApexSelectorModule(ctx, apexSelectorModuleName, &p.properties.ApexFileProperties)
 
 	apexFileSource := ":" + apexSelectorModuleName
-	p.createDeapexerModuleIfNeeded(ctx, deapexerModuleName(baseModuleName), apexFileSource)
+	p.createDeapexerModuleIfNeeded(ctx, deapexerModuleName(p.Name()), apexFileSource)
 
 	// Add a source reference to retrieve the selected apex from the selector module.
 	p.prebuiltCommonProperties.Selected_apex = proptools.StringPtr(apexFileSource)
@@ -756,12 +778,47 @@
 	p.prebuiltApexContentsDeps(ctx)
 }
 
+func (p *prebuiltCommon) DepsMutator(ctx android.BottomUpMutatorContext) {
+	if p.hasExportedDeps() {
+		// Create a dependency from the prebuilt apex (prebuilt_apex/apex_set) to the internal deapexer module
+		// The deapexer will return a provider that will be bubbled up to the rdeps of apexes (e.g. dex_bootjars)
+		ctx.AddDependency(ctx.Module(), android.DeapexerTag, deapexerModuleName(p.Name()))
+	}
+}
+
 var _ ApexInfoMutator = (*Prebuilt)(nil)
 
 func (p *Prebuilt) ApexInfoMutator(mctx android.TopDownMutatorContext) {
 	p.apexInfoMutator(mctx)
 }
 
+// Set a provider containing information about the jars and .prof provided by the apex
+// Apexes built from prebuilts retrieve this information by visiting its internal deapexer module
+// Used by dex_bootjars to generate the boot image
+func (p *prebuiltCommon) provideApexExportsInfo(ctx android.ModuleContext) {
+	if !p.hasExportedDeps() {
+		// nothing to do
+		return
+	}
+	if di, err := android.FindDeapexerProviderForModule(ctx); err == nil {
+		javaModuleToDexPath := map[string]android.Path{}
+		for _, commonModule := range di.GetExportedModuleNames() {
+			if dex := di.PrebuiltExportPath(java.ApexRootRelativePathToJavaLib(commonModule)); dex != nil {
+				javaModuleToDexPath[commonModule] = dex
+			}
+		}
+
+		exports := android.ApexExportsInfo{
+			ApexName:                      p.ApexVariationName(),
+			ProfilePathOnHost:             di.PrebuiltExportPath(java.ProfileInstallPathInApex),
+			LibraryNameToDexJarPathOnHost: javaModuleToDexPath,
+		}
+		android.SetProvider(ctx, android.ApexExportsInfoProvider, exports)
+	} else {
+		ctx.ModuleErrorf(err.Error())
+	}
+}
+
 func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	p.apexKeysPath = writeApexKeys(ctx, p)
 	// TODO(jungjw): Check the key validity.
@@ -783,6 +840,12 @@
 		return
 	}
 
+	// dexpreopt any system server jars if present
+	p.dexpreoptSystemServerJars(ctx)
+
+	// provide info used for generating the boot image
+	p.provideApexExportsInfo(ctx)
+
 	// Save the files that need to be made available to Make.
 	p.initApexFilesForAndroidMk(ctx)
 
@@ -797,6 +860,8 @@
 		p.installedFile = ctx.InstallFile(p.installDir, p.installFilename, p.inputApex, p.compatSymlinks...)
 		p.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, p.inputApex, p.installedFile)
 	}
+
+	android.CollectDependencyAconfigFiles(ctx, &p.mergedAconfigFiles)
 }
 
 func (p *Prebuilt) ProvenanceMetaDataFile() android.OutputPath {
@@ -957,13 +1022,11 @@
 // from those provided this creates an extractor module which extracts the appropriate .apex file
 // from the zip file containing them.
 func (a *ApexSet) createPrebuiltApexModules(ctx android.TopDownMutatorContext) {
-	baseModuleName := a.BaseModuleName()
-
-	apexExtractorModuleName := apexExtractorModuleName(baseModuleName)
+	apexExtractorModuleName := apexExtractorModuleName(a.Name())
 	createApexExtractorModule(ctx, apexExtractorModuleName, &a.properties.ApexExtractorProperties)
 
 	apexFileSource := ":" + apexExtractorModuleName
-	a.createDeapexerModuleIfNeeded(ctx, deapexerModuleName(baseModuleName), apexFileSource)
+	a.createDeapexerModuleIfNeeded(ctx, deapexerModuleName(a.Name()), apexFileSource)
 
 	// After passing the arch specific src properties to the creating the apex selector module
 	a.prebuiltCommonProperties.Selected_apex = proptools.StringPtr(apexFileSource)
@@ -999,6 +1062,12 @@
 		return
 	}
 
+	// dexpreopt any system server jars if present
+	a.dexpreoptSystemServerJars(ctx)
+
+	// provide info used for generating the boot image
+	a.provideApexExportsInfo(ctx)
+
 	// Save the files that need to be made available to Make.
 	a.initApexFilesForAndroidMk(ctx)
 
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index 40d0581..f6c53b2 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -106,6 +106,7 @@
 	})
 
 	java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex", []string{
+		`dex2oatd`,
 		`myapex.key`,
 		`mysystemserverclasspathfragment`,
 	})
@@ -162,6 +163,7 @@
 	})
 
 	java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex", []string{
+		`dex2oatd`,
 		`myapex.key`,
 		`mysystemserverclasspathfragment`,
 	})
@@ -272,24 +274,26 @@
 	ctx := result.TestContext
 
 	java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex", []string{
-		`myapex.apex.selector`,
+		`dex2oatd`,
+		`prebuilt_myapex.apex.selector`,
+		`prebuilt_myapex.deapexer`,
 		`prebuilt_mysystemserverclasspathfragment`,
 	})
 
 	java.CheckModuleDependencies(t, ctx, "mysystemserverclasspathfragment", "android_common_myapex", []string{
-		`myapex.deapexer`,
 		`prebuilt_bar`,
 		`prebuilt_foo`,
+		`prebuilt_myapex.deapexer`,
 	})
 
-	ensureExactDeapexedContents(t, ctx, "myapex", "android_common", []string{
+	ensureExactDeapexedContents(t, ctx, "prebuilt_myapex", "android_common", []string{
 		"javalib/foo.jar",
 		"javalib/bar.jar",
 		"javalib/bar.jar.prof",
 	})
 
-	assertProfileGuided(t, ctx, "foo", "android_common_myapex", false)
-	assertProfileGuided(t, ctx, "bar", "android_common_myapex", true)
+	assertProfileGuidedPrebuilt(t, ctx, "myapex", "foo", false)
+	assertProfileGuidedPrebuilt(t, ctx, "myapex", "bar", true)
 }
 
 func TestSystemserverclasspathFragmentStandaloneContents(t *testing.T) {
@@ -428,19 +432,19 @@
 	ctx := result.TestContext
 
 	java.CheckModuleDependencies(t, ctx, "mysystemserverclasspathfragment", "android_common_myapex", []string{
-		`myapex.deapexer`,
 		`prebuilt_bar`,
 		`prebuilt_foo`,
+		`prebuilt_myapex.deapexer`,
 	})
 
-	ensureExactDeapexedContents(t, ctx, "myapex", "android_common", []string{
+	ensureExactDeapexedContents(t, ctx, "prebuilt_myapex", "android_common", []string{
 		"javalib/foo.jar",
 		"javalib/bar.jar",
 		"javalib/bar.jar.prof",
 	})
 
-	assertProfileGuided(t, ctx, "foo", "android_common_myapex", false)
-	assertProfileGuided(t, ctx, "bar", "android_common_myapex", true)
+	assertProfileGuidedPrebuilt(t, ctx, "myapex", "foo", false)
+	assertProfileGuidedPrebuilt(t, ctx, "myapex", "bar", true)
 }
 
 func assertProfileGuided(t *testing.T, ctx *android.TestContext, moduleName string, variant string, expected bool) {
@@ -450,3 +454,11 @@
 		t.Fatalf("Expected profile-guided to be %v, got %v", expected, actual)
 	}
 }
+
+func assertProfileGuidedPrebuilt(t *testing.T, ctx *android.TestContext, apexName string, moduleName string, expected bool) {
+	dexpreopt := ctx.ModuleForTests(apexName, "android_common_"+apexName).Rule("dexpreopt." + moduleName)
+	actual := strings.Contains(dexpreopt.RuleParams.Command, "--profile-file=")
+	if expected != actual {
+		t.Fatalf("Expected profile-guided to be %v, got %v", expected, actual)
+	}
+}
diff --git a/apex/vndk.go b/apex/vndk.go
index 26c60ed..377c1c0 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -65,24 +65,27 @@
 		}
 
 		vndkVersion := ab.vndkVersion(mctx.DeviceConfig())
-		apiLevel, err := android.ApiLevelFromUser(mctx, vndkVersion)
-		if err != nil {
-			mctx.PropertyErrorf("vndk_version", "%s", err.Error())
-			return
-		}
+		if vndkVersion != "" {
+			apiLevel, err := android.ApiLevelFromUser(mctx, vndkVersion)
+			if err != nil {
+				mctx.PropertyErrorf("vndk_version", "%s", err.Error())
+				return
+			}
 
-		targets := mctx.MultiTargets()
-		if len(targets) > 0 && apiLevel.LessThan(cc.MinApiForArch(mctx, targets[0].Arch.ArchType)) &&
-			vndkVersion != mctx.DeviceConfig().PlatformVndkVersion() {
-			// Disable VNDK APEXes for VNDK versions less than the minimum supported API
-			// level for the primary architecture. This validation is skipped if the VNDK
-			// version matches the platform VNDK version, which can occur when the device
-			// config targets the 'current' VNDK (see `vndkVersion`).
-			ab.Disable()
-		}
-		if proptools.String(ab.vndkProperties.Vndk_version) != "" &&
-			apiLevel.GreaterThanOrEqualTo(android.ApiLevelOrPanic(mctx, mctx.DeviceConfig().PlatformVndkVersion())) {
-			ab.Disable()
+			targets := mctx.MultiTargets()
+			if len(targets) > 0 && apiLevel.LessThan(cc.MinApiForArch(mctx, targets[0].Arch.ArchType)) &&
+				vndkVersion != mctx.DeviceConfig().PlatformVndkVersion() {
+				// Disable VNDK APEXes for VNDK versions less than the minimum supported API
+				// level for the primary architecture. This validation is skipped if the VNDK
+				// version matches the platform VNDK version, which can occur when the device
+				// config targets the 'current' VNDK (see `vndkVersion`).
+				ab.Disable()
+			}
+			if proptools.String(ab.vndkProperties.Vndk_version) != "" &&
+				mctx.DeviceConfig().PlatformVndkVersion() != "" &&
+				apiLevel.GreaterThanOrEqualTo(android.ApiLevelOrPanic(mctx, mctx.DeviceConfig().PlatformVndkVersion())) {
+				ab.Disable()
+			}
 		}
 	}
 }
@@ -94,6 +97,11 @@
 		if vndkVersion == "" {
 			vndkVersion = mctx.DeviceConfig().PlatformVndkVersion()
 		}
+
+		if vndkVersion == "" {
+			return
+		}
+
 		if vndkVersion == mctx.DeviceConfig().PlatformVndkVersion() {
 			vndkVersion = "current"
 		} else {
diff --git a/apex/vndk_test.go b/apex/vndk_test.go
index e2aee96..894aece 100644
--- a/apex/vndk_test.go
+++ b/apex/vndk_test.go
@@ -8,66 +8,6 @@
 	"android/soong/android"
 )
 
-func TestVndkApexForVndkLite(t *testing.T) {
-	ctx := testApex(t, `
-		apex_vndk {
-			name: "com.android.vndk.current",
-			key: "com.android.vndk.current.key",
-			updatable: false,
-		}
-
-		apex_key {
-			name: "com.android.vndk.current.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		cc_library {
-			name: "libvndk",
-			srcs: ["mylib.cpp"],
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			system_shared_libs: [],
-			stl: "none",
-			apex_available: [ "com.android.vndk.current" ],
-		}
-
-		cc_library {
-			name: "libvndksp",
-			srcs: ["mylib.cpp"],
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-			system_shared_libs: [],
-			stl: "none",
-			apex_available: [ "com.android.vndk.current" ],
-		}
-	`+vndkLibrariesTxtFiles("current"),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.DeviceVndkVersion = proptools.StringPtr("")
-			variables.KeepVndk = proptools.BoolPtr(true)
-		}),
-	)
-	// VNDK-Lite contains only core variants of VNDK-Sp libraries
-	ensureExactContents(t, ctx, "com.android.vndk.current", "android_common", []string{
-		"lib/libvndksp.so",
-		"lib/libc++.so",
-		"lib64/libvndksp.so",
-		"lib64/libc++.so",
-		"etc/llndk.libraries.29.txt",
-		"etc/vndkcore.libraries.29.txt",
-		"etc/vndksp.libraries.29.txt",
-		"etc/vndkprivate.libraries.29.txt",
-		"etc/vndkproduct.libraries.29.txt",
-	})
-}
-
 func TestVndkApexUsesVendorVariant(t *testing.T) {
 	bp := `
 		apex_vndk {
diff --git a/bin/soongdbg b/bin/soongdbg
new file mode 100755
index 0000000..bfdbbde
--- /dev/null
+++ b/bin/soongdbg
@@ -0,0 +1,424 @@
+#!/usr/bin/env python3
+
+import argparse
+import fnmatch
+import html
+import io
+import json
+import os
+import pathlib
+import subprocess
+import types
+import sys
+
+
+class Graph:
+    def __init__(self, modules):
+        def get_or_make_node(dictionary, id, module):
+            node = dictionary.get(id)
+            if node:
+                if module and not node.module:
+                    node.module = module
+                return node
+            node = Node(id, module)
+            dictionary[id] = node
+            return node
+        self.nodes = dict()
+        for module in modules.values():
+            node = get_or_make_node(self.nodes, module.id, module)
+            for d in module.deps:
+                dep = get_or_make_node(self.nodes, d.id, None)
+                node.deps.add(dep)
+                dep.rdeps.add(node)
+                node.dep_tags.setdefault(dep, list()).append(d)
+
+    def find_paths(self, id1, id2):
+        # Throws KeyError if one of the names isn't found
+        def recurse(node1, node2, visited):
+            result = set()
+            for dep in node1.rdeps:
+                if dep == node2:
+                    result.add(node2)
+                if dep not in visited:
+                    visited.add(dep)
+                    found = recurse(dep, node2, visited)
+                    if found:
+                        result |= found
+                        result.add(dep)
+            return result
+        node1 = self.nodes[id1]
+        node2 = self.nodes[id2]
+        # Take either direction
+        p = recurse(node1, node2, set())
+        if p:
+            p.add(node1)
+            return p
+        p = recurse(node2, node1, set())
+        p.add(node2)
+        return p
+
+
+class Node:
+    def __init__(self, id, module):
+        self.id = id
+        self.module = module
+        self.deps = set()
+        self.rdeps = set()
+        self.dep_tags = {}
+
+
+PROVIDERS = [
+    "android/soong/java.JarJarProviderData",
+    "android/soong/java.BaseJarJarProviderData",
+]
+
+
+def format_dep_label(node, dep):
+    tags = node.dep_tags.get(dep)
+    labels = []
+    if tags:
+        labels = [tag.tag_type.split("/")[-1] for tag in tags]
+        labels = sorted(set(labels))
+    if labels:
+        result = "<<table border=\"0\" cellborder=\"0\" cellspacing=\"0\" cellpadding=\"0\">"
+        for label in labels:
+            result += f"<tr><td>{label}</td></tr>"
+        result += "</table>>"
+        return result
+
+
+def format_node_label(node, module_formatter):
+    result = "<<table border=\"0\" cellborder=\"0\" cellspacing=\"0\" cellpadding=\"0\">"
+
+    # node name
+    result += f"<tr><td><b>{node.module.name if node.module else node.id}</b></td></tr>"
+
+    if node.module:
+        # node_type
+        result += f"<tr><td>{node.module.type}</td></tr>"
+
+        # module_formatter will return a list of rows
+        for row in module_formatter(node.module):
+            row = html.escape(row)
+            result += f"<tr><td><font color=\"#666666\">{row}</font></td></tr>"
+
+    result += "</table>>"
+    return result
+
+
+def format_source_pos(file, lineno):
+    result = file
+    if lineno:
+        result += f":{lineno}"
+    return result
+
+
+STRIP_TYPE_PREFIXES = [
+    "android/soong/",
+    "github.com/google/",
+]
+
+
+def format_provider(provider):
+    result = ""
+    for prefix in STRIP_TYPE_PREFIXES:
+        if provider.type.startswith(prefix):
+            result = provider.type[len(prefix):]
+            break
+    if not result:
+        result = provider.type
+    if True and provider.debug:
+        result += " (" + provider.debug + ")"
+    return result
+
+
+def load_soong_debug():
+    # Read the json
+    try:
+        with open(SOONG_DEBUG_DATA_FILENAME) as f:
+            info = json.load(f, object_hook=lambda d: types.SimpleNamespace(**d))
+    except IOError:
+        sys.stderr.write(f"error: Unable to open {SOONG_DEBUG_DATA_FILENAME}. Make sure you have"
+                         + " built with GENERATE_SOONG_DEBUG.\n")
+        sys.exit(1)
+
+    # Construct IDs, which are name + variant if the
+    name_counts = dict()
+    for m in info.modules:
+        name_counts[m.name] = name_counts.get(m.name, 0) + 1
+    def get_id(m):
+        result = m.name
+        if name_counts[m.name] > 1 and m.variant:
+            result += "@@" + m.variant
+        return result
+    for m in info.modules:
+        m.id = get_id(m)
+        for dep in m.deps:
+            dep.id = get_id(dep)
+
+    return info
+
+
+def load_modules():
+    info = load_soong_debug()
+
+    # Filter out unnamed modules
+    modules = dict()
+    for m in info.modules:
+        if not m.name:
+            continue
+        modules[m.id] = m
+
+    return modules
+
+
+def load_graph():
+    modules=load_modules()
+    return Graph(modules)
+
+
+def module_selection_args(parser):
+    parser.add_argument("modules", nargs="*",
+                        help="Modules to match. Can be glob-style wildcards.")
+    parser.add_argument("--provider", nargs="+",
+                        help="Match the given providers.")
+    parser.add_argument("--dep", nargs="+",
+                        help="Match the given providers.")
+
+
+def load_and_filter_modules(args):
+    # Which modules are printed
+    matchers = []
+    if args.modules:
+        matchers.append(lambda m: [True for pattern in args.modules
+                                   if fnmatch.fnmatchcase(m.name, pattern)])
+    if args.provider:
+        matchers.append(lambda m: [True for pattern in args.provider
+                                   if [True for p in m.providers if p.type.endswith(pattern)]])
+    if args.dep:
+        matchers.append(lambda m: [True for pattern in args.dep
+                                   if [True for d in m.deps if d.id == pattern]])
+
+    if not matchers:
+        sys.stderr.write("error: At least one module matcher must be supplied\n")
+        sys.exit(1)
+
+    info = load_soong_debug()
+    for m in sorted(info.modules, key=lambda m: (m.name, m.variant)):
+        if len([matcher for matcher in matchers if matcher(m)]) == len(matchers):
+            yield m
+
+
+def print_args(parser):
+    parser.add_argument("--label", action="append", metavar="JQ_FILTER",
+                        help="jq query for each module metadata")
+    parser.add_argument("--deptags", action="store_true",
+                        help="show dependency tags (makes the graph much more complex)")
+
+    group = parser.add_argument_group("output formats",
+                                      "If no format is provided, a dot file will be written to"
+                                      + " stdout.")
+    output = group.add_mutually_exclusive_group()
+    output.add_argument("--dot", type=str, metavar="FILENAME",
+                        help="Write the graph to this file as dot (graphviz format)")
+    output.add_argument("--svg", type=str, metavar="FILENAME",
+                        help="Write the graph to this file as svg")
+
+
+def print_nodes(args, nodes, module_formatter):
+    # Generate the graphviz
+    dep_tag_id = 0
+    dot = io.StringIO()
+    dot.write("digraph {\n")
+    dot.write("node [shape=box];")
+
+    for node in nodes:
+        dot.write(f"\"{node.id}\" [label={format_node_label(node, module_formatter)}];\n")
+        for dep in node.deps:
+            if dep in nodes:
+                if args.deptags:
+                    dot.write(f"\"{node.id}\" -> \"__dep_tag_{dep_tag_id}\" [ arrowhead=none ];\n")
+                    dot.write(f"\"__dep_tag_{dep_tag_id}\" -> \"{dep.id}\";\n")
+                    dot.write(f"\"__dep_tag_{dep_tag_id}\""
+                                  + f"[label={format_dep_label(node, dep)} shape=ellipse"
+                                  + " color=\"#666666\" fontcolor=\"#666666\"];\n")
+                else:
+                    dot.write(f"\"{node.id}\" -> \"{dep.id}\";\n")
+                dep_tag_id += 1
+    dot.write("}\n")
+    text = dot.getvalue()
+
+    # Write it somewhere
+    if args.dot:
+        with open(args.dot, "w") as f:
+            f.write(text)
+    elif args.svg:
+        subprocess.run(["dot", "-Tsvg", "-o", args.svg],
+                              input=text, text=True, check=True)
+    else:
+        sys.stdout.write(text)
+
+
+def get_deps(nodes, root, maxdepth, reverse):
+    if root in nodes:
+        return
+    nodes.add(root)
+    if maxdepth != 0:
+        for dep in (root.rdeps if reverse else root.deps):
+            get_deps(nodes, dep, maxdepth-1, reverse)
+
+
+def new_module_formatter(args):
+    def module_formatter(module):
+        if not args.label:
+            return []
+        result = []
+        text = json.dumps(module, default=lambda o: o.__dict__)
+        for jq_filter in args.label:
+            proc = subprocess.run(["jq", jq_filter],
+                                  input=text, text=True, check=True, stdout=subprocess.PIPE)
+            if proc.stdout:
+                o = json.loads(proc.stdout)
+                if type(o) == list:
+                    for row in o:
+                        if row:
+                            result.append(row)
+                elif type(o) == dict:
+                    result.append(str(proc.stdout).strip())
+                else:
+                    if o:
+                        result.append(str(o).strip())
+        return result
+    return module_formatter
+
+
+class BetweenCommand:
+    help = "Print the module graph between two nodes."
+
+    def args(self, parser):
+        parser.add_argument("module", nargs=2,
+                            help="the two modules")
+        print_args(parser)
+
+    def run(self, args):
+        graph = load_graph()
+        print_nodes(args, graph.find_paths(args.module[0], args.module[1]),
+                    new_module_formatter(args))
+
+
+class DepsCommand:
+    help = "Print the module graph of dependencies of one or more modules"
+
+    def args(self, parser):
+        parser.add_argument("module", nargs="+",
+                            help="Module to print dependencies of")
+        parser.add_argument("--reverse", action="store_true",
+                            help="traverse reverse dependencies")
+        parser.add_argument("--depth", type=int, default=-1,
+                            help="max depth of dependencies (can keep the graph size reasonable)")
+        print_args(parser)
+
+    def run(self, args):
+        graph = load_graph()
+        nodes = set()
+        err = False
+        for id in args.module:
+            root = graph.nodes.get(id)
+            if not root:
+                sys.stderr.write(f"error: Can't find root: {id}\n")
+                err = True
+                continue
+            get_deps(nodes, root, args.depth, args.reverse)
+        if err:
+            sys.exit(1)
+        print_nodes(args, nodes, new_module_formatter(args))
+
+
+class IdCommand:
+    help = "Print the id (name + variant) of matching modules"
+
+    def args(self, parser):
+        module_selection_args(parser)
+
+    def run(self, args):
+        for m in load_and_filter_modules(args):
+            print(m.id)
+
+
+class JsonCommand:
+    help = "Print metadata about modules in json format"
+
+    def args(self, parser):
+        module_selection_args(parser)
+        parser.add_argument("--list", action="store_true",
+                            help="Print the results in a json list. If not set and multiple"
+                            + " modules are matched, the output won't be valid json.")
+
+    def run(self, args):
+        modules = load_and_filter_modules(args)
+        if args.list:
+            json.dump([m for m in modules], sys.stdout, indent=4, default=lambda o: o.__dict__)
+        else:
+            for m in modules:
+                json.dump(m, sys.stdout, indent=4, default=lambda o: o.__dict__)
+                print()
+
+
+class QueryCommand:
+    help = "Query details about modules"
+
+    def args(self, parser):
+        module_selection_args(parser)
+
+    def run(self, args):
+        for m in load_and_filter_modules(args):
+            print(m.id)
+            print(f"    type:     {m.type}")
+            print(f"    location: {format_source_pos(m.source_file, m.source_line)}")
+            for p in m.providers:
+                print(f"    provider: {format_provider(p)}")
+            for d in m.deps:
+                print(f"    dep:      {d.id}")
+
+
+COMMANDS = {
+    "between": BetweenCommand(),
+    "deps": DepsCommand(),
+    "id": IdCommand(),
+    "json": JsonCommand(),
+    "query": QueryCommand(),
+}
+
+
+def assert_env(name):
+    val = os.getenv(name)
+    if not val:
+        sys.stderr.write(f"{name} not set. please make sure you've run lunch.")
+    return val
+
+ANDROID_BUILD_TOP = assert_env("ANDROID_BUILD_TOP")
+
+TARGET_PRODUCT = assert_env("TARGET_PRODUCT")
+OUT_DIR = os.getenv("OUT_DIR")
+if not OUT_DIR:
+    OUT_DIR = "out"
+if OUT_DIR[0] != "/":
+    OUT_DIR = pathlib.Path(ANDROID_BUILD_TOP).joinpath(OUT_DIR)
+SOONG_DEBUG_DATA_FILENAME = pathlib.Path(OUT_DIR).joinpath("soong/soong-debug-info.json")
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    subparsers = parser.add_subparsers(required=True, dest="command")
+    for name in sorted(COMMANDS.keys()):
+        command = COMMANDS[name]
+        subparser = subparsers.add_parser(name, help=command.help)
+        command.args(subparser)
+    args = parser.parse_args()
+    COMMANDS[args.command].run(args)
+    sys.exit(0)
+
+
+if __name__ == "__main__":
+    main()
+
diff --git a/bloaty/bloaty.go b/bloaty/bloaty.go
index 3cff60f..b72b6d3 100644
--- a/bloaty/bloaty.go
+++ b/bloaty/bloaty.go
@@ -26,7 +26,7 @@
 const protoFilename = "binary_sizes.pb.gz"
 
 var (
-	fileSizeMeasurerKey blueprint.ProviderKey
+	fileSizeMeasurerKey blueprint.ProviderKey[measuredFiles]
 	pctx                = android.NewPackageContext("android/soong/bloaty")
 
 	// bloaty is used to measure a binary section sizes.
@@ -52,7 +52,7 @@
 	pctx.SourcePathVariable("bloaty", "prebuilts/build-tools/${hostPrebuiltTag}/bin/bloaty")
 	pctx.HostBinToolVariable("bloatyMerger", "bloaty_merger")
 	android.RegisterParallelSingletonType("file_metrics", fileSizesSingleton)
-	fileSizeMeasurerKey = blueprint.NewProvider(measuredFiles{})
+	fileSizeMeasurerKey = blueprint.NewProvider[measuredFiles]()
 }
 
 // measuredFiles contains the paths of the files measured by a module.
@@ -73,7 +73,7 @@
 			mf.paths = append(mf.paths, p)
 		}
 	}
-	ctx.SetProvider(fileSizeMeasurerKey, mf)
+	android.SetProvider(ctx, fileSizeMeasurerKey, mf)
 }
 
 type sizesSingleton struct{}
@@ -85,10 +85,13 @@
 func (singleton *sizesSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var deps android.Paths
 	ctx.VisitAllModules(func(m android.Module) {
-		if !ctx.ModuleHasProvider(m, fileSizeMeasurerKey) {
+		if !m.ExportedToMake() {
 			return
 		}
-		filePaths := ctx.ModuleProvider(m, fileSizeMeasurerKey).(measuredFiles)
+		filePaths, ok := android.SingletonModuleProvider(ctx, m, fileSizeMeasurerKey)
+		if !ok {
+			return
+		}
 		for _, path := range filePaths.paths {
 			filePath := path.(android.ModuleOutPath)
 			sizeFile := filePath.InSameDir(ctx, filePath.Base()+bloatyDescriptorExt)
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index af2f550..bd56768 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -446,7 +446,7 @@
 	if !emitZeroValues && isZero(propertyValue) {
 		// A property value being set or unset actually matters -- Soong does set default
 		// values for unset properties, like system_shared_libs = ["libc", "libm", "libdl"] at
-		// https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=281-287;drc=f70926eef0b9b57faf04c17a1062ce50d209e480
+		// https://cs.android.com/android/platform/superproject/+/main:build/soong/cc/linker.go;l=281-287;drc=f70926eef0b9b57faf04c17a1062ce50d209e480
 		//
 		// In Bazel-parlance, we would use "attr.<type>(default = <default
 		// value>)" to set the default value of unset attributes. In the cases
diff --git a/bpf/bpf.go b/bpf/bpf.go
index ce57b46..38fbd88 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -203,7 +203,16 @@
 		}
 
 	}
-	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()})
+
+	installDir := android.PathForModuleInstall(ctx, "etc", "bpf")
+	if len(bpf.properties.Sub_dir) > 0 {
+		installDir = installDir.Join(ctx, bpf.properties.Sub_dir)
+	}
+	for _, obj := range bpf.objs {
+		ctx.PackageFile(installDir, obj.Base(), obj)
+	}
+
+	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()})
 }
 
 func (bpf *bpf) AndroidMk() android.AndroidMkData {
@@ -231,6 +240,10 @@
 				fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", obj.Base())
 				fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC")
 				fmt.Fprintln(w, localModulePath)
+				// AconfigUpdateAndroidMkData may have added elements to Extra.  Process them here.
+				for _, extra := range data.Extra {
+					extra(w, nil)
+				}
 				fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
 				fmt.Fprintln(w)
 			}
diff --git a/cc/afdo.go b/cc/afdo.go
index bad16a1..00b2245 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -21,29 +21,17 @@
 	"android/soong/android"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/proptools"
 )
 
 // This flag needs to be in both CFlags and LdFlags to ensure correct symbol ordering
 const afdoFlagsFormat = "-fprofile-sample-use=%s -fprofile-sample-accurate"
 
-func recordMissingAfdoProfileFile(ctx android.BaseModuleContext, missing string) {
-	getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true)
-}
-
-type afdoRdep struct {
-	VariationName *string
-	ProfilePath   *string
-}
-
 type AfdoProperties struct {
 	// Afdo allows developers self-service enroll for
 	// automatic feedback-directed optimization using profile data.
 	Afdo bool
 
-	FdoProfilePath *string `blueprint:"mutated"`
-
-	AfdoRDeps []afdoRdep `blueprint:"mutated"`
+	AfdoDep bool `blueprint:"mutated"`
 }
 
 type afdo struct {
@@ -62,13 +50,32 @@
 }
 
 // afdoEnabled returns true for binaries and shared libraries
-// that set afdo prop to True and there is a profile available
+// that set afdo prop to True.
 func (afdo *afdo) afdoEnabled() bool {
 	return afdo != nil && afdo.Properties.Afdo
 }
 
+func (afdo *afdo) isAfdoCompile(ctx ModuleContext) bool {
+	fdoProfilePath := getFdoProfilePathFromDep(ctx)
+	return !ctx.Host() && (afdo.Properties.Afdo || afdo.Properties.AfdoDep) && (fdoProfilePath != "")
+}
+
+func getFdoProfilePathFromDep(ctx ModuleContext) string {
+	fdoProfileDeps := ctx.GetDirectDepsWithTag(FdoProfileTag)
+	if len(fdoProfileDeps) > 0 && fdoProfileDeps[0] != nil {
+		if info, ok := android.OtherModuleProvider(ctx, fdoProfileDeps[0], FdoProfileProvider); ok {
+			return info.Path.String()
+		}
+	}
+	return ""
+}
+
 func (afdo *afdo) flags(ctx ModuleContext, flags Flags) Flags {
-	if afdo.Properties.Afdo {
+	if ctx.Host() {
+		return flags
+	}
+
+	if afdo.Properties.Afdo || afdo.Properties.AfdoDep {
 		// We use `-funique-internal-linkage-names` to associate profiles to the right internal
 		// functions. This option should be used before generating a profile. Because a profile
 		// generated for a binary without unique names doesn't work well building a binary with
@@ -86,15 +93,15 @@
 		// TODO(b/266595187): Remove the following feature once it is enabled in LLVM by default.
 		flags.Local.CFlags = append([]string{"-mllvm", "-improved-fs-discriminator=true"}, flags.Local.CFlags...)
 	}
-	if path := afdo.Properties.FdoProfilePath; path != nil {
+	if fdoProfilePath := getFdoProfilePathFromDep(ctx); fdoProfilePath != "" {
 		// The flags are prepended to allow overriding.
-		profileUseFlag := fmt.Sprintf(afdoFlagsFormat, *path)
+		profileUseFlag := fmt.Sprintf(afdoFlagsFormat, fdoProfilePath)
 		flags.Local.CFlags = append([]string{profileUseFlag}, flags.Local.CFlags...)
 		flags.Local.LdFlags = append([]string{profileUseFlag, "-Wl,-mllvm,-no-warn-sample-unused=true"}, flags.Local.LdFlags...)
 
 		// Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
 		// if profileFile gets updated
-		pathForSrc := android.PathForSource(ctx, *path)
+		pathForSrc := android.PathForSource(ctx, fdoProfilePath)
 		flags.CFlagsDeps = append(flags.CFlagsDeps, pathForSrc)
 		flags.LdFlagsDeps = append(flags.LdFlagsDeps, pathForSrc)
 	}
@@ -102,123 +109,86 @@
 	return flags
 }
 
-func (afdo *afdo) addDep(ctx BaseModuleContext, actx android.BottomUpMutatorContext) {
+func (a *afdo) addDep(ctx android.BottomUpMutatorContext, fdoProfileTarget string) {
+	if fdoProfileName, err := ctx.DeviceConfig().AfdoProfile(fdoProfileTarget); fdoProfileName != "" && err == nil {
+		ctx.AddFarVariationDependencies(
+			[]blueprint.Variation{
+				{Mutator: "arch", Variation: ctx.Target().ArchVariation()},
+				{Mutator: "os", Variation: "android"},
+			},
+			FdoProfileTag,
+			fdoProfileName)
+	}
+}
+
+func afdoPropagateViaDepTag(tag blueprint.DependencyTag) bool {
+	libTag, isLibTag := tag.(libraryDependencyTag)
+	// Do not recurse down non-static dependencies
+	if isLibTag {
+		return libTag.static()
+	} else {
+		return tag == objDepTag || tag == reuseObjTag || tag == staticVariantTag
+	}
+}
+
+// afdoTransitionMutator creates afdo variants of cc modules.
+type afdoTransitionMutator struct{}
+
+func (a *afdoTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+	return []string{""}
+}
+
+func (a *afdoTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
 	if ctx.Host() {
-		return
+		return ""
 	}
 
-	if ctx.static() && !ctx.staticBinary() {
-		return
-	}
-
-	if c, ok := ctx.Module().(*Module); ok && c.Enabled() {
-		if fdoProfileName, err := actx.DeviceConfig().AfdoProfile(actx.ModuleName()); fdoProfileName != nil && err == nil {
-			actx.AddFarVariationDependencies(
-				[]blueprint.Variation{
-					{Mutator: "arch", Variation: actx.Target().ArchVariation()},
-					{Mutator: "os", Variation: "android"},
-				},
-				FdoProfileTag,
-				[]string{*fdoProfileName}...,
-			)
+	if m, ok := ctx.Module().(*Module); ok && m.afdo != nil {
+		if !afdoPropagateViaDepTag(ctx.DepTag()) {
+			return ""
 		}
+
+		if sourceVariation != "" {
+			return sourceVariation
+		}
+
+		if !m.afdo.afdoEnabled() {
+			return ""
+		}
+
+		// TODO(b/324141705): this is designed to prevent propagating AFDO from static libraries that have afdo: true set, but
+		//  it should be m.static() && !m.staticBinary() so that static binaries use AFDO variants of dependencies.
+		if m.static() {
+			return ""
+		}
+
+		return encodeTarget(ctx.Module().Name())
 	}
+	return ""
 }
 
-// FdoProfileMutator reads the FdoProfileProvider from a direct dep with FdoProfileTag
-// assigns FdoProfileInfo.Path to the FdoProfilePath mutated property
-func (c *Module) fdoProfileMutator(ctx android.BottomUpMutatorContext) {
-	if !c.Enabled() {
-		return
+func (a *afdoTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	if m, ok := ctx.Module().(*Module); ok && m.afdo != nil {
+		return incomingVariation
 	}
-
-	if !c.afdo.afdoEnabled() {
-		return
-	}
-
-	ctx.VisitDirectDepsWithTag(FdoProfileTag, func(m android.Module) {
-		if ctx.OtherModuleHasProvider(m, FdoProfileProvider) {
-			info := ctx.OtherModuleProvider(m, FdoProfileProvider).(FdoProfileInfo)
-			c.afdo.Properties.FdoProfilePath = proptools.StringPtr(info.Path.String())
-		}
-	})
+	return ""
 }
 
-var _ FdoProfileMutatorInterface = (*Module)(nil)
-
-// Propagate afdo requirements down from binaries and shared libraries
-func afdoDepsMutator(mctx android.TopDownMutatorContext) {
-	if m, ok := mctx.Module().(*Module); ok && m.afdo.afdoEnabled() {
-		path := m.afdo.Properties.FdoProfilePath
-		mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
-			tag := mctx.OtherModuleDependencyTag(dep)
-			libTag, isLibTag := tag.(libraryDependencyTag)
-
-			// Do not recurse down non-static dependencies
-			if isLibTag {
-				if !libTag.static() {
-					return false
-				}
-			} else {
-				if tag != objDepTag && tag != reuseObjTag {
-					return false
-				}
+func (a *afdoTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	if m, ok := ctx.Module().(*Module); ok && m.afdo != nil {
+		if variation == "" {
+			// The empty variation is either a module that has enabled AFDO for itself, or the non-AFDO
+			// variant of a dependency.
+			if m.afdo.afdoEnabled() && !(m.static() && !m.staticBinary()) && !m.Host() {
+				m.afdo.addDep(ctx, ctx.ModuleName())
 			}
-
-			if dep, ok := dep.(*Module); ok {
-				dep.afdo.Properties.AfdoRDeps = append(
-					dep.afdo.Properties.AfdoRDeps,
-					afdoRdep{
-						VariationName: proptools.StringPtr(encodeTarget(m.Name())),
-						ProfilePath:   path,
-					},
-				)
-			}
-
-			return true
-		})
-	}
-}
-
-// Create afdo variants for modules that need them
-func afdoMutator(mctx android.BottomUpMutatorContext) {
-	if m, ok := mctx.Module().(*Module); ok && m.afdo != nil {
-		if !m.static() && m.afdo.Properties.Afdo {
-			mctx.SetDependencyVariation(encodeTarget(m.Name()))
-			return
-		}
-
-		variationNames := []string{""}
-
-		variantNameToProfilePath := make(map[string]*string)
-
-		for _, afdoRDep := range m.afdo.Properties.AfdoRDeps {
-			variantName := *afdoRDep.VariationName
-			// An rdep can be set twice in AfdoRDeps because there can be
-			// more than one path from an afdo-enabled module to
-			// a static dep such as
-			// afdo_enabled_foo -> static_bar ----> static_baz
-			//                   \                      ^
-			//                    ----------------------|
-			// We only need to create one variant per unique rdep
-			if _, exists := variantNameToProfilePath[variantName]; !exists {
-				variationNames = append(variationNames, variantName)
-				variantNameToProfilePath[variantName] = afdoRDep.ProfilePath
-			}
-		}
-
-		if len(variationNames) > 1 {
-			modules := mctx.CreateVariations(variationNames...)
-			for i, name := range variationNames {
-				if name == "" {
-					continue
-				}
-				variation := modules[i].(*Module)
-				variation.Properties.PreventInstall = true
-				variation.Properties.HideFromMake = true
-				variation.afdo.Properties.Afdo = true
-				variation.afdo.Properties.FdoProfilePath = variantNameToProfilePath[name]
-			}
+		} else {
+			// The non-empty variation is the AFDO variant of a dependency of a module that enabled AFDO
+			// for itself.
+			m.Properties.PreventInstall = true
+			m.Properties.HideFromMake = true
+			m.afdo.Properties.AfdoDep = true
+			m.afdo.addDep(ctx, decodeTarget(variation))
 		}
 	}
 }
diff --git a/cc/afdo_test.go b/cc/afdo_test.go
index b250ad1..0679d13 100644
--- a/cc/afdo_test.go
+++ b/cc/afdo_test.go
@@ -15,6 +15,7 @@
 package cc
 
 import (
+	"runtime"
 	"strings"
 	"testing"
 
@@ -42,19 +43,25 @@
 	bp := `
 	cc_library_shared {
 		name: "libTest",
+		host_supported: true,
 		srcs: ["test.c"],
 		static_libs: ["libFoo"],
 		afdo: true,
+		lto: {
+			thin: true,
+		},
 	}
 
 	cc_library_static {
 		name: "libFoo",
+		host_supported: true,
 		srcs: ["foo.c"],
 		static_libs: ["libBar"],
 	}
 
 	cc_library_static {
 		name: "libBar",
+		host_supported: true,
 		srcs: ["bar.c"],
 	}
 	`
@@ -72,13 +79,20 @@
 			"afdo_profiles_package/Android.bp": []byte(`
 				fdo_profile {
 					name: "libTest_afdo",
-					profile: "libTest.afdo",
+					arch: {
+						arm64: {
+							profile: "libTest.afdo",
+						},
+					},
 				}
 			`),
 		}.AddToFixture(),
 	).RunTestWithBp(t, bp)
 
-	expectedCFlag := "-fprofile-sample-use=afdo_profiles_package/libTest.afdo"
+	profileSampleCFlag := "-fprofile-sample-use=afdo_profiles_package/libTest.afdo"
+	uniqueInternalLinkageNamesCFlag := "-funique-internal-linkage-names"
+	afdoLtoLdFlag := "-Wl,-plugin-opt,-import-instr-limit=40"
+	noAfdoLtoLdFlag := "-Wl,-plugin-opt,-import-instr-limit=5"
 
 	libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared")
 	libFooAfdoVariant := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest")
@@ -86,18 +100,32 @@
 
 	// Check cFlags of afdo-enabled module and the afdo-variant of its static deps
 	cFlags := libTest.Rule("cc").Args["cFlags"]
-	if !strings.Contains(cFlags, expectedCFlag) {
-		t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags)
+	if !strings.Contains(cFlags, profileSampleCFlag) {
+		t.Errorf("Expected 'libTest' to enable afdo profile, but did not find %q in cflags %q", profileSampleCFlag, cFlags)
+	}
+	if !strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+		t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", profileSampleCFlag, cFlags)
+	}
+
+	ldFlags := libTest.Rule("ld").Args["ldFlags"]
+	if !strings.Contains(ldFlags, afdoLtoLdFlag) {
+		t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in ldflags %q", afdoLtoLdFlag, ldFlags)
 	}
 
 	cFlags = libFooAfdoVariant.Rule("cc").Args["cFlags"]
-	if !strings.Contains(cFlags, expectedCFlag) {
-		t.Errorf("Expected 'libFooAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags)
+	if !strings.Contains(cFlags, profileSampleCFlag) {
+		t.Errorf("Expected 'libFooAfdoVariant' to enable afdo profile, but did not find %q in cflags %q", profileSampleCFlag, cFlags)
+	}
+	if !strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+		t.Errorf("Expected 'libFooAfdoVariant' to enable afdo, but did not find %q in cflags %q", profileSampleCFlag, cFlags)
 	}
 
 	cFlags = libBarAfdoVariant.Rule("cc").Args["cFlags"]
-	if !strings.Contains(cFlags, expectedCFlag) {
-		t.Errorf("Expected 'libBarAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags)
+	if !strings.Contains(cFlags, profileSampleCFlag) {
+		t.Errorf("Expected 'libBarAfdoVariant' to enable afdo profile, but did not find %q in cflags %q", profileSampleCFlag, cFlags)
+	}
+	if !strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+		t.Errorf("Expected 'libBarAfdoVariant' to enable afdo, but did not find %q in cflags %q", profileSampleCFlag, cFlags)
 	}
 
 	// Check dependency edge from afdo-enabled module to static deps
@@ -114,12 +142,18 @@
 	libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static")
 
 	cFlags = libFoo.Rule("cc").Args["cFlags"]
-	if strings.Contains(cFlags, expectedCFlag) {
-		t.Errorf("Expected 'libFoo' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags)
+	if strings.Contains(cFlags, profileSampleCFlag) {
+		t.Errorf("Expected 'libFoo' to not enable afdo profile, but found %q in cflags %q", profileSampleCFlag, cFlags)
+	}
+	if strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+		t.Errorf("Expected 'libFoo' to not enable afdo, but found %q in cflags %q", profileSampleCFlag, cFlags)
 	}
 	cFlags = libBar.Rule("cc").Args["cFlags"]
-	if strings.Contains(cFlags, expectedCFlag) {
-		t.Errorf("Expected 'libBar' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags)
+	if strings.Contains(cFlags, profileSampleCFlag) {
+		t.Errorf("Expected 'libBar' to not enable afdo profile, but found %q in cflags %q", profileSampleCFlag, cFlags)
+	}
+	if strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+		t.Errorf("Expected 'libBar' to not enable afdo, but found %q in cflags %q", profileSampleCFlag, cFlags)
 	}
 
 	// Check dependency edges of static deps
@@ -130,6 +164,104 @@
 	if !hasDirectDep(result, libFoo.Module(), libBar.Module()) {
 		t.Errorf("libFoo missing dependency on non-afdo variant of libBar")
 	}
+
+	// Verify that the arm variant does not have FDO since the fdo_profile module only has a profile for arm64
+	libTest32 := result.ModuleForTests("libTest", "android_arm_armv7-a-neon_shared")
+	libFooAfdoVariant32 := result.ModuleForTests("libFoo", "android_arm_armv7-a-neon_static_afdo-libTest_lto-thin")
+	libBarAfdoVariant32 := result.ModuleForTests("libBar", "android_arm_armv7-a-neon_static_afdo-libTest_lto-thin")
+
+	cFlags = libTest32.Rule("cc").Args["cFlags"]
+	if strings.Contains(cFlags, profileSampleCFlag) {
+		t.Errorf("Expected arm32 'libTest' not to enable afdo, but found %q in cflags %q", profileSampleCFlag, cFlags)
+	}
+
+	// TODO(b/324141705): when the fdo_profile module doesn't provide a source file the dependencies don't get
+	//  -funique-internal-linkage-names but the module does.
+	if !strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+		t.Errorf("Expected arm32 'libTest' to enable -funique-internal-linkage-names but did not find %q in cflags %q",
+			uniqueInternalLinkageNamesCFlag, cFlags)
+	}
+
+	ldFlags = libTest32.Rule("ld").Args["ldFlags"]
+	if !strings.Contains(ldFlags, noAfdoLtoLdFlag) {
+		t.Errorf("Expected arm32 'libTest' to not enable afdo, but did not find %q in ldflags %q", noAfdoLtoLdFlag, ldFlags)
+	}
+	if strings.Contains(ldFlags, afdoLtoLdFlag) {
+		t.Errorf("Expected arm32 'libTest' to not enable afdo, but found %q in ldflags %q", afdoLtoLdFlag, ldFlags)
+	}
+
+	// Check dependency edge from afdo-enabled module to static deps
+	if !hasDirectDep(result, libTest32.Module(), libFooAfdoVariant32.Module()) {
+		t.Errorf("arm32 libTest missing dependency on afdo variant of libFoo")
+	}
+
+	if !hasDirectDep(result, libFooAfdoVariant32.Module(), libBarAfdoVariant32.Module()) {
+		t.Errorf("arm32 libTest missing dependency on afdo variant of libBar")
+	}
+
+	cFlags = libFooAfdoVariant32.Rule("cc").Args["cFlags"]
+	if strings.Contains(cFlags, profileSampleCFlag) {
+		t.Errorf("Expected arm32 'libFoo' to not enable afdo profile, but found %q in cflags %q", uniqueInternalLinkageNamesCFlag, cFlags)
+	}
+	if !strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+		t.Errorf("Expected arm32 'libFoo' to enable afdo, but did not find %q in cflags %q", uniqueInternalLinkageNamesCFlag, cFlags)
+	}
+	cFlags = libBarAfdoVariant32.Rule("cc").Args["cFlags"]
+	if strings.Contains(cFlags, profileSampleCFlag) {
+		t.Errorf("Expected arm32 'libBar' to not enable afdo profile, but found %q in cflags %q", uniqueInternalLinkageNamesCFlag, cFlags)
+	}
+	if !strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+		t.Errorf("Expected arm32 'libBar' to enable afdo, but did not find %q in cflags %q", uniqueInternalLinkageNamesCFlag, cFlags)
+	}
+
+	// Verify that the host variants don't enable afdo
+	libTestHost := result.ModuleForTests("libTest", result.Config.BuildOSTarget.String()+"_shared")
+	libFooHost := result.ModuleForTests("libFoo", result.Config.BuildOSTarget.String()+"_static_lto-thin")
+	libBarHost := result.ModuleForTests("libBar", result.Config.BuildOSTarget.String()+"_static_lto-thin")
+
+	cFlags = libTestHost.Rule("cc").Args["cFlags"]
+	if strings.Contains(cFlags, profileSampleCFlag) {
+		t.Errorf("Expected host 'libTest' to not enable afdo profile, but found %q in cflags %q", profileSampleCFlag, cFlags)
+	}
+
+	if strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+		t.Errorf("Expected host 'libTest' to not enable afdo but found %q in cflags %q",
+			uniqueInternalLinkageNamesCFlag, cFlags)
+	}
+
+	if runtime.GOOS != "darwin" {
+		ldFlags := libTestHost.Rule("ld").Args["ldFlags"]
+		if !strings.Contains(ldFlags, noAfdoLtoLdFlag) {
+			t.Errorf("Expected host 'libTest' to not enable afdo, but did not find %q in ldflags %q", noAfdoLtoLdFlag, ldFlags)
+		}
+		if strings.Contains(ldFlags, afdoLtoLdFlag) {
+			t.Errorf("Expected host 'libTest' to not enable afdo, but found %q in ldflags %q", afdoLtoLdFlag, ldFlags)
+		}
+	}
+
+	// Check dependency edge from afdo-enabled module to static deps
+	if !hasDirectDep(result, libTestHost.Module(), libFooHost.Module()) {
+		t.Errorf("host libTest missing dependency on non-afdo variant of libFoo")
+	}
+
+	if !hasDirectDep(result, libFooHost.Module(), libBarHost.Module()) {
+		t.Errorf("host libTest missing dependency on non-afdo variant of libBar")
+	}
+
+	cFlags = libFooHost.Rule("cc").Args["cFlags"]
+	if strings.Contains(cFlags, profileSampleCFlag) {
+		t.Errorf("Expected host 'libFoo' to not enable afdo profile, but found %q in cflags %q", uniqueInternalLinkageNamesCFlag, cFlags)
+	}
+	if strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+		t.Errorf("Expected host 'libFoo' to not enable afdo, but found %q in cflags %q", uniqueInternalLinkageNamesCFlag, cFlags)
+	}
+	cFlags = libBarHost.Rule("cc").Args["cFlags"]
+	if strings.Contains(cFlags, profileSampleCFlag) {
+		t.Errorf("Expected host 'libBar' to not enable afdo profile, but found %q in cflags %q", uniqueInternalLinkageNamesCFlag, cFlags)
+	}
+	if strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+		t.Errorf("Expected host 'libBar' to not enable afdo, but found %q in cflags %q", uniqueInternalLinkageNamesCFlag, cFlags)
+	}
 }
 
 func TestAfdoEnabledOnStaticDepNoAfdo(t *testing.T) {
@@ -174,11 +306,11 @@
 	libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static").Module()
 
 	if !hasDirectDep(result, libTest, libFoo.Module()) {
-		t.Errorf("libTest missing dependency on afdo variant of libFoo")
+		t.Errorf("libTest missing dependency on non-afdo variant of libFoo")
 	}
 
 	if !hasDirectDep(result, libFoo.Module(), libBar) {
-		t.Errorf("libFoo missing dependency on afdo variant of libBar")
+		t.Errorf("libFoo missing dependency on non-afdo variant of libBar")
 	}
 
 	fooVariants := result.ModuleVariantsForTests("foo")
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 8cae634..20673e8 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -50,6 +50,7 @@
 	InVendorRamdisk() bool
 	InRecovery() bool
 	NotInPlatform() bool
+	InVendorOrProduct() bool
 }
 
 type subAndroidMkProvider interface {
@@ -82,8 +83,9 @@
 		// causing multiple ART APEXes (com.android.art and com.android.art.debug)
 		// to be installed. And this is breaking some older devices (like marlin)
 		// where system.img is small.
-		Required: c.Properties.AndroidMkRuntimeLibs,
-		Include:  "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk",
+		Required:     c.Properties.AndroidMkRuntimeLibs,
+		OverrideName: c.BaseModuleName(),
+		Include:      "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk",
 
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
@@ -100,21 +102,11 @@
 				if len(c.Properties.AndroidMkSharedLibs) > 0 {
 					entries.AddStrings("LOCAL_SHARED_LIBRARIES", c.Properties.AndroidMkSharedLibs...)
 				}
-				if len(c.Properties.AndroidMkStaticLibs) > 0 {
-					entries.AddStrings("LOCAL_STATIC_LIBRARIES", c.Properties.AndroidMkStaticLibs...)
-				}
-				if len(c.Properties.AndroidMkWholeStaticLibs) > 0 {
-					entries.AddStrings("LOCAL_WHOLE_STATIC_LIBRARIES", c.Properties.AndroidMkWholeStaticLibs...)
-				}
-				if len(c.Properties.AndroidMkHeaderLibs) > 0 {
-					entries.AddStrings("LOCAL_HEADER_LIBRARIES", c.Properties.AndroidMkHeaderLibs...)
-				}
 				if len(c.Properties.AndroidMkRuntimeLibs) > 0 {
 					entries.AddStrings("LOCAL_RUNTIME_LIBRARIES", c.Properties.AndroidMkRuntimeLibs...)
 				}
 				entries.SetString("LOCAL_SOONG_LINK_TYPE", c.makeLinkType)
-				if c.UseVndk() {
-					entries.SetBool("LOCAL_USE_VNDK", true)
+				if c.InVendorOrProduct() {
 					if c.IsVndk() && !c.static() {
 						entries.SetString("LOCAL_SOONG_VNDK_VERSION", c.VndkVersion())
 						// VNDK libraries available to vendor are not installed because
@@ -124,6 +116,11 @@
 						}
 					}
 				}
+				if c.InVendor() {
+					entries.SetBool("LOCAL_IN_VENDOR", true)
+				} else if c.InProduct() {
+					entries.SetBool("LOCAL_IN_PRODUCT", true)
+				}
 				if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake {
 					// Make the SDK variant uninstallable so that there are not two rules to install
 					// to the same location.
@@ -133,8 +130,7 @@
 					entries.SetString("SOONG_SDK_VARIANT_MODULES",
 						"$(SOONG_SDK_VARIANT_MODULES) $(patsubst %.sdk,%,$(LOCAL_MODULE))")
 				}
-				// TODO(b/311155208): The container here should be system.
-				entries.SetPaths("LOCAL_ACONFIG_FILES", c.mergedAconfigFiles[""])
+				android.SetAconfigFileMkEntries(c.AndroidModuleBase(), entries, c.mergedAconfigFiles)
 			},
 		},
 		ExtraFooters: []android.AndroidMkExtraFootersFunc{
@@ -303,7 +299,7 @@
 	// they can be exceptionally used directly when APEXes are not available (e.g. during the
 	// very early stage in the boot process).
 	if len(library.Properties.Stubs.Versions) > 0 && !ctx.Host() && ctx.NotInPlatform() &&
-		!ctx.InRamdisk() && !ctx.InVendorRamdisk() && !ctx.InRecovery() && !ctx.UseVndk() && !ctx.static() {
+		!ctx.InRamdisk() && !ctx.InVendorRamdisk() && !ctx.InRecovery() && !ctx.InVendorOrProduct() && !ctx.static() {
 		if library.buildStubs() && library.isLatestStubVersion() {
 			entries.SubName = ""
 		}
diff --git a/cc/binary.go b/cc/binary.go
index 61541ad..7aa8e20 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -541,6 +541,11 @@
 	return binary.Properties.Overrides
 }
 
+func (binary *binaryDecorator) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
+	moduleInfoJSON.Class = []string{"EXECUTABLES"}
+	binary.baseLinker.moduleInfoJSON(ctx, moduleInfoJSON)
+}
+
 var _ overridable = (*binaryDecorator)(nil)
 
 func init() {
diff --git a/cc/cc.go b/cc/cc.go
index b326cd8..a7d087f 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -25,10 +25,10 @@
 	"strings"
 
 	"android/soong/testing"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
-	"android/soong/aconfig"
 	"android/soong/aidl_library"
 	"android/soong/android"
 	"android/soong/cc/config"
@@ -55,7 +55,6 @@
 		ctx.BottomUp("test_per_src", TestPerSrcMutator).Parallel()
 		ctx.BottomUp("version", versionMutator).Parallel()
 		ctx.BottomUp("begin", BeginMutator).Parallel()
-		ctx.BottomUp("fdo_profile", fdoProfileMutator)
 	})
 
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
@@ -68,16 +67,13 @@
 
 		ctx.TopDown("fuzz_deps", fuzzMutatorDeps)
 
-		ctx.BottomUp("coverage", coverageMutator).Parallel()
+		ctx.Transition("coverage", &coverageTransitionMutator{})
 
-		ctx.TopDown("afdo_deps", afdoDepsMutator)
-		ctx.BottomUp("afdo", afdoMutator).Parallel()
+		ctx.Transition("afdo", &afdoTransitionMutator{})
 
-		ctx.TopDown("orderfile_deps", orderfileDepsMutator)
-		ctx.BottomUp("orderfile", orderfileMutator).Parallel()
+		ctx.Transition("orderfile", &orderfileTransitionMutator{})
 
-		ctx.TopDown("lto_deps", ltoDepsMutator)
-		ctx.BottomUp("lto", ltoMutator).Parallel()
+		ctx.Transition("lto", &ltoTransitionMutator{})
 
 		ctx.BottomUp("check_linktype", checkLinkTypeMutator).Parallel()
 		ctx.TopDown("double_loadable", checkDoubleLoadableLibraries).Parallel()
@@ -301,8 +297,8 @@
 	// Set by DepsMutator.
 	AndroidMkSystemSharedLibs []string `blueprint:"mutated"`
 
-	// The name of the image this module is built for, suffixed with a '.'
-	ImageVariationPrefix string `blueprint:"mutated"`
+	// The name of the image this module is built for
+	ImageVariation string `blueprint:"mutated"`
 
 	// The VNDK version this module is built against. If empty, the module is not
 	// build against the VNDK.
@@ -525,10 +521,11 @@
 	inRamdisk() bool
 	inVendorRamdisk() bool
 	inRecovery() bool
+	InVendorOrProduct() bool
 	selectedStl() string
 	baseModuleName() string
 	getVndkExtendsModuleName() string
-	isAfdoCompile() bool
+	isAfdoCompile(ctx ModuleContext) bool
 	isOrderfileCompile() bool
 	isCfi() bool
 	isFuzzer() bool
@@ -544,6 +541,7 @@
 	isPreventInstall() bool
 	isCfiAssemblySupportEnabled() bool
 	getSharedFlags() *SharedFlags
+	notInPlatform() bool
 }
 
 type SharedFlags struct {
@@ -624,6 +622,8 @@
 
 	// Get the deps that have been explicitly specified in the properties.
 	linkerSpecifiedDeps(specifiedDeps specifiedDeps) specifiedDeps
+
+	moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON)
 }
 
 // specifiedDeps is a tuple struct representing dependencies of a linked binary owned by the linker.
@@ -1282,7 +1282,7 @@
 
 func (c *Module) canUseSdk() bool {
 	return c.Os() == android.Android && c.Target().NativeBridge == android.NativeBridgeDisabled &&
-		!c.UseVndk() && !c.InRamdisk() && !c.InRecovery() && !c.InVendorRamdisk()
+		!c.InVendorOrProduct() && !c.InRamdisk() && !c.InRecovery() && !c.InVendorRamdisk()
 }
 
 func (c *Module) UseSdk() bool {
@@ -1379,9 +1379,9 @@
 	return false
 }
 
-func (c *Module) isAfdoCompile() bool {
+func (c *Module) isAfdoCompile(ctx ModuleContext) bool {
 	if afdo := c.afdo; afdo != nil {
-		return afdo.Properties.FdoProfilePath != nil
+		return afdo.isAfdoCompile(ctx)
 	}
 	return false
 }
@@ -1604,9 +1604,10 @@
 
 func (ctx *moduleContextImpl) sdkVersion() string {
 	if ctx.ctx.Device() {
-		if ctx.useVndk() {
+		config := ctx.ctx.Config()
+		if !config.IsVndkDeprecated() && ctx.useVndk() {
 			vndkVer := ctx.mod.VndkVersion()
-			if inList(vndkVer, ctx.ctx.Config().PlatformVersionActiveCodenames()) {
+			if inList(vndkVer, config.PlatformVersionActiveCodenames()) {
 				return "current"
 			}
 			return vndkVer
@@ -1624,6 +1625,17 @@
 	if ver == "apex_inherit" || ver == "" {
 		ver = ctx.sdkVersion()
 	}
+
+	if ctx.ctx.Device() {
+		config := ctx.ctx.Config()
+		if config.IsVndkDeprecated() && ctx.inVendor() {
+			// If building for vendor with final API, then use the latest _stable_ API as "current".
+			if config.VendorApiLevelFrozen() && (ver == "" || ver == "current") {
+				ver = config.PlatformSdkVersion().String()
+			}
+		}
+	}
+
 	// For crt objects, the meaning of min_sdk_version is very different from other types of
 	// module. For them, min_sdk_version defines the oldest version that the build system will
 	// create versioned variants for. For example, if min_sdk_version is 16, then sdk variant of
@@ -1664,6 +1676,10 @@
 	return ctx.mod.UseVndk()
 }
 
+func (ctx *moduleContextImpl) InVendorOrProduct() bool {
+	return ctx.mod.InVendorOrProduct()
+}
+
 func (ctx *moduleContextImpl) isNdk(config android.Config) bool {
 	return ctx.mod.IsNdk(config)
 }
@@ -1688,8 +1704,8 @@
 	return ctx.mod.IsVndk()
 }
 
-func (ctx *moduleContextImpl) isAfdoCompile() bool {
-	return ctx.mod.isAfdoCompile()
+func (ctx *moduleContextImpl) isAfdoCompile(mctx ModuleContext) bool {
+	return ctx.mod.isAfdoCompile(mctx)
 }
 
 func (ctx *moduleContextImpl) isOrderfileCompile() bool {
@@ -1736,7 +1752,7 @@
 }
 
 func (ctx *moduleContextImpl) baseModuleName() string {
-	return ctx.mod.ModuleBase.BaseModuleName()
+	return ctx.mod.BaseModuleName()
 }
 
 func (ctx *moduleContextImpl) getVndkExtendsModuleName() string {
@@ -1744,11 +1760,13 @@
 }
 
 func (ctx *moduleContextImpl) isForPlatform() bool {
-	return ctx.ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
+	apexInfo, _ := android.ModuleProvider(ctx.ctx, android.ApexInfoProvider)
+	return apexInfo.IsForPlatform()
 }
 
 func (ctx *moduleContextImpl) apexVariationName() string {
-	return ctx.ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).ApexVariationName
+	apexInfo, _ := android.ModuleProvider(ctx.ctx, android.ApexInfoProvider)
+	return apexInfo.ApexVariationName
 }
 
 func (ctx *moduleContextImpl) apexSdkVersion() android.ApiLevel {
@@ -1784,6 +1802,10 @@
 	return ctx.mod.isCfiAssemblySupportEnabled()
 }
 
+func (ctx *moduleContextImpl) notInPlatform() bool {
+	return ctx.mod.NotInPlatform()
+}
+
 func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
 	return &Module{
 		hod:      hod,
@@ -1887,7 +1909,7 @@
 	}
 
 	llndk := c.IsLlndk()
-	if llndk || (c.UseVndk() && c.HasNonSystemVariants()) {
+	if llndk || (c.InVendorOrProduct() && c.HasNonSystemVariants()) {
 		// .vendor.{version} suffix is added for vendor variant or .product.{version} suffix is
 		// added for product variant only when we have vendor and product variants with core
 		// variant. The suffix is not added for vendor-only or product-only module.
@@ -1940,6 +1962,7 @@
 		"libdl_android": true,
 		"libm":          true,
 		"libdl":         true,
+		"libz":          true,
 		// art apex
 		"libandroidio":    true,
 		"libdexfile":      true,
@@ -1979,6 +2002,10 @@
 	return false
 }
 
+func (d *Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	android.CollectDependencyAconfigFiles(ctx, &d.mergedAconfigFiles)
+}
+
 func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
 	// Handle the case of a test module split by `test_per_src` mutator.
 	//
@@ -1991,7 +2018,7 @@
 	}
 
 	c.Properties.SubName = GetSubnameProperty(actx, c)
-	apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.ModuleProvider(actx, android.ApexInfoProvider)
 	if !apexInfo.IsForPlatform() {
 		c.hideApexVariantFromMake = true
 	}
@@ -2131,13 +2158,46 @@
 		}
 	}
 	if c.testModule {
-		ctx.SetProvider(testing.TestModuleProviderKey, testing.TestModuleProviderData{})
+		android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 	}
-	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: deps.GeneratedSources.Strings()})
+	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: deps.GeneratedSources.Strings()})
 
-	aconfig.CollectDependencyAconfigFiles(ctx, &c.mergedAconfigFiles)
+	android.CollectDependencyAconfigFiles(ctx, &c.mergedAconfigFiles)
 
 	c.maybeInstall(ctx, apexInfo)
+
+	if c.linker != nil {
+		moduleInfoJSON := ctx.ModuleInfoJSON()
+		c.linker.moduleInfoJSON(ctx, moduleInfoJSON)
+		moduleInfoJSON.SharedLibs = c.Properties.AndroidMkSharedLibs
+		moduleInfoJSON.StaticLibs = c.Properties.AndroidMkStaticLibs
+		moduleInfoJSON.SystemSharedLibs = c.Properties.AndroidMkSystemSharedLibs
+		moduleInfoJSON.RuntimeDependencies = c.Properties.AndroidMkRuntimeLibs
+
+		moduleInfoJSON.Dependencies = append(moduleInfoJSON.Dependencies, c.Properties.AndroidMkSharedLibs...)
+		moduleInfoJSON.Dependencies = append(moduleInfoJSON.Dependencies, c.Properties.AndroidMkStaticLibs...)
+		moduleInfoJSON.Dependencies = append(moduleInfoJSON.Dependencies, c.Properties.AndroidMkHeaderLibs...)
+		moduleInfoJSON.Dependencies = append(moduleInfoJSON.Dependencies, c.Properties.AndroidMkWholeStaticLibs...)
+
+		if c.sanitize != nil && len(moduleInfoJSON.Class) > 0 &&
+			(moduleInfoJSON.Class[0] == "STATIC_LIBRARIES" || moduleInfoJSON.Class[0] == "HEADER_LIBRARIES") {
+			if Bool(c.sanitize.Properties.SanitizeMutated.Cfi) {
+				moduleInfoJSON.SubName += ".cfi"
+			}
+			if Bool(c.sanitize.Properties.SanitizeMutated.Hwaddress) {
+				moduleInfoJSON.SubName += ".hwasan"
+			}
+			if Bool(c.sanitize.Properties.SanitizeMutated.Scs) {
+				moduleInfoJSON.SubName += ".scs"
+			}
+		}
+		moduleInfoJSON.SubName += c.Properties.SubName
+
+		if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake {
+			moduleInfoJSON.Uninstallable = true
+		}
+
+	}
 }
 
 func (c *Module) maybeUnhideFromMake() {
@@ -2149,7 +2209,7 @@
 	// is explicitly referenced via .bootstrap suffix or the module is marked with
 	// 'bootstrap: true').
 	if c.HasStubsVariants() && c.NotInPlatform() && !c.InRamdisk() &&
-		!c.InRecovery() && !c.UseVndk() && !c.static() && !c.isCoverageVariant() &&
+		!c.InRecovery() && !c.InVendorOrProduct() && !c.static() && !c.isCoverageVariant() &&
 		c.IsStubs() && !c.InVendorRamdisk() {
 		c.Properties.HideFromMake = false // unhide
 		// Note: this is still non-installable
@@ -2289,10 +2349,6 @@
 	}
 	ctx.ctx = ctx
 
-	if !actx.Host() || !ctx.static() || ctx.staticBinary() {
-		c.afdo.addDep(ctx, actx)
-	}
-
 	c.begin(ctx)
 }
 
@@ -2368,9 +2424,9 @@
 		if actx.OtherModuleExists("api_imports") {
 			apiImportModule = actx.AddDependency(c, nil, "api_imports")
 			if len(apiImportModule) > 0 && apiImportModule[0] != nil {
-				apiInfo := actx.OtherModuleProvider(apiImportModule[0], multitree.ApiImportsProvider).(multitree.ApiImportInfo)
+				apiInfo, _ := android.OtherModuleProvider(actx, apiImportModule[0], multitree.ApiImportsProvider)
 				apiImportInfo = apiInfo
-				actx.SetProvider(multitree.ApiImportsProvider, apiInfo)
+				android.SetProvider(actx, multitree.ApiImportsProvider, apiInfo)
 			}
 		}
 	}
@@ -2385,16 +2441,16 @@
 		// Only retrieve the snapshot on demand in order to avoid circular dependencies
 		// between the modules in the snapshot and the snapshot itself.
 		var snapshotModule []blueprint.Module
-		if c.InVendor() && c.VndkVersion() == actx.DeviceConfig().VndkVersion() {
+		if c.InVendor() && c.VndkVersion() == actx.DeviceConfig().VndkVersion() && actx.OtherModuleExists("vendor_snapshot") {
 			snapshotModule = actx.AddVariationDependencies(nil, nil, "vendor_snapshot")
-		} else if recoverySnapshotVersion := actx.DeviceConfig().RecoverySnapshotVersion(); recoverySnapshotVersion != "current" && recoverySnapshotVersion != "" && c.InRecovery() {
+		} else if recoverySnapshotVersion := actx.DeviceConfig().RecoverySnapshotVersion(); recoverySnapshotVersion != "current" && recoverySnapshotVersion != "" && c.InRecovery() && actx.OtherModuleExists("recovery_snapshot") {
 			snapshotModule = actx.AddVariationDependencies(nil, nil, "recovery_snapshot")
 		}
 		if len(snapshotModule) > 0 && snapshotModule[0] != nil {
-			snapshot := actx.OtherModuleProvider(snapshotModule[0], SnapshotInfoProvider).(SnapshotInfo)
+			snapshot, _ := android.OtherModuleProvider(actx, snapshotModule[0], SnapshotInfoProvider)
 			*snapshotInfo = &snapshot
 			// republish the snapshot for use in later mutators on this module
-			actx.SetProvider(SnapshotInfoProvider, snapshot)
+			android.SetProvider(actx, SnapshotInfoProvider, snapshot)
 		}
 	}
 	if *snapshotInfo == nil {
@@ -2998,7 +3054,7 @@
 		depPaths.ReexportedGeneratedHeaders = append(depPaths.ReexportedGeneratedHeaders, exporter.GeneratedHeaders...)
 	}
 
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	c.apexSdkVersion = findApexSdkVersion(ctx, apexInfo)
 
 	skipModuleList := map[string]bool{}
@@ -3008,7 +3064,7 @@
 
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		if dep.Name() == "api_imports" {
-			apiImportInfo = ctx.OtherModuleProvider(dep, multitree.ApiImportsProvider).(multitree.ApiImportInfo)
+			apiImportInfo, _ = android.OtherModuleProvider(ctx, dep, multitree.ApiImportsProvider)
 			hasApiImportInfo = true
 		}
 	})
@@ -3059,12 +3115,10 @@
 		}
 
 		if depTag == aidlLibraryTag {
-			if ctx.OtherModuleHasProvider(dep, aidl_library.AidlLibraryProvider) {
+			if aidlLibraryInfo, ok := android.OtherModuleProvider(ctx, dep, aidl_library.AidlLibraryProvider); ok {
 				depPaths.AidlLibraryInfos = append(
 					depPaths.AidlLibraryInfos,
-					ctx.OtherModuleProvider(
-						dep,
-						aidl_library.AidlLibraryProvider).(aidl_library.AidlLibraryInfo),
+					aidlLibraryInfo,
 				)
 			}
 		}
@@ -3129,10 +3183,10 @@
 			// version mutator, so the stubs variant is created from the shared variant that
 			// already has the reuseObjTag dependency on the static variant.
 			if !c.library.buildStubs() {
-				staticAnalogue := ctx.OtherModuleProvider(dep, StaticLibraryInfoProvider).(StaticLibraryInfo)
+				staticAnalogue, _ := android.OtherModuleProvider(ctx, dep, StaticLibraryInfoProvider)
 				objs := staticAnalogue.ReuseObjects
 				depPaths.Objs = depPaths.Objs.Append(objs)
-				depExporterInfo := ctx.OtherModuleProvider(dep, FlagExporterInfoProvider).(FlagExporterInfo)
+				depExporterInfo, _ := android.OtherModuleProvider(ctx, dep, FlagExporterInfoProvider)
 				reexportExporter(depExporterInfo)
 			}
 			return
@@ -3153,7 +3207,7 @@
 				return
 			}
 
-			depExporterInfo := ctx.OtherModuleProvider(dep, FlagExporterInfoProvider).(FlagExporterInfo)
+			depExporterInfo, _ := android.OtherModuleProvider(ctx, dep, FlagExporterInfoProvider)
 
 			var ptr *android.Paths
 			var depPtr *android.Paths
@@ -3162,7 +3216,7 @@
 
 			switch {
 			case libDepTag.header():
-				if !ctx.OtherModuleHasProvider(dep, HeaderLibraryInfoProvider) {
+				if _, isHeaderLib := android.OtherModuleProvider(ctx, dep, HeaderLibraryInfoProvider); !isHeaderLib {
 					if !ctx.Config().AllowMissingDependencies() {
 						ctx.ModuleErrorf("module %q is not a header library", depName)
 					} else {
@@ -3171,7 +3225,7 @@
 					return
 				}
 			case libDepTag.shared():
-				if !ctx.OtherModuleHasProvider(dep, SharedLibraryInfoProvider) {
+				if _, isSharedLib := android.OtherModuleProvider(ctx, dep, SharedLibraryInfoProvider); !isSharedLib {
 					if !ctx.Config().AllowMissingDependencies() {
 						ctx.ModuleErrorf("module %q is not a shared library", depName)
 					} else {
@@ -3208,7 +3262,8 @@
 					panic(fmt.Errorf("unexpected library dependency order %d", libDepTag.Order))
 				}
 			case libDepTag.static():
-				if !ctx.OtherModuleHasProvider(dep, StaticLibraryInfoProvider) {
+				staticLibraryInfo, isStaticLib := android.OtherModuleProvider(ctx, dep, StaticLibraryInfoProvider)
+				if !isStaticLib {
 					if !ctx.Config().AllowMissingDependencies() {
 						ctx.ModuleErrorf("module %q is not a static library", depName)
 					} else {
@@ -3223,7 +3278,6 @@
 					break
 				}
 
-				staticLibraryInfo := ctx.OtherModuleProvider(dep, StaticLibraryInfoProvider).(StaticLibraryInfo)
 				linkFile = android.OptionalPathForPath(staticLibraryInfo.StaticLibrary)
 				if libDepTag.wholeStatic {
 					ptr = &depPaths.WholeStaticLibs
@@ -3323,7 +3377,7 @@
 					c.sabi.Properties.ReexportedIncludes, depExporterInfo.IncludeDirs.Strings()...)
 			}
 
-			makeLibName := MakeLibName(ctx, c, ccDep, depName) + libDepTag.makeSuffix
+			makeLibName := MakeLibName(ctx, c, ccDep, ccDep.BaseModuleName()) + libDepTag.makeSuffix
 			switch {
 			case libDepTag.header():
 				c.Properties.AndroidMkHeaderLibs = append(
@@ -3333,7 +3387,7 @@
 					if lib.buildStubs() && dep.(android.ApexModule).InAnyApex() {
 						// Add the dependency to the APEX(es) providing the library so that
 						// m <module> can trigger building the APEXes as well.
-						depApexInfo := ctx.OtherModuleProvider(dep, android.ApexInfoProvider).(android.ApexInfo)
+						depApexInfo, _ := android.OtherModuleProvider(ctx, dep, android.ApexInfoProvider)
 						for _, an := range depApexInfo.InApexVariants {
 							c.Properties.ApexesProvidingSharedLibs = append(
 								c.Properties.ApexesProvidingSharedLibs, an)
@@ -3364,7 +3418,7 @@
 			switch depTag {
 			case runtimeDepTag:
 				c.Properties.AndroidMkRuntimeLibs = append(
-					c.Properties.AndroidMkRuntimeLibs, MakeLibName(ctx, c, ccDep, depName)+libDepTag.makeSuffix)
+					c.Properties.AndroidMkRuntimeLibs, MakeLibName(ctx, c, ccDep, ccDep.BaseModuleName())+libDepTag.makeSuffix)
 				// Record BaseLibName for snapshots.
 				c.Properties.SnapshotRuntimeLibs = append(c.Properties.SnapshotRuntimeLibs, BaseLibName(depName))
 			case objDepTag:
@@ -3409,20 +3463,20 @@
 		panic(fmt.Errorf("Not an APEX module: %q", ctx.ModuleName()))
 	}
 
-	useVndk := false
+	inVendorOrProduct := false
 	bootstrap := false
 	if linkable, ok := ctx.Module().(LinkableInterface); !ok {
 		panic(fmt.Errorf("Not a Linkable module: %q", ctx.ModuleName()))
 	} else {
-		useVndk = linkable.UseVndk()
+		inVendorOrProduct = linkable.InVendorOrProduct()
 		bootstrap = linkable.Bootstrap()
 	}
 
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 
 	useStubs := false
 
-	if lib := moduleLibraryInterface(dep); lib.buildStubs() && useVndk { // LLNDK
+	if lib := moduleLibraryInterface(dep); lib.buildStubs() && 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
@@ -3452,7 +3506,7 @@
 			// Another exception: if this module is a test for an APEX, then
 			// it is linked with the non-stub variant of a module in the APEX
 			// as if this is part of the APEX.
-			testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
+			testFor, _ := android.ModuleProvider(ctx, android.ApexTestForInfoProvider)
 			for _, apexContents := range testFor.ApexContents {
 				if apexContents.DirectlyInApex(depName) {
 					useStubs = false
@@ -3498,9 +3552,9 @@
 		panic(fmt.Errorf("Unexpected dependency tag: %T", depTag))
 	}
 
-	sharedLibraryInfo := ctx.OtherModuleProvider(dep, SharedLibraryInfoProvider).(SharedLibraryInfo)
-	depExporterInfo := ctx.OtherModuleProvider(dep, FlagExporterInfoProvider).(FlagExporterInfo)
-	sharedLibraryStubsInfo := ctx.OtherModuleProvider(dep, SharedLibraryStubsProvider).(SharedLibraryStubsInfo)
+	sharedLibraryInfo, _ := android.OtherModuleProvider(ctx, dep, SharedLibraryInfoProvider)
+	depExporterInfo, _ := android.OtherModuleProvider(ctx, dep, FlagExporterInfoProvider)
+	sharedLibraryStubsInfo, _ := android.OtherModuleProvider(ctx, dep, SharedLibraryStubsProvider)
 
 	if !libDepTag.explicitlyVersioned && len(sharedLibraryStubsInfo.SharedStubLibraries) > 0 {
 		// when to use (unspecified) stubs, use the latest one.
@@ -3574,7 +3628,7 @@
 		// The vendor module is a no-vendor-variant VNDK library.  Depend on the
 		// core module instead.
 		return libName
-	} else if ccDep.UseVndk() && nonSystemVariantsExist {
+	} else if ccDep.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()
@@ -3751,7 +3805,7 @@
 }
 
 func GetMakeLinkType(actx android.ModuleContext, c LinkableInterface) string {
-	if c.UseVndk() {
+	if c.InVendorOrProduct() {
 		if c.IsLlndk() {
 			if !c.IsLlndkPublic() {
 				return "native:vndk_private"
@@ -4045,6 +4099,9 @@
 	android.ModuleBase
 	android.DefaultsModuleBase
 	android.ApexModuleBase
+
+	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
+	mergedAconfigFiles map[string]android.Paths
 }
 
 // cc_defaults provides a set of properties that can be inherited by other cc
@@ -4136,6 +4193,18 @@
 	return ""
 }
 
+type sourceModuleName interface {
+	sourceModuleName() string
+}
+
+func (c *Module) BaseModuleName() string {
+	if smn, ok := c.linker.(sourceModuleName); ok && smn.sourceModuleName() != "" {
+		// if the prebuilt module sets a source_module_name in Android.bp, use that
+		return smn.sourceModuleName()
+	}
+	return c.ModuleBase.BaseModuleName()
+}
+
 var Bool = proptools.Bool
 var BoolDefault = proptools.BoolDefault
 var BoolPtr = proptools.BoolPtr
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 3631f19..6cc500b 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -26,6 +26,8 @@
 
 	"android/soong/aidl_library"
 	"android/soong/android"
+
+	"github.com/google/blueprint"
 )
 
 func init() {
@@ -41,10 +43,19 @@
 	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 		variables.VendorApiLevel = StringPtr("202404")
 		variables.DeviceVndkVersion = StringPtr("current")
+		variables.KeepVndk = BoolPtr(true)
 		variables.Platform_vndk_version = StringPtr("29")
 	}),
 )
 
+// TODO(b/316829758) Update prepareForCcTest with this configuration and remove prepareForCcTestWithoutVndk
+var prepareForCcTestWithoutVndk = android.GroupFixturePreparers(
+	PrepareForIntegrationTestWithCc,
+	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+		variables.VendorApiLevel = StringPtr("202404")
+	}),
+)
+
 var apexVariationName = "apex28"
 var apexVersion = "28"
 
@@ -194,13 +205,13 @@
 		}
 	}
 	socSpecific := func(m *Module) bool {
-		return m.SocSpecific() || m.socSpecificModuleContext()
+		return m.SocSpecific() || m.InstallInVendor()
 	}
 	deviceSpecific := func(m *Module) bool {
-		return m.DeviceSpecific() || m.deviceSpecificModuleContext()
+		return m.DeviceSpecific() || m.InstallInOdm()
 	}
 	productSpecific := func(m *Module) bool {
-		return m.ProductSpecific() || m.productSpecificModuleContext()
+		return m.ProductSpecific() || m.InstallInProduct()
 	}
 	systemExtSpecific := func(m *Module) bool {
 		return m.SystemExtSpecific()
@@ -2544,8 +2555,8 @@
 
 	variant := "android_arm64_armv8-a_static"
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
-	actual := android.Paths(ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).
-		TransitiveStaticLibrariesForOrdering.ToList()).RelativeToTop()
+	staticLibInfo, _ := android.SingletonModuleProvider(ctx, moduleA, StaticLibraryInfoProvider)
+	actual := android.Paths(staticLibInfo.TransitiveStaticLibrariesForOrdering.ToList()).RelativeToTop()
 	expected := GetOutputPaths(ctx, variant, []string{"a", "c", "b", "d"})
 
 	if !reflect.DeepEqual(actual, expected) {
@@ -2580,8 +2591,8 @@
 
 	variant := "android_arm64_armv8-a_static"
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
-	actual := android.Paths(ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).
-		TransitiveStaticLibrariesForOrdering.ToList()).RelativeToTop()
+	staticLibInfo, _ := android.SingletonModuleProvider(ctx, moduleA, StaticLibraryInfoProvider)
+	actual := android.Paths(staticLibInfo.TransitiveStaticLibrariesForOrdering.ToList()).RelativeToTop()
 	expected := GetOutputPaths(ctx, variant, []string{"a", "c", "b"})
 
 	if !reflect.DeepEqual(actual, expected) {
@@ -2640,6 +2651,7 @@
 		name: "libexternal_headers",
 		export_include_dirs: ["include"],
 		vendor_available: true,
+		product_available: true,
 	}
 	cc_library_headers {
 		name: "libexternal_llndk_headers",
@@ -2681,7 +2693,7 @@
 	checkExportedIncludeDirs := func(module, variant string, expectedDirs ...string) {
 		t.Helper()
 		m := result.ModuleForTests(module, variant).Module()
-		f := result.ModuleProvider(m, FlagExporterInfoProvider).(FlagExporterInfo)
+		f, _ := android.SingletonModuleProvider(result, m, FlagExporterInfoProvider)
 		android.AssertPathsRelativeToTopEquals(t, "exported include dirs for "+module+"["+variant+"]",
 			expectedDirs, f.IncludeDirs)
 	}
@@ -4113,7 +4125,7 @@
 
 	checkIncludeDirs := func(t *testing.T, ctx *android.TestContext, module android.Module, checkers ...exportedChecker) {
 		t.Helper()
-		exported := ctx.ModuleProvider(module, FlagExporterInfoProvider).(FlagExporterInfo)
+		exported, _ := android.SingletonModuleProvider(ctx, module, FlagExporterInfoProvider)
 		name := module.Name()
 
 		for _, checker := range checkers {
@@ -4784,3 +4796,91 @@
 		return
 	}
 }
+
+// TODO(b/316829758) Remove this test and do not set VNDK version from other tests
+func TestImageVariantsWithoutVndk(t *testing.T) {
+	t.Parallel()
+
+	bp := `
+	cc_binary {
+		name: "binfoo",
+		srcs: ["binfoo.cc"],
+		vendor_available: true,
+		product_available: true,
+		shared_libs: ["libbar"]
+	}
+	cc_library {
+		name: "libbar",
+		srcs: ["libbar.cc"],
+		vendor_available: true,
+		product_available: true,
+	}
+	`
+
+	ctx := prepareForCcTestWithoutVndk.RunTestWithBp(t, bp)
+
+	hasDep := func(m android.Module, wantDep android.Module) bool {
+		t.Helper()
+		var found bool
+		ctx.VisitDirectDeps(m, func(dep blueprint.Module) {
+			if dep == wantDep {
+				found = true
+			}
+		})
+		return found
+	}
+
+	testDepWithVariant := func(imageVariant string) {
+		imageVariantStr := ""
+		if imageVariant != "core" {
+			imageVariantStr = "_" + imageVariant
+		}
+		binFooModule := ctx.ModuleForTests("binfoo", "android"+imageVariantStr+"_arm64_armv8-a").Module()
+		libBarModule := ctx.ModuleForTests("libbar", "android"+imageVariantStr+"_arm64_armv8-a_shared").Module()
+		android.AssertBoolEquals(t, "binfoo should have dependency on libbar with image variant "+imageVariant, true, hasDep(binFooModule, libBarModule))
+	}
+
+	testDepWithVariant("core")
+	testDepWithVariant("vendor")
+	testDepWithVariant("product")
+}
+
+func TestVendorSdkVersionWithoutVndk(t *testing.T) {
+	t.Parallel()
+
+	bp := `
+		cc_library {
+			name: "libfoo",
+			srcs: ["libfoo.cc"],
+			vendor_available: true,
+		}
+
+		cc_library {
+			name: "libbar",
+			srcs: ["libbar.cc"],
+			vendor_available: true,
+			min_sdk_version: "29",
+		}
+	`
+
+	ctx := prepareForCcTestWithoutVndk.RunTestWithBp(t, bp)
+	testSdkVersionFlag := func(module, version string) {
+		flags := ctx.ModuleForTests(module, "android_vendor_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+		android.AssertStringDoesContain(t, "min sdk version", flags, "-target aarch64-linux-android"+version)
+	}
+
+	testSdkVersionFlag("libfoo", "10000")
+	testSdkVersionFlag("libbar", "29")
+
+	ctx = android.GroupFixturePreparers(
+		prepareForCcTestWithoutVndk,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			if variables.BuildFlags == nil {
+				variables.BuildFlags = make(map[string]string)
+			}
+			variables.BuildFlags["RELEASE_BOARD_API_LEVEL_FROZEN"] = "true"
+		}),
+	).RunTestWithBp(t, bp)
+	testSdkVersionFlag("libfoo", "30")
+	testSdkVersionFlag("libbar", "29")
+}
diff --git a/cc/check.go b/cc/check.go
index 58ff5b2..32d1f06 100644
--- a/cc/check.go
+++ b/cc/check.go
@@ -45,6 +45,8 @@
 				ctx.PropertyErrorf(prop, "-Weverything is not allowed in Android.bp files.  "+
 					"Build with `m ANDROID_TEMPORARILY_ALLOW_WEVERYTHING=true` to experiment locally with -Weverything.")
 			}
+		} else if strings.HasPrefix(flag, "-target") || strings.HasPrefix(flag, "--target") {
+			ctx.PropertyErrorf(prop, "Bad flag: `%s`, use the correct target soong rule.", flag)
 		} else if strings.Contains(flag, " ") {
 			args := strings.Split(flag, " ")
 			if args[0] == "-include" {
diff --git a/cc/compiler.go b/cc/compiler.go
index 2c68fa7..40e8974 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -116,6 +116,10 @@
 	// if set to false, use -std=c++* instead of -std=gnu++*
 	Gnu_extensions *bool
 
+	// cc Build rules targeting BPF must set this to true. The correct fix is to
+	// ban targeting bpf in cc rules instead use bpf_rules. (b/323415017)
+	Bpf_target *bool
+
 	Yacc *YaccProperties
 	Lex  *LexProperties
 
@@ -395,7 +399,7 @@
 		flags.Local.YasmFlags = append(flags.Local.YasmFlags, "-I"+modulePath)
 	}
 
-	if !(ctx.useSdk() || ctx.useVndk()) || ctx.Host() {
+	if !(ctx.useSdk() || ctx.InVendorOrProduct()) || ctx.Host() {
 		flags.SystemIncludeFlags = append(flags.SystemIncludeFlags,
 			"${config.CommonGlobalIncludes}",
 			tc.IncludeFlags())
@@ -412,7 +416,7 @@
 			"-isystem "+getCurrentIncludePath(ctx).Join(ctx, config.NDKTriple(tc)).String())
 	}
 
-	if ctx.useVndk() {
+	if ctx.InVendorOrProduct() {
 		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VNDK__")
 		if ctx.inVendor() {
 			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VENDOR__")
@@ -493,6 +497,11 @@
 		}
 	}
 
+	// bpf targets don't need the default target triple. b/308826679
+	if proptools.Bool(compiler.Properties.Bpf_target) {
+		target = "--target=bpf"
+	}
+
 	flags.Global.CFlags = append(flags.Global.CFlags, target)
 	flags.Global.AsFlags = append(flags.Global.AsFlags, target)
 	flags.Global.LdFlags = append(flags.Global.LdFlags, target)
@@ -508,8 +517,12 @@
 
 	flags.Global.AsFlags = append(flags.Global.AsFlags, tc.Asflags())
 	flags.Global.CppFlags = append([]string{"${config.CommonGlobalCppflags}"}, flags.Global.CppFlags...)
+
+	// bpf targets don't need the target specific toolchain cflags. b/308826679
+	if !proptools.Bool(compiler.Properties.Bpf_target) {
+		flags.Global.CommonFlags = append(flags.Global.CommonFlags, tc.Cflags())
+	}
 	flags.Global.CommonFlags = append(flags.Global.CommonFlags,
-		tc.Cflags(),
 		"${config.CommonGlobalCflags}",
 		fmt.Sprintf("${config.%sGlobalCflags}", hod))
 
@@ -531,7 +544,11 @@
 
 	flags.Global.YasmFlags = append(flags.Global.YasmFlags, tc.YasmFlags())
 
-	flags.Global.CommonFlags = append(flags.Global.CommonFlags, tc.ToolchainCflags())
+	// bpf targets don't need the target specific toolchain cflags. b/308826679
+	if !proptools.Bool(compiler.Properties.Bpf_target) {
+		flags.Global.CommonFlags = append(flags.Global.CommonFlags, tc.ToolchainCflags())
+	}
+
 
 	cStd := parseCStd(compiler.Properties.C_std)
 	cppStd := parseCppStd(compiler.Properties.Cpp_std)
@@ -730,7 +747,7 @@
 
 	// Compile files listed in c.Properties.Srcs into objects
 	objs := compileObjs(ctx, buildFlags, "", srcs,
-		android.PathsForModuleSrc(ctx, compiler.Properties.Tidy_disabled_srcs),
+		append(android.PathsForModuleSrc(ctx, compiler.Properties.Tidy_disabled_srcs), compiler.generatedSources...),
 		android.PathsForModuleSrc(ctx, compiler.Properties.Tidy_timeout_srcs),
 		pathDeps, compiler.cFlagsDeps)
 
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index 3d6890c..603bc6d 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -28,13 +28,20 @@
 
 	armCflags = []string{
 		"-fomit-frame-pointer",
+		// Revert this after b/322359235 is fixed
+		"-mllvm", "-enable-shrink-wrap=false",
 	}
 
-	armCppflags = []string{}
+	armCppflags = []string{
+		// Revert this after b/322359235 is fixed
+		"-mllvm", "-enable-shrink-wrap=false",
+  }
 
 	armLdflags = []string{
 		"-Wl,--hash-style=gnu",
 		"-Wl,-m,armelf",
+		// Revert this after b/322359235 is fixed
+		"-Wl,-mllvm", "-Wl,-enable-shrink-wrap=false",
 	}
 
 	armLldflags = armLdflags
diff --git a/cc/config/bionic.go b/cc/config/bionic.go
index a1e3851..ed724f5 100644
--- a/cc/config/bionic.go
+++ b/cc/config/bionic.go
@@ -24,6 +24,7 @@
 	bionicCrtBeginStaticBinary, bionicCrtEndStaticBinary   = []string{"crtbegin_static"}, []string{"crtend_android"}
 	bionicCrtBeginSharedBinary, bionicCrtEndSharedBinary   = []string{"crtbegin_dynamic"}, []string{"crtend_android"}
 	bionicCrtBeginSharedLibrary, bionicCrtEndSharedLibrary = []string{"crtbegin_so"}, []string{"crtend_so"}
+	bionicCrtPadSegmentSharedLibrary                       = []string{"crt_pad_segment"}
 )
 
 func (toolchainBionic) Bionic() bool { return true }
@@ -36,9 +37,10 @@
 
 func (toolchainBionic) AvailableLibraries() []string { return nil }
 
-func (toolchainBionic) CrtBeginStaticBinary() []string  { return bionicCrtBeginStaticBinary }
-func (toolchainBionic) CrtBeginSharedBinary() []string  { return bionicCrtBeginSharedBinary }
-func (toolchainBionic) CrtBeginSharedLibrary() []string { return bionicCrtBeginSharedLibrary }
-func (toolchainBionic) CrtEndStaticBinary() []string    { return bionicCrtEndStaticBinary }
-func (toolchainBionic) CrtEndSharedBinary() []string    { return bionicCrtEndSharedBinary }
-func (toolchainBionic) CrtEndSharedLibrary() []string   { return bionicCrtEndSharedLibrary }
+func (toolchainBionic) CrtBeginStaticBinary() []string       { return bionicCrtBeginStaticBinary }
+func (toolchainBionic) CrtBeginSharedBinary() []string       { return bionicCrtBeginSharedBinary }
+func (toolchainBionic) CrtBeginSharedLibrary() []string      { return bionicCrtBeginSharedLibrary }
+func (toolchainBionic) CrtEndStaticBinary() []string         { return bionicCrtEndStaticBinary }
+func (toolchainBionic) CrtEndSharedBinary() []string         { return bionicCrtEndSharedBinary }
+func (toolchainBionic) CrtEndSharedLibrary() []string        { return bionicCrtEndSharedLibrary }
+func (toolchainBionic) CrtPadSegmentSharedLibrary() []string { return bionicCrtPadSegmentSharedLibrary }
diff --git a/cc/config/darwin_host.go b/cc/config/darwin_host.go
index b789590..47c61b0 100644
--- a/cc/config/darwin_host.go
+++ b/cc/config/darwin_host.go
@@ -29,11 +29,6 @@
 		"-fPIC",
 		"-funwind-tables",
 
-		// Workaround differences in inttypes.h between host and target.
-		//See bug 12708004.
-		"-D__STDC_FORMAT_MACROS",
-		"-D__STDC_CONSTANT_MACROS",
-
 		"-isysroot ${macSdkRoot}",
 		"-mmacosx-version-min=${macMinVersion}",
 		"-DMACOSX_DEPLOYMENT_TARGET=${macMinVersion}",
diff --git a/cc/config/global.go b/cc/config/global.go
index 85ebd60..b21d56c 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -16,6 +16,7 @@
 
 import (
 	"runtime"
+	"slices"
 	"strings"
 
 	"android/soong/android"
@@ -252,6 +253,14 @@
 		// http://b/161386391 for -Wno-pointer-to-int-cast
 		"-Wno-pointer-to-int-cast",
 		"-Werror=fortify-source",
+		// http://b/315246135 temporarily disabled
+		"-Wno-unused-variable",
+		// http://b/315250603 temporarily disabled
+		"-Wno-error=format",
+		// Disabled because it produces many false positives. http://b/323050926
+		"-Wno-missing-field-initializers",
+		// http://b/323050889
+		"-Wno-packed-non-pod",
 
 		"-Werror=address-of-temporary",
 		"-Werror=incompatible-function-pointer-types",
@@ -271,7 +280,8 @@
 		"-Wno-range-loop-construct",                 // http://b/153747076
 		"-Wno-zero-as-null-pointer-constant",        // http://b/68236239
 		"-Wno-deprecated-anon-enum-enum-conversion", // http://b/153746485
-		"-Wno-pessimizing-move",                     // http://b/154270751
+		"-Wno-deprecated-enum-enum-conversion",
+		"-Wno-pessimizing-move", // http://b/154270751
 		// New warnings to be fixed after clang-r399163
 		"-Wno-non-c-typedef-for-linkage", // http://b/161304145
 		// New warnings to be fixed after clang-r428724
@@ -285,6 +295,13 @@
 		// New warnings to be fixed after clang-r475365
 		"-Wno-error=single-bit-bitfield-constant-conversion", // http://b/243965903
 		"-Wno-error=enum-constexpr-conversion",               // http://b/243964282
+
+		// Irrelevant on Android because _we_ don't use exceptions, but causes
+		// lots of build noise because libcxx/libcxxabi do. This can probably
+		// go away when we're on a new enough libc++, but has to be global
+		// until then because it causes warnings in the _callers_, not the
+		// project itself.
+		"-Wno-deprecated-dynamic-exception-spec",
 	}
 
 	noOverride64GlobalCflags = []string{}
@@ -319,6 +336,9 @@
 
 		// http://b/239661264
 		"-Wno-deprecated-non-prototype",
+
+		"-Wno-unused",
+		"-Wno-deprecated",
 	}
 
 	// Similar to noOverrideGlobalCflags, but applies only to third-party code
@@ -339,11 +359,14 @@
 		// enabling since it's a cosmetic issue.
 		"-Wno-bitwise-instead-of-logical",
 
-		"-Wno-unused-but-set-variable",
+		"-Wno-unused",
+		"-Wno-unused-parameter",
 		"-Wno-unused-but-set-parameter",
 		"-Wno-unqualified-std-cast-call",
 		"-Wno-array-parameter",
 		"-Wno-gnu-offsetof-extensions",
+		// TODO: Enable this warning http://b/315245071
+		"-Wno-fortify-source",
 	}
 
 	llvmNextExtraCommonGlobalCflags = []string{
@@ -363,8 +386,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r498229b"
-	ClangDefaultShortVersion = "17"
+	ClangDefaultVersion      = "clang-r510928"
+	ClangDefaultShortVersion = "18"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
@@ -400,7 +423,7 @@
 	exportedVars.ExportStringList("CommonGlobalCflags", commonGlobalCflags)
 
 	pctx.VariableFunc("CommonGlobalCflags", func(ctx android.PackageVarContext) string {
-		flags := commonGlobalCflags
+		flags := slices.Clone(commonGlobalCflags)
 
 		// http://b/131390872
 		// Automatically initialize any uninitialized stack variables.
diff --git a/cc/config/riscv64_device.go b/cc/config/riscv64_device.go
index 0964015..6a84fee 100644
--- a/cc/config/riscv64_device.go
+++ b/cc/config/riscv64_device.go
@@ -26,9 +26,11 @@
 		// Help catch common 32/64-bit errors.
 		"-Werror=implicit-function-declaration",
 		"-march=rv64gcv_zba_zbb_zbs",
-		// Equivalent to "-munaligned-access", but our clang doesn't have that yet.
-		"-Xclang -target-feature -Xclang +unaligned-scalar-mem",
-		"-Xclang -target-feature -Xclang +unaligned-vector-mem",
+		"-munaligned-access",
+		// Until https://gitlab.com/qemu-project/qemu/-/issues/1976 is fixed...
+		"-mno-implicit-float",
+		// (https://github.com/google/android-riscv64/issues/124)
+		"-mllvm -jump-is-expensive=false",
 	}
 
 	riscv64ArchVariantCflags = map[string][]string{}
@@ -36,12 +38,10 @@
 	riscv64Ldflags = []string{
 		"-Wl,--hash-style=gnu",
 		"-march=rv64gcv_zba_zbb_zbs",
-		// Equivalent to "-munaligned-access", but our clang doesn't have that yet.
-		"-Xclang -target-feature -Xclang +unaligned-scalar-mem",
-		"-Xclang -target-feature -Xclang +unaligned-vector-mem",
+		"-munaligned-access",
 		// We should change the default for this in clang, but for now...
 		// (https://github.com/google/android-riscv64/issues/124)
-		"-mllvm -jump-is-expensive=false",
+		"-Wl,-mllvm -Wl,-jump-is-expensive=false",
 	}
 
 	riscv64Lldflags = append(riscv64Ldflags,
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index 62f75d1..71e98fe 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -100,6 +100,7 @@
 	CrtEndStaticBinary() []string
 	CrtEndSharedBinary() []string
 	CrtEndSharedLibrary() []string
+	CrtPadSegmentSharedLibrary() []string
 
 	// DefaultSharedLibraries returns the list of shared libraries that will be added to all
 	// targets unless they explicitly specify system_shared_libs.
@@ -155,12 +156,13 @@
 
 type toolchainNoCrt struct{}
 
-func (toolchainNoCrt) CrtBeginStaticBinary() []string  { return nil }
-func (toolchainNoCrt) CrtBeginSharedBinary() []string  { return nil }
-func (toolchainNoCrt) CrtBeginSharedLibrary() []string { return nil }
-func (toolchainNoCrt) CrtEndStaticBinary() []string    { return nil }
-func (toolchainNoCrt) CrtEndSharedBinary() []string    { return nil }
-func (toolchainNoCrt) CrtEndSharedLibrary() []string   { return nil }
+func (toolchainNoCrt) CrtBeginStaticBinary() []string       { return nil }
+func (toolchainNoCrt) CrtBeginSharedBinary() []string       { return nil }
+func (toolchainNoCrt) CrtBeginSharedLibrary() []string      { return nil }
+func (toolchainNoCrt) CrtEndStaticBinary() []string         { return nil }
+func (toolchainNoCrt) CrtEndSharedBinary() []string         { return nil }
+func (toolchainNoCrt) CrtEndSharedLibrary() []string        { return nil }
+func (toolchainNoCrt) CrtPadSegmentSharedLibrary() []string { return nil }
 
 func (toolchainBase) DefaultSharedLibraries() []string {
 	return nil
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index e43848c..b97d511 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -49,6 +49,11 @@
 		"goldmont-plus": []string{
 			"-march=goldmont-plus",
 		},
+		"goldmont-without-sha-xsaves": []string{
+			"-march=goldmont",
+			"-mno-sha",
+			"-mno-xsaves",
+		},
 		"haswell": []string{
 			"-march=core-avx2",
 		},
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index c826d3c..2faa670 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -56,6 +56,11 @@
 		"goldmont-plus": []string{
 			"-march=goldmont-plus",
 		},
+		"goldmont-without-sha-xsaves": []string{
+			"-march=goldmont",
+			"-mno-sha",
+			"-mno-xsaves",
+		},
 		"haswell": []string{
 			"-march=core-avx2",
 		},
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index f95da0b..9bc54d6 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -30,11 +30,6 @@
 		"-D_FORTIFY_SOURCE=2",
 		"-fstack-protector",
 
-		// Workaround differences in inttypes.h between host and target.
-		//See bug 12708004.
-		"-D__STDC_FORMAT_MACROS",
-		"-D__STDC_CONSTANT_MACROS",
-
 		"--gcc-toolchain=${LinuxGccRoot}",
 		"-fstack-protector-strong",
 	}
@@ -328,12 +323,13 @@
 
 func (toolchainMusl) Musl() bool { return true }
 
-func (toolchainMusl) CrtBeginStaticBinary() []string  { return muslCrtBeginStaticBinary }
-func (toolchainMusl) CrtBeginSharedBinary() []string  { return muslCrtBeginSharedBinary }
-func (toolchainMusl) CrtBeginSharedLibrary() []string { return muslCrtBeginSharedLibrary }
-func (toolchainMusl) CrtEndStaticBinary() []string    { return muslCrtEndStaticBinary }
-func (toolchainMusl) CrtEndSharedBinary() []string    { return muslCrtEndSharedBinary }
-func (toolchainMusl) CrtEndSharedLibrary() []string   { return muslCrtEndSharedLibrary }
+func (toolchainMusl) CrtBeginStaticBinary() []string       { return muslCrtBeginStaticBinary }
+func (toolchainMusl) CrtBeginSharedBinary() []string       { return muslCrtBeginSharedBinary }
+func (toolchainMusl) CrtBeginSharedLibrary() []string      { return muslCrtBeginSharedLibrary }
+func (toolchainMusl) CrtEndStaticBinary() []string         { return muslCrtEndStaticBinary }
+func (toolchainMusl) CrtEndSharedBinary() []string         { return muslCrtEndSharedBinary }
+func (toolchainMusl) CrtEndSharedLibrary() []string        { return muslCrtEndSharedLibrary }
+func (toolchainMusl) CrtPadSegmentSharedLibrary() []string { return nil }
 
 func (toolchainMusl) DefaultSharedLibraries() []string { return MuslDefaultSharedLibraries }
 
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index 561c500..1e61b01 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -27,9 +27,8 @@
 		"-DWIN32_LEAN_AND_MEAN",
 		"-Wno-unused-parameter",
 
-		// Workaround differences in inttypes.h between host and target.
-		//See bug 12708004.
-		"-D__STDC_FORMAT_MACROS",
+		// Workaround differences in <stdint.h> between host and target.
+		// Context: http://b/12708004
 		"-D__STDC_CONSTANT_MACROS",
 
 		// Use C99-compliant printf functions (%zd).
diff --git a/cc/coverage.go b/cc/coverage.go
index cbd8a6f..f6092e4 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -22,6 +22,29 @@
 	"android/soong/android"
 )
 
+var (
+ 	clangCoverageHostLdFlags = []string{
+ 		"-Wl,--no-as-needed",
+ 		"-Wl,--wrap,open",
+ 	}
+ 	clangContinuousCoverageFlags = []string{
+ 		"-mllvm",
+ 		"-runtime-counter-relocation",
+ 	}
+ 	clangCoverageCFlags = []string{
+ 		"-Wno-frame-larger-than=",
+ 	}
+ 	clangCoverageCommonFlags = []string{
+ 		"-fcoverage-mapping",
+ 		"-Wno-pass-failed",
+ 		"-D__ANDROID_CLANG_COVERAGE__",
+ 	}
+ 	clangCoverageHWASanFlags = []string{
+ 		"-mllvm",
+ 		"-hwasan-globals=0",
+ 	}
+)
+
 const profileInstrFlag = "-fprofile-instr-generate=/data/misc/trace/clang-%p-%m.profraw"
 
 type CoverageProperties struct {
@@ -68,7 +91,7 @@
 }
 
 func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps {
-	if cov.Properties.NeedCoverageVariant {
+	if cov.Properties.NeedCoverageVariant && ctx.Device() {
 		ctx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
 		}, CoverageDepTag, getGcovProfileLibraryName(ctx))
@@ -102,19 +125,19 @@
 			// flags that the module may use.
 			flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=", "-O0")
 		} else if clangCoverage {
-			flags.Local.CommonFlags = append(flags.Local.CommonFlags, profileInstrFlag,
-				"-fcoverage-mapping", "-Wno-pass-failed", "-D__ANDROID_CLANG_COVERAGE__")
+			flags.Local.CommonFlags = append(flags.Local.CommonFlags, profileInstrFlag)
+			flags.Local.CommonFlags = append(flags.Local.CommonFlags, clangCoverageCommonFlags...)
 			// Override -Wframe-larger-than.  We can expect frame size increase after
 			// coverage instrumentation.
-			flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=")
+			flags.Local.CFlags = append(flags.Local.CFlags, clangCoverageCFlags...)
 			if EnableContinuousCoverage(ctx) {
-				flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-mllvm", "-runtime-counter-relocation")
+				flags.Local.CommonFlags = append(flags.Local.CommonFlags, clangContinuousCoverageFlags...)
 			}
 
 			// http://b/248022906, http://b/247941801  enabling coverage and hwasan-globals
 			// instrumentation together causes duplicate-symbol errors for __llvm_profile_filename.
 			if c, ok := ctx.Module().(*Module); ok && c.sanitize.isSanitizerEnabled(Hwasan) {
-				flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-mllvm", "-hwasan-globals=0")
+				flags.Local.CommonFlags = append(flags.Local.CommonFlags, clangCoverageHWASanFlags...)
 			}
 		}
 	}
@@ -161,19 +184,22 @@
 		if gcovCoverage {
 			flags.Local.LdFlags = append(flags.Local.LdFlags, "--coverage")
 
-			coverage := ctx.GetDirectDepWithTag(getGcovProfileLibraryName(ctx), CoverageDepTag).(*Module)
-			deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
-
-			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv")
+			if ctx.Device() {
+				coverage := ctx.GetDirectDepWithTag(getGcovProfileLibraryName(ctx), CoverageDepTag).(*Module)
+				deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
+				flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv")
+			}
 		} else if clangCoverage {
 			flags.Local.LdFlags = append(flags.Local.LdFlags, profileInstrFlag)
 			if EnableContinuousCoverage(ctx) {
 				flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm=-runtime-counter-relocation")
 			}
 
-			coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), CoverageDepTag).(*Module)
-			deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
-			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,open")
+			if ctx.Device() {
+				coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), CoverageDepTag).(*Module)
+				deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
+				flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,open")
+			}
 		}
 	}
 
@@ -181,7 +207,7 @@
 }
 
 func (cov *coverage) begin(ctx BaseModuleContext) {
-	if ctx.Host() {
+	if ctx.Host() && !ctx.Os().Linux() {
 		// TODO(dwillemsen): because of -nodefaultlibs, we must depend on libclang_rt.profile-*.a
 		// Just turn off for now.
 	} else {
@@ -223,7 +249,7 @@
 
 type UseCoverage interface {
 	android.Module
-	IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool
+	IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool
 }
 
 // Coverage is an interface for non-CC modules to implement to be mutated for coverage
@@ -235,43 +261,86 @@
 	EnableCoverageIfNeeded()
 }
 
-func coverageMutator(mctx android.BottomUpMutatorContext) {
-	if c, ok := mctx.Module().(*Module); ok && c.coverage != nil {
-		needCoverageVariant := c.coverage.Properties.NeedCoverageVariant
-		needCoverageBuild := c.coverage.Properties.NeedCoverageBuild
-		if needCoverageVariant {
-			m := mctx.CreateVariations("", "cov")
+type coverageTransitionMutator struct{}
 
-			// Setup the non-coverage version and set HideFromMake and
-			// PreventInstall to true.
-			m[0].(*Module).coverage.Properties.CoverageEnabled = false
-			m[0].(*Module).coverage.Properties.IsCoverageVariant = false
-			m[0].(*Module).Properties.HideFromMake = true
-			m[0].(*Module).Properties.PreventInstall = true
+var _ android.TransitionMutator = (*coverageTransitionMutator)(nil)
 
-			// The coverage-enabled version inherits HideFromMake,
-			// PreventInstall from the original module.
-			m[1].(*Module).coverage.Properties.CoverageEnabled = needCoverageBuild
-			m[1].(*Module).coverage.Properties.IsCoverageVariant = true
+func (c coverageTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+	if c, ok := ctx.Module().(*Module); ok && c.coverage != nil {
+		if c.coverage.Properties.NeedCoverageVariant {
+			return []string{"", "cov"}
 		}
-	} else if cov, ok := mctx.Module().(Coverage); ok && cov.IsNativeCoverageNeeded(mctx) {
+	} else if cov, ok := ctx.Module().(Coverage); ok && cov.IsNativeCoverageNeeded(ctx) {
 		// APEX and Rust modules fall here
 
 		// Note: variant "" is also created because an APEX can be depended on by another
 		// module which are split into "" and "cov" variants. e.g. when cc_test refers
 		// to an APEX via 'data' property.
-		m := mctx.CreateVariations("", "cov")
-		m[0].(Coverage).MarkAsCoverageVariant(false)
-		m[0].(Coverage).SetPreventInstall()
-		m[0].(Coverage).HideFromMake()
-
-		m[1].(Coverage).MarkAsCoverageVariant(true)
-		m[1].(Coverage).EnableCoverageIfNeeded()
-	} else if cov, ok := mctx.Module().(UseCoverage); ok && cov.IsNativeCoverageNeeded(mctx) {
+		return []string{"", "cov"}
+	} else if cov, ok := ctx.Module().(UseCoverage); ok && cov.IsNativeCoverageNeeded(ctx) {
 		// Module itself doesn't have to have "cov" variant, but it should use "cov" variants of
 		// deps.
-		mctx.CreateVariations("cov")
-		mctx.AliasVariation("cov")
+		return []string{"cov"}
+	}
+
+	return []string{""}
+}
+
+func (c coverageTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	return sourceVariation
+}
+
+func (c coverageTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	if c, ok := ctx.Module().(*Module); ok && c.coverage != nil {
+		if !c.coverage.Properties.NeedCoverageVariant {
+			return ""
+		}
+	} else if cov, ok := ctx.Module().(Coverage); ok {
+		if !cov.IsNativeCoverageNeeded(ctx) {
+			return ""
+		}
+	} else if cov, ok := ctx.Module().(UseCoverage); ok && cov.IsNativeCoverageNeeded(ctx) {
+		// Module only has a "cov" variation, so all incoming variations should use "cov".
+		return "cov"
+	} else {
+		return ""
+	}
+
+	return incomingVariation
+}
+
+func (c coverageTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	if c, ok := ctx.Module().(*Module); ok && c.coverage != nil {
+		if variation == "" && c.coverage.Properties.NeedCoverageVariant {
+			// Setup the non-coverage version and set HideFromMake and
+			// PreventInstall to true.
+			c.coverage.Properties.CoverageEnabled = false
+			c.coverage.Properties.IsCoverageVariant = false
+			c.Properties.HideFromMake = true
+			c.Properties.PreventInstall = true
+		} else if variation == "cov" {
+			// The coverage-enabled version inherits HideFromMake,
+			// PreventInstall from the original module.
+			c.coverage.Properties.CoverageEnabled = c.coverage.Properties.NeedCoverageBuild
+			c.coverage.Properties.IsCoverageVariant = true
+		}
+	} else if cov, ok := ctx.Module().(Coverage); ok && cov.IsNativeCoverageNeeded(ctx) {
+		// APEX and Rust modules fall here
+
+		// Note: variant "" is also created because an APEX can be depended on by another
+		// module which are split into "" and "cov" variants. e.g. when cc_test refers
+		// to an APEX via 'data' property.
+		if variation == "" {
+			cov.MarkAsCoverageVariant(false)
+			cov.SetPreventInstall()
+			cov.HideFromMake()
+		} else if variation == "cov" {
+			cov.MarkAsCoverageVariant(true)
+			cov.EnableCoverageIfNeeded()
+		}
+	} else if cov, ok := ctx.Module().(UseCoverage); ok && cov.IsNativeCoverageNeeded(ctx) {
+		// Module itself doesn't have to have "cov" variant, but it should use "cov" variants of
+		// deps.
 	}
 }
 
diff --git a/cc/fdo_profile.go b/cc/fdo_profile.go
index 02f68b5..1a33957 100644
--- a/cc/fdo_profile.go
+++ b/cc/fdo_profile.go
@@ -43,39 +43,18 @@
 }
 
 // FdoProfileProvider is used to provide path to an fdo profile
-var FdoProfileProvider = blueprint.NewMutatorProvider(FdoProfileInfo{}, "fdo_profile")
-
-// FdoProfileMutatorInterface is the interface implemented by fdo_profile module type
-// module types that can depend on an fdo_profile module
-type FdoProfileMutatorInterface interface {
-	// FdoProfileMutator eithers set or get FdoProfileProvider
-	fdoProfileMutator(ctx android.BottomUpMutatorContext)
-}
-
-var _ FdoProfileMutatorInterface = (*fdoProfile)(nil)
+var FdoProfileProvider = blueprint.NewProvider[FdoProfileInfo]()
 
 // GenerateAndroidBuildActions of fdo_profile does not have any build actions
-func (fp *fdoProfile) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
-
-// FdoProfileMutator sets FdoProfileProvider to fdo_profile module
-// or sets afdo.Properties.FdoProfilePath to path in FdoProfileProvider of the depended fdo_profile
-func (fp *fdoProfile) fdoProfileMutator(ctx android.BottomUpMutatorContext) {
+func (fp *fdoProfile) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if fp.properties.Profile != nil {
 		path := android.PathForModuleSrc(ctx, *fp.properties.Profile)
-		ctx.SetProvider(FdoProfileProvider, FdoProfileInfo{
+		android.SetProvider(ctx, FdoProfileProvider, FdoProfileInfo{
 			Path: path,
 		})
 	}
 }
 
-// fdoProfileMutator calls the generic fdoProfileMutator function of fdoProfileMutator
-// which is implemented by cc and cc.FdoProfile
-func fdoProfileMutator(ctx android.BottomUpMutatorContext) {
-	if f, ok := ctx.Module().(FdoProfileMutatorInterface); ok {
-		f.fdoProfileMutator(ctx)
-	}
-}
-
 func FdoProfileFactory() android.Module {
 	m := &fdoProfile{}
 	m.AddProperties(&m.properties)
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 6b3a739..2436f33 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -144,25 +144,35 @@
 }
 
 func (fuzz *fuzzBinary) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+	subdir := "lib"
+	if ctx.inVendor() {
+		subdir = "lib/vendor"
+	}
+
 	flags = fuzz.binaryDecorator.linkerFlags(ctx, flags)
 	// RunPaths on devices isn't instantiated by the base linker. `../lib` for
 	// installed fuzz targets (both host and device), and `./lib` for fuzz
 	// target packages.
-	flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/lib`)
+	flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/`+subdir)
 
 	// When running on device, fuzz targets with vendor: true set will be in
 	// fuzzer_name/vendor/fuzzer_name (note the extra 'vendor' and thus need to
 	// link with libraries in ../../lib/. Non-vendor binaries only need to look
 	// one level up, in ../lib/.
 	if ctx.inVendor() {
-		flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../../lib`)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../../`+subdir)
 	} else {
-		flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../`+subdir)
 	}
 
 	return flags
 }
 
+func (fuzz *fuzzBinary) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
+	fuzz.binaryDecorator.moduleInfoJSON(ctx, moduleInfoJSON)
+	moduleInfoJSON.Class = []string{"EXECUTABLES"}
+}
+
 // 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:
@@ -216,19 +226,27 @@
 }
 
 func SharedLibraryInstallLocation(
-	libraryBase string, isHost bool, fuzzDir string, archString string) string {
+	libraryBase string, isHost bool, isVendor bool, fuzzDir string, archString string) string {
 	installLocation := "$(PRODUCT_OUT)/data"
 	if isHost {
 		installLocation = "$(HOST_OUT)"
 	}
+	subdir := "lib"
+	if isVendor {
+		subdir = "lib/vendor"
+	}
 	installLocation = filepath.Join(
-		installLocation, fuzzDir, archString, "lib", libraryBase)
+		installLocation, fuzzDir, archString, subdir, libraryBase)
 	return installLocation
 }
 
 // Get the device-only shared library symbols install directory.
-func SharedLibrarySymbolsInstallLocation(libraryBase string, fuzzDir string, archString string) string {
-	return filepath.Join("$(PRODUCT_OUT)/symbols/data/", fuzzDir, archString, "/lib/", libraryBase)
+func SharedLibrarySymbolsInstallLocation(libraryBase string, isVendor bool, fuzzDir string, archString string) string {
+	subdir := "lib"
+	if isVendor {
+		subdir = "lib/vendor"
+	}
+	return filepath.Join("$(PRODUCT_OUT)/symbols/data/", fuzzDir, archString, subdir, libraryBase)
 }
 
 func (fuzzBin *fuzzBinary) install(ctx ModuleContext, file android.Path) {
@@ -239,16 +257,29 @@
 	// Grab the list of required shared libraries.
 	fuzzBin.sharedLibraries, _ = CollectAllSharedDependencies(ctx)
 
+	// TODO: does not mirror Android linkernamespaces
+	// the logic here has special cases for vendor, but it would need more work to
+	// work in arbitrary partitions, so just surface errors early for a few cases
+	//
+	// Even without these, there are certain situations across linkernamespaces
+	// that this won't support. For instance, you might have:
+	//
+	//     my_fuzzer (vendor) -> libbinder_ndk (core) -> libbinder (vendor)
+	//
+	// This dependency chain wouldn't be possible to express in the current
+	// logic because all the deps currently match the variant of the source
+	// module.
+
 	for _, ruleBuilderInstall := range fuzzBin.sharedLibraries {
 		install := ruleBuilderInstall.To
 		fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
 			SharedLibraryInstallLocation(
-				install, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
+				install, ctx.Host(), ctx.inVendor(), installBase, ctx.Arch().ArchType.String()))
 
 		// Also add the dependency on the shared library symbols dir.
 		if !ctx.Host() {
 			fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
-				SharedLibrarySymbolsInstallLocation(install, installBase, ctx.Arch().ArchType.String()))
+				SharedLibrarySymbolsInstallLocation(install, ctx.inVendor(), installBase, ctx.Arch().ArchType.String()))
 		}
 	}
 
@@ -407,6 +438,10 @@
 		}
 
 		sharedLibsInstallDirPrefix := "lib"
+		if ccModule.InVendor() {
+			sharedLibsInstallDirPrefix = "lib/vendor"
+		}
+
 		if !ccModule.IsFuzzModule() {
 			return
 		}
@@ -499,7 +534,7 @@
 		// install it to the output directory. Setup the install destination here,
 		// which will be used by $(copy-many-files) in the Make backend.
 		installDestination := SharedLibraryInstallLocation(
-			install, module.Host(), fuzzDir, archString)
+			install, module.Host(), module.InVendor(), fuzzDir, archString)
 		if (*sharedLibraryInstalled)[installDestination] {
 			continue
 		}
@@ -517,7 +552,7 @@
 		// we want symbolization tools (like `stack`) to be able to find the symbols
 		// in $ANDROID_PRODUCT_OUT/symbols automagically.
 		if !module.Host() {
-			symbolsInstallDestination := SharedLibrarySymbolsInstallLocation(install, fuzzDir, archString)
+			symbolsInstallDestination := SharedLibrarySymbolsInstallLocation(install, module.InVendor(), fuzzDir, archString)
 			symbolsInstallDestination = strings.ReplaceAll(symbolsInstallDestination, "$", "$$")
 			s.SharedLibInstallStrings = append(s.SharedLibInstallStrings,
 				library.String()+":"+symbolsInstallDestination)
@@ -544,7 +579,8 @@
 		if !IsValidSharedDependency(dep) {
 			return
 		}
-		if !ctx.OtherModuleHasProvider(dep, SharedLibraryInfoProvider) {
+		sharedLibraryInfo, hasSharedLibraryInfo := android.OtherModuleProvider(ctx, dep, SharedLibraryInfoProvider)
+		if !hasSharedLibraryInfo {
 			return
 		}
 		if seen[ctx.OtherModuleName(dep)] {
@@ -553,7 +589,6 @@
 		seen[ctx.OtherModuleName(dep)] = true
 		deps = append(deps, dep)
 
-		sharedLibraryInfo := ctx.OtherModuleProvider(dep, SharedLibraryInfoProvider).(SharedLibraryInfo)
 		installDestination := sharedLibraryInfo.SharedLibrary.Base()
 		ruleBuilderInstall := android.RuleBuilderInstall{android.OutputFileForModule(ctx, dep, "unstripped"), installDestination}
 		sharedLibraries = append(sharedLibraries, ruleBuilderInstall)
@@ -574,14 +609,14 @@
 		if !IsValidSharedDependency(child) {
 			return false
 		}
-		if !ctx.OtherModuleHasProvider(child, SharedLibraryInfoProvider) {
+		sharedLibraryInfo, hasSharedLibraryInfo := android.OtherModuleProvider(ctx, child, SharedLibraryInfoProvider)
+		if !hasSharedLibraryInfo {
 			return false
 		}
 		if !seen[ctx.OtherModuleName(child)] {
 			seen[ctx.OtherModuleName(child)] = true
 			deps = append(deps, child)
 
-			sharedLibraryInfo := ctx.OtherModuleProvider(child, SharedLibraryInfoProvider).(SharedLibraryInfo)
 			installDestination := sharedLibraryInfo.SharedLibrary.Base()
 			ruleBuilderInstall := android.RuleBuilderInstall{android.OutputFileForModule(ctx, child, "unstripped"), installDestination}
 			sharedLibraries = append(sharedLibraries, ruleBuilderInstall)
diff --git a/cc/gen.go b/cc/gen.go
index 2e72e30..e351fdd 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -225,6 +225,10 @@
 	// The files that can be used as order only dependencies in order to ensure that the sysprop
 	// header files are up to date.
 	syspropOrderOnlyDeps android.Paths
+
+	// List of generated code path.
+	//   ex) '*.cpp' files generated from '*.ll / *.yy'.
+	generatedSources android.Paths
 }
 
 func genSources(
@@ -254,30 +258,37 @@
 		return yaccRule_
 	}
 
+	var generatedSources android.Paths = nil
+
 	for i, srcFile := range srcFiles {
 		switch srcFile.Ext() {
 		case ".y":
 			cFile := android.GenPathWithExt(ctx, "yacc", srcFile, "c")
 			srcFiles[i] = cFile
 			deps = append(deps, genYacc(ctx, yaccRule(), srcFile, cFile, buildFlags.yacc)...)
+			generatedSources = append(generatedSources, cFile)
 		case ".yy":
 			cppFile := android.GenPathWithExt(ctx, "yacc", srcFile, "cpp")
 			srcFiles[i] = cppFile
 			deps = append(deps, genYacc(ctx, yaccRule(), srcFile, cppFile, buildFlags.yacc)...)
+			generatedSources = append(generatedSources, cppFile)
 		case ".l":
 			cFile := android.GenPathWithExt(ctx, "lex", srcFile, "c")
 			srcFiles[i] = cFile
 			genLex(ctx, srcFile, cFile, buildFlags.lex)
+			generatedSources = append(generatedSources, cFile)
 		case ".ll":
 			cppFile := android.GenPathWithExt(ctx, "lex", srcFile, "cpp")
 			srcFiles[i] = cppFile
 			genLex(ctx, srcFile, cppFile, buildFlags.lex)
+			generatedSources = append(generatedSources, cppFile)
 		case ".proto":
 			ccFile, headerFile := genProto(ctx, srcFile, buildFlags)
 			srcFiles[i] = ccFile
 			info.protoHeaders = append(info.protoHeaders, headerFile)
 			// Use the generated header as an order only dep to ensure that it is up to date when needed.
 			info.protoOrderOnlyDeps = append(info.protoOrderOnlyDeps, headerFile)
+			generatedSources = append(generatedSources, ccFile)
 		case ".aidl":
 			if aidlRule == nil {
 				aidlRule = android.NewRuleBuilder(pctx, ctx).Sbox(android.PathForModuleGen(ctx, "aidl"),
@@ -299,10 +310,12 @@
 			// needed.
 			// TODO: Reduce the size of the ninja file by using one order only dep for the whole rule
 			info.aidlOrderOnlyDeps = append(info.aidlOrderOnlyDeps, aidlHeaders...)
+			generatedSources = append(generatedSources, cppFile)
 		case ".rscript", ".fs":
 			cppFile := rsGeneratedCppFile(ctx, srcFile)
 			rsFiles = append(rsFiles, srcFiles[i])
 			srcFiles[i] = cppFile
+			generatedSources = append(generatedSources, cppFile)
 		case ".sysprop":
 			cppFile, headerFiles := genSysprop(ctx, srcFile)
 			srcFiles[i] = cppFile
@@ -310,9 +323,12 @@
 			// Use the generated headers as order only deps to ensure that they are up to date when
 			// needed.
 			info.syspropOrderOnlyDeps = append(info.syspropOrderOnlyDeps, headerFiles...)
+			generatedSources = append(generatedSources, cppFile)
 		}
 	}
 
+	info.generatedSources = generatedSources
+
 	for _, aidlLibraryInfo := range aidlLibraryInfos {
 		if aidlLibraryRule == nil {
 			aidlLibraryRule = android.NewRuleBuilder(pctx, ctx).Sbox(
diff --git a/cc/genrule.go b/cc/genrule.go
index 0c06ae6..0fb3e2d 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -79,15 +79,7 @@
 func (g *GenruleExtraProperties) ImageMutatorBegin(ctx android.BaseModuleContext) {}
 
 func (g *GenruleExtraProperties) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
-	if ctx.DeviceConfig().VndkVersion() == "" {
-		return true
-	}
-
-	if ctx.ProductSpecific() {
-		return false
-	}
-
-	return !(ctx.SocSpecific() || ctx.DeviceSpecific())
+	return !(ctx.SocSpecific() || ctx.DeviceSpecific() || ctx.ProductSpecific())
 }
 
 func (g *GenruleExtraProperties) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
@@ -115,26 +107,33 @@
 }
 
 func (g *GenruleExtraProperties) ExtraImageVariations(ctx android.BaseModuleContext) []string {
-	if ctx.DeviceConfig().VndkVersion() == "" {
-		return nil
-	}
-
 	var variants []string
-	if Bool(g.Vendor_available) || Bool(g.Odm_available) || ctx.SocSpecific() || ctx.DeviceSpecific() {
-		vndkVersion := ctx.DeviceConfig().VndkVersion()
-		// If vndkVersion is current, we can always use PlatformVndkVersion.
-		// If not, we assume modules under proprietary paths are compatible for
-		// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, that is
-		// PLATFORM_VNDK_VERSION.
-		if vndkVersion == "current" || !snapshot.IsVendorProprietaryModule(ctx) {
-			variants = append(variants, VendorVariationPrefix+ctx.DeviceConfig().PlatformVndkVersion())
-		} else {
-			variants = append(variants, VendorVariationPrefix+vndkVersion)
-		}
-	}
+	vndkVersion := ctx.DeviceConfig().VndkVersion()
+	vendorVariantRequired := Bool(g.Vendor_available) || Bool(g.Odm_available) || ctx.SocSpecific() || ctx.DeviceSpecific()
+	productVariantRequired := Bool(g.Product_available) || ctx.ProductSpecific()
 
-	if Bool(g.Product_available) || ctx.ProductSpecific() {
-		variants = append(variants, ProductVariationPrefix+ctx.DeviceConfig().PlatformVndkVersion())
+	if vndkVersion == "" {
+		if vendorVariantRequired {
+			variants = append(variants, VendorVariation)
+		}
+		if productVariantRequired {
+			variants = append(variants, ProductVariation)
+		}
+	} else {
+		if vendorVariantRequired {
+			// If vndkVersion is current, we can always use PlatformVndkVersion.
+			// If not, we assume modules under proprietary paths are compatible for
+			// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, that is
+			// PLATFORM_VNDK_VERSION.
+			if vndkVersion == "current" || !snapshot.IsVendorProprietaryModule(ctx) {
+				variants = append(variants, VendorVariationPrefix+ctx.DeviceConfig().PlatformVndkVersion())
+			} else {
+				variants = append(variants, VendorVariationPrefix+vndkVersion)
+			}
+		}
+		if productVariantRequired {
+			variants = append(variants, ProductVariationPrefix+ctx.DeviceConfig().PlatformVndkVersion())
+		}
 	}
 
 	return variants
diff --git a/cc/genrule_test.go b/cc/genrule_test.go
index 9295244..05c644f 100644
--- a/cc/genrule_test.go
+++ b/cc/genrule_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"reflect"
+	"slices"
 	"testing"
 
 	"android/soong/android"
@@ -186,3 +187,26 @@
 		})
 	}
 }
+
+func TestVendorProductVariantGenrule(t *testing.T) {
+	bp := `
+	cc_genrule {
+		name: "gen",
+		tool_files: ["tool"],
+		cmd: "$(location tool) $(in) $(out)",
+		out: ["out"],
+		vendor_available: true,
+		product_available: true,
+	}
+	`
+	t.Helper()
+	ctx := PrepareForTestWithCcIncludeVndk.RunTestWithBp(t, bp)
+
+	variants := ctx.ModuleVariantsForTests("gen")
+	if !slices.Contains(variants, "android_vendor_arm64_armv8-a") {
+		t.Errorf(`expected vendor variant, but does not exist in %v`, variants)
+	}
+	if !slices.Contains(variants, "android_product_arm64_armv8-a") {
+		t.Errorf(`expected product variant, but does not exist in %v`, variants)
+	}
+}
diff --git a/cc/image.go b/cc/image.go
index 4f36111..d02a2f3 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -42,27 +42,23 @@
 )
 
 const (
+	// VendorVariation is the variant name used for /vendor code that does not
+	// compile against the VNDK.
+	VendorVariation = "vendor"
+
 	// VendorVariationPrefix is the variant prefix used for /vendor code that compiles
 	// against the VNDK.
 	VendorVariationPrefix = "vendor."
 
+	// ProductVariation is the variant name used for /product code that does not
+	// compile against the VNDK.
+	ProductVariation = "product"
+
 	// ProductVariationPrefix is the variant prefix used for /product code that compiles
 	// against the VNDK.
 	ProductVariationPrefix = "product."
 )
 
-func (ctx *moduleContext) ProductSpecific() bool {
-	return ctx.ModuleContext.ProductSpecific() || ctx.mod.productSpecificModuleContext()
-}
-
-func (ctx *moduleContext) SocSpecific() bool {
-	return ctx.ModuleContext.SocSpecific() || ctx.mod.socSpecificModuleContext()
-}
-
-func (ctx *moduleContext) DeviceSpecific() bool {
-	return ctx.ModuleContext.DeviceSpecific() || ctx.mod.deviceSpecificModuleContext()
-}
-
 func (ctx *moduleContextImpl) inProduct() bool {
 	return ctx.mod.InProduct()
 }
@@ -83,20 +79,20 @@
 	return ctx.mod.InRecovery()
 }
 
-func (c *Module) productSpecificModuleContext() bool {
+func (c *Module) InstallInProduct() bool {
 	// Additionally check if this module is inProduct() that means it is a "product" variant of a
 	// module. As well as product specific modules, product variants must be installed to /product.
 	return c.InProduct()
 }
 
-func (c *Module) socSpecificModuleContext() bool {
+func (c *Module) InstallInVendor() bool {
 	// Additionally check if this module is inVendor() that means it is a "vendor" variant of a
 	// module. As well as SoC specific modules, vendor variants must be installed to /vendor
 	// unless they have "odm_available: true".
 	return c.HasVendorVariant() && c.InVendor() && !c.VendorVariantToOdm()
 }
 
-func (c *Module) deviceSpecificModuleContext() bool {
+func (c *Module) InstallInOdm() bool {
 	// Some vendor variants want to be installed to /odm by setting "odm_available: true".
 	return c.InVendor() && c.VendorVariantToOdm()
 }
@@ -124,12 +120,18 @@
 
 // Returns true if the module is "product" variant. Usually these modules are installed in /product
 func (c *Module) InProduct() bool {
-	return c.Properties.ImageVariationPrefix == ProductVariationPrefix
+	return c.Properties.ImageVariation == ProductVariation
 }
 
 // Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
 func (c *Module) InVendor() bool {
-	return c.Properties.ImageVariationPrefix == VendorVariationPrefix
+	return c.Properties.ImageVariation == VendorVariation
+}
+
+// Returns true if the module is "vendor" or "product" variant. This replaces previous UseVndk usages
+// which were misused to check if the module variant is vendor or product.
+func (c *Module) InVendorOrProduct() bool {
+	return c.InVendor() || c.InProduct()
 }
 
 func (c *Module) InRamdisk() bool {
@@ -451,10 +453,8 @@
 		// and vendor and product variants will be created with LLNDK stubs.
 		// The LLNDK libraries need vendor variants even if there is no VNDK.
 		coreVariantNeeded = true
-		if platformVndkVersion != "" {
-			vendorVariants = append(vendorVariants, platformVndkVersion)
-			productVariants = append(productVariants, platformVndkVersion)
-		}
+		vendorVariants = append(vendorVariants, platformVndkVersion)
+		productVariants = append(productVariants, platformVndkVersion)
 		// Generate vendor variants for boardVndkVersion only if the VNDK snapshot does not
 		// provide the LLNDK stub libraries.
 		if needVndkVersionVendorVariantForLlndk {
@@ -465,13 +465,7 @@
 		// for system and product.
 		coreVariantNeeded = true
 		vendorVariants = append(vendorVariants, boardVndkVersion)
-		if platformVndkVersion != "" {
-			productVariants = append(productVariants, platformVndkVersion)
-		}
-	} else if boardVndkVersion == "" {
-		// If the device isn't compiling against the VNDK, we always
-		// use the core mode.
-		coreVariantNeeded = true
+		productVariants = append(productVariants, platformVndkVersion)
 	} else if m.IsSnapshotPrebuilt() {
 		// Make vendor variants only for the versions in BOARD_VNDK_VERSION and
 		// PRODUCT_EXTRA_VNDK_VERSIONS.
@@ -569,11 +563,19 @@
 	}
 
 	for _, variant := range android.FirstUniqueStrings(vendorVariants) {
-		m.AppendExtraVariant(VendorVariationPrefix + variant)
+		if variant == "" {
+			m.AppendExtraVariant(VendorVariation)
+		} else {
+			m.AppendExtraVariant(VendorVariationPrefix + variant)
+		}
 	}
 
 	for _, variant := range android.FirstUniqueStrings(productVariants) {
-		m.AppendExtraVariant(ProductVariationPrefix + variant)
+		if variant == "" {
+			m.AppendExtraVariant(ProductVariation)
+		} else {
+			m.AppendExtraVariant(ProductVariationPrefix + variant)
+		}
 	}
 
 	m.SetRamdiskVariantNeeded(ramdiskVariantNeeded)
@@ -684,9 +686,12 @@
 	} else if variant == android.RecoveryVariation {
 		m.MakeAsPlatform()
 		squashRecoverySrcs(m)
-	} else if strings.HasPrefix(variant, VendorVariationPrefix) {
-		m.Properties.ImageVariationPrefix = VendorVariationPrefix
-		m.Properties.VndkVersion = strings.TrimPrefix(variant, VendorVariationPrefix)
+	} else if strings.HasPrefix(variant, VendorVariation) {
+		m.Properties.ImageVariation = VendorVariation
+
+		if strings.HasPrefix(variant, VendorVariationPrefix) {
+			m.Properties.VndkVersion = strings.TrimPrefix(variant, VendorVariationPrefix)
+		}
 		squashVendorSrcs(m)
 
 		// Makefile shouldn't know vendor modules other than BOARD_VNDK_VERSION.
@@ -696,9 +701,11 @@
 			m.Properties.HideFromMake = true
 			m.HideFromMake()
 		}
-	} else if strings.HasPrefix(variant, ProductVariationPrefix) {
-		m.Properties.ImageVariationPrefix = ProductVariationPrefix
-		m.Properties.VndkVersion = strings.TrimPrefix(variant, ProductVariationPrefix)
+	} else if strings.HasPrefix(variant, ProductVariation) {
+		m.Properties.ImageVariation = ProductVariation
+		if strings.HasPrefix(variant, ProductVariationPrefix) {
+			m.Properties.VndkVersion = strings.TrimPrefix(variant, ProductVariationPrefix)
+		}
 		squashProductSrcs(m)
 	}
 
diff --git a/cc/installer.go b/cc/installer.go
index a0b6295..30f9612 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -87,7 +87,7 @@
 	} else if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
 		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
 	}
-	if installer.location == InstallInData && ctx.useVndk() {
+	if installer.location == InstallInData && ctx.InVendorOrProduct() {
 		if ctx.inProduct() {
 			dir = filepath.Join(dir, "product")
 		} else {
diff --git a/cc/kernel_headers.go b/cc/kernel_headers.go
index 9ea988a..4f685be 100644
--- a/cc/kernel_headers.go
+++ b/cc/kernel_headers.go
@@ -34,7 +34,7 @@
 // kernel_headers retrieves the list of kernel headers directories from
 // TARGET_BOARD_KERNEL_HEADERS and TARGET_PRODUCT_KERNEL_HEADERS variables in
 // a makefile for compilation. See
-// https://android.googlesource.com/platform/build/+/master/core/config.mk
+// https://android.googlesource.com/platform/build/+/main/core/config.mk
 // for more details on them.
 func kernelHeadersFactory() android.Module {
 	module, library := NewLibrary(android.HostAndDeviceSupported)
diff --git a/cc/library.go b/cc/library.go
index 7d8594d..222b380 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"io"
+	"log"
 	"path/filepath"
 	"regexp"
 	"strconv"
@@ -24,6 +25,7 @@
 	"sync"
 
 	"android/soong/android"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
@@ -348,7 +350,7 @@
 }
 
 func (f *flagExporter) setProvider(ctx android.ModuleContext) {
-	ctx.SetProvider(FlagExporterInfoProvider, FlagExporterInfo{
+	android.SetProvider(ctx, FlagExporterInfoProvider, FlagExporterInfo{
 		// Comes from Export_include_dirs property, and those of exported transitive deps
 		IncludeDirs: android.FirstUniquePaths(f.dirs),
 		// Comes from Export_system_include_dirs property, and those of exported transitive deps
@@ -877,12 +879,18 @@
 func (library *libraryDecorator) getLibName(ctx BaseModuleContext) string {
 	name := library.getLibNameHelper(ctx.baseModuleName(), ctx.inVendor(), ctx.inProduct())
 
+	// Replace name with VNDK ext as original lib only when VNDK is enabled
 	if ctx.IsVndkExt() {
-		// vndk-ext lib should have the same name with original lib
-		ctx.VisitDirectDepsWithTag(vndkExtDepTag, func(module android.Module) {
-			originalName := module.(*Module).outputFile.Path()
-			name = strings.TrimSuffix(originalName.Base(), originalName.Ext())
-		})
+		if ctx.DeviceConfig().VndkVersion() != "" {
+			// vndk-ext lib should have the same name with original lib
+			ctx.VisitDirectDepsWithTag(vndkExtDepTag, func(module android.Module) {
+				originalName := module.(*Module).outputFile.Path()
+				name = strings.TrimSuffix(originalName.Base(), originalName.Ext())
+			})
+		} else {
+			// TODO(b/320208784) : Suggest a solution for former VNDK-ext libraries before VNDK deprecation.
+			log.Printf("VNDK Extension on module %s will not be available once VNDK is deprecated", ctx.baseModuleName())
+		}
 	}
 
 	if ctx.Host() && Bool(library.Properties.Unique_host_soname) {
@@ -973,6 +981,10 @@
 		if library.baseLinker.Properties.crt() {
 			deps.CrtBegin = append(deps.CrtBegin, ctx.toolchain().CrtBeginSharedLibrary()...)
 			deps.CrtEnd = append(deps.CrtEnd, ctx.toolchain().CrtEndSharedLibrary()...)
+
+		}
+		if library.baseLinker.Properties.crtPadSegment() {
+			deps.CrtEnd = append(deps.CrtEnd, ctx.toolchain().CrtPadSegmentSharedLibrary()...)
 		}
 		deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.SharedProperties.Shared.Whole_static_libs...)
 		deps.StaticLibs = append(deps.StaticLibs, library.SharedProperties.Shared.Static_libs...)
@@ -1048,6 +1060,40 @@
 	return specifiedDeps
 }
 
+func (library *libraryDecorator) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
+	if library.static() {
+		moduleInfoJSON.Class = []string{"STATIC_LIBRARIES"}
+		moduleInfoJSON.Uninstallable = true
+	} else if library.shared() {
+		moduleInfoJSON.Class = []string{"SHARED_LIBRARIES"}
+	} else if library.header() {
+		moduleInfoJSON.Class = []string{"HEADER_LIBRARIES"}
+		moduleInfoJSON.Uninstallable = true
+	}
+
+	if library.buildStubs() && library.stubsVersion() != "" {
+		moduleInfoJSON.SubName += "." + library.stubsVersion()
+	}
+
+	// If a library providing a stub is included in an APEX, the private APIs of the library
+	// is accessible only inside the APEX. From outside of the APEX, clients can only use the
+	// public APIs via the stub. To enforce this, the (latest version of the) stub gets the
+	// name of the library. The impl library instead gets the `.bootstrap` suffix to so that
+	// they can be exceptionally used directly when APEXes are not available (e.g. during the
+	// very early stage in the boot process).
+	if len(library.Properties.Stubs.Versions) > 0 && !ctx.Host() && ctx.notInPlatform() &&
+		!ctx.inRamdisk() && !ctx.inVendorRamdisk() && !ctx.inRecovery() && !ctx.useVndk() && !ctx.static() {
+		if library.buildStubs() && library.isLatestStubVersion() {
+			moduleInfoJSON.SubName = ""
+		}
+		if !library.buildStubs() {
+			moduleInfoJSON.SubName = ".bootstrap"
+		}
+	}
+
+	library.baseLinker.moduleInfoJSON(ctx, moduleInfoJSON)
+}
+
 func (library *libraryDecorator) linkStatic(ctx ModuleContext,
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 
@@ -1078,7 +1124,7 @@
 	ctx.CheckbuildFile(outputFile)
 
 	if library.static() {
-		ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
+		android.SetProvider(ctx, StaticLibraryInfoProvider, StaticLibraryInfo{
 			StaticLibrary:                outputFile,
 			ReuseObjects:                 library.reuseObjects,
 			Objects:                      library.objects,
@@ -1092,7 +1138,7 @@
 	}
 
 	if library.header() {
-		ctx.SetProvider(HeaderLibraryInfoProvider, HeaderLibraryInfo{})
+		android.SetProvider(ctx, HeaderLibraryInfoProvider, HeaderLibraryInfo{})
 	}
 
 	return outputFile
@@ -1242,11 +1288,11 @@
 
 	var transitiveStaticLibrariesForOrdering *android.DepSet[android.Path]
 	if static := ctx.GetDirectDepsWithTag(staticVariantTag); len(static) > 0 {
-		s := ctx.OtherModuleProvider(static[0], StaticLibraryInfoProvider).(StaticLibraryInfo)
+		s, _ := android.OtherModuleProvider(ctx, static[0], StaticLibraryInfoProvider)
 		transitiveStaticLibrariesForOrdering = s.TransitiveStaticLibrariesForOrdering
 	}
 
-	ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
+	android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
 		TableOfContents:                      android.OptionalPathForPath(tocFile),
 		SharedLibrary:                        unstrippedOutputFile,
 		TransitiveStaticLibrariesForOrdering: transitiveStaticLibrariesForOrdering,
@@ -1263,15 +1309,15 @@
 	if len(stubs) > 0 {
 		var stubsInfo []SharedStubLibrary
 		for _, stub := range stubs {
-			stubInfo := ctx.OtherModuleProvider(stub, SharedLibraryInfoProvider).(SharedLibraryInfo)
-			flagInfo := ctx.OtherModuleProvider(stub, FlagExporterInfoProvider).(FlagExporterInfo)
+			stubInfo, _ := android.OtherModuleProvider(ctx, stub, SharedLibraryInfoProvider)
+			flagInfo, _ := android.OtherModuleProvider(ctx, stub, FlagExporterInfoProvider)
 			stubsInfo = append(stubsInfo, SharedStubLibrary{
 				Version:           moduleLibraryInterface(stub).stubsVersion(),
 				SharedLibraryInfo: stubInfo,
 				FlagExporterInfo:  flagInfo,
 			})
 		}
-		ctx.SetProvider(SharedLibraryStubsProvider, SharedLibraryStubsInfo{
+		android.SetProvider(ctx, SharedLibraryStubsProvider, SharedLibraryStubsInfo{
 			SharedStubLibraries: stubsInfo,
 			IsLLNDK:             ctx.IsLlndk(),
 		})
@@ -1315,42 +1361,29 @@
 		fileName+".lsdump")
 }
 
-func getRefAbiDumpDir(isNdk, isVndk bool) string {
-	var dirName string
-	if isNdk {
-		dirName = "ndk"
-	} else if isVndk {
-		dirName = "vndk"
-	} else {
-		dirName = "platform"
-	}
-	return filepath.Join("prebuilts", "abi-dumps", dirName)
-}
-
-func prevRefAbiDumpVersion(ctx ModuleContext, dumpDir string) int {
+// Return the previous and current SDK versions for cross-version ABI diff.
+func crossVersionAbiDiffSdkVersions(ctx ModuleContext, dumpDir string) (string, string) {
 	sdkVersionInt := ctx.Config().PlatformSdkVersion().FinalInt()
 	sdkVersionStr := ctx.Config().PlatformSdkVersion().String()
 
 	if ctx.Config().PlatformSdkFinal() {
-		return sdkVersionInt - 1
+		return strconv.Itoa(sdkVersionInt - 1), sdkVersionStr
 	} else {
 		// The platform SDK version can be upgraded before finalization while the corresponding abi dumps hasn't
 		// been generated. Thus the Cross-Version Check chooses PLATFORM_SDK_VERION - 1 as previous version.
 		// This situation could be identified by checking the existence of the PLATFORM_SDK_VERION dump directory.
 		versionedDumpDir := android.ExistentPathForSource(ctx, dumpDir, sdkVersionStr)
 		if versionedDumpDir.Valid() {
-			return sdkVersionInt
+			return sdkVersionStr, strconv.Itoa(sdkVersionInt + 1)
 		} else {
-			return sdkVersionInt - 1
+			return strconv.Itoa(sdkVersionInt - 1), sdkVersionStr
 		}
 	}
 }
 
-func currRefAbiDumpVersion(ctx ModuleContext, isVndk bool) string {
-	if isVndk {
-		// Each version of VNDK is independent, so follow the VNDK version which is the codename or PLATFORM_SDK_VERSION.
-		return ctx.Module().(*Module).VndkVersion()
-	} else if ctx.Config().PlatformSdkFinal() {
+// Return the SDK version for same-version ABI diff.
+func currRefAbiDumpSdkVersion(ctx ModuleContext) string {
+	if ctx.Config().PlatformSdkFinal() {
 		// After sdk finalization, the ABI of the latest API level must be consistent with the source code,
 		// so choose PLATFORM_SDK_VERSION as the current version.
 		return ctx.Config().PlatformSdkVersion().String()
@@ -1392,24 +1425,24 @@
 func (library *libraryDecorator) crossVersionAbiDiff(ctx android.ModuleContext, referenceDump android.Path,
 	baseName string, isLlndkOrNdk bool, sourceVersion, prevVersion string) {
 
-	errorMessage := "error: Please follow https://android.googlesource.com/platform/development/+/master/vndk/tools/header-checker/README.md#configure-cross_version-abi-check to resolve the ABI difference between your source code and version " + prevVersion + "."
+	errorMessage := "error: Please follow https://android.googlesource.com/platform/development/+/main/vndk/tools/header-checker/README.md#configure-cross_version-abi-check to resolve the ABI difference between your source code and version " + prevVersion + "."
 
 	library.sourceAbiDiff(ctx, referenceDump, baseName, prevVersion,
 		isLlndkOrNdk, true /* allowExtensions */, sourceVersion, errorMessage)
 }
 
 func (library *libraryDecorator) sameVersionAbiDiff(ctx android.ModuleContext, referenceDump android.Path,
-	baseName string, isLlndkOrNdk, allowExtensions bool) {
+	baseName, nameExt string, isLlndkOrNdk bool) {
 
 	libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
 	errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l " + libName
 
-	library.sourceAbiDiff(ctx, referenceDump, baseName, "",
-		isLlndkOrNdk, allowExtensions, "current", errorMessage)
+	library.sourceAbiDiff(ctx, referenceDump, baseName, nameExt,
+		isLlndkOrNdk, false /* allowExtensions */, "current", errorMessage)
 }
 
 func (library *libraryDecorator) optInAbiDiff(ctx android.ModuleContext, referenceDump android.Path,
-	baseName, nameExt string, isLlndkOrNdk bool, refDumpDir string) {
+	baseName, nameExt string, refDumpDir string) {
 
 	libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
 	errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l " + libName + " -ref-dump-dir $$ANDROID_BUILD_TOP/" + refDumpDir
@@ -1419,7 +1452,7 @@
 	}
 
 	library.sourceAbiDiff(ctx, referenceDump, baseName, nameExt,
-		isLlndkOrNdk, false /* allowExtensions */, "current", errorMessage)
+		false /* isLlndkOrNdk */, false /* allowExtensions */, "current", errorMessage)
 }
 
 func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objects, fileName string, soFile android.Path) {
@@ -1434,39 +1467,56 @@
 		}
 		exportedHeaderFlags := strings.Join(SourceAbiFlags, " ")
 		headerAbiChecker := library.getHeaderAbiCheckerProperties(ctx)
-		// The logic must be consistent with classifySourceAbiDump.
-		isVndk := ctx.useVndk() && ctx.isVndk()
-		isNdk := ctx.isNdk(ctx.Config())
-		isLlndk := ctx.isImplementationForLLNDKPublic()
-		currVersion := currRefAbiDumpVersion(ctx, isVndk)
+		currSdkVersion := currRefAbiDumpSdkVersion(ctx)
+		currVendorVersion := ctx.Config().VendorApiLevel()
 		library.sAbiOutputFile = transformDumpToLinkedDump(ctx, objs.sAbiDumpFiles, soFile, fileName, exportedHeaderFlags,
 			android.OptionalPathForModuleSrc(ctx, library.symbolFileForAbiCheck(ctx)),
 			headerAbiChecker.Exclude_symbol_versions,
 			headerAbiChecker.Exclude_symbol_tags,
-			currVersion)
+			currSdkVersion)
 
-		addLsdumpPath(classifySourceAbiDump(ctx) + ":" + library.sAbiOutputFile.String())
+		for _, tag := range classifySourceAbiDump(ctx) {
+			addLsdumpPath(string(tag) + ":" + library.sAbiOutputFile.String())
 
-		dumpDir := getRefAbiDumpDir(isNdk, isVndk)
-		binderBitness := ctx.DeviceConfig().BinderBitness()
-		// If NDK or PLATFORM library, check against previous version ABI.
-		if !isVndk {
-			prevVersionInt := prevRefAbiDumpVersion(ctx, dumpDir)
-			prevVersion := strconv.Itoa(prevVersionInt)
+			dumpDirName := tag.dirName()
+			if dumpDirName == "" {
+				continue
+			}
+			dumpDir := filepath.Join("prebuilts", "abi-dumps", dumpDirName)
+			isLlndk := (tag == llndkLsdumpTag)
+			isNdk := (tag == ndkLsdumpTag)
+			binderBitness := ctx.DeviceConfig().BinderBitness()
+			nameExt := ""
+			if isLlndk {
+				nameExt = "llndk"
+			}
+			// Check against the previous version.
+			var prevVersion, currVersion string
+			// If this release config does not define VendorApiLevel, fall back to the old policy.
+			if isLlndk && currVendorVersion != "" {
+				prevVersion = ctx.Config().PrevVendorApiLevel()
+				currVersion = currVendorVersion
+			} else {
+				prevVersion, currVersion = crossVersionAbiDiffSdkVersions(ctx, dumpDir)
+			}
 			prevDumpDir := filepath.Join(dumpDir, prevVersion, binderBitness)
 			prevDumpFile := getRefAbiDumpFile(ctx, prevDumpDir, fileName)
 			if prevDumpFile.Valid() {
 				library.crossVersionAbiDiff(ctx, prevDumpFile.Path(),
-					fileName, isLlndk || isNdk,
-					strconv.Itoa(prevVersionInt+1), prevVersion)
+					fileName, isLlndk || isNdk, currVersion, nameExt+prevVersion)
 			}
-		}
-		// Check against the current version.
-		currDumpDir := filepath.Join(dumpDir, currVersion, binderBitness)
-		currDumpFile := getRefAbiDumpFile(ctx, currDumpDir, fileName)
-		if currDumpFile.Valid() {
-			library.sameVersionAbiDiff(ctx, currDumpFile.Path(),
-				fileName, isLlndk || isNdk, ctx.IsVndkExt())
+			// Check against the current version.
+			if isLlndk && currVendorVersion != "" {
+				currVersion = currVendorVersion
+			} else {
+				currVersion = currSdkVersion
+			}
+			currDumpDir := filepath.Join(dumpDir, currVersion, binderBitness)
+			currDumpFile := getRefAbiDumpFile(ctx, currDumpDir, fileName)
+			if currDumpFile.Valid() {
+				library.sameVersionAbiDiff(ctx, currDumpFile.Path(),
+					fileName, nameExt, isLlndk || isNdk)
+			}
 		}
 		// Check against the opt-in reference dumps.
 		for i, optInDumpDir := range headerAbiChecker.Ref_dump_dirs {
@@ -1478,8 +1528,7 @@
 				continue
 			}
 			library.optInAbiDiff(ctx, optInDumpFile.Path(),
-				fileName, "opt"+strconv.Itoa(i), isLlndk || isNdk,
-				optInDumpDirPath.String())
+				fileName, "opt"+strconv.Itoa(i), optInDumpDirPath.String())
 		}
 	}
 }
@@ -1751,7 +1800,7 @@
 	}
 
 	if Bool(library.Properties.Static_ndk_lib) && library.static() &&
-		!ctx.useVndk() && !ctx.inRamdisk() && !ctx.inVendorRamdisk() && !ctx.inRecovery() && ctx.Device() &&
+		!ctx.InVendorOrProduct() && !ctx.inRamdisk() && !ctx.inVendorRamdisk() && !ctx.inRecovery() && ctx.Device() &&
 		library.baseLinker.sanitize.isUnsanitizedVariant() &&
 		ctx.isForPlatform() && !ctx.isPreventInstall() {
 		installPath := getUnversionedLibraryInstallPath(ctx).Join(ctx, file.Base())
@@ -1871,7 +1920,7 @@
 		return nil
 	}
 
-	if library.hasLLNDKStubs() && ctx.Module().(*Module).UseVndk() {
+	if library.hasLLNDKStubs() && ctx.Module().(*Module).InVendorOrProduct() {
 		// LLNDK libraries only need a single stubs variant.
 		return []string{android.FutureApiLevel.String()}
 	}
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index 52e78d7..a65b1ba 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -511,7 +511,7 @@
 		}
 	}
 
-	exportedInfo := ctx.SdkModuleContext().OtherModuleProvider(variant, FlagExporterInfoProvider).(FlagExporterInfo)
+	exportedInfo, _ := android.OtherModuleProvider(ctx.SdkModuleContext(), variant, FlagExporterInfoProvider)
 
 	// Separate out the generated include dirs (which are arch specific) from the
 	// include dirs (which may not be).
diff --git a/cc/library_stub.go b/cc/library_stub.go
index 18d3f21..aab6664 100644
--- a/cc/library_stub.go
+++ b/cc/library_stub.go
@@ -48,7 +48,7 @@
 		return
 	}
 
-	if m.UseVndk() && apiLibrary.hasLLNDKStubs() {
+	if m.InVendorOrProduct() && apiLibrary.hasLLNDKStubs() {
 		// Add LLNDK variant dependency
 		if inList("llndk", apiLibrary.properties.Variants) {
 			variantName := BuildApiVariantName(m.BaseModuleName(), "llndk", "")
@@ -193,7 +193,7 @@
 		}
 	}
 
-	if m.UseVndk() && d.hasLLNDKStubs() {
+	if m.InVendorOrProduct() && d.hasLLNDKStubs() {
 		// LLNDK variant
 		load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "llndk", ""))
 	} else if m.IsSdkVariant() {
@@ -244,7 +244,7 @@
 		},
 	})
 
-	ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
+	android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
 		SharedLibrary: outputFile,
 		Target:        ctx.Target(),
 
@@ -262,15 +262,15 @@
 	if len(stubs) > 0 {
 		var stubsInfo []SharedStubLibrary
 		for _, stub := range stubs {
-			stubInfo := ctx.OtherModuleProvider(stub, SharedLibraryInfoProvider).(SharedLibraryInfo)
-			flagInfo := ctx.OtherModuleProvider(stub, FlagExporterInfoProvider).(FlagExporterInfo)
+			stubInfo, _ := android.OtherModuleProvider(ctx, stub, SharedLibraryInfoProvider)
+			flagInfo, _ := android.OtherModuleProvider(ctx, stub, FlagExporterInfoProvider)
 			stubsInfo = append(stubsInfo, SharedStubLibrary{
 				Version:           moduleLibraryInterface(stub).stubsVersion(),
 				SharedLibraryInfo: stubInfo,
 				FlagExporterInfo:  flagInfo,
 			})
 		}
-		ctx.SetProvider(SharedLibraryStubsProvider, SharedLibraryStubsInfo{
+		android.SetProvider(ctx, SharedLibraryStubsProvider, SharedLibraryStubsInfo{
 			SharedStubLibraries: stubsInfo,
 
 			IsLLNDK: ctx.IsLlndk(),
@@ -312,7 +312,7 @@
 		}
 	}
 
-	if d.hasLLNDKStubs() && m.UseVndk() {
+	if d.hasLLNDKStubs() && m.InVendorOrProduct() {
 		// LLNDK libraries only need a single stubs variant.
 		return []string{android.FutureApiLevel.String()}
 	}
diff --git a/cc/linkable.go b/cc/linkable.go
index 994517c..6f22091 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -218,6 +218,7 @@
 	ProductSpecific() bool
 	InProduct() bool
 	SdkAndPlatformVariantVisibleToMake() bool
+	InVendorOrProduct() bool
 
 	// SubName returns the modules SubName, used for image and NDK/SDK variations.
 	SubName() string
@@ -368,7 +369,7 @@
 	TransitiveStaticLibrariesForOrdering *android.DepSet[android.Path]
 }
 
-var SharedLibraryInfoProvider = blueprint.NewProvider(SharedLibraryInfo{})
+var SharedLibraryInfoProvider = blueprint.NewProvider[SharedLibraryInfo]()
 
 // SharedStubLibrary is a struct containing information about a stub shared library.
 // Stub libraries are used for cross-APEX dependencies; when a library is to depend on a shared
@@ -391,7 +392,7 @@
 	IsLLNDK bool
 }
 
-var SharedLibraryStubsProvider = blueprint.NewProvider(SharedLibraryStubsInfo{})
+var SharedLibraryStubsProvider = blueprint.NewProvider[SharedLibraryStubsInfo]()
 
 // StaticLibraryInfo is a provider to propagate information about a static C++ library.
 type StaticLibraryInfo struct {
@@ -410,14 +411,14 @@
 	TransitiveStaticLibrariesForOrdering *android.DepSet[android.Path]
 }
 
-var StaticLibraryInfoProvider = blueprint.NewProvider(StaticLibraryInfo{})
+var StaticLibraryInfoProvider = blueprint.NewProvider[StaticLibraryInfo]()
 
 // HeaderLibraryInfo is a marker provider that identifies a module as a header library.
 type HeaderLibraryInfo struct {
 }
 
 // HeaderLibraryInfoProvider is a marker provider that identifies a module as a header library.
-var HeaderLibraryInfoProvider = blueprint.NewProvider(HeaderLibraryInfo{})
+var HeaderLibraryInfoProvider = blueprint.NewProvider[HeaderLibraryInfo]()
 
 // FlagExporterInfo is a provider to propagate transitive library information
 // pertaining to exported include paths and flags.
@@ -429,4 +430,4 @@
 	GeneratedHeaders  android.Paths
 }
 
-var FlagExporterInfoProvider = blueprint.NewProvider(FlagExporterInfo{})
+var FlagExporterInfoProvider = blueprint.NewProvider[FlagExporterInfo]()
diff --git a/cc/linker.go b/cc/linker.go
index 357d1ce..2c50db2 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -91,6 +91,10 @@
 	// compiling crt or libc.
 	Nocrt *bool `android:"arch_variant"`
 
+	// don't link in crt_pad_segment. This flag is currently only used internal to
+	// soong for testing and for vndk prebuilt shared libraries.
+	No_crt_pad_segment *bool `android:"arch_variant"`
+
 	// deprecated and ignored because lld makes it unnecessary. See b/189475744.
 	Group_static_libs *bool `android:"arch_variant"`
 
@@ -253,6 +257,10 @@
 	return blp.No_libcrt == nil || !*blp.No_libcrt
 }
 
+func (blp *BaseLinkerProperties) crtPadSegment() bool {
+	return blp.No_crt_pad_segment == nil || !*blp.No_crt_pad_segment
+}
+
 func NewBaseLinker(sanitize *sanitize) *baseLinker {
 	return &baseLinker{sanitize: sanitize}
 }
@@ -648,6 +656,9 @@
 	return specifiedDeps
 }
 
+func (linker *baseLinker) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
+}
+
 // Injecting version symbols
 // Some host modules want a version number, but we don't want to rebuild it every time.  Optionally add a step
 // after linking that injects a constant placeholder with the current version number.
diff --git a/cc/lto.go b/cc/lto.go
index 8b0880c..05fa8ee 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -15,9 +15,12 @@
 package cc
 
 import (
-	"android/soong/android"
+	"fmt"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
 )
 
 // LTO (link-time optimization) allows the compiler to optimize and generate
@@ -49,11 +52,6 @@
 	LtoEnabled bool `blueprint:"mutated"`
 	LtoDefault bool `blueprint:"mutated"`
 
-	// Dep properties indicate that this module needs to be built with LTO
-	// since it is an object dependency of an LTO module.
-	LtoDep   bool `blueprint:"mutated"`
-	NoLtoDep bool `blueprint:"mutated"`
-
 	// Use -fwhole-program-vtables cflag.
 	Whole_program_vtables *bool
 }
@@ -102,7 +100,7 @@
 	lto.Properties.LtoEnabled = ltoEnabled
 }
 
-func (lto *lto) flags(ctx BaseModuleContext, flags Flags) Flags {
+func (lto *lto) flags(ctx ModuleContext, flags Flags) Flags {
 	// TODO(b/131771163): CFI and Fuzzer controls LTO flags by themselves.
 	// This has be checked late because these properties can be mutated.
 	if ctx.isCfi() || ctx.isFuzzer() {
@@ -141,7 +139,7 @@
 		// Reduce the inlining threshold for a better balance of binary size and
 		// performance.
 		if !ctx.Darwin() {
-			if ctx.isAfdoCompile() {
+			if ctx.isAfdoCompile(ctx) {
 				ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=40")
 			} else {
 				ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=5")
@@ -176,86 +174,83 @@
 	return lto != nil && proptools.Bool(lto.Properties.Lto.Never)
 }
 
-// Propagate lto requirements down from binaries
-func ltoDepsMutator(mctx android.TopDownMutatorContext) {
-	if m, ok := mctx.Module().(*Module); ok {
-		if m.lto == nil || m.lto.Properties.LtoEnabled == m.lto.Properties.LtoDefault {
-			return
-		}
-
-		mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
-			tag := mctx.OtherModuleDependencyTag(dep)
-			libTag, isLibTag := tag.(libraryDependencyTag)
-
-			// Do not recurse down non-static dependencies
-			if isLibTag {
-				if !libTag.static() {
-					return false
-				}
-			} else {
-				if tag != objDepTag && tag != reuseObjTag {
-					return false
-				}
-			}
-
-			if dep, ok := dep.(*Module); ok {
-				if m.lto.Properties.LtoEnabled {
-					dep.lto.Properties.LtoDep = true
-				} else {
-					dep.lto.Properties.NoLtoDep = true
-				}
-			}
-
-			// Recursively walk static dependencies
-			return true
-		})
+func ltoPropagateViaDepTag(tag blueprint.DependencyTag) bool {
+	libTag, isLibTag := tag.(libraryDependencyTag)
+	// Do not recurse down non-static dependencies
+	if isLibTag {
+		return libTag.static()
+	} else {
+		return tag == objDepTag || tag == reuseObjTag || tag == staticVariantTag
 	}
 }
 
-// Create lto variants for modules that need them
-func ltoMutator(mctx android.BottomUpMutatorContext) {
-	if m, ok := mctx.Module().(*Module); ok && m.lto != nil {
-		// Create variations for LTO types required as static
-		// dependencies
-		variationNames := []string{""}
-		if m.lto.Properties.LtoDep {
-			variationNames = append(variationNames, "lto-thin")
-		}
-		if m.lto.Properties.NoLtoDep {
-			variationNames = append(variationNames, "lto-none")
+// ltoTransitionMutator creates LTO variants of cc modules.  Variant "" is the default variant, which may
+// or may not have LTO enabled depending on the config and the module's type and properties.  "lto-thin" or
+// "lto-none" variants are created when a module needs to compile in the non-default state for that module.
+type ltoTransitionMutator struct{}
+
+const LTO_NONE_VARIATION = "lto-none"
+const LTO_THIN_VARIATION = "lto-thin"
+
+func (l *ltoTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+	return []string{""}
+}
+
+func (l *ltoTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	if m, ok := ctx.Module().(*Module); ok && m.lto != nil {
+		if !ltoPropagateViaDepTag(ctx.DepTag()) {
+			return ""
 		}
 
-		if !m.lto.Properties.LtoEnabled {
-			mctx.SetDependencyVariation("lto-none")
+		if sourceVariation != "" {
+			return sourceVariation
 		}
+
+		// Always request an explicit variation, IncomingTransition will rewrite it back to the default variation
+		// if necessary.
 		if m.lto.Properties.LtoEnabled {
-			mctx.SetDependencyVariation("lto-thin")
+			return LTO_THIN_VARIATION
+		} else {
+			return LTO_NONE_VARIATION
 		}
+	}
+	return ""
+}
 
-		if len(variationNames) > 1 {
-			modules := mctx.CreateVariations(variationNames...)
-			for i, name := range variationNames {
-				variation := modules[i].(*Module)
-				// Default module which will be
-				// installed. Variation set above according to
-				// explicit LTO properties
-				if name == "" {
-					continue
-				}
-
-				// LTO properties for dependencies
-				if name == "lto-thin" {
-					variation.lto.Properties.LtoEnabled = true
-				}
-				if name == "lto-none" {
-					variation.lto.Properties.LtoEnabled = false
-				}
-				variation.Properties.PreventInstall = true
-				variation.Properties.HideFromMake = true
-				variation.lto.Properties.LtoDefault = m.lto.Properties.LtoDefault
-				variation.lto.Properties.LtoDep = false
-				variation.lto.Properties.NoLtoDep = false
-			}
+func (l *ltoTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	if m, ok := ctx.Module().(*Module); ok && m.lto != nil {
+		if m.lto.Never() {
+			return ""
 		}
+		// Rewrite explicit variations back to the default variation if the default variation matches.
+		if incomingVariation == LTO_THIN_VARIATION && m.lto.Properties.LtoDefault {
+			return ""
+		} else if incomingVariation == LTO_NONE_VARIATION && !m.lto.Properties.LtoDefault {
+			return ""
+		}
+		return incomingVariation
+	}
+	return ""
+}
+
+func (l *ltoTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	// Default module which will be installed. Variation set above according to explicit LTO properties.
+	if variation == "" {
+		return
+	}
+
+	if m, ok := ctx.Module().(*Module); ok && m.lto != nil {
+		// Non-default variation, set the LTO properties to match the variation.
+		switch variation {
+		case LTO_THIN_VARIATION:
+			m.lto.Properties.LtoEnabled = true
+		case LTO_NONE_VARIATION:
+			m.lto.Properties.LtoEnabled = false
+		default:
+			panic(fmt.Errorf("unknown variation %s", variation))
+		}
+		// Non-default variations are never installed.
+		m.Properties.PreventInstall = true
+		m.Properties.HideFromMake = true
 	}
 }
diff --git a/cc/lto_test.go b/cc/lto_test.go
index 7b7fe8c..e4b5a3a 100644
--- a/cc/lto_test.go
+++ b/cc/lto_test.go
@@ -146,8 +146,9 @@
 		t.Errorf("'root' missing dependency on the default variant of 'foo'")
 	}
 
-	if !hasDep(result, libRootLtoNever, libFoo.Module()) {
-		t.Errorf("'root_no_lto' missing dependency on the default variant of 'foo'")
+	libFooNoLto := result.ModuleForTests("foo", "android_arm64_armv8-a_static_lto-none")
+	if !hasDep(result, libRootLtoNever, libFooNoLto.Module()) {
+		t.Errorf("'root_no_lto' missing dependency on the lto_none variant of 'foo'")
 	}
 
 	libFooCFlags := libFoo.Rule("cc").Args["cFlags"]
diff --git a/cc/makevars.go b/cc/makevars.go
index 6c3f551..70fdd57 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -123,6 +123,13 @@
 	ctx.Strict("SOONG_MODULES_USING_WNO_ERROR", makeStringOfKeys(ctx, modulesUsingWnoErrorKey))
 	ctx.Strict("SOONG_MODULES_MISSING_PGO_PROFILE_FILE", makeStringOfKeys(ctx, modulesMissingProfileFileKey))
 
+ 	ctx.Strict("CLANG_COVERAGE_CONFIG_CFLAGS", strings.Join(clangCoverageCFlags, " "))
+ 	ctx.Strict("CLANG_COVERAGE_CONFIG_COMMFLAGS", strings.Join(clangCoverageCommonFlags, " "))
+ 	ctx.Strict("CLANG_COVERAGE_HOST_LDFLAGS", strings.Join(clangCoverageHostLdFlags, " "))
+ 	ctx.Strict("CLANG_COVERAGE_INSTR_PROFILE", profileInstrFlag)
+ 	ctx.Strict("CLANG_COVERAGE_CONTINUOUS_FLAGS", strings.Join(clangContinuousCoverageFlags, " "))
+ 	ctx.Strict("CLANG_COVERAGE_HWASAN_FLAGS", strings.Join(clangCoverageHWASanFlags, " "))
+
 	ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS", strings.Join(asanCflags, " "))
 	ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_LDFLAGS", strings.Join(asanLdflags, " "))
 
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 0fbc8d0..567cb7c 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -182,7 +182,7 @@
 }
 
 // Like ndk_headers, but preprocesses the headers with the bionic versioner:
-// https://android.googlesource.com/platform/bionic/+/master/tools/versioner/README.md.
+// https://android.googlesource.com/platform/bionic/+/main/tools/versioner/README.md.
 //
 // Unlike ndk_headers, we don't operate on a list of sources but rather a whole directory, the
 // module does not have the srcs property, and operates on a full directory (the `from` property).
@@ -267,7 +267,7 @@
 }
 
 // versioned_ndk_headers preprocesses the headers with the bionic versioner:
-// https://android.googlesource.com/platform/bionic/+/master/tools/versioner/README.md.
+// https://android.googlesource.com/platform/bionic/+/main/tools/versioner/README.md.
 // Unlike the ndk_headers soong module, versioned_ndk_headers operates on a
 // directory level specified in `from` property. This is only used to process
 // the bionic/libc/include directory.
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 98fb334..183e818 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -486,6 +486,12 @@
 	}
 }
 
+func (linker *stubDecorator) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
+	linker.libraryDecorator.moduleInfoJSON(ctx, moduleInfoJSON)
+	// Overwrites the SubName computed by libraryDecorator
+	moduleInfoJSON.SubName = ndkLibrarySuffix + "." + linker.apiLevel.String()
+}
+
 func (linker *stubDecorator) Name(name string) string {
 	return name + ndkLibrarySuffix
 }
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
index 842bdf6..f503982 100644
--- a/cc/ndk_prebuilt.go
+++ b/cc/ndk_prebuilt.go
@@ -117,13 +117,13 @@
 
 	if ndk.static() {
 		depSet := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(lib).Build()
-		ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
+		android.SetProvider(ctx, StaticLibraryInfoProvider, StaticLibraryInfo{
 			StaticLibrary: lib,
 
 			TransitiveStaticLibrariesForOrdering: depSet,
 		})
 	} else {
-		ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
+		android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
 			SharedLibrary: lib,
 			Target:        ctx.Target(),
 		})
diff --git a/cc/object.go b/cc/object.go
index 0dba99f..6c0391f 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -238,3 +238,8 @@
 func (object *objectLinker) isCrt() bool {
 	return Bool(object.Properties.Crt)
 }
+
+func (object *objectLinker) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
+	object.baseLinker.moduleInfoJSON(ctx, moduleInfoJSON)
+	moduleInfoJSON.Class = []string{"STATIC_LIBRARIES"}
+}
diff --git a/cc/object_test.go b/cc/object_test.go
index e6a3fdd..c0d1331 100644
--- a/cc/object_test.go
+++ b/cc/object_test.go
@@ -22,7 +22,7 @@
 )
 
 func TestMinSdkVersionsOfCrtObjects(t *testing.T) {
-	ctx := testCc(t, `
+	bp := `
 		cc_object {
 			name: "crt_foo",
 			srcs: ["foo.c"],
@@ -30,8 +30,8 @@
 			stl: "none",
 			min_sdk_version: "28",
 			vendor_available: true,
-		}`)
-
+		}
+	`
 	variants := []struct {
 		variant string
 		num     string
@@ -43,11 +43,17 @@
 		{"android_arm64_armv8-a_sdk_current", "10000"},
 		{"android_vendor.29_arm64_armv8-a", "29"},
 	}
+
+	ctx := prepareForCcTest.RunTestWithBp(t, bp)
 	for _, v := range variants {
 		cflags := ctx.ModuleForTests("crt_foo", v.variant).Rule("cc").Args["cFlags"]
 		expected := "-target aarch64-linux-android" + v.num + " "
 		android.AssertStringDoesContain(t, "cflag", cflags, expected)
 	}
+	ctx = prepareForCcTestWithoutVndk.RunTestWithBp(t, bp)
+	android.AssertStringDoesContain(t, "cflag",
+		ctx.ModuleForTests("crt_foo", "android_vendor_arm64_armv8-a").Rule("cc").Args["cFlags"],
+		"-target aarch64-linux-android10000 ")
 }
 
 func TestUseCrtObjectOfCorrectVersion(t *testing.T) {
diff --git a/cc/orderfile.go b/cc/orderfile.go
index 9192e81..38b8905 100644
--- a/cc/orderfile.go
+++ b/cc/orderfile.go
@@ -20,6 +20,8 @@
 import (
 	"fmt"
 
+	"github.com/google/blueprint"
+
 	"android/soong/android"
 )
 
@@ -190,66 +192,61 @@
 	return flags
 }
 
-// Propagate profile orderfile flags down from binaries and shared libraries
-// We do not allow propagation for load flags because the orderfile is specific
-// to the module (binary / shared library)
-func orderfileDepsMutator(mctx android.TopDownMutatorContext) {
-	if m, ok := mctx.Module().(*Module); ok {
-		if !m.orderfile.orderfileLinkEnabled() {
-			return
-		}
-		mctx.WalkDeps(func(dep android.
-			Module, parent android.Module) bool {
-			tag := mctx.OtherModuleDependencyTag(dep)
-			libTag, isLibTag := tag.(libraryDependencyTag)
-
-			// Do not recurse down non-static dependencies
-			if isLibTag {
-				if !libTag.static() {
-					return false
-				}
-			} else {
-				if tag != objDepTag && tag != reuseObjTag {
-					return false
-				}
-			}
-
-			if dep, ok := dep.(*Module); ok {
-				if m.orderfile.Properties.OrderfileInstrLink {
-					dep.orderfile.Properties.OrderfileInstrLink = true
-				}
-			}
-
-			return true
-		})
+func orderfilePropagateViaDepTag(tag blueprint.DependencyTag) bool {
+	libTag, isLibTag := tag.(libraryDependencyTag)
+	// Do not recurse down non-static dependencies
+	if isLibTag {
+		return libTag.static()
+	} else {
+		return tag == objDepTag || tag == reuseObjTag || tag == staticVariantTag
 	}
 }
 
-// Create orderfile variants for modules that need them
-func orderfileMutator(mctx android.BottomUpMutatorContext) {
-	if m, ok := mctx.Module().(*Module); ok && m.orderfile != nil {
-		if !m.static() && m.orderfile.orderfileEnabled() {
-			mctx.SetDependencyVariation("orderfile")
-			return
+// orderfileTransitionMutator creates orderfile variants of cc modules.
+type orderfileTransitionMutator struct{}
+
+const ORDERFILE_VARIATION = "orderfile"
+
+func (o *orderfileTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+	return []string{""}
+}
+
+func (o *orderfileTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	if m, ok := ctx.Module().(*Module); ok && m.orderfile != nil {
+		if !orderfilePropagateViaDepTag(ctx.DepTag()) {
+			return ""
 		}
 
-		variationNames := []string{""}
-		if m.orderfile.Properties.OrderfileInstrLink {
-			variationNames = append(variationNames, "orderfile")
+		if sourceVariation != "" {
+			return sourceVariation
 		}
 
-		if len(variationNames) > 1 {
-			modules := mctx.CreateVariations(variationNames...)
-			for i, name := range variationNames {
-				if name == "" {
-					continue
-				}
-				variation := modules[i].(*Module)
-				variation.Properties.PreventInstall = true
-				variation.Properties.HideFromMake = true
-				variation.orderfile.Properties.ShouldProfileModule = true
-				variation.orderfile.Properties.OrderfileLoad = false
-			}
+		// Propagate profile orderfile flags down from binaries and shared libraries
+		if m.orderfile.orderfileLinkEnabled() {
+			return ORDERFILE_VARIATION
 		}
 	}
+	return ""
+}
+
+func (o *orderfileTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	if m, ok := ctx.Module().(*Module); ok && m.orderfile != nil {
+		return incomingVariation
+	}
+	return ""
+}
+
+func (o *orderfileTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	if variation == "" {
+		return
+	}
+
+	if m, ok := ctx.Module().(*Module); ok && m.orderfile != nil {
+		m.Properties.PreventInstall = true
+		m.Properties.HideFromMake = true
+		m.orderfile.Properties.ShouldProfileModule = true
+		// We do not allow propagation for load flags because the orderfile is specific
+		// to the module (binary / shared library)
+		m.orderfile.Properties.OrderfileLoad = false
+	}
 }
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index f68fa24..cbb5d58 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -17,6 +17,8 @@
 import (
 	"path/filepath"
 
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 )
 
@@ -36,9 +38,15 @@
 type prebuiltLinkerInterface interface {
 	Name(string) string
 	prebuilt() *android.Prebuilt
+	sourceModuleName() string
 }
 
 type prebuiltLinkerProperties struct {
+	// Name of the source soong module that gets shadowed by this prebuilt
+	// If unspecified, follows the naming convention that the source module of
+	// the prebuilt is Name() without "prebuilt_" prefix
+	Source_module_name *string
+
 	// a prebuilt library or binary. Can reference a genrule module that generates an executable file.
 	Srcs []string `android:"path,arch_variant"`
 
@@ -132,7 +140,7 @@
 
 		if p.static() {
 			depSet := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(in).Build()
-			ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
+			android.SetProvider(ctx, StaticLibraryInfoProvider, StaticLibraryInfo{
 				StaticLibrary: in,
 
 				TransitiveStaticLibrariesForOrdering: depSet,
@@ -190,7 +198,7 @@
 				},
 			})
 
-			ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
+			android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
 				SharedLibrary: outputFile,
 				Target:        ctx.Target(),
 
@@ -213,7 +221,7 @@
 	}
 
 	if p.header() {
-		ctx.SetProvider(HeaderLibraryInfoProvider, HeaderLibraryInfo{})
+		android.SetProvider(ctx, HeaderLibraryInfoProvider, HeaderLibraryInfo{})
 
 		// Need to return an output path so that the AndroidMk logic doesn't skip
 		// the prebuilt header. For compatibility, in case Android.mk files use a
@@ -252,6 +260,9 @@
 
 func (p *prebuiltLibraryLinker) disablePrebuilt() {
 	p.properties.Srcs = nil
+	p.properties.Sanitized.None.Srcs = nil
+	p.properties.Sanitized.Address.Srcs = nil
+	p.properties.Sanitized.Hwaddress.Srcs = nil
 }
 
 // Implements versionedInterface
@@ -337,7 +348,11 @@
 }
 
 type prebuiltObjectProperties struct {
-	Srcs []string `android:"path,arch_variant"`
+	// Name of the source soong module that gets shadowed by this prebuilt
+	// If unspecified, follows the naming convention that the source module of
+	// the prebuilt is Name() without "prebuilt_" prefix
+	Source_module_name *string
+	Srcs               []string `android:"path,arch_variant"`
 }
 
 type prebuiltObjectLinker struct {
@@ -351,6 +366,10 @@
 	return &p.Prebuilt
 }
 
+func (p *prebuiltObjectLinker) sourceModuleName() string {
+	return proptools.String(p.properties.Source_module_name)
+}
+
 var _ prebuiltLinkerInterface = (*prebuiltObjectLinker)(nil)
 
 func (p *prebuiltObjectLinker) link(ctx ModuleContext,
@@ -520,3 +539,7 @@
 	}
 	return sanitized.None.Srcs
 }
+
+func (p *prebuiltLinker) sourceModuleName() string {
+	return proptools.String(p.properties.Source_module_name)
+}
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index 8b4174b..71b7e43 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -15,6 +15,7 @@
 package cc
 
 import (
+	"fmt"
 	"runtime"
 	"testing"
 
@@ -509,3 +510,253 @@
 }`
 	testCcError(t, `Android.bp:4:6: module "bintest" variant "android_arm64_armv8-a": srcs: multiple prebuilt source files`, bp)
 }
+
+func TestMultiplePrebuilts(t *testing.T) {
+	bp := `
+		// an rdep
+		cc_library {
+			name: "libfoo",
+			shared_libs: ["libbar"],
+		}
+
+		// multiple variations of dep
+		// source
+		cc_library {
+			name: "libbar",
+		}
+		// prebuilt "v1"
+		cc_prebuilt_library_shared {
+			name: "libbar",
+			srcs: ["libbar.so"],
+		}
+		// prebuilt "v2"
+		cc_prebuilt_library_shared {
+			name: "libbar.v2",
+			stem: "libbar",
+			source_module_name: "libbar",
+			srcs: ["libbar.so"],
+		}
+
+		// selectors
+		apex_contributions {
+			name: "myapex_contributions",
+			contents: ["%v"],
+		}
+		all_apex_contributions {name: "all_apex_contributions"}
+	`
+	hasDep := func(ctx *android.TestContext, m android.Module, wantDep android.Module) bool {
+		t.Helper()
+		var found bool
+		ctx.VisitDirectDeps(m, func(dep blueprint.Module) {
+			if dep == wantDep {
+				found = true
+			}
+		})
+		return found
+	}
+
+	testCases := []struct {
+		desc                   string
+		selectedDependencyName string
+		expectedDependencyName string
+	}{
+		{
+			desc:                   "Source library is selected using apex_contributions",
+			selectedDependencyName: "libbar",
+			expectedDependencyName: "libbar",
+		},
+		{
+			desc:                   "Prebuilt library v1 is selected using apex_contributions",
+			selectedDependencyName: "prebuilt_libbar",
+			expectedDependencyName: "prebuilt_libbar",
+		},
+		{
+			desc:                   "Prebuilt library v2 is selected using apex_contributions",
+			selectedDependencyName: "prebuilt_libbar.v2",
+			expectedDependencyName: "prebuilt_libbar.v2",
+		},
+	}
+
+	for _, tc := range testCases {
+		preparer := android.GroupFixturePreparers(
+			android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+				android.RegisterApexContributionsBuildComponents(ctx)
+			}),
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.BuildFlags = map[string]string{
+					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
+				}
+			}),
+		)
+		ctx := testPrebuilt(t, fmt.Sprintf(bp, tc.selectedDependencyName), map[string][]byte{
+			"libbar.so": nil,
+			"crtx.o":    nil,
+		}, preparer)
+		libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+		expectedDependency := ctx.ModuleForTests(tc.expectedDependencyName, "android_arm64_armv8-a_shared").Module()
+		android.AssertBoolEquals(t, fmt.Sprintf("expected dependency from %s to %s\n", libfoo.Name(), tc.expectedDependencyName), true, hasDep(ctx, libfoo, expectedDependency))
+		// check that LOCAL_SHARED_LIBRARIES contains libbar and not libbar.v<N>
+		entries := android.AndroidMkEntriesForTest(t, ctx, libfoo)[0]
+		android.AssertStringListContains(t, "Version should not be present in LOCAL_SHARED_LIBRARIES", entries.EntryMap["LOCAL_SHARED_LIBRARIES"], "libbar")
+
+		// check installation rules
+		// the selected soong module should be exported to make
+		libbar := ctx.ModuleForTests(tc.expectedDependencyName, "android_arm64_armv8-a_shared").Module()
+		android.AssertBoolEquals(t, fmt.Sprintf("dependency %s should be exported to make\n", expectedDependency), true, !libbar.IsHideFromMake())
+
+		// check LOCAL_MODULE of the selected module name
+		// the prebuilt should have the same LOCAL_MODULE when exported to make
+		entries = android.AndroidMkEntriesForTest(t, ctx, libbar)[0]
+		android.AssertStringEquals(t, "unexpected LOCAL_MODULE", "libbar", entries.EntryMap["LOCAL_MODULE"][0])
+	}
+}
+
+// Setting prefer on multiple prebuilts is an error, unless one of them is also listed in apex_contributions
+func TestMultiplePrebuiltsPreferredUsingLegacyFlags(t *testing.T) {
+	bp := `
+		// an rdep
+		cc_library {
+			name: "libfoo",
+			shared_libs: ["libbar"],
+		}
+
+		// multiple variations of dep
+		// source
+		cc_library {
+			name: "libbar",
+		}
+		// prebuilt "v1"
+		cc_prebuilt_library_shared {
+			name: "libbar",
+			srcs: ["libbar.so"],
+			prefer: true,
+		}
+		// prebuilt "v2"
+		cc_prebuilt_library_shared {
+			name: "libbar.v2",
+			stem: "libbar",
+			source_module_name: "libbar",
+			srcs: ["libbar.so"],
+			prefer: true,
+		}
+
+		// selectors
+		apex_contributions {
+			name: "myapex_contributions",
+			contents: [%v],
+		}
+		all_apex_contributions {name: "all_apex_contributions"}
+	`
+	hasDep := func(ctx *android.TestContext, m android.Module, wantDep android.Module) bool {
+		t.Helper()
+		var found bool
+		ctx.VisitDirectDeps(m, func(dep blueprint.Module) {
+			if dep == wantDep {
+				found = true
+			}
+		})
+		return found
+	}
+
+	testCases := []struct {
+		desc                   string
+		selectedDependencyName string
+		expectedDependencyName string
+		expectedErr            string
+	}{
+		{
+			desc:        "Multiple prebuilts have prefer: true",
+			expectedErr: "Multiple prebuilt modules prebuilt_libbar and prebuilt_libbar.v2 have been marked as preferred for this source module",
+		},
+		{
+			desc:                   "Multiple prebuilts have prefer: true. The prebuilt listed in apex_contributions wins.",
+			selectedDependencyName: `"prebuilt_libbar"`,
+			expectedDependencyName: "prebuilt_libbar",
+		},
+	}
+
+	for _, tc := range testCases {
+		preparer := android.GroupFixturePreparers(
+			android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+				android.RegisterApexContributionsBuildComponents(ctx)
+			}),
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.BuildFlags = map[string]string{
+					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
+				}
+			}),
+		)
+		if tc.expectedErr != "" {
+			preparer = preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(tc.expectedErr))
+		}
+
+		ctx := testPrebuilt(t, fmt.Sprintf(bp, tc.selectedDependencyName), map[string][]byte{
+			"libbar.so": nil,
+			"crtx.o":    nil,
+		}, preparer)
+		if tc.expectedErr != "" {
+			return // the fixture will assert that the excepted err has been raised
+		}
+		libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+		expectedDependency := ctx.ModuleForTests(tc.expectedDependencyName, "android_arm64_armv8-a_shared").Module()
+		android.AssertBoolEquals(t, fmt.Sprintf("expected dependency from %s to %s\n", libfoo.Name(), tc.expectedDependencyName), true, hasDep(ctx, libfoo, expectedDependency))
+	}
+}
+
+// If module sdk cannot provide a cc module variant (e.g. static), then the module variant from source should be used
+func TestMissingVariantInModuleSdk(t *testing.T) {
+	bp := `
+		// an rdep
+		cc_library {
+			name: "libfoo",
+			static_libs: ["libbar"],
+		}
+
+		// source
+		cc_library {
+			name: "libbar",
+		}
+		// prebuilt
+		// libbar only exists as a shared library
+		cc_prebuilt_library_shared {
+			name: "libbar",
+			srcs: ["libbar.so"],
+		}
+		// selectors
+		apex_contributions {
+			name: "myapex_contributions",
+			contents: ["prebuilt_libbar"],
+		}
+		all_apex_contributions {name: "all_apex_contributions"}
+	`
+	hasDep := func(ctx *android.TestContext, m android.Module, wantDep android.Module) bool {
+		t.Helper()
+		var found bool
+		ctx.VisitDirectDeps(m, func(dep blueprint.Module) {
+			if dep == wantDep {
+				found = true
+			}
+		})
+		return found
+	}
+
+	preparer := android.GroupFixturePreparers(
+		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+			android.RegisterApexContributionsBuildComponents(ctx)
+		}),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
+			}
+		}),
+	)
+	ctx := testPrebuilt(t, bp, map[string][]byte{
+		"libbar.so": nil,
+		"crtx.o":    nil,
+	}, preparer)
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+	sourceLibBar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_static").Module()
+	// Even though the prebuilt is listed in apex_contributions, the prebuilt does not have a static variant.
+	// Therefore source of libbar should be used.
+	android.AssertBoolEquals(t, fmt.Sprintf("expected dependency from libfoo to source libbar"), true, hasDep(ctx, libfoo, sourceLibBar))
+}
diff --git a/cc/sabi.go b/cc/sabi.go
index 4cd776a..af26726 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -26,6 +26,30 @@
 	lsdumpPathsLock sync.Mutex
 )
 
+type lsdumpTag string
+
+const (
+	llndkLsdumpTag    lsdumpTag = "LLNDK"
+	ndkLsdumpTag      lsdumpTag = "NDK"
+	platformLsdumpTag lsdumpTag = "PLATFORM"
+	productLsdumpTag  lsdumpTag = "PRODUCT"
+	vendorLsdumpTag   lsdumpTag = "VENDOR"
+)
+
+// Return the prebuilt ABI dump directory for a tag; an empty string for an opt-in dump.
+func (tag *lsdumpTag) dirName() string {
+	switch *tag {
+	case ndkLsdumpTag:
+		return "ndk"
+	case llndkLsdumpTag:
+		return "vndk"
+	case platformLsdumpTag:
+		return "platform"
+	default:
+		return ""
+	}
+}
+
 // Properties for ABI compatibility checker in Android.bp.
 type headerAbiCheckerProperties struct {
 	// Enable ABI checks (even if this is not an LLNDK/VNDK lib)
@@ -97,49 +121,34 @@
 	return sabi != nil && sabi.Properties.ShouldCreateSourceAbiDump
 }
 
-// Returns a string that represents the class of the ABI dump.
-// Returns an empty string if ABI check is disabled for this library.
-func classifySourceAbiDump(ctx android.BaseModuleContext) string {
+// Returns a slice of strings that represent the ABI dumps generated for this module.
+func classifySourceAbiDump(ctx android.BaseModuleContext) []lsdumpTag {
+	result := []lsdumpTag{}
 	m := ctx.Module().(*Module)
 	headerAbiChecker := m.library.getHeaderAbiCheckerProperties(ctx)
 	if headerAbiChecker.explicitlyDisabled() {
-		return ""
+		return result
 	}
-	// Return NDK if the library is both NDK and LLNDK.
-	if m.IsNdk(ctx.Config()) {
-		return "NDK"
-	}
-	if m.isImplementationForLLNDKPublic() {
-		return "LLNDK"
-	}
-	if m.UseVndk() && m.IsVndk() && !m.IsVndkPrivate() {
-		if m.IsVndkSp() {
-			if m.IsVndkExt() {
-				return "VNDK-SP-ext"
-			} else {
-				return "VNDK-SP"
-			}
-		} else {
-			if m.IsVndkExt() {
-				return "VNDK-ext"
-			} else {
-				return "VNDK-core"
-			}
+	if !m.InProduct() && !m.InVendor() {
+		if m.isImplementationForLLNDKPublic() {
+			result = append(result, llndkLsdumpTag)
 		}
-	}
-	if m.library.hasStubsVariants() && !m.InProduct() && !m.InVendor() {
-		return "PLATFORM"
-	}
-	if headerAbiChecker.enabled() {
+		// Return NDK if the library is both NDK and APEX.
+		// TODO(b/309880485): Split NDK and APEX ABI.
+		if m.IsNdk(ctx.Config()) {
+			result = append(result, ndkLsdumpTag)
+		} else if m.library.hasStubsVariants() || headerAbiChecker.enabled() {
+			result = append(result, platformLsdumpTag)
+		}
+	} else if headerAbiChecker.enabled() {
 		if m.InProduct() {
-			return "PRODUCT"
+			result = append(result, productLsdumpTag)
 		}
 		if m.InVendor() {
-			return "VENDOR"
+			result = append(result, vendorLsdumpTag)
 		}
-		return "PLATFORM"
 	}
-	return ""
+	return result
 }
 
 // Called from sabiDepsMutator to check whether ABI dumps should be created for this module.
@@ -194,8 +203,8 @@
 		return false
 	}
 
-	isPlatformVariant := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
-	if isPlatformVariant {
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	if apexInfo.IsForPlatform() {
 		// Bionic libraries that are installed to the bootstrap directory are not ABI checked.
 		// Only the runtime APEX variants, which are the implementation libraries of bionic NDK stubs,
 		// are checked.
@@ -208,7 +217,7 @@
 			return false
 		}
 	}
-	return classifySourceAbiDump(ctx) != ""
+	return len(classifySourceAbiDump(ctx)) > 0
 }
 
 // Mark the direct and transitive dependencies of libraries that need ABI check, so that ABI dumps
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 31c0500..52b5be9 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -1627,7 +1627,7 @@
 
 		addStaticDeps := func(dep string, hideSymbols bool) {
 			// If we're using snapshots, redirect to snapshot whenever possible
-			snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
+			snapshot, _ := android.ModuleProvider(mctx, SnapshotInfoProvider)
 			if lib, ok := snapshot.StaticLibs[dep]; ok {
 				dep = lib
 			}
@@ -1714,7 +1714,7 @@
 				addStaticDeps(runtimeSharedLibrary, true)
 			} else if !c.static() && !c.Header() {
 				// If we're using snapshots, redirect to snapshot whenever possible
-				snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
+				snapshot, _ := android.ModuleProvider(mctx, SnapshotInfoProvider)
 				if lib, ok := snapshot.SharedLibs[runtimeSharedLibrary]; ok {
 					runtimeSharedLibrary = lib
 				}
diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go
index 31e668e..44f38e1 100644
--- a/cc/sanitize_test.go
+++ b/cc/sanitize_test.go
@@ -22,8 +22,6 @@
 	"testing"
 
 	"android/soong/android"
-
-	"github.com/google/blueprint"
 )
 
 var prepareForAsanTest = android.FixtureAddFile("asan/Android.bp", []byte(`
@@ -49,7 +47,7 @@
 `))
 
 type providerInterface interface {
-	ModuleProvider(blueprint.Module, blueprint.ProviderKey) interface{}
+	android.SingletonModuleProviderContext
 }
 
 // expectSharedLinkDep verifies that the from module links against the to module as a
@@ -57,7 +55,7 @@
 func expectSharedLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
 	t.Helper()
 	fromLink := from.Description("link")
-	toInfo := ctx.ModuleProvider(to.Module(), SharedLibraryInfoProvider).(SharedLibraryInfo)
+	toInfo, _ := android.SingletonModuleProvider(ctx, to.Module(), SharedLibraryInfoProvider)
 
 	if g, w := fromLink.OrderOnly.Strings(), toInfo.SharedLibrary.RelativeToTop().String(); !android.InList(w, g) {
 		t.Errorf("%s should link against %s, expected %q, got %q",
@@ -70,7 +68,7 @@
 func expectNoSharedLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
 	t.Helper()
 	fromLink := from.Description("link")
-	toInfo := ctx.ModuleProvider(to.Module(), SharedLibraryInfoProvider).(SharedLibraryInfo)
+	toInfo, _ := android.SingletonModuleProvider(ctx, to.Module(), SharedLibraryInfoProvider)
 
 	if g, w := fromLink.OrderOnly.Strings(), toInfo.SharedLibrary.RelativeToTop().String(); android.InList(w, g) {
 		t.Errorf("%s should not link against %s, expected %q, got %q",
@@ -83,7 +81,7 @@
 func expectStaticLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
 	t.Helper()
 	fromLink := from.Description("link")
-	toInfo := ctx.ModuleProvider(to.Module(), StaticLibraryInfoProvider).(StaticLibraryInfo)
+	toInfo, _ := android.SingletonModuleProvider(ctx, to.Module(), StaticLibraryInfoProvider)
 
 	if g, w := fromLink.Implicits.Strings(), toInfo.StaticLibrary.RelativeToTop().String(); !android.InList(w, g) {
 		t.Errorf("%s should link against %s, expected %q, got %q",
@@ -97,7 +95,7 @@
 func expectNoStaticLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
 	t.Helper()
 	fromLink := from.Description("link")
-	toInfo := ctx.ModuleProvider(to.Module(), StaticLibraryInfoProvider).(StaticLibraryInfo)
+	toInfo, _ := android.SingletonModuleProvider(ctx, to.Module(), StaticLibraryInfoProvider)
 
 	if g, w := fromLink.Implicits.Strings(), toInfo.StaticLibrary.RelativeToTop().String(); android.InList(w, g) {
 		t.Errorf("%s should not link against %s, expected %q, got %q",
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index e29c446..e769fe9 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -194,7 +194,7 @@
 		sharedLibs[k] = v
 	}
 
-	ctx.SetProvider(SnapshotInfoProvider, SnapshotInfo{
+	android.SetProvider(ctx, SnapshotInfoProvider, SnapshotInfo{
 		HeaderLibs: headers,
 		Binaries:   binaries,
 		Objects:    objects,
@@ -209,7 +209,7 @@
 	HeaderLibs, Binaries, Objects, StaticLibs, SharedLibs, Rlibs, Dylibs map[string]string
 }
 
-var SnapshotInfoProvider = blueprint.NewMutatorProvider(SnapshotInfo{}, "deps")
+var SnapshotInfoProvider = blueprint.NewMutatorProvider[SnapshotInfo]("deps")
 
 var _ android.ImageInterface = (*snapshotModule)(nil)
 
@@ -494,7 +494,7 @@
 		p.tocFile = android.OptionalPathForPath(tocFile)
 		TransformSharedObjectToToc(ctx, in, tocFile)
 
-		ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
+		android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
 			SharedLibrary: in,
 			Target:        ctx.Target(),
 
@@ -504,7 +504,7 @@
 
 	if p.static() {
 		depSet := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(in).Build()
-		ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
+		android.SetProvider(ctx, StaticLibraryInfoProvider, StaticLibraryInfo{
 			StaticLibrary: in,
 
 			TransitiveStaticLibrariesForOrdering: depSet,
diff --git a/cc/stl.go b/cc/stl.go
index 63c23d7..de2066f 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -205,12 +205,14 @@
 			flags.extraLibFlags = append(flags.extraLibFlags, "-nostdlib++")
 			if ctx.Windows() {
 				flags.Local.CppFlags = append(flags.Local.CppFlags,
-					// Disable visiblity annotations since we're using static
-					// libc++.
-					"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
-					"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
+					// These macros can also be defined by libc++'s __config
+					// or __config_site headers so define them the same way
+					// (i.e. to nothing). Disable visibility annotations since
+					// we're using static libc++.
+					"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS=",
+					"-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS=",
 					// Use Win32 threads in libc++.
-					"-D_LIBCPP_HAS_THREAD_API_WIN32")
+					"-D_LIBCPP_HAS_THREAD_API_WIN32=")
 			}
 		}
 	case "libstdc++":
diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py
index 94c8567..345e9f9 100644
--- a/cc/symbolfile/__init__.py
+++ b/cc/symbolfile/__init__.py
@@ -46,6 +46,15 @@
     Arch('x86_64'),
 )
 
+# TODO: it would be nice to dedupe with 'has_*_tag' property methods
+SUPPORTED_TAGS = ALL_ARCHITECTURES + (
+    Tag('apex'),
+    Tag('llndk'),
+    Tag('platform-only'),
+    Tag('systemapi'),
+    Tag('var'),
+    Tag('weak'),
+)
 
 # Arbitrary magic number. We use the same one in api-level.h for this purpose.
 FUTURE_API_LEVEL = 10000
@@ -136,6 +145,8 @@
 
 def is_api_level_tag(tag: Tag) -> bool:
     """Returns true if this tag has an API level that may need decoding."""
+    if tag.startswith('llndk-deprecated='):
+        return True
     if tag.startswith('introduced='):
         return True
     if tag.startswith('introduced-'):
@@ -170,6 +181,9 @@
         ParseError: An unknown version name was found in a tag.
     """
     if not is_api_level_tag(tag):
+        if tag not in SUPPORTED_TAGS:
+            raise ParseError(f'Unsupported tag: {tag}')
+
         return tag
 
     name, value = split_tag(tag)
diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py
index 856b9d7..83becc2 100644
--- a/cc/symbolfile/test_symbolfile.py
+++ b/cc/symbolfile/test_symbolfile.py
@@ -40,10 +40,20 @@
         self.assertEqual(Tags(), symbolfile.get_tags('foo bar baz', {}))
 
     def test_get_tags(self) -> None:
-        self.assertEqual(Tags.from_strs(['foo', 'bar']),
-                         symbolfile.get_tags('# foo bar', {}))
-        self.assertEqual(Tags.from_strs(['bar', 'baz']),
-                         symbolfile.get_tags('foo # bar baz', {}))
+        self.assertEqual(Tags.from_strs(['llndk', 'apex']),
+                         symbolfile.get_tags('# llndk apex', {}))
+        self.assertEqual(Tags.from_strs(['llndk', 'apex']),
+                         symbolfile.get_tags('foo # llndk apex', {}))
+
+    def test_get_unrecognized_tags(self) -> None:
+        with self.assertRaises(symbolfile.ParseError):
+            symbolfile.get_tags('# bar', {})
+        with self.assertRaises(symbolfile.ParseError):
+            symbolfile.get_tags('foo # bar', {})
+        with self.assertRaises(symbolfile.ParseError):
+            symbolfile.get_tags('# #', {})
+        with self.assertRaises(symbolfile.ParseError):
+            symbolfile.get_tags('# apex # llndk', {})
 
     def test_split_tag(self) -> None:
         self.assertTupleEqual(('foo', 'bar'),
@@ -425,13 +435,13 @@
 
     def test_parse_version(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
-            VERSION_1 { # foo bar
+            VERSION_1 { # weak introduced=35
                 baz;
-                qux; # woodly doodly
+                qux; # apex llndk
             };
 
             VERSION_2 {
-            } VERSION_1; # asdf
+            } VERSION_1; # not-a-tag
         """))
         parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
 
@@ -439,11 +449,11 @@
         version = parser.parse_version()
         self.assertEqual('VERSION_1', version.name)
         self.assertIsNone(version.base)
-        self.assertEqual(Tags.from_strs(['foo', 'bar']), version.tags)
+        self.assertEqual(Tags.from_strs(['weak', 'introduced=35']), version.tags)
 
         expected_symbols = [
             Symbol('baz', Tags()),
-            Symbol('qux', Tags.from_strs(['woodly', 'doodly'])),
+            Symbol('qux', Tags.from_strs(['apex', 'llndk'])),
         ]
         self.assertEqual(expected_symbols, version.symbols)
 
@@ -476,7 +486,7 @@
     def test_parse_symbol(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             foo;
-            bar; # baz qux
+            bar; # llndk apex
         """))
         parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
 
@@ -488,7 +498,7 @@
         parser.next_line()
         symbol = parser.parse_symbol()
         self.assertEqual('bar', symbol.name)
-        self.assertEqual(Tags.from_strs(['baz', 'qux']), symbol.tags)
+        self.assertEqual(Tags.from_strs(['llndk', 'apex']), symbol.tags)
 
     def test_wildcard_symbol_global(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
@@ -537,13 +547,13 @@
                     hidden1;
                 global:
                     foo;
-                    bar; # baz
+                    bar; # llndk
             };
 
-            VERSION_2 { # wasd
+            VERSION_2 { # weak
                 # Implicit global scope.
                     woodly;
-                    doodly; # asdf
+                    doodly; # llndk
                 local:
                     qwerty;
             } VERSION_1;
@@ -554,12 +564,12 @@
         expected = [
             symbolfile.Version('VERSION_1', None, Tags(), [
                 Symbol('foo', Tags()),
-                Symbol('bar', Tags.from_strs(['baz'])),
+                Symbol('bar', Tags.from_strs(['llndk'])),
             ]),
             symbolfile.Version(
-                'VERSION_2', 'VERSION_1', Tags.from_strs(['wasd']), [
+                'VERSION_2', 'VERSION_1', Tags.from_strs(['weak']), [
                     Symbol('woodly', Tags()),
-                    Symbol('doodly', Tags.from_strs(['asdf'])),
+                    Symbol('doodly', Tags.from_strs(['llndk'])),
                 ]),
         ]
 
diff --git a/cc/test.go b/cc/test.go
index d02145b..3a1a3af 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -321,6 +321,13 @@
 	return []interface{}{&test.InstallerProperties}
 }
 
+func (test *testDecorator) moduleInfoJSON(ctx android.ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
+	if android.PrefixInList(moduleInfoJSON.CompatibilitySuites, "mts-") &&
+		!android.InList("mts", moduleInfoJSON.CompatibilitySuites) {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "mts")
+	}
+}
+
 func NewTestInstaller() *baseInstaller {
 	return NewBaseInstaller("nativetest", "nativetest64", InstallInData)
 }
@@ -355,6 +362,38 @@
 	return flags
 }
 
+func (test *testBinary) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
+	if ctx.Host() && Bool(test.Properties.Test_options.Unit_test) {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "host-unit-tests")
+	}
+	moduleInfoJSON.TestOptionsTags = append(moduleInfoJSON.TestOptionsTags, test.Properties.Test_options.Tags...)
+	moduleInfoJSON.TestMainlineModules = append(moduleInfoJSON.TestMainlineModules, test.Properties.Test_mainline_modules...)
+	if test.testConfig != nil {
+		if _, ok := test.testConfig.(android.WritablePath); ok {
+			moduleInfoJSON.AutoTestConfig = []string{"true"}
+		}
+		moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, test.testConfig.String())
+	}
+	moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, test.extraTestConfigs.Strings()...)
+
+	if Bool(test.Properties.Test_per_src) {
+		moduleInfoJSON.SubName = "_" + String(test.binaryDecorator.Properties.Stem)
+	}
+
+	moduleInfoJSON.DataDependencies = append(moduleInfoJSON.DataDependencies, test.Properties.Data_bins...)
+
+	if len(test.InstallerProperties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, test.InstallerProperties.Test_suites...)
+	} else {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
+	}
+
+	test.binaryDecorator.moduleInfoJSON(ctx, moduleInfoJSON)
+	test.testDecorator.moduleInfoJSON(ctx, moduleInfoJSON)
+	moduleInfoJSON.Class = []string{"NATIVE_TESTS"}
+
+}
+
 func (test *testBinary) installerProps() []interface{} {
 	return append(test.baseInstaller.installerProps(), test.testDecorator.installerProps()...)
 }
@@ -391,8 +430,7 @@
 		}
 	})
 
-	useVendor := ctx.inVendor() || ctx.useVndk()
-	testInstallBase := getTestInstallBase(useVendor)
+	testInstallBase := getTestInstallBase(ctx.InVendorOrProduct())
 	configs := getTradefedConfigOptions(ctx, &test.Properties, test.isolated(ctx), ctx.Device())
 
 	test.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
@@ -532,6 +570,15 @@
 	return flags
 }
 
+func (test *testLibrary) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
+	if len(test.InstallerProperties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, test.InstallerProperties.Test_suites...)
+	}
+
+	test.libraryDecorator.moduleInfoJSON(ctx, moduleInfoJSON)
+	test.testDecorator.moduleInfoJSON(ctx, moduleInfoJSON)
+}
+
 func (test *testLibrary) installerProps() []interface{} {
 	return append(test.baseInstaller.installerProps(), test.testDecorator.installerProps()...)
 }
@@ -626,6 +673,29 @@
 	benchmark.binaryDecorator.baseInstaller.install(ctx, file)
 }
 
+func (benchmark *benchmarkDecorator) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
+	benchmark.binaryDecorator.moduleInfoJSON(ctx, moduleInfoJSON)
+
+	moduleInfoJSON.Class = []string{"NATIVE_TESTS"}
+	if len(benchmark.Properties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, benchmark.Properties.Test_suites...)
+	} else {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
+	}
+
+	if android.PrefixInList(moduleInfoJSON.CompatibilitySuites, "mts-") &&
+		!android.InList("mts", moduleInfoJSON.CompatibilitySuites) {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "mts")
+	}
+
+	if benchmark.testConfig != nil {
+		if _, ok := benchmark.testConfig.(android.WritablePath); ok {
+			moduleInfoJSON.AutoTestConfig = []string{"true"}
+		}
+		moduleInfoJSON.TestConfig = []string{benchmark.testConfig.String()}
+	}
+}
+
 func NewBenchmark(hod android.HostOrDeviceSupported) *Module {
 	module, binary := newBinary(hod)
 	module.multilib = android.MultilibBoth
diff --git a/cc/testing.go b/cc/testing.go
index b1583f1..9c2900c 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -77,6 +77,7 @@
 			no_libcrt: true,
 			sdk_version: "minimum",
 			nocrt: true,
+			no_crt_pad_segment: true,
 			system_shared_libs: [],
 			stl: "none",
 			check_elf_files: false,
@@ -385,6 +386,11 @@
 		}
 
 		cc_object {
+			name: "crt_pad_segment",
+			defaults: ["crt_defaults"],
+		}
+
+		cc_object {
 			name: "crtbrand",
 			defaults: ["crt_defaults"],
 			srcs: ["crtbrand.c"],
@@ -571,6 +577,7 @@
 	android.MockFS{
 		"defaults/cc/common/libc.map.txt":      nil,
 		"defaults/cc/common/libdl.map.txt":     nil,
+		"defaults/cc/common/libft2.map.txt":    nil,
 		"defaults/cc/common/libm.map.txt":      nil,
 		"defaults/cc/common/ndk_libc++_shared": nil,
 		"defaults/cc/common/crtbegin_so.c":     nil,
diff --git a/cc/tidy_test.go b/cc/tidy_test.go
index 7036ecb..9481778 100644
--- a/cc/tidy_test.go
+++ b/cc/tidy_test.go
@@ -244,3 +244,30 @@
 		})
 	}
 }
+
+func TestWithGeneratedCode(t *testing.T) {
+	bp := `
+		cc_library_shared {
+			name: "libfoo",
+			srcs: ["foo_1.y", "foo_2.yy", "foo_3.l", "foo_4.ll", "foo_5.proto",
+			       "foo_6.aidl", "foo_7.rscript", "foo_8.fs", "foo_9.sysprop",
+			       "foo_src.cpp"],
+			tidy: true,
+		}`
+	variant := "android_arm64_armv8-a_shared"
+
+	testEnv := map[string]string{}
+	testEnv["ALLOW_LOCAL_TIDY_TRUE"] = "1"
+
+	ctx := android.GroupFixturePreparers(prepareForCcTest, android.FixtureMergeEnv(testEnv)).RunTestWithBp(t, bp)
+
+	t.Run("tidy should be only run for source code, not for generated code", func(t *testing.T) {
+		depFiles := ctx.ModuleForTests("libfoo", variant).Rule("ld").Validations.Strings()
+
+		tidyFileForCpp := "out/soong/.intermediates/libfoo/" + variant + "/obj/foo_src.tidy"
+
+		android.AssertArrayString(t,
+			"only one .tidy file for source code should exist for libfoo",
+			[]string{tidyFileForCpp}, depFiles)
+	})
+}
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index e8e930e..a33ed5f 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -275,7 +275,7 @@
 		var propOut string
 
 		if m.IsSnapshotLibrary() {
-			exporterInfo := ctx.ModuleProvider(m.Module(), FlagExporterInfoProvider).(FlagExporterInfo)
+			exporterInfo, _ := android.SingletonModuleProvider(ctx, m.Module(), FlagExporterInfoProvider)
 
 			// library flags
 			prop.ExportedFlags = exporterInfo.Flags
@@ -407,7 +407,7 @@
 
 		moduleDir := ctx.ModuleDir(module)
 		inProprietaryPath := s.Image.IsProprietaryPath(moduleDir, ctx.DeviceConfig())
-		apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
+		apexInfo, _ := android.SingletonModuleProvider(ctx, module, android.ApexInfoProvider)
 
 		if s.Image.ExcludeFromSnapshot(m) {
 			if inProprietaryPath {
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index 890a533..0a55431 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -341,6 +341,7 @@
 		vendor: true,
 		nocrt: true,
 		no_libcrt: true,
+		no_crt_pad_segment: true,
 		stl: "none",
 		system_shared_libs: [],
 		compile_multilib: "64",
@@ -458,6 +459,7 @@
 		vendor: true,
 		nocrt: true,
 		no_libcrt: true,
+		no_crt_pad_segment: true,
 		stl: "none",
 		system_shared_libs: [],
 	}
@@ -467,6 +469,7 @@
 		vendor: true,
 		nocrt: true,
 		no_libcrt: true,
+		no_crt_pad_segment: true,
 		stl: "none",
 		system_shared_libs: [],
 		shared_libs: ["libvndk", "libvendor_available", "libllndk"],
@@ -487,6 +490,7 @@
 		vendor: true,
 		nocrt: true,
 		no_libcrt: true,
+		no_crt_pad_segment: true,
 		stl: "none",
 		system_shared_libs: [],
 		static_libs: ["libvendor"],
@@ -501,6 +505,7 @@
 		vendor: true,
 		nocrt: true,
 		no_libcrt: true,
+		no_crt_pad_segment: true,
 		stl: "none",
 		system_shared_libs: [],
 		vndk: {
@@ -597,6 +602,7 @@
 		target_arch: "arm64",
 		compile_multilib: "both",
 		vendor: true,
+		no_crt_pad_segment: true,
 		shared_libs: [
 			"libvendor_without_snapshot",
 			"libvendor_available",
@@ -620,6 +626,7 @@
 		target_arch: "arm64",
 		compile_multilib: "both",
 		vendor: true,
+		no_crt_pad_segment: true,
 		overrides: ["libvendor"],
 		shared_libs: [
 			"libvendor_without_snapshot",
@@ -657,6 +664,7 @@
 		target_arch: "arm64",
 		compile_multilib: "32",
 		vendor: true,
+		no_crt_pad_segment: true,
 		arch: {
 			arm: {
 				src: "lib32.so",
@@ -683,6 +691,7 @@
 		target_arch: "arm64",
 		compile_multilib: "64",
 		vendor: true,
+		no_crt_pad_segment: true,
 		arch: {
 			arm64: {
 				src: "lib64.so",
@@ -722,6 +731,7 @@
 		target_arch: "arm64",
 		compile_multilib: "both",
 		vendor: true,
+		no_crt_pad_segment: true,
 		arch: {
 			arm64: {
 				src: "libvendor_available.so",
diff --git a/cc/vndk.go b/cc/vndk.go
index a849455..2b2ea64 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -377,22 +377,17 @@
 			return false
 		}
 
-		// ignore prebuilt vndk modules that are newer than or equal to the platform vndk version
-		platformVndkApiLevel := android.ApiLevelOrPanic(mctx, mctx.DeviceConfig().PlatformVndkVersion())
-		if platformVndkApiLevel.LessThanOrEqualTo(android.ApiLevelOrPanic(mctx, p.Version())) {
-			return false
+		platformVndkVersion := mctx.DeviceConfig().PlatformVndkVersion()
+		if platformVndkVersion != "" {
+			// ignore prebuilt vndk modules that are newer than or equal to the platform vndk version
+			platformVndkApiLevel := android.ApiLevelOrPanic(mctx, platformVndkVersion)
+			if platformVndkApiLevel.LessThanOrEqualTo(android.ApiLevelOrPanic(mctx, p.Version())) {
+				return false
+			}
 		}
 	}
 
 	if lib, ok := m.linker.(libraryInterface); ok {
-		// VNDK APEX for VNDK-Lite devices will have VNDK-SP libraries from core variants
-		if mctx.DeviceConfig().VndkVersion() == "" {
-			// b/73296261: filter out libz.so because it is considered as LLNDK for VNDK-lite devices
-			if mctx.ModuleName() == "libz" {
-				return false
-			}
-			return m.ImageVariation().Variation == android.CoreVariation && lib.shared() && m.IsVndkSp() && !m.IsVndkExt()
-		}
 		// VNDK APEX doesn't need stub variants
 		if lib.buildStubs() {
 			return false
@@ -418,11 +413,11 @@
 	lib, isLib := m.linker.(*libraryDecorator)
 	prebuiltLib, isPrebuiltLib := m.linker.(*prebuiltLibraryLinker)
 
-	if m.UseVndk() && isLib && lib.hasLLNDKStubs() {
+	if m.InVendorOrProduct() && isLib && lib.hasLLNDKStubs() {
 		m.VendorProperties.IsLLNDK = true
 		m.VendorProperties.IsVNDKPrivate = Bool(lib.Properties.Llndk.Private)
 	}
-	if m.UseVndk() && isPrebuiltLib && prebuiltLib.hasLLNDKStubs() {
+	if m.InVendorOrProduct() && isPrebuiltLib && prebuiltLib.hasLLNDKStubs() {
 		m.VendorProperties.IsLLNDK = true
 		m.VendorProperties.IsVNDKPrivate = Bool(prebuiltLib.Properties.Llndk.Private)
 	}
@@ -772,7 +767,7 @@
 		prop.MinSdkVersion = m.MinSdkVersion()
 
 		if ctx.Config().VndkSnapshotBuildArtifacts() {
-			exportedInfo := ctx.ModuleProvider(m, FlagExporterInfoProvider).(FlagExporterInfo)
+			exportedInfo, _ := android.SingletonModuleProvider(ctx, m, FlagExporterInfoProvider)
 			prop.ExportedFlags = exportedInfo.Flags
 			prop.ExportedDirs = exportedInfo.IncludeDirs.Strings()
 			prop.ExportedSystemDirs = exportedInfo.SystemIncludeDirs.Strings()
@@ -797,7 +792,7 @@
 			return
 		}
 
-		apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
+		apexInfo, _ := android.SingletonModuleProvider(ctx, module, android.ApexInfoProvider)
 
 		vndkType, ok := isVndkSnapshotAware(ctx.DeviceConfig(), m, apexInfo)
 		if !ok {
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index 5e526db..43030b8 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -131,11 +131,14 @@
 
 func (p *vndkPrebuiltLibraryDecorator) link(ctx ModuleContext,
 	flags Flags, deps PathDeps, objs Objects) android.Path {
-	platformVndkApiLevel := android.ApiLevelOrPanic(ctx, ctx.DeviceConfig().PlatformVndkVersion())
-	if platformVndkApiLevel.LessThanOrEqualTo(android.ApiLevelOrPanic(ctx, p.Version())) {
-		// This prebuilt VNDK module is not required for the current build
-		ctx.Module().HideFromMake()
-		return nil
+	platformVndkVersion := ctx.DeviceConfig().PlatformVndkVersion()
+	if platformVndkVersion != "" {
+		platformVndkApiLevel := android.ApiLevelOrPanic(ctx, platformVndkVersion)
+		if platformVndkApiLevel.LessThanOrEqualTo(android.ApiLevelOrPanic(ctx, p.Version())) {
+			// This prebuilt VNDK module is not required for the current build
+			ctx.Module().HideFromMake()
+			return nil
+		}
 	}
 
 	if !p.MatchesWithDevice(ctx.DeviceConfig()) {
@@ -171,7 +174,7 @@
 			p.androidMkSuffix = ""
 		}
 
-		ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
+		android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
 			SharedLibrary: in,
 			Target:        ctx.Target(),
 
@@ -187,6 +190,11 @@
 	return nil
 }
 
+func (p *vndkPrebuiltLibraryDecorator) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
+	p.libraryDecorator.moduleInfoJSON(ctx, moduleInfoJSON)
+	moduleInfoJSON.SubName += p.androidMkSuffix
+}
+
 func (p *vndkPrebuiltLibraryDecorator) MatchesWithDevice(config android.DeviceConfig) bool {
 	arches := config.Arches()
 	if len(arches) == 0 || arches[0].ArchType.String() != p.arch() {
@@ -227,6 +235,7 @@
 	prebuilt.properties.Check_elf_files = BoolPtr(false)
 	prebuilt.baseLinker.Properties.No_libcrt = BoolPtr(true)
 	prebuilt.baseLinker.Properties.Nocrt = BoolPtr(true)
+	prebuilt.baseLinker.Properties.No_crt_pad_segment = BoolPtr(true)
 
 	// Prevent default system libs (libc, libm, and libdl) from being linked
 	if prebuilt.baseLinker.Properties.System_shared_libs == nil {
@@ -241,14 +250,6 @@
 		&prebuilt.properties,
 	)
 
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		// empty BOARD_VNDK_VERSION implies that the device won't support
-		// system only OTA. In this case, VNDK snapshots aren't needed.
-		if ctx.DeviceConfig().VndkVersion() == "" {
-			ctx.Module().Disable()
-		}
-	})
-
 	return module
 }
 
diff --git a/cmd/merge_module_info_json/Android.bp b/cmd/merge_module_info_json/Android.bp
new file mode 100644
index 0000000..1ae6a47
--- /dev/null
+++ b/cmd/merge_module_info_json/Android.bp
@@ -0,0 +1,30 @@
+// Copyright 2021 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+    name: "merge_module_info_json",
+    srcs: [
+        "merge_module_info_json.go",
+    ],
+    deps: [
+        "soong-response",
+    ],
+    testSrcs: [
+        "merge_module_info_json_test.go",
+    ],
+}
diff --git a/cmd/merge_module_info_json/merge_module_info_json.go b/cmd/merge_module_info_json/merge_module_info_json.go
new file mode 100644
index 0000000..0143984
--- /dev/null
+++ b/cmd/merge_module_info_json/merge_module_info_json.go
@@ -0,0 +1,223 @@
+// Copyright 2021 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.
+
+// merge_module_info_json is a utility that merges module_info.json files generated by
+// Soong and Make.
+
+package main
+
+import (
+	"android/soong/response"
+	"cmp"
+	"encoding/json"
+	"flag"
+	"fmt"
+	"os"
+	"slices"
+)
+
+var (
+	out      = flag.String("o", "", "output file")
+	listFile = flag.String("l", "", "input file list file")
+)
+
+func usage() {
+	fmt.Fprintf(os.Stderr, "usage: %s -o <output file> <input files>\n", os.Args[0])
+	flag.PrintDefaults()
+	fmt.Fprintln(os.Stderr, "merge_module_info_json reads input files that each contain an array of json objects")
+	fmt.Fprintln(os.Stderr, "and writes them out as a single json array to the output file.")
+
+	os.Exit(2)
+}
+
+func main() {
+	flag.Usage = usage
+	flag.Parse()
+
+	if *out == "" {
+		fmt.Fprintf(os.Stderr, "%s: error: -o is required\n", os.Args[0])
+		usage()
+	}
+
+	inputs := flag.Args()
+	if *listFile != "" {
+		listFileInputs, err := readListFile(*listFile)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "failed to read list file %s: %s", *listFile, err)
+			os.Exit(1)
+		}
+		inputs = append(inputs, listFileInputs...)
+	}
+
+	err := mergeJsonObjects(*out, inputs)
+
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "%s: error: %s\n", os.Args[0], err.Error())
+		os.Exit(1)
+	}
+}
+
+func readListFile(file string) ([]string, error) {
+	f, err := os.Open(*listFile)
+	if err != nil {
+		return nil, err
+	}
+	return response.ReadRspFile(f)
+}
+
+func mergeJsonObjects(output string, inputs []string) error {
+	combined := make(map[string]any)
+	for _, input := range inputs {
+		objects, err := decodeObjectFromJson(input)
+		if err != nil {
+			return err
+		}
+
+		for _, object := range objects {
+			for k, v := range object {
+				if old, exists := combined[k]; exists {
+					v = combine(old, v)
+				}
+				combined[k] = v
+			}
+		}
+	}
+
+	f, err := os.Create(output)
+	if err != nil {
+		return fmt.Errorf("failed to open output file: %w", err)
+	}
+	encoder := json.NewEncoder(f)
+	encoder.SetIndent("", "  ")
+	err = encoder.Encode(combined)
+	if err != nil {
+		return fmt.Errorf("failed to encode to output file: %w", err)
+	}
+
+	return nil
+}
+
+func decodeObjectFromJson(input string) ([]map[string]any, error) {
+	f, err := os.Open(input)
+	if err != nil {
+		return nil, fmt.Errorf("failed to open input file: %w", err)
+	}
+
+	decoder := json.NewDecoder(f)
+	var object any
+	err = decoder.Decode(&object)
+	if err != nil {
+		return nil, fmt.Errorf("failed to parse input file %q: %w", input, err)
+	}
+
+	switch o := object.(type) {
+	case []any:
+		var ret []map[string]any
+		for _, arrayElement := range o {
+			if m, ok := arrayElement.(map[string]any); ok {
+				ret = append(ret, m)
+			} else {
+				return nil, fmt.Errorf("unknown JSON type in array %T", arrayElement)
+			}
+		}
+		return ret, nil
+
+	case map[string]any:
+		return []map[string]any{o}, nil
+	}
+
+	return nil, fmt.Errorf("unknown JSON type %T", object)
+}
+
+func combine(old, new any) any {
+	//	fmt.Printf("%#v %#v\n", old, new)
+	switch oldTyped := old.(type) {
+	case map[string]any:
+		if newObject, ok := new.(map[string]any); ok {
+			return combineObjects(oldTyped, newObject)
+		} else {
+			panic(fmt.Errorf("expected map[string]any, got %#v", new))
+		}
+	case []any:
+		if newArray, ok := new.([]any); ok {
+			return combineArrays(oldTyped, newArray)
+		} else {
+			panic(fmt.Errorf("expected []any, got %#v", new))
+		}
+	case string:
+		if newString, ok := new.(string); ok {
+			if oldTyped != newString {
+				panic(fmt.Errorf("strings %q and %q don't match", oldTyped, newString))
+			}
+			return oldTyped
+		} else {
+			panic(fmt.Errorf("expected []any, got %#v", new))
+		}
+	default:
+		panic(fmt.Errorf("can't combine type %T", old))
+	}
+}
+
+func combineObjects(old, new map[string]any) map[string]any {
+	for k, newField := range new {
+		// HACK: Don't merge "test_config" field.  This matches the behavior in base_rules.mk that overwrites
+		// instead of appending ALL_MODULES.$(my_register_name).TEST_CONFIG, keeping the
+		if k == "test_config" {
+			old[k] = newField
+			continue
+		}
+		if oldField, exists := old[k]; exists {
+			oldField = combine(oldField, newField)
+			old[k] = oldField
+		} else {
+			old[k] = newField
+		}
+	}
+
+	return old
+}
+
+func combineArrays(old, new []any) []any {
+	containsNonStrings := false
+	for _, oldElement := range old {
+		switch oldElement.(type) {
+		case string:
+		default:
+			containsNonStrings = true
+		}
+	}
+	for _, newElement := range new {
+		found := false
+		for _, oldElement := range old {
+			if oldElement == newElement {
+				found = true
+				break
+			}
+		}
+		if !found {
+			switch newElement.(type) {
+			case string:
+			default:
+				containsNonStrings = true
+			}
+			old = append(old, newElement)
+		}
+	}
+	if !containsNonStrings {
+		slices.SortFunc(old, func(a, b any) int {
+			return cmp.Compare(a.(string), b.(string))
+		})
+	}
+	return old
+}
diff --git a/cmd/merge_module_info_json/merge_module_info_json_test.go b/cmd/merge_module_info_json/merge_module_info_json_test.go
new file mode 100644
index 0000000..dbf1aa1
--- /dev/null
+++ b/cmd/merge_module_info_json/merge_module_info_json_test.go
@@ -0,0 +1,58 @@
+// Copyright 2021 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 main
+
+import (
+	"reflect"
+	"testing"
+)
+
+func Test_combine(t *testing.T) {
+	tests := []struct {
+		name string
+		old  any
+		new  any
+		want any
+	}{
+		{
+			name: "objects",
+			old: map[string]any{
+				"foo": "bar",
+				"baz": []any{"a"},
+			},
+			new: map[string]any{
+				"foo": "bar",
+				"baz": []any{"b"},
+			},
+			want: map[string]any{
+				"foo": "bar",
+				"baz": []any{"a", "b"},
+			},
+		},
+		{
+			name: "arrays",
+			old:  []any{"foo", "bar"},
+			new:  []any{"foo", "baz"},
+			want: []any{"bar", "baz", "foo"},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := combine(tt.old, tt.new); !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("combine() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index 1aa6f6f..2c57180 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -342,7 +342,8 @@
 func (oz *OutputZip) getUninitializedPythonPackages(inputZips []InputZip) ([]string, error) {
 	// the runfiles packages needs to be populated with "__init__.py".
 	// the runfiles dirs have been treated as packages.
-	allPackages := make(map[string]bool)
+	var allPackages []string // Using a slice to preserve input order.
+	seenPkgs := make(map[string]bool)
 	initedPackages := make(map[string]bool)
 	getPackage := func(path string) string {
 		ret := filepath.Dir(path)
@@ -369,16 +370,17 @@
 				initedPackages[pyPkg] = true
 			}
 			for pyPkg != "" {
-				if _, found := allPackages[pyPkg]; found {
+				if _, found := seenPkgs[pyPkg]; found {
 					break
 				}
-				allPackages[pyPkg] = true
+				seenPkgs[pyPkg] = true
+				allPackages = append(allPackages, pyPkg)
 				pyPkg = getPackage(pyPkg)
 			}
 		}
 	}
 	noInitPackages := make([]string, 0)
-	for pyPkg := range allPackages {
+	for _, pyPkg := range allPackages {
 		if _, found := initedPackages[pyPkg]; !found {
 			noInitPackages = append(noInitPackages, pyPkg)
 		}
diff --git a/cmd/merge_zips/merge_zips_test.go b/cmd/merge_zips/merge_zips_test.go
index 64b08d0..17228c4 100644
--- a/cmd/merge_zips/merge_zips_test.go
+++ b/cmd/merge_zips/merge_zips_test.go
@@ -103,6 +103,7 @@
 		stripFiles       []string
 		stripDirs        []string
 		jar              bool
+		par              bool
 		sort             bool
 		ignoreDuplicates bool
 		stripDirEntries  bool
@@ -265,6 +266,34 @@
 			},
 			out: []testZipEntry{withoutTimestamp, a},
 		},
+		{
+			name: "emulate par",
+			in: [][]testZipEntry{
+				{
+					testZipEntry{name: "3/main.py"},
+					testZipEntry{name: "c/main.py"},
+					testZipEntry{name: "a/main.py"},
+					testZipEntry{name: "2/main.py"},
+					testZipEntry{name: "b/main.py"},
+					testZipEntry{name: "1/main.py"},
+				},
+			},
+			out: []testZipEntry{
+				testZipEntry{name: "3/__init__.py", mode: 0700, timestamp: jar.DefaultTime},
+				testZipEntry{name: "c/__init__.py", mode: 0700, timestamp: jar.DefaultTime},
+				testZipEntry{name: "a/__init__.py", mode: 0700, timestamp: jar.DefaultTime},
+				testZipEntry{name: "2/__init__.py", mode: 0700, timestamp: jar.DefaultTime},
+				testZipEntry{name: "b/__init__.py", mode: 0700, timestamp: jar.DefaultTime},
+				testZipEntry{name: "1/__init__.py", mode: 0700, timestamp: jar.DefaultTime},
+				testZipEntry{name: "3/main.py", timestamp: jar.DefaultTime},
+				testZipEntry{name: "c/main.py", timestamp: jar.DefaultTime},
+				testZipEntry{name: "a/main.py", timestamp: jar.DefaultTime},
+				testZipEntry{name: "2/main.py", timestamp: jar.DefaultTime},
+				testZipEntry{name: "b/main.py", timestamp: jar.DefaultTime},
+				testZipEntry{name: "1/main.py", timestamp: jar.DefaultTime},
+			},
+			par: true,
+		},
 	}
 
 	for _, test := range testCases {
@@ -280,7 +309,7 @@
 			writer := zip.NewWriter(out)
 
 			err := mergeZips(inputZips, writer, "", "",
-				test.sort, test.jar, false, test.stripDirEntries, test.ignoreDuplicates,
+				test.sort, test.jar, test.par, test.stripDirEntries, test.ignoreDuplicates,
 				test.stripFiles, test.stripDirs, test.zipsToNotStrip)
 
 			closeErr := writer.Close()
diff --git a/cmd/path_interposer/main.go b/cmd/path_interposer/main.go
index 8b9de52..c8c1464 100644
--- a/cmd/path_interposer/main.go
+++ b/cmd/path_interposer/main.go
@@ -140,7 +140,7 @@
 			defer func() { <-waitForLog }()
 		}
 		if config.Error {
-			return 1, fmt.Errorf("%q is not allowed to be used. See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.", base)
+			return 1, fmt.Errorf("%q is not allowed to be used. See https://android.googlesource.com/platform/build/+/main/Changes.md#PATH_Tools for more information.", base)
 		}
 	}
 
diff --git a/cmd/path_interposer/main_test.go b/cmd/path_interposer/main_test.go
index c89d623..8ae1d7d 100644
--- a/cmd/path_interposer/main_test.go
+++ b/cmd/path_interposer/main_test.go
@@ -138,7 +138,7 @@
 			args: []string{"path_interposer_test_not_allowed"},
 
 			exitCode: 1,
-			err:      fmt.Errorf(`"path_interposer_test_not_allowed" is not allowed to be used. See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.`),
+			err:      fmt.Errorf(`"path_interposer_test_not_allowed" is not allowed to be used. See https://android.googlesource.com/platform/build/+/main/Changes.md#PATH_Tools for more information.`),
 			logEntry: "path_interposer_test_not_allowed",
 		},
 	}
diff --git a/cmd/pom2bp/pom2bp.go b/cmd/pom2bp/pom2bp.go
index 3fb4454..6d1caf9 100644
--- a/cmd/pom2bp/pom2bp.go
+++ b/cmd/pom2bp/pom2bp.go
@@ -651,7 +651,7 @@
         {{- end}}
     ],
     {{- end}}
-    java_version: "1.7",
+    java_version: "1.8",
 }
 `))
 
diff --git a/cmd/pom2mk/pom2mk.go b/cmd/pom2mk/pom2mk.go
index b347155..5ca770e 100644
--- a/cmd/pom2mk/pom2mk.go
+++ b/cmd/pom2mk/pom2mk.go
@@ -262,7 +262,7 @@
   {{.MkName}}-nodeps{{end}}{{range .MkAarDeps}}  \
   {{.}}{{end}}
 LOCAL_JAR_EXCLUDE_FILES := none
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_JAVA_LANGUAGE_VERSION := 1.8
 LOCAL_USE_AAPT2 := true
 include $(BUILD_STATIC_JAVA_LIBRARY)
 `))
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index 3364f50..e69a930 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -22,6 +22,7 @@
 	"flag"
 	"fmt"
 	"io"
+	"io/fs"
 	"io/ioutil"
 	"os"
 	"os/exec"
@@ -478,7 +479,8 @@
 // copyOneFile copies a file and its permissions.  If forceExecutable is true it adds u+x to the
 // permissions.  If exists is allowFromNotExists it returns nil if the from path doesn't exist.
 // If write is onlyWriteIfChanged then the output file is compared to the input file and not written to
-// if it is the same, avoiding updating the timestamp.
+// if it is the same, avoiding updating the timestamp. If from is a symlink, the symlink itself
+// will be copied, instead of what it points to.
 func copyOneFile(from string, to string, forceExecutable bool, exists existsType,
 	write writeType) error {
 	err := os.MkdirAll(filepath.Dir(to), 0777)
@@ -486,7 +488,7 @@
 		return err
 	}
 
-	stat, err := os.Stat(from)
+	stat, err := os.Lstat(from)
 	if err != nil {
 		if os.IsNotExist(err) && exists == allowFromNotExists {
 			return nil
@@ -494,6 +496,25 @@
 		return err
 	}
 
+	if stat.Mode()&fs.ModeSymlink != 0 {
+		linkTarget, err := os.Readlink(from)
+		if err != nil {
+			return err
+		}
+		if write == onlyWriteIfChanged {
+			toLinkTarget, err := os.Readlink(to)
+			if err == nil && toLinkTarget == linkTarget {
+				return nil
+			}
+		}
+		err = os.Remove(to)
+		if err != nil && !os.IsNotExist(err) {
+			return err
+		}
+
+		return os.Symlink(linkTarget, to)
+	}
+
 	perm := stat.Mode()
 	if forceExecutable {
 		perm = perm | 0100 // u+x
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 7742492..d64010e 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -28,6 +28,7 @@
 	"android/soong/android/allowlists"
 	"android/soong/bp2build"
 	"android/soong/shared"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/deptools"
@@ -75,9 +76,9 @@
 	flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output")
 	flag.StringVar(&cmdlineArgs.SoongVariables, "soong_variables", "soong.variables", "the file contains all build variables")
 	flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
-	flag.BoolVar(&cmdlineArgs.MultitreeBuild, "multitree-build", false, "this is a multitree build")
 	flag.BoolVar(&cmdlineArgs.BuildFromSourceStub, "build-from-source-stub", false, "build Java stubs from source files instead of API text files")
 	flag.BoolVar(&cmdlineArgs.EnsureAllowlistIntegrity, "ensure-allowlist-integrity", false, "verify that allowlisted modules are mixed-built")
+	flag.StringVar(&cmdlineArgs.ModuleDebugFile, "soong_module_debug", "", "soong module debug info file to write")
 	// Flags that probably shouldn't be flags of soong_build, but we haven't found
 	// the time to remove them yet
 	flag.BoolVar(&cmdlineArgs.RunGoTests, "t", false, "build and run go tests during bootstrap")
@@ -196,7 +197,7 @@
 	ctx.Context.PrintJSONGraphAndActions(graphFile, actionsFile)
 }
 
-func writeBuildGlobsNinjaFile(ctx *android.Context) []string {
+func writeBuildGlobsNinjaFile(ctx *android.Context) {
 	ctx.EventHandler.Begin("globs_ninja_file")
 	defer ctx.EventHandler.End("globs_ninja_file")
 
@@ -208,7 +209,6 @@
 		SrcDir:     ctx.SrcDir(),
 	}, ctx.Config())
 	maybeQuit(err, "")
-	return bootstrap.GlobFileListFiles(globDir)
 }
 
 func writeDepFile(outputFile string, eventHandler *metrics.EventHandler, ninjaDeps []string) {
@@ -238,8 +238,7 @@
 	maybeQuit(err, "")
 	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
-	globListFiles := writeBuildGlobsNinjaFile(ctx)
-	ninjaDeps = append(ninjaDeps, globListFiles...)
+	writeBuildGlobsNinjaFile(ctx)
 
 	// Convert the Soong module graph into Bazel BUILD files.
 	switch ctx.Config().BuildMode {
diff --git a/dexpreopt/DEXPREOPT_IMPLEMENTATION.md b/dexpreopt/DEXPREOPT_IMPLEMENTATION.md
index c3a1730..1cb0add 100644
--- a/dexpreopt/DEXPREOPT_IMPLEMENTATION.md
+++ b/dexpreopt/DEXPREOPT_IMPLEMENTATION.md
@@ -237,22 +237,22 @@
 app.
 
 
-[make/core/dex_preopt.mk]: https://cs.android.com/android/platform/superproject/+/master:build/make/core/dex_preopt.mk
-[make/core/dex_preopt_config.mk]: https://cs.android.com/android/platform/superproject/+/master:build/make/core/dex_preopt_config.mk
-[make/core/dex_preopt_config_merger.py]: https://cs.android.com/android/platform/superproject/+/master:build/make/core/dex_preopt_config_merger.py
-[make/core/dex_preopt_odex_install.mk]: https://cs.android.com/android/platform/superproject/+/master:build/make/core/dex_preopt_odex_install.mk
-[soong/dexpreopt]: https://cs.android.com/android/platform/superproject/+/master:build/soong/dexpreopt
-[soong/dexpreopt/class_loader_context.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/dexpreopt/class_loader_context.go
-[soong/dexpreopt/config.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/dexpreopt/config.go
-[soong/dexpreopt/dexpreopt.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/dexpreopt/dexpreopt.go
-[soong/java]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java
-[soong/java/app.go:deps]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/app.go?q=%22func%20\(u%20*usesLibrary\)%20deps%22
-[soong/java/app.go:verifyUsesLibraries]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/app.go?q=%22func%20\(u%20*usesLibrary\)%20verifyUsesLibraries%22
-[soong/java/bootclasspath_fragment.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/bootclasspath_fragment.go
-[soong/java/dexpreopt.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/dexpreopt.go
-[soong/java/dexpreopt_bootjars.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/dexpreopt_bootjars.go
-[soong/java/dexpreopt_config.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/dexpreopt_config.go
-[soong/java/java.go:addCLCFromDep]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/java.go?q=%22func%20addCLCfromDep%22
-[soong/java/platform_bootclasspath.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/platform_bootclasspath.go
-[soong/scripts/construct_context.py]: https://cs.android.com/android/platform/superproject/+/master:build/soong/scripts/construct_context.py
-[soong/scripts/manifest_check.py]: https://cs.android.com/android/platform/superproject/+/master:build/soong/scripts/manifest_check.py
+[make/core/dex_preopt.mk]: https://cs.android.com/android/platform/superproject/+/main:build/make/core/dex_preopt.mk
+[make/core/dex_preopt_config.mk]: https://cs.android.com/android/platform/superproject/+/main:build/make/core/dex_preopt_config.mk
+[make/core/dex_preopt_config_merger.py]: https://cs.android.com/android/platform/superproject/+/main:build/make/core/dex_preopt_config_merger.py
+[make/core/dex_preopt_odex_install.mk]: https://cs.android.com/android/platform/superproject/+/main:build/make/core/dex_preopt_odex_install.mk
+[soong/dexpreopt]: https://cs.android.com/android/platform/superproject/+/main:build/soong/dexpreopt
+[soong/dexpreopt/class_loader_context.go]: https://cs.android.com/android/platform/superproject/+/main:build/soong/dexpreopt/class_loader_context.go
+[soong/dexpreopt/config.go]: https://cs.android.com/android/platform/superproject/+/main:build/soong/dexpreopt/config.go
+[soong/dexpreopt/dexpreopt.go]: https://cs.android.com/android/platform/superproject/+/main:build/soong/dexpreopt/dexpreopt.go
+[soong/java]: https://cs.android.com/android/platform/superproject/+/main:build/soong/java
+[soong/java/app.go:deps]: https://cs.android.com/android/platform/superproject/+/main:build/soong/java/app.go?q=%22func%20\(u%20*usesLibrary\)%20deps%22
+[soong/java/app.go:verifyUsesLibraries]: https://cs.android.com/android/platform/superproject/+/main:build/soong/java/app.go?q=%22func%20\(u%20*usesLibrary\)%20verifyUsesLibraries%22
+[soong/java/bootclasspath_fragment.go]: https://cs.android.com/android/platform/superproject/+/main:build/soong/java/bootclasspath_fragment.go
+[soong/java/dexpreopt.go]: https://cs.android.com/android/platform/superproject/+/main:build/soong/java/dexpreopt.go
+[soong/java/dexpreopt_bootjars.go]: https://cs.android.com/android/platform/superproject/+/main:build/soong/java/dexpreopt_bootjars.go
+[soong/java/dexpreopt_config.go]: https://cs.android.com/android/platform/superproject/+/main:build/soong/java/dexpreopt_config.go
+[soong/java/java.go:addCLCFromDep]: https://cs.android.com/android/platform/superproject/+/main:build/soong/java/java.go?q=%22func%20addCLCfromDep%22
+[soong/java/platform_bootclasspath.go]: https://cs.android.com/android/platform/superproject/+/main:build/soong/java/platform_bootclasspath.go
+[soong/scripts/construct_context.py]: https://cs.android.com/android/platform/superproject/+/main:build/soong/scripts/construct_context.py
+[soong/scripts/manifest_check.py]: https://cs.android.com/android/platform/superproject/+/main:build/soong/scripts/manifest_check.py
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 6163952..fe6317c 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -98,7 +98,9 @@
 	// measure, as it masks real errors and affects performance.
 	RelaxUsesLibraryCheck bool
 
-	EnableUffdGc bool // preopt with the assumption that userfaultfd GC will be used on device.
+	// "true" to force preopt with CMC GC (a.k.a., UFFD GC); "false" to force preopt with CC GC;
+	// "default" to determine the GC type based on the kernel version file.
+	EnableUffdGc string
 }
 
 var allPlatformSystemServerJarsKey = android.NewOnceKey("allPlatformSystemServerJars")
@@ -154,6 +156,7 @@
 	Zip2zip          android.Path
 	ManifestCheck    android.Path
 	ConstructContext android.Path
+	UffdGcFlag       android.WritablePath
 }
 
 type ModuleConfig struct {
@@ -537,6 +540,7 @@
 		Zip2zip:          ctx.Config().HostToolPath(ctx, "zip2zip"),
 		ManifestCheck:    ctx.Config().HostToolPath(ctx, "manifest_check"),
 		ConstructContext: ctx.Config().HostToolPath(ctx, "construct_context"),
+		UffdGcFlag:       getUffdGcFlagPath(ctx),
 	}
 }
 
@@ -588,6 +592,7 @@
 	Zip2zip          string
 	ManifestCheck    string
 	ConstructContext string
+	UffdGcFlag       string
 }
 
 // ParseGlobalSoongConfig parses the given data assumed to be read from the
@@ -609,6 +614,7 @@
 		Zip2zip:          constructPath(ctx, jc.Zip2zip),
 		ManifestCheck:    constructPath(ctx, jc.ManifestCheck),
 		ConstructContext: constructPath(ctx, jc.ConstructContext),
+		UffdGcFlag:       constructWritablePath(ctx, jc.UffdGcFlag),
 	}
 
 	return config, nil
@@ -633,12 +639,15 @@
 }
 
 func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	checkBootJarsConfigConsistency(ctx, GetGlobalConfig(ctx), ctx.Config())
+	global := GetGlobalConfig(ctx)
+	checkBootJarsConfigConsistency(ctx, global, ctx.Config())
 
-	if GetGlobalConfig(ctx).DisablePreopt {
+	if global.DisablePreopt {
 		return
 	}
 
+	buildUffdGcFlag(ctx, global)
+
 	config := GetCachedGlobalSoongConfig(ctx)
 	if config == nil {
 		// No module has enabled dexpreopting, so we assume there will be no calls
@@ -654,6 +663,7 @@
 		Zip2zip:          config.Zip2zip.String(),
 		ManifestCheck:    config.ManifestCheck.String(),
 		ConstructContext: config.ConstructContext.String(),
+		UffdGcFlag:       config.UffdGcFlag.String(),
 	}
 
 	data, err := json.Marshal(jc)
@@ -684,9 +694,32 @@
 		config.Zip2zip.String(),
 		config.ManifestCheck.String(),
 		config.ConstructContext.String(),
+		config.UffdGcFlag.String(),
 	}, " "))
 }
 
+func buildUffdGcFlag(ctx android.BuilderContext, global *GlobalConfig) {
+	uffdGcFlag := getUffdGcFlagPath(ctx)
+
+	if global.EnableUffdGc == "true" {
+		android.WriteFileRuleVerbatim(ctx, uffdGcFlag, "--runtime-arg -Xgc:CMC")
+	} else if global.EnableUffdGc == "false" {
+		android.WriteFileRuleVerbatim(ctx, uffdGcFlag, "")
+	} else if global.EnableUffdGc == "default" {
+		// Generated by `build/make/core/Makefile`.
+		kernelVersionFile := android.PathForOutput(ctx, "dexpreopt/kernel_version_for_uffd_gc.txt")
+		// Determine the UFFD GC flag by the kernel version file.
+		rule := android.NewRuleBuilder(pctx, ctx)
+		rule.Command().
+			Tool(ctx.Config().HostToolPath(ctx, "construct_uffd_gc_flag")).
+			Input(kernelVersionFile).
+			Output(uffdGcFlag)
+		rule.Restat().Build("dexpreopt_uffd_gc_flag", "dexpreopt_uffd_gc_flag")
+	} else {
+		panic(fmt.Sprintf("Unknown value of PRODUCT_ENABLE_UFFD_GC: %s", global.EnableUffdGc))
+	}
+}
+
 func GlobalConfigForTests(ctx android.PathContext) *GlobalConfig {
 	return &GlobalConfig{
 		DisablePreopt:                  false,
@@ -731,7 +764,7 @@
 	}
 }
 
-func globalSoongConfigForTests() *GlobalSoongConfig {
+func globalSoongConfigForTests(ctx android.BuilderContext) *GlobalSoongConfig {
 	return &GlobalSoongConfig{
 		Profman:          android.PathForTesting("profman"),
 		Dex2oat:          android.PathForTesting("dex2oat"),
@@ -740,5 +773,19 @@
 		Zip2zip:          android.PathForTesting("zip2zip"),
 		ManifestCheck:    android.PathForTesting("manifest_check"),
 		ConstructContext: android.PathForTesting("construct_context"),
+		UffdGcFlag:       android.PathForOutput(ctx, "dexpreopt_test", "uffd_gc_flag.txt"),
 	}
 }
+
+func GetDexpreoptDirName(ctx android.PathContext) string {
+	prefix := "dexpreopt_"
+	targets := ctx.Config().Targets[android.Android]
+	if len(targets) > 0 {
+		return prefix + targets[0].Arch.ArchType.String()
+	}
+	return prefix + "unknown_target"
+}
+
+func getUffdGcFlagPath(ctx android.PathContext) android.WritablePath {
+	return android.PathForOutput(ctx, "dexpreopt/uffd_gc_flag.txt")
+}
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 94707ba..04bc61d 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -390,7 +390,8 @@
 		Flag("--generate-build-id").
 		Flag("--abort-on-hard-verifier-error").
 		Flag("--force-determinism").
-		FlagWithArg("--no-inline-from=", "core-oj.jar")
+		FlagWithArg("--no-inline-from=", "core-oj.jar").
+		Text("$(cat").Input(globalSoong.UffdGcFlag).Text(")")
 
 	var preoptFlags []string
 	if len(module.PreoptFlags) > 0 {
@@ -506,10 +507,6 @@
 		cmd.FlagWithInput("--profile-file=", profile)
 	}
 
-	if global.EnableUffdGc {
-		cmd.Flag("--runtime-arg").Flag("-Xgc:CMC")
-	}
-
 	rule.Install(odexPath, odexInstallPath)
 	rule.Install(vdexPath, vdexInstallPath)
 }
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 230fbb4..7071f3e 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -96,7 +96,7 @@
 func TestDexPreopt(t *testing.T) {
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
-	globalSoong := globalSoongConfigForTests()
+	globalSoong := globalSoongConfigForTests(ctx)
 	global := GlobalConfigForTests(ctx)
 	module := testSystemModuleConfig(ctx, "test")
 	productPackages := android.PathForTesting("product_packages.txt")
@@ -114,12 +114,15 @@
 	if rule.Installs().String() != wantInstalls.String() {
 		t.Errorf("\nwant installs:\n   %v\ngot:\n   %v", wantInstalls, rule.Installs())
 	}
+
+	android.AssertStringListContains(t, "", rule.Inputs().RelativeToTop().Strings(),
+		"out/soong/dexpreopt_test/uffd_gc_flag.txt")
 }
 
 func TestDexPreoptSystemOther(t *testing.T) {
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
-	globalSoong := globalSoongConfigForTests()
+	globalSoong := globalSoongConfigForTests(ctx)
 	global := GlobalConfigForTests(ctx)
 	systemModule := testSystemModuleConfig(ctx, "Stest")
 	systemProductModule := testSystemProductModuleConfig(ctx, "SPtest")
@@ -180,7 +183,7 @@
 func TestDexPreoptApexSystemServerJars(t *testing.T) {
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
-	globalSoong := globalSoongConfigForTests()
+	globalSoong := globalSoongConfigForTests(ctx)
 	global := GlobalConfigForTests(ctx)
 	module := testApexModuleConfig(ctx, "service-A", "com.android.apex1")
 	productPackages := android.PathForTesting("product_packages.txt")
@@ -204,7 +207,7 @@
 func TestDexPreoptStandaloneSystemServerJars(t *testing.T) {
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
-	globalSoong := globalSoongConfigForTests()
+	globalSoong := globalSoongConfigForTests(ctx)
 	global := GlobalConfigForTests(ctx)
 	module := testPlatformSystemServerModuleConfig(ctx, "service-A")
 	productPackages := android.PathForTesting("product_packages.txt")
@@ -228,7 +231,7 @@
 func TestDexPreoptSystemExtSystemServerJars(t *testing.T) {
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
-	globalSoong := globalSoongConfigForTests()
+	globalSoong := globalSoongConfigForTests(ctx)
 	global := GlobalConfigForTests(ctx)
 	module := testSystemExtSystemServerModuleConfig(ctx, "service-A")
 	productPackages := android.PathForTesting("product_packages.txt")
@@ -252,7 +255,7 @@
 func TestDexPreoptApexStandaloneSystemServerJars(t *testing.T) {
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
-	globalSoong := globalSoongConfigForTests()
+	globalSoong := globalSoongConfigForTests(ctx)
 	global := GlobalConfigForTests(ctx)
 	module := testApexModuleConfig(ctx, "service-A", "com.android.apex1")
 	productPackages := android.PathForTesting("product_packages.txt")
@@ -276,7 +279,7 @@
 func TestDexPreoptProfile(t *testing.T) {
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
-	globalSoong := globalSoongConfigForTests()
+	globalSoong := globalSoongConfigForTests(ctx)
 	global := GlobalConfigForTests(ctx)
 	module := testSystemModuleConfig(ctx, "test")
 	productPackages := android.PathForTesting("product_packages.txt")
@@ -316,3 +319,55 @@
 	after := fmt.Sprintf("%v", parsed)
 	android.AssertStringEquals(t, "The result must be the same as the original after marshalling and unmarshalling it.", before, after)
 }
+
+func TestUffdGcFlagForce(t *testing.T) {
+	for _, enableUffdGc := range []string{"true", "false"} {
+		t.Run(enableUffdGc, func(t *testing.T) {
+			preparers := android.GroupFixturePreparers(
+				PrepareForTestWithFakeDex2oatd,
+				PrepareForTestWithDexpreoptConfig,
+				FixtureSetEnableUffdGc(enableUffdGc),
+			)
+
+			result := preparers.RunTest(t)
+			ctx := result.TestContext
+
+			ctx.SingletonForTests("dexpreopt-soong-config").Output("out/soong/dexpreopt/uffd_gc_flag.txt")
+		})
+	}
+}
+
+func TestUffdGcFlagDefault(t *testing.T) {
+	preparers := android.GroupFixturePreparers(
+		PrepareForTestWithFakeDex2oatd,
+		PrepareForTestWithDexpreoptConfig,
+		FixtureSetEnableUffdGc("default"),
+	)
+
+	result := preparers.RunTest(t)
+	ctx := result.TestContext
+	config := ctx.Config()
+
+	rule := ctx.SingletonForTests("dexpreopt-soong-config").Rule("dexpreopt_uffd_gc_flag")
+
+	android.AssertStringDoesContain(t, "", rule.RuleParams.Command, "construct_uffd_gc_flag")
+	android.AssertStringPathsRelativeToTopEquals(t, "", config, []string{
+		"out/soong/dexpreopt/uffd_gc_flag.txt",
+	}, rule.AllOutputs())
+	android.AssertPathsRelativeToTopEquals(t, "", []string{
+		"out/soong/dexpreopt/kernel_version_for_uffd_gc.txt",
+	}, rule.Implicits)
+}
+
+func TestUffdGcFlagBogus(t *testing.T) {
+	preparers := android.GroupFixturePreparers(
+		PrepareForTestWithFakeDex2oatd,
+		PrepareForTestWithDexpreoptConfig,
+		FixtureSetEnableUffdGc("bogus"),
+	)
+
+	preparers.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+			"Unknown value of PRODUCT_ENABLE_UFFD_GC: bogus")).
+		RunTest(t)
+}
diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go
index 147a562..b1fbef5 100644
--- a/dexpreopt/testing.go
+++ b/dexpreopt/testing.go
@@ -88,6 +88,15 @@
 	FixtureModifyGlobalConfig(func(android.PathContext, *GlobalConfig) {}),
 )
 
+var PrepareForTestWithDexpreoptConfig = android.GroupFixturePreparers(
+	android.PrepareForTestWithAndroidBuildComponents,
+	android.FixtureModifyContext(func(ctx *android.TestContext) {
+		ctx.RegisterParallelSingletonType("dexpreopt-soong-config", func() android.Singleton {
+			return &globalSoongConfigSingleton{}
+		})
+	}),
+)
+
 // FixtureModifyGlobalConfig enables dexpreopt (unless modified by the mutator) and modifies the
 // configuration.
 func FixtureModifyGlobalConfig(configModifier func(ctx android.PathContext, dexpreoptConfig *GlobalConfig)) android.FixturePreparer {
@@ -195,3 +204,10 @@
 		dexpreoptConfig.DisablePreopt = disable
 	})
 }
+
+// FixtureSetEnableUffdGc sets the EnableUffdGc property in the global config.
+func FixtureSetEnableUffdGc(value string) android.FixturePreparer {
+	return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
+		dexpreoptConfig.EnableUffdGc = value
+	})
+}
diff --git a/docs/best_practices.md b/docs/best_practices.md
index bc760b8..48ed996 100644
--- a/docs/best_practices.md
+++ b/docs/best_practices.md
@@ -285,6 +285,6 @@
 and will require ongoing maintenance as the build system is changed; so
 plugins should be used only when absolutely required.
 
-See [art/build/art.go](https://android.googlesource.com/platform/art/+/master/build/art.go)
-or [external/llvm/soong/llvm.go](https://android.googlesource.com/platform/external/llvm/+/master/soong/llvm.go)
+See [art/build/art.go](https://android.googlesource.com/platform/art/+/main/build/art.go)
+or [external/llvm/soong/llvm.go](https://android.googlesource.com/platform/external/llvm/+/main/soong/llvm.go)
 for examples of more complex conditionals on product variables or environment variables.
diff --git a/docs/clion.md b/docs/clion.md
index 110891b..8e2c597 100644
--- a/docs/clion.md
+++ b/docs/clion.md
@@ -4,7 +4,7 @@
 only. Build should still be done via make/m/mm(a)/mmm(a).
 
 Note: alternatively, you can use
-[aidegen to generate a Clion or VSCode project](https://android.googlesource.com/platform/tools/asuite/+/refs/heads/master/aidegen/README.md)
+[aidegen to generate a Clion or VSCode project](https://android.googlesource.com/platform/tools/asuite/+/refs/heads/main/aidegen/README.md)
 with a single command, using the `-i c` flag.
 
 CMakeLists.txt project file generation is enabled via environment variable:
diff --git a/docs/map_files.md b/docs/map_files.md
index 35e8cbb..e1ddefc 100644
--- a/docs/map_files.md
+++ b/docs/map_files.md
@@ -5,8 +5,8 @@
 semantically meaningful to [gen_stub_libs.py]. For an example of a map file, see
 [libc.map.txt].
 
-[gen_stub_libs.py]: https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/gen_stub_libs.py
-[libc.map.txt]: https://cs.android.com/android/platform/superproject/+/master:bionic/libc/libc.map.txt
+[gen_stub_libs.py]: https://cs.android.com/android/platform/superproject/+/main:build/soong/cc/gen_stub_libs.py
+[libc.map.txt]: https://cs.android.com/android/platform/superproject/+/main:bionic/libc/libc.map.txt
 [linker version scripts]: https://www.gnu.org/software/gnulib/manual/html_node/LD-Version-Scripts.html
 
 ## Basic format
@@ -134,6 +134,9 @@
 
 Historically this annotation was spelled `vndk`, but it has always meant LL-NDK.
 
+When an llndk API is deprecated, the `llndk` tag is dropped and
+`llndk-deprecate=<V>` is added.
+
 ### platform-only
 
 Indicates that the version or symbol is public in the implementation library but
diff --git a/docs/rbe.md b/docs/rbe.md
index cfe86d7..be60c83 100644
--- a/docs/rbe.md
+++ b/docs/rbe.md
@@ -11,7 +11,7 @@
 
 To enable RBE, you need to set several environment variables before triggering
 the build. You can set them through a
-[environment variables config file](https://android.googlesource.com/platform/build/soong/+/master/README.md#environment-variables-config-file).
+[environment variables config file](https://android.googlesource.com/platform/build/soong/+/main/README.md#environment-variables-config-file).
 As an example, [build/soong/docs/rbe.json](rbe.json) is a config that enables
 RBE in the build. Once the config file is created, you need to let Soong load
 the config file by specifying `ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR` environment
diff --git a/docs/tidy.md b/docs/tidy.md
index 2eb8234..ae0ca93 100644
--- a/docs/tidy.md
+++ b/docs/tidy.md
@@ -24,7 +24,7 @@
 ```
 
 The default global clang-tidy checks and flags are defined in
-[build/soong/cc/config/tidy.go](https://android.googlesource.com/platform/build/soong/+/refs/heads/master/cc/config/tidy.go).
+[build/soong/cc/config/tidy.go](https://android.googlesource.com/platform/build/soong/+/refs/heads/main/cc/config/tidy.go).
 
 
 ## Module clang-tidy properties
@@ -34,7 +34,7 @@
 ### `tidy`, `tidy_checks`, and `ALLOW_LOCAL_TIDY_TRUE`
 
 For example, in
-[system/bpf/Android.bp](https://android.googlesource.com/platform/system/bpf/+/refs/heads/master/Android.bp),
+[system/bpf/Android.bp](https://android.googlesource.com/platform/system/bpf/+/refs/heads/main/Android.bp),
 clang-tidy is enabled explicitly and with a different check list:
 ```
 cc_defaults {
@@ -69,7 +69,7 @@
 Some modules might want to disable clang-tidy even when
 environment variable `WITH_TIDY=1` is set.
 Examples can be found in
-[system/netd/tests/Android.bp](https://android.googlesource.com/platform/system/netd/+/refs/heads/master/tests/Android.bp)
+[system/netd/tests/Android.bp](https://android.googlesource.com/platform/system/netd/+/refs/heads/main/tests/Android.bp)
 ```
 cc_test {
     name: "netd_integration_test",
@@ -78,7 +78,7 @@
     tidy: false,  // cuts test build time by almost 1 minute
 ```
 and in
-[bionic/tests/Android.bp](https://android.googlesource.com/platform/bionic/+/refs/heads/master/tests/Android.bp).
+[bionic/tests/Android.bp](https://android.googlesource.com/platform/bionic/+/refs/heads/main/tests/Android.bp).
 ```
 cc_test_library {
     name: "fortify_disabled_for_tidy",
@@ -97,7 +97,7 @@
 If a C/C++ module wants to be free of certain clang-tidy warnings,
 it can chose those checks to be treated as errors.
 For example
-[system/core/libsysutils/Android.bp](https://android.googlesource.com/platform/system/core/+/refs/heads/master/libsysutils/Android.bp)
+[system/core/libsysutils/Android.bp](https://android.googlesource.com/platform/system/core/+/refs/heads/main/libsysutils/Android.bp)
 has enabled clang-tidy explicitly, selected its own tidy checks,
 and set three groups of tidy checks as errors:
 ```
@@ -130,7 +130,7 @@
 
 Some other tidy flags examples are `-format-style=` and `-header-filter=`
 For example, in
-[art/odrefresh/Android.bp](https://android.googlesource.com/platform/art/+/refs/heads/master/odrefresh/Android.bp),
+[art/odrefresh/Android.bp](https://android.googlesource.com/platform/art/+/refs/heads/main/odrefresh/Android.bp),
 we found
 ```
 cc_defaults {
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 66df101..7642378 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -155,6 +155,9 @@
 	additionalDependencies *android.Paths
 
 	makeClass string
+
+	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
+	mergedAconfigFiles map[string]android.Paths
 }
 
 type Defaults struct {
@@ -365,6 +368,7 @@
 		symlinks:       p.properties.Symlinks,
 	}
 	p.addInstallRules(ctx, ip)
+	android.CollectDependencyAconfigFiles(ctx, &p.mergedAconfigFiles)
 }
 
 type installProperties struct {
@@ -433,11 +437,16 @@
 				if p.additionalDependencies != nil {
 					entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES", p.additionalDependencies.Strings()...)
 				}
+				android.SetAconfigFileMkEntries(p.AndroidModuleBase(), entries, p.mergedAconfigFiles)
 			},
 		},
 	}}
 }
 
+func (p *PrebuiltEtc) AndroidModuleBase() *android.ModuleBase {
+	return &p.ModuleBase
+}
+
 func InitPrebuiltEtcModule(p *PrebuiltEtc, dirBase string) {
 	p.installDirBase = dirBase
 	p.AddProperties(&p.properties)
diff --git a/filesystem/Android.bp b/filesystem/Android.bp
index 07d57c9..cdf6682 100644
--- a/filesystem/Android.bp
+++ b/filesystem/Android.bp
@@ -9,6 +9,7 @@
         "blueprint",
         "soong",
         "soong-android",
+        "soong-bpf", // for testing
         "soong-linkerconfig",
     ],
     srcs: [
diff --git a/filesystem/avb_add_hash_footer.go b/filesystem/avb_add_hash_footer.go
index ead579f..469f1fb 100644
--- a/filesystem/avb_add_hash_footer.go
+++ b/filesystem/avb_add_hash_footer.go
@@ -138,7 +138,7 @@
 		if rollbackIndex < 0 {
 			ctx.PropertyErrorf("rollback_index", "Rollback index must be non-negative")
 		}
-		cmd.Flag(fmt.Sprintf(" --rollback_index %x", rollbackIndex))
+		cmd.Flag(fmt.Sprintf(" --rollback_index %d", rollbackIndex))
 	}
 
 	cmd.FlagWithOutput("--image ", a.output)
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 7b207d6..6612a6f 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -50,8 +50,8 @@
 	// Function that builds extra files under the root directory and returns the files
 	buildExtraFiles func(ctx android.ModuleContext, root android.OutputPath) android.OutputPaths
 
-	// Function that filters PackagingSpecs returned by PackagingBase.GatherPackagingSpecs()
-	filterPackagingSpecs func(specs map[string]android.PackagingSpec)
+	// Function that filters PackagingSpec in PackagingBase.GatherPackagingSpecs()
+	filterPackagingSpec func(spec android.PackagingSpec) bool
 
 	output     android.OutputPath
 	installDir android.InstallPath
@@ -493,10 +493,7 @@
 // Note that "apex" module installs its contents to "apex"(fake partition) as well
 // for symbol lookup by imitating "activated" paths.
 func (f *filesystem) gatherFilteredPackagingSpecs(ctx android.ModuleContext) map[string]android.PackagingSpec {
-	specs := f.PackagingBase.GatherPackagingSpecs(ctx)
-	if f.filterPackagingSpecs != nil {
-		f.filterPackagingSpecs(specs)
-	}
+	specs := f.PackagingBase.GatherPackagingSpecsWithFilter(ctx, f.filterPackagingSpec)
 	return specs
 }
 
@@ -512,6 +509,6 @@
 
 var _ cc.UseCoverage = (*filesystem)(nil)
 
-func (*filesystem) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
+func (*filesystem) IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool {
 	return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
 }
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index aef4756..c448105 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -19,6 +19,7 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/bpf"
 	"android/soong/cc"
 	"android/soong/etc"
 
@@ -31,6 +32,7 @@
 
 var fixture = android.GroupFixturePreparers(
 	android.PrepareForIntegrationTestWithAndroid,
+	bpf.PrepareForTestWithBpf,
 	etc.PrepareForTestWithPrebuiltEtc,
 	cc.PrepareForIntegrationTestWithCc,
 	PrepareForTestWithFilesystemBuildComponents,
@@ -40,11 +42,55 @@
 	result := fixture.RunTestWithBp(t, `
 		android_filesystem {
 			name: "myfilesystem",
+			multilib: {
+				common: {
+					deps: [
+						"bpf.o",
+					],
+				},
+				lib32: {
+					deps: [
+						"foo",
+						"libbar",
+					],
+				},
+				lib64: {
+					deps: [
+						"libbar",
+					],
+				},
+			},
+			compile_multilib: "both",
+		}
+
+		bpf {
+			name: "bpf.o",
+			srcs: ["bpf.c"],
+		}
+
+		cc_binary {
+			name: "foo",
+			compile_multilib: "prefer32",
+		}
+
+		cc_library {
+			name: "libbar",
 		}
 	`)
 
 	// produces "myfilesystem.img"
 	result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img")
+
+	fs := result.ModuleForTests("myfilesystem", "android_common").Module().(*filesystem)
+	expected := []string{
+		"bin/foo",
+		"lib/libbar.so",
+		"lib64/libbar.so",
+		"etc/bpf/bpf.o",
+	}
+	for _, e := range expected {
+		android.AssertStringListContains(t, "missing entry", fs.entries, e)
+	}
 }
 
 func TestFileSystemFillsLinkerConfigWithStubLibs(t *testing.T) {
diff --git a/filesystem/system_image.go b/filesystem/system_image.go
index 75abf70..34f4ffb 100644
--- a/filesystem/system_image.go
+++ b/filesystem/system_image.go
@@ -37,7 +37,7 @@
 	module := &systemImage{}
 	module.AddProperties(&module.properties)
 	module.filesystem.buildExtraFiles = module.buildExtraFiles
-	module.filesystem.filterPackagingSpecs = module.filterPackagingSpecs
+	module.filesystem.filterPackagingSpec = module.filterPackagingSpec
 	initFilesystemModule(&module.filesystem)
 	return module
 }
@@ -73,10 +73,6 @@
 // Filter the result of GatherPackagingSpecs to discard items targeting outside "system" partition.
 // Note that "apex" module installs its contents to "apex"(fake partition) as well
 // for symbol lookup by imitating "activated" paths.
-func (s *systemImage) filterPackagingSpecs(specs map[string]android.PackagingSpec) {
-	for k, ps := range specs {
-		if ps.Partition() != "system" {
-			delete(specs, k)
-		}
-	}
+func (s *systemImage) filterPackagingSpec(ps android.PackagingSpec) bool {
+	return ps.Partition() == "system"
 }
diff --git a/finder/finder_test.go b/finder/finder_test.go
index 8f73719..be22d13 100644
--- a/finder/finder_test.go
+++ b/finder/finder_test.go
@@ -813,6 +813,7 @@
 			IncludeFiles: []string{"findme.txt"},
 		},
 	)
+	finder.WaitForDbDump()
 	filesystem.Clock.Tick()
 	foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
 	finder.Shutdown()
@@ -1445,6 +1446,7 @@
 			IncludeFiles: []string{"hi.txt"},
 		},
 	)
+	finder.WaitForDbDump()
 	filesystem.Clock.Tick()
 	foundPaths := finder.FindAll()
 	finder.Shutdown()
@@ -1506,6 +1508,7 @@
 			IncludeFiles: []string{"hi.txt"},
 		},
 	)
+	finder.WaitForDbDump()
 	filesystem.Clock.Tick()
 	foundPaths := finder.FindAll()
 	finder.Shutdown()
@@ -1552,6 +1555,7 @@
 			IncludeFiles: []string{"hi.txt"},
 		},
 	)
+	finder.WaitForDbDump()
 	filesystem.Clock.Tick()
 	foundPaths := finder.FindAll()
 	finder.Shutdown()
@@ -1573,6 +1577,7 @@
 			IncludeFiles: []string{"hi.txt"},
 		},
 	)
+	finder.WaitForDbDump()
 	filesystem.Clock.Tick()
 	foundPaths := finder.FindAll()
 	finder.Shutdown()
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index 5d7bc2b..47fd8f4 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -525,12 +525,10 @@
 	builder.Build("create-"+fuzzZip.String(),
 		"Package "+module.Name()+" for "+archString+"-"+hostOrTargetString)
 
-	// Don't add modules to 'make haiku-rust' that are set to not be
-	// exported to the fuzzing infrastructure.
 	if config := fuzzModule.FuzzProperties.Fuzz_config; config != nil {
 		if strings.Contains(hostOrTargetString, "host") && !BoolDefault(config.Fuzz_on_haiku_host, true) {
 			return archDirs[archOs], false
-		} else if !BoolDefault(config.Fuzz_on_haiku_device, true) {
+		} else if !strings.Contains(hostOrTargetString, "host") && !BoolDefault(config.Fuzz_on_haiku_device, true) {
 			return archDirs[archOs], false
 		}
 	}
diff --git a/genrule/allowlists.go b/genrule/allowlists.go
index 8abf73e..60b1366 100644
--- a/genrule/allowlists.go
+++ b/genrule/allowlists.go
@@ -15,18 +15,10 @@
 package genrule
 
 var (
-	DepfileAllowList = []string{
-		// go/keep-sorted start
-		"depfile_allowed_for_test",
-		// go/keep-sorted end
-	}
-
 	SandboxingDenyModuleList = []string{
 		// go/keep-sorted start
-		"CtsApkVerityTestDebugFiles",
 		"aidl_camera_build_version",
-		"camera-its",
-		"chre_atoms_log.h",
+		"com.google.pixel.camera.hal.manifest",
 		// go/keep-sorted end
 	}
 
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 0512c15..6f66088 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -124,14 +124,10 @@
 	//  $(locations <label>): the paths to the tools, tool_files, inputs or outputs with name <label>. Use $(locations) if <label> refers to a rule that outputs two or more files.
 	//  $(in): one or more input files.
 	//  $(out): a single output file.
-	//  $(depfile): a file to which dependencies will be written, if the depfile property is set to true.
 	//  $(genDir): the sandbox directory for this tool; contains $(out).
 	//  $$: a literal $
 	Cmd *string
 
-	// Enable reading a file containing dependencies in gcc format after the command completes
-	Depfile *bool
-
 	// name of the modules (if any) that produces the host executable.   Leave empty for
 	// prebuilts or scripts that do not need a module to build them.
 	Tools []string
@@ -184,6 +180,9 @@
 
 	subName string
 	subDir  string
+
+	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
+	mergedAconfigFiles map[string]android.Paths
 }
 
 type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask
@@ -191,10 +190,8 @@
 type generateTask struct {
 	in          android.Paths
 	out         android.WritablePaths
-	depFile     android.WritablePath
 	copyTo      android.WritablePaths // For gensrcs to set on gensrcsMerge rule.
 	genDir      android.WritablePath
-	extraTools  android.Paths // dependencies on tools used by the generator
 	extraInputs map[string][]string
 
 	cmd string
@@ -393,7 +390,7 @@
 		return srcFiles
 	}
 	srcFiles := addLabelsForInputs("srcs", g.properties.Srcs, g.properties.Exclude_srcs)
-	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcFiles.Strings()})
+	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcFiles.Strings()})
 
 	var copyFrom android.Paths
 	var outputFiles android.WritablePaths
@@ -445,8 +442,6 @@
 			addLocationLabel(out.Rel(), outputLocation{out})
 		}
 
-		referencedDepfile := false
-
 		rawCommand, err := android.Expand(task.cmd, func(name string) (string, error) {
 			// report the error directly without returning an error to android.Expand to catch multiple errors in a
 			// single run
@@ -478,12 +473,6 @@
 					sandboxOuts = append(sandboxOuts, cmd.PathForOutput(out))
 				}
 				return strings.Join(proptools.ShellEscapeList(sandboxOuts), " "), nil
-			case "depfile":
-				referencedDepfile = true
-				if !Bool(g.properties.Depfile) {
-					return reportError("$(depfile) used without depfile property")
-				}
-				return "__SBOX_DEPFILE__", nil
 			case "genDir":
 				return proptools.ShellEscape(cmd.PathForOutput(task.genDir)), nil
 			default:
@@ -523,10 +512,6 @@
 			return
 		}
 
-		if Bool(g.properties.Depfile) && !referencedDepfile {
-			ctx.PropertyErrorf("cmd", "specified depfile=true but did not include a reference to '${depfile}' in cmd")
-			return
-		}
 		g.rawCommands = append(g.rawCommands, rawCommand)
 
 		cmd.Text(rawCommand)
@@ -535,11 +520,7 @@
 		cmd.ImplicitOutputs(task.out)
 		cmd.Implicits(task.in)
 		cmd.ImplicitTools(tools)
-		cmd.ImplicitTools(task.extraTools)
 		cmd.ImplicitPackagedTools(packagedTools)
-		if Bool(g.properties.Depfile) {
-			cmd.ImplicitDepFile(task.depFile)
-		}
 
 		// Create the rule to run the genrule command inside sbox.
 		rule.Build(name, desc)
@@ -580,19 +561,6 @@
 }
 
 func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// Allowlist genrule to use depfile until we have a solution to remove it.
-	// TODO(b/307824623): Remove depfile property
-	if Bool(g.properties.Depfile) {
-		sandboxingAllowlistSets := getSandboxingAllowlistSets(ctx)
-		if ctx.DeviceConfig().GenruleSandboxing() && !sandboxingAllowlistSets.depfileAllowSet[g.Name()] {
-			ctx.PropertyErrorf(
-				"depfile",
-				"Deprecated because with genrule sandboxing, dependencies must be known before the action is run "+
-					"in order to add them to the sandbox. "+
-					"Please specify the dependencies explicitly so that there is no need to use depfile.")
-		}
-	}
-
 	g.generateCommonBuildActions(ctx)
 
 	// For <= 6 outputs, just embed those directly in the users. Right now, that covers >90% of
@@ -610,6 +578,24 @@
 		})
 		g.outputDeps = android.Paths{phonyFile}
 	}
+	android.CollectDependencyAconfigFiles(ctx, &g.mergedAconfigFiles)
+}
+
+func (g *Module) AndroidMkEntries() []android.AndroidMkEntries {
+	ret := android.AndroidMkEntries{
+		OutputFile: android.OptionalPathForPath(g.outputFiles[0]),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				android.SetAconfigFileMkEntries(g.AndroidModuleBase(), entries, g.mergedAconfigFiles)
+			},
+		},
+	}
+
+	return []android.AndroidMkEntries{ret}
+}
+
+func (g *Module) AndroidModuleBase() *android.ModuleBase {
+	return &g.ModuleBase
 }
 
 // Collect information for opening IDE project files in java/jdeps.go.
@@ -700,7 +686,6 @@
 		for i, shard := range shards {
 			var commands []string
 			var outFiles android.WritablePaths
-			var commandDepFiles []string
 			var copyTo android.WritablePaths
 
 			// When sharding is enabled (i.e. len(shards) > 1), the sbox rules for each
@@ -740,12 +725,6 @@
 						return in.String(), nil
 					case "out":
 						return rule.Command().PathForOutput(outFile), nil
-					case "depfile":
-						// Generate a depfile for each output file.  Store the list for
-						// later in order to combine them all into a single depfile.
-						depFile := rule.Command().PathForOutput(outFile.ReplaceExtension(ctx, "d"))
-						commandDepFiles = append(commandDepFiles, depFile)
-						return depFile, nil
 					default:
 						return "$(" + name + ")", nil
 					}
@@ -760,30 +739,14 @@
 			}
 			fullCommand := strings.Join(commands, " && ")
 
-			var outputDepfile android.WritablePath
-			var extraTools android.Paths
-			if len(commandDepFiles) > 0 {
-				// Each command wrote to a depfile, but ninja can only handle one
-				// depfile per rule.  Use the dep_fixer tool at the end of the
-				// command to combine all the depfiles into a single output depfile.
-				outputDepfile = android.PathForModuleGen(ctx, genSubDir, "gensrcs.d")
-				depFixerTool := ctx.Config().HostToolPath(ctx, "dep_fixer")
-				fullCommand += fmt.Sprintf(" && %s -o $(depfile) %s",
-					rule.Command().PathForTool(depFixerTool),
-					strings.Join(commandDepFiles, " "))
-				extraTools = append(extraTools, depFixerTool)
-			}
-
 			generateTasks = append(generateTasks, generateTask{
-				in:         shard,
-				out:        outFiles,
-				depFile:    outputDepfile,
-				copyTo:     copyTo,
-				genDir:     genDir,
-				cmd:        fullCommand,
-				shard:      i,
-				shards:     len(shards),
-				extraTools: extraTools,
+				in:     shard,
+				out:    outFiles,
+				copyTo: copyTo,
+				genDir: genDir,
+				cmd:    fullCommand,
+				shard:  i,
+				shards: len(shards),
 				extraInputs: map[string][]string{
 					"data": properties.Data,
 				},
@@ -822,20 +785,14 @@
 
 	taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask {
 		outs := make(android.WritablePaths, len(properties.Out))
-		var depFile android.WritablePath
 		for i, out := range properties.Out {
-			outPath := android.PathForModuleGen(ctx, out)
-			if i == 0 {
-				depFile = outPath.ReplaceExtension(ctx, "d")
-			}
-			outs[i] = outPath
+			outs[i] = android.PathForModuleGen(ctx, out)
 		}
 		return []generateTask{{
-			in:      srcFiles,
-			out:     outs,
-			depFile: depFile,
-			genDir:  android.PathForModuleGen(ctx),
-			cmd:     rawCommand,
+			in:     srcFiles,
+			out:    outs,
+			genDir: android.PathForModuleGen(ctx),
+			cmd:    rawCommand,
 		}}
 	}
 
@@ -886,22 +843,18 @@
 type sandboxingAllowlistSets struct {
 	sandboxingDenyModuleSet map[string]bool
 	sandboxingDenyPathSet   map[string]bool
-	depfileAllowSet         map[string]bool
 }
 
 func getSandboxingAllowlistSets(ctx android.PathContext) *sandboxingAllowlistSets {
 	return ctx.Config().Once(sandboxingAllowlistKey, func() interface{} {
 		sandboxingDenyModuleSet := map[string]bool{}
 		sandboxingDenyPathSet := map[string]bool{}
-		depfileAllowSet := map[string]bool{}
 
-		android.AddToStringSet(sandboxingDenyModuleSet, append(DepfileAllowList, SandboxingDenyModuleList...))
+		android.AddToStringSet(sandboxingDenyModuleSet, SandboxingDenyModuleList)
 		android.AddToStringSet(sandboxingDenyPathSet, SandboxingDenyPathList)
-		android.AddToStringSet(depfileAllowSet, DepfileAllowList)
 		return &sandboxingAllowlistSets{
 			sandboxingDenyModuleSet: sandboxingDenyModuleSet,
 			sandboxingDenyPathSet:   sandboxingDenyPathSet,
-			depfileAllowSet:         depfileAllowSet,
 		}
 	}).(*sandboxingAllowlistSets)
 }
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index fa0ee17..2dc6a79 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -287,16 +287,6 @@
 			expect: "echo foo > __SBOX_SANDBOX_DIR__/out/out2",
 		},
 		{
-			name:       "depfile",
-			moduleName: "depfile_allowed_for_test",
-			prop: `
-				out: ["out"],
-				depfile: true,
-				cmd: "echo foo > $(out) && touch $(depfile)",
-			`,
-			expect: "echo foo > __SBOX_SANDBOX_DIR__/out/out && touch __SBOX_DEPFILE__",
-		},
-		{
 			name: "gendir",
 			prop: `
 				out: ["out"],
@@ -392,24 +382,6 @@
 			err: `unknown variable '$(foo)'`,
 		},
 		{
-			name: "error depfile",
-			prop: `
-				out: ["out"],
-				cmd: "echo foo > $(out) && touch $(depfile)",
-			`,
-			err: "$(depfile) used without depfile property",
-		},
-		{
-			name:       "error no depfile",
-			moduleName: "depfile_allowed_for_test",
-			prop: `
-				out: ["out"],
-				depfile: true,
-				cmd: "echo foo > $(out)",
-			`,
-			err: "specified depfile=true but did not include a reference to '${depfile}' in cmd",
-		},
-		{
 			name: "error no out",
 			prop: `
 				cmd: "echo foo > $(out)",
@@ -695,60 +667,6 @@
 	}
 }
 
-func TestGenruleAllowlistingDepfile(t *testing.T) {
-	tests := []struct {
-		name       string
-		prop       string
-		err        string
-		moduleName string
-	}{
-		{
-			name: `error when module is not allowlisted`,
-			prop: `
-				depfile: true,
-				cmd: "cat $(in) > $(out) && cat $(depfile)",
-			`,
-			err: "depfile: Deprecated because with genrule sandboxing, dependencies must be known before the action is run in order to add them to the sandbox",
-		},
-		{
-			name: `no error when module is allowlisted`,
-			prop: `
-				depfile: true,
-				cmd: "cat $(in) > $(out) && cat $(depfile)",
-			`,
-			moduleName: `depfile_allowed_for_test`,
-		},
-	}
-	for _, test := range tests {
-		t.Run(test.name, func(t *testing.T) {
-			moduleName := "foo"
-			if test.moduleName != "" {
-				moduleName = test.moduleName
-			}
-			bp := fmt.Sprintf(`
-			gensrcs {
-			   name: "%s",
-			   srcs: ["data.txt"],
-			   %s
-			}`, moduleName, test.prop)
-
-			var expectedErrors []string
-			if test.err != "" {
-				expectedErrors = append(expectedErrors, test.err)
-			}
-			android.GroupFixturePreparers(
-				prepareForGenRuleTest,
-				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-					variables.GenruleSandboxing = proptools.BoolPtr(true)
-				}),
-			).
-				ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
-				RunTestWithBp(t, bp)
-		})
-
-	}
-}
-
 func TestGenruleDefaults(t *testing.T) {
 	bp := `
 				genrule_defaults {
diff --git a/java/Android.bp b/java/Android.bp
index 14c1cc1..54b36ab 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -110,8 +110,9 @@
         "proto_test.go",
         "ravenwood_test.go",
         "rro_test.go",
-        "sdk_test.go",
         "sdk_library_test.go",
+        "sdk_test.go",
+        "sdk_version_test.go",
         "system_modules_test.go",
         "systemserver_classpath_fragment_test.go",
         "test_spec_test.go",
diff --git a/java/aar.go b/java/aar.go
index 662ebce..b3239ba 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -23,6 +23,7 @@
 
 	"android/soong/android"
 	"android/soong/dexpreopt"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -44,7 +45,7 @@
 	ctx.RegisterModuleType("android_library_import", AARImportFactory)
 	ctx.RegisterModuleType("android_library", AndroidLibraryFactory)
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator).Parallel()
+		ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator)
 	})
 }
 
@@ -159,8 +160,8 @@
 	}
 }
 
-func (a *aapt) useResourceProcessorBusyBox() bool {
-	return BoolDefault(a.aaptProperties.Use_resource_processor, false)
+func (a *aapt) useResourceProcessorBusyBox(ctx android.BaseModuleContext) bool {
+	return BoolDefault(a.aaptProperties.Use_resource_processor, ctx.Config().UseResourceProcessorByDefault())
 }
 
 func (a *aapt) filterProduct() string {
@@ -416,17 +417,11 @@
 		linkFlags = append(linkFlags, "--static-lib")
 	}
 
-	if a.isLibrary && a.useResourceProcessorBusyBox() {
-		// When building an android_library using ResourceProcessorBusyBox the resources are merged into
-		// package-res.apk with --merge-only, but --no-static-lib-packages is not used so that R.txt only
-		// contains resources from this library.
+	linkFlags = append(linkFlags, "--no-static-lib-packages")
+	if a.isLibrary && a.useResourceProcessorBusyBox(ctx) {
+		// When building an android_library using ResourceProcessorBusyBox pass --merge-only to skip resource
+		// references validation until the final app link step when all static libraries are present.
 		linkFlags = append(linkFlags, "--merge-only")
-	} else {
-		// When building and app or when building an android_library without ResourceProcessorBusyBox
-		// --no-static-lib-packages is used to put all the resources into the app.  If ResourceProcessorBusyBox
-		// is used then the app's R.txt will be post-processed along with the R.txt files from dependencies to
-		// sort resources into the right packages in R.class.
-		linkFlags = append(linkFlags, "--no-static-lib-packages")
 	}
 
 	packageRes := android.PathForModuleOut(ctx, "package-res.apk")
@@ -455,7 +450,7 @@
 	// of transitiveStaticLibs.
 	transitiveStaticLibs := android.ReversePaths(staticDeps.resPackages())
 
-	if a.isLibrary && a.useResourceProcessorBusyBox() {
+	if a.isLibrary && a.useResourceProcessorBusyBox(ctx) {
 		// When building an android_library with ResourceProcessorBusyBox enabled treat static library dependencies
 		// as imports.  The resources from dependencies will not be merged into this module's package-res.apk, and
 		// instead modules depending on this module will reference package-res.apk from all transitive static
@@ -517,7 +512,7 @@
 		})
 	}
 
-	if !a.useResourceProcessorBusyBox() {
+	if !a.useResourceProcessorBusyBox(ctx) {
 		// the subdir "android" is required to be filtered by package names
 		srcJar = android.PathForModuleGen(ctx, "android", "R.srcjar")
 	}
@@ -544,9 +539,9 @@
 		a.assetPackage = android.OptionalPathForPath(assets)
 	}
 
-	if a.useResourceProcessorBusyBox() {
+	if a.useResourceProcessorBusyBox(ctx) {
 		rJar := android.PathForModuleOut(ctx, "busybox/R.jar")
-		resourceProcessorBusyBoxGenerateBinaryR(ctx, rTxt, a.mergedManifestFile, rJar, staticDeps, a.isLibrary)
+		resourceProcessorBusyBoxGenerateBinaryR(ctx, rTxt, a.mergedManifestFile, rJar, staticDeps, a.isLibrary, a.aaptProperties.Aaptflags)
 		aapt2ExtractExtraPackages(ctx, extraPackages, rJar)
 		transitiveRJars = append(transitiveRJars, rJar)
 		a.rJar = rJar
@@ -579,7 +574,7 @@
 			rJar:                a.rJar,
 			assets:              a.assetPackage,
 
-			usedResourceProcessor: a.useResourceProcessorBusyBox(),
+			usedResourceProcessor: a.useResourceProcessorBusyBox(ctx),
 		}).
 		Transitive(staticResourcesNodesDepSet).Build()
 	a.rroDirsDepSet = android.NewDepSetBuilder[rroDir](android.TOPOLOGICAL).
@@ -606,7 +601,7 @@
 // using Bazel's ResourceProcessorBusyBox tool, which is faster than compiling the R.java files and
 // supports producing classes for static dependencies that only include resources from that dependency.
 func resourceProcessorBusyBoxGenerateBinaryR(ctx android.ModuleContext, rTxt, manifest android.Path,
-	rJar android.WritablePath, transitiveDeps transitiveAarDeps, isLibrary bool) {
+	rJar android.WritablePath, transitiveDeps transitiveAarDeps, isLibrary bool, aaptFlags []string) {
 
 	var args []string
 	var deps android.Paths
@@ -623,6 +618,17 @@
 		args = append(args, "--finalFields=false")
 	}
 
+	for i, arg := range aaptFlags {
+		const AAPT_CUSTOM_PACKAGE = "--custom-package"
+		if strings.HasPrefix(arg, AAPT_CUSTOM_PACKAGE) {
+			pkg := strings.TrimSpace(strings.TrimPrefix(arg, AAPT_CUSTOM_PACKAGE))
+			if pkg == "" && i+1 < len(aaptFlags) {
+				pkg = aaptFlags[i+1]
+			}
+			args = append(args, "--packageForR "+pkg)
+		}
+	}
+
 	deps = append(deps, rTxt, manifest)
 
 	ctx.Build(pctx, android.BuildParams{
@@ -799,6 +805,9 @@
 func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.aapt.isLibrary = true
 	a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
+	if a.usesLibrary.shouldDisableDexpreopt {
+		a.dexpreopter.disableDexpreopt()
+	}
 	a.aapt.buildActions(ctx,
 		aaptBuildActionOptions{
 			sdkContext:                     android.SdkContext(a),
@@ -807,13 +816,14 @@
 		},
 	)
 
-	a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	a.hideApexVariantFromMake = !apexInfo.IsForPlatform()
 
 	a.stem = proptools.StringDefault(a.overridableDeviceProperties.Stem, ctx.ModuleName())
 
 	ctx.CheckbuildFile(a.aapt.proguardOptionsFile)
 	ctx.CheckbuildFile(a.aapt.exportPackage)
-	if a.useResourceProcessorBusyBox() {
+	if a.useResourceProcessorBusyBox(ctx) {
 		ctx.CheckbuildFile(a.aapt.rJar)
 	} else {
 		ctx.CheckbuildFile(a.aapt.aaptSrcJar)
@@ -827,7 +837,7 @@
 	a.linter.resources = a.aapt.resourceFiles
 
 	proguardSpecInfo := a.collectProguardSpecInfo(ctx)
-	ctx.SetProvider(ProguardSpecInfoProvider, proguardSpecInfo)
+	android.SetProvider(ctx, ProguardSpecInfoProvider, proguardSpecInfo)
 	exportedProguardFlagsFiles := proguardSpecInfo.ProguardFlagsFiles.ToList()
 	a.extraProguardFlagsFiles = append(a.extraProguardFlagsFiles, exportedProguardFlagsFiles...)
 	a.extraProguardFlagsFiles = append(a.extraProguardFlagsFiles, a.proguardOptionsFile)
@@ -839,7 +849,7 @@
 	var extraSrcJars android.Paths
 	var extraCombinedJars android.Paths
 	var extraClasspathJars android.Paths
-	if a.useResourceProcessorBusyBox() {
+	if a.useResourceProcessorBusyBox(ctx) {
 		// When building a library with ResourceProcessorBusyBox enabled ResourceProcessorBusyBox for this
 		// library and each of the transitive static android_library dependencies has already created an
 		// R.class file for the appropriate package.  Add all of those R.class files to the classpath.
@@ -862,12 +872,12 @@
 
 	prebuiltJniPackages := android.Paths{}
 	ctx.VisitDirectDeps(func(module android.Module) {
-		if info, ok := ctx.OtherModuleProvider(module, JniPackageProvider).(JniPackageInfo); ok {
+		if info, ok := android.OtherModuleProvider(ctx, module, JniPackageProvider); ok {
 			prebuiltJniPackages = append(prebuiltJniPackages, info.JniPackages...)
 		}
 	})
 	if len(prebuiltJniPackages) > 0 {
-		ctx.SetProvider(JniPackageProvider, JniPackageInfo{
+		android.SetProvider(ctx, JniPackageProvider, JniPackageInfo{
 			JniPackages: prebuiltJniPackages,
 		})
 	}
@@ -879,7 +889,7 @@
 }
 
 func (a *aapt) IDEInfo(dpInfo *android.IdeInfo) {
-	if a.useResourceProcessorBusyBox() {
+	if a.rJar != nil {
 		dpInfo.Jars = append(dpInfo.Jars, a.rJar.String())
 	}
 }
@@ -970,6 +980,9 @@
 
 	sdkVersion    android.SdkSpec
 	minSdkVersion android.ApiLevel
+
+	// Single aconfig "cache file" merged from this module and all dependencies.
+	mergedAconfigFiles map[string]android.Paths
 }
 
 var _ android.OutputFileProducer = (*AARImport)(nil)
@@ -1071,7 +1084,7 @@
 	JniPackages android.Paths
 }
 
-var JniPackageProvider = blueprint.NewProvider(JniPackageInfo{})
+var JniPackageProvider = blueprint.NewProvider[JniPackageInfo]()
 
 // Unzip an AAR and extract the JNI libs for $archString.
 var extractJNI = pctx.AndroidStaticRule("extractJNI",
@@ -1108,7 +1121,8 @@
 	a.sdkVersion = a.SdkVersion(ctx)
 	a.minSdkVersion = a.MinSdkVersion(ctx)
 
-	a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	a.hideApexVariantFromMake = !apexInfo.IsForPlatform()
 
 	aarName := ctx.ModuleName() + ".aar"
 	a.aarPath = android.PathForModuleSrc(ctx, a.properties.Aars[0])
@@ -1122,10 +1136,10 @@
 	extractedAARDir := android.PathForModuleOut(ctx, "aar")
 	a.classpathFile = extractedAARDir.Join(ctx, "classes-combined.jar")
 	a.manifest = extractedAARDir.Join(ctx, "AndroidManifest.xml")
-	aarRTxt := extractedAARDir.Join(ctx, "R.txt")
+	a.rTxt = extractedAARDir.Join(ctx, "R.txt")
 	a.assetsPackage = android.PathForModuleOut(ctx, "assets.zip")
 	a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt")
-	ctx.SetProvider(ProguardSpecInfoProvider, ProguardSpecInfo{
+	android.SetProvider(ctx, ProguardSpecInfoProvider, ProguardSpecInfo{
 		ProguardFlagsFiles: android.NewDepSet[android.Path](
 			android.POSTORDER,
 			android.Paths{a.proguardFlags},
@@ -1136,7 +1150,7 @@
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        unzipAAR,
 		Input:       a.aarPath,
-		Outputs:     android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest, a.assetsPackage, aarRTxt},
+		Outputs:     android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest, a.assetsPackage, a.rTxt},
 		Description: "unzip AAR",
 		Args: map[string]string{
 			"outDir":             extractedAARDir.String(),
@@ -1154,7 +1168,7 @@
 
 	a.exportPackage = android.PathForModuleOut(ctx, "package-res.apk")
 	proguardOptionsFile := android.PathForModuleGen(ctx, "proguard.options")
-	a.rTxt = android.PathForModuleOut(ctx, "R.txt")
+	aaptRTxt := android.PathForModuleOut(ctx, "R.txt")
 	a.extraAaptPackagesFile = android.PathForModuleOut(ctx, "extra_packages")
 
 	var linkDeps android.Paths
@@ -1163,6 +1177,7 @@
 		"--static-lib",
 		"--merge-only",
 		"--auto-add-overlay",
+		"--no-static-lib-packages",
 	}
 
 	linkFlags = append(linkFlags, "--manifest "+a.manifest.String())
@@ -1190,11 +1205,11 @@
 	}
 
 	transitiveAssets := android.ReverseSliceInPlace(staticDeps.assets())
-	aapt2Link(ctx, a.exportPackage, nil, proguardOptionsFile, a.rTxt,
+	aapt2Link(ctx, a.exportPackage, nil, proguardOptionsFile, aaptRTxt,
 		linkFlags, linkDeps, nil, overlayRes, transitiveAssets, nil, nil)
 
 	a.rJar = android.PathForModuleOut(ctx, "busybox/R.jar")
-	resourceProcessorBusyBoxGenerateBinaryR(ctx, a.rTxt, a.manifest, a.rJar, nil, true)
+	resourceProcessorBusyBoxGenerateBinaryR(ctx, a.rTxt, a.manifest, a.rJar, nil, true, nil)
 
 	aapt2ExtractExtraPackages(ctx, a.extraAaptPackagesFile, a.rJar)
 
@@ -1230,12 +1245,13 @@
 	a.transitiveAaptResourcePackagesFile = transitiveAaptResourcePackagesFile
 
 	a.collectTransitiveHeaderJars(ctx)
-	ctx.SetProvider(JavaInfoProvider, JavaInfo{
+	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
 		HeaderJars:                     android.PathsIfNonNil(a.classpathFile),
 		TransitiveLibsHeaderJars:       a.transitiveLibsHeaderJars,
 		TransitiveStaticLibsHeaderJars: a.transitiveStaticLibsHeaderJars,
 		ImplementationAndResourcesJars: android.PathsIfNonNil(a.classpathFile),
 		ImplementationJars:             android.PathsIfNonNil(a.classpathFile),
+		StubsLinkType:                  Implementation,
 		// TransitiveAconfigFiles: // TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
 	})
 
@@ -1258,11 +1274,12 @@
 				},
 			})
 		}
-
-		ctx.SetProvider(JniPackageProvider, JniPackageInfo{
-			JniPackages: a.jniPackages,
-		})
 	}
+
+	android.SetProvider(ctx, JniPackageProvider, JniPackageInfo{
+		JniPackages: a.jniPackages,
+	})
+	android.CollectDependencyAconfigFiles(ctx, &a.mergedAconfigFiles)
 }
 
 func (a *AARImport) HeaderJars() android.Paths {
@@ -1273,7 +1290,7 @@
 	return android.Paths{a.classpathFile}
 }
 
-func (a *AARImport) DexJarBuildPath() android.Path {
+func (a *AARImport) DexJarBuildPath(ctx android.ModuleErrorfContext) android.Path {
 	return nil
 }
 
diff --git a/java/aar_test.go b/java/aar_test.go
index 8afa039..4d4e5d0 100644
--- a/java/aar_test.go
+++ b/java/aar_test.go
@@ -52,7 +52,7 @@
 			appMod := ctx.Module(tc.name, "android_common")
 			appTestMod := ctx.ModuleForTests(tc.name, "android_common")
 
-			info, ok := ctx.ModuleProvider(appMod, JniPackageProvider).(JniPackageInfo)
+			info, ok := android.SingletonModuleProvider(ctx, appMod, JniPackageProvider)
 			if !ok {
 				t.Errorf("expected android_library_import to have JniPackageProvider")
 			}
diff --git a/java/android_manifest.go b/java/android_manifest.go
index 082b00e..8599003 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -152,9 +152,10 @@
 	if params.SdkContext != nil {
 		targetSdkVersion := targetSdkVersionForManifestFixer(ctx, params)
 
-		if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
-			targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
-			deps = append(deps, ApiFingerprintPath(ctx))
+		if useApiFingerprint, fingerprintTargetSdkVersion, fingerprintDeps :=
+			UseApiFingerprint(ctx); useApiFingerprint && ctx.ModuleName() != "framework-res" {
+			targetSdkVersion = fingerprintTargetSdkVersion
+			deps = append(deps, fingerprintDeps)
 		}
 
 		args = append(args, "--targetSdkVersion ", targetSdkVersion)
@@ -169,9 +170,10 @@
 			ctx.ModuleErrorf("invalid ReplaceMaxSdkVersionPlaceholder: %s", err)
 		}
 
-		if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
-			minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
-			deps = append(deps, ApiFingerprintPath(ctx))
+		if useApiFingerprint, fingerprintMinSdkVersion, fingerprintDeps :=
+			UseApiFingerprint(ctx); useApiFingerprint && ctx.ModuleName() != "framework-res" {
+			minSdkVersion = fingerprintMinSdkVersion
+			deps = append(deps, fingerprintDeps)
 		}
 
 		if err != nil {
diff --git a/java/androidmk.go b/java/androidmk.go
index a3f94cd..498962f 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -128,9 +128,7 @@
 					if library.dexpreopter.configPath != nil {
 						entries.SetPath("LOCAL_SOONG_DEXPREOPT_CONFIG", library.dexpreopter.configPath)
 					}
-					// TODO(b/311155208): The container here should be system.
-
-					entries.SetPaths("LOCAL_ACONFIG_FILES", library.mergedAconfigFiles[""])
+					android.SetAconfigFileMkEntries(&library.ModuleBase, entries, library.mergedAconfigFiles)
 				},
 			},
 		})
@@ -208,16 +206,13 @@
 
 func (prebuilt *Import) AndroidMkEntries() []android.AndroidMkEntries {
 	if prebuilt.hideApexVariantFromMake {
-		// For a library imported from a prebuilt APEX, we don't need a Make module for itself, as we
-		// don't need to install it. However, we need to add its dexpreopt outputs as sub-modules, if it
-		// is preopted.
-		dexpreoptEntries := prebuilt.dexpreopter.AndroidMkEntriesForApex()
-		return append(dexpreoptEntries, android.AndroidMkEntries{Disabled: true})
+		return []android.AndroidMkEntries{}
 	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
-		Class:      "JAVA_LIBRARIES",
-		OutputFile: android.OptionalPathForPath(prebuilt.combinedClasspathFile),
-		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+		Class:        "JAVA_LIBRARIES",
+		OverrideName: prebuilt.BaseModuleName(),
+		OutputFile:   android.OptionalPathForPath(prebuilt.combinedClasspathFile),
+		Include:      "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !Bool(prebuilt.properties.Installable))
@@ -307,8 +302,7 @@
 					if len(binary.dexpreopter.builtInstalled) > 0 {
 						entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", binary.dexpreopter.builtInstalled)
 					}
-					// TODO(b/311155208): The container here should be system.
-					entries.SetPaths("LOCAL_ACONFIG_FILES", binary.mergedAconfigFiles[""])
+					android.SetAconfigFileMkEntries(&binary.ModuleBase, entries, binary.mergedAconfigFiles)
 				},
 			},
 			ExtraFooters: []android.AndroidMkExtraFootersFunc{
@@ -461,8 +455,7 @@
 				entries.SetOptionalPaths("LOCAL_SOONG_LINT_REPORTS", app.linter.reports)
 
 				if app.Name() != "framework-res" {
-					// TODO(b/311155208): The container here should be system.
-					entries.SetPaths("LOCAL_ACONFIG_FILES", app.mergedAconfigFiles[""])
+					android.SetAconfigFileMkEntries(&app.ModuleBase, entries, app.mergedAconfigFiles)
 				}
 			},
 		},
@@ -540,8 +533,7 @@
 		entries.SetPath("LOCAL_FULL_MANIFEST_FILE", a.mergedManifestFile)
 		entries.SetPath("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", a.combinedExportedProguardFlagsFile)
 		entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", true)
-		// TODO(b/311155208): The container here should be system.
-		entries.SetPaths("LOCAL_ACONFIG_FILES", a.mergedAconfigFiles[""])
+		android.SetAconfigFileMkEntries(&a.ModuleBase, entries, a.mergedAconfigFiles)
 	})
 
 	return entriesList
@@ -557,8 +549,8 @@
 				if BoolDefault(jd.properties.Installable, true) {
 					entries.SetPath("LOCAL_DROIDDOC_DOC_ZIP", jd.docZip)
 				}
-				if jd.stubsSrcJar != nil {
-					entries.SetPath("LOCAL_DROIDDOC_STUBS_SRCJAR", jd.stubsSrcJar)
+				if jd.exportableStubsSrcJar != nil {
+					entries.SetPath("LOCAL_DROIDDOC_STUBS_SRCJAR", jd.exportableStubsSrcJar)
 				}
 			},
 		},
@@ -596,7 +588,7 @@
 		outputFile = android.OptionalPathForPath(dstubs.apiFile)
 	}
 	if !outputFile.Valid() {
-		outputFile = android.OptionalPathForPath(dstubs.apiVersionsXml)
+		outputFile = android.OptionalPathForPath(dstubs.everythingArtifacts.apiVersionsXml)
 	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
@@ -604,17 +596,17 @@
 		Include:    "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				if dstubs.Javadoc.stubsSrcJar != nil {
-					entries.SetPath("LOCAL_DROIDDOC_STUBS_SRCJAR", dstubs.Javadoc.stubsSrcJar)
+				if dstubs.Javadoc.exportableStubsSrcJar != nil {
+					entries.SetPath("LOCAL_DROIDDOC_STUBS_SRCJAR", dstubs.Javadoc.exportableStubsSrcJar)
 				}
-				if dstubs.apiVersionsXml != nil {
-					entries.SetPath("LOCAL_DROIDDOC_API_VERSIONS_XML", dstubs.apiVersionsXml)
+				if dstubs.everythingArtifacts.apiVersionsXml != nil {
+					entries.SetPath("LOCAL_DROIDDOC_API_VERSIONS_XML", dstubs.exportableArtifacts.apiVersionsXml)
 				}
-				if dstubs.annotationsZip != nil {
-					entries.SetPath("LOCAL_DROIDDOC_ANNOTATIONS_ZIP", dstubs.annotationsZip)
+				if dstubs.everythingArtifacts.annotationsZip != nil {
+					entries.SetPath("LOCAL_DROIDDOC_ANNOTATIONS_ZIP", dstubs.exportableArtifacts.annotationsZip)
 				}
-				if dstubs.metadataZip != nil {
-					entries.SetPath("LOCAL_DROIDDOC_METADATA_ZIP", dstubs.metadataZip)
+				if dstubs.everythingArtifacts.metadataZip != nil {
+					entries.SetPath("LOCAL_DROIDDOC_METADATA_ZIP", dstubs.exportableArtifacts.metadataZip)
 				}
 			},
 		},
@@ -699,9 +691,10 @@
 		return nil
 	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
-		Class:      "APPS",
-		OutputFile: android.OptionalPathForPath(a.outputFile),
-		Include:    "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
+		Class:        "APPS",
+		OutputFile:   android.OptionalPathForPath(a.outputFile),
+		OverrideName: a.BaseModuleName(), // TODO (spandandas): Add a test
+		Include:      "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", a.Privileged())
diff --git a/java/app.go b/java/app.go
index d5c4eba..9736ffd 100755
--- a/java/app.go
+++ b/java/app.go
@@ -22,7 +22,6 @@
 	"path/filepath"
 	"strings"
 
-	"android/soong/aconfig"
 	"android/soong/testing"
 
 	"github.com/google/blueprint"
@@ -139,12 +138,6 @@
 	// PRODUCT_CHARACTERISTICS.
 	Generate_product_characteristics_rro *bool
 
-	// A list of files or dependencies to make available to the build sandbox. This is
-	// useful if source files are symlinks, the targets of the symlinks must be listed here.
-	// Note that currently not all actions implemented by android_apps are sandboxed, so you
-	// may only see this being necessary in lint builds.
-	Compile_data []string
-
 	ProductCharacteristicsRROPackageName        *string `blueprint:"mutated"`
 	ProductCharacteristicsRROManifestModuleName *string `blueprint:"mutated"`
 }
@@ -417,7 +410,7 @@
 		ctx.PropertyErrorf("min_sdk_version", "invalid value %q: %s", a.MinSdkVersion(ctx), err)
 	}
 
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	return (minSdkVersion.FinalOrFutureInt() >= 23 && Bool(a.appProperties.Use_embedded_native_libs)) ||
 		!apexInfo.IsForPlatform()
 }
@@ -438,11 +431,11 @@
 		return false
 	}
 
-	return shouldUncompressDex(ctx, &a.dexpreopter)
+	return shouldUncompressDex(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), &a.dexpreopter)
 }
 
 func (a *AndroidApp) shouldEmbedJnis(ctx android.BaseModuleContext) bool {
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	return ctx.Config().UnbundledBuild() || Bool(a.appProperties.Use_embedded_native_libs) ||
 		!apexInfo.IsForPlatform() || a.appProperties.AlwaysPackageNativeLibs
 }
@@ -479,8 +472,9 @@
 	// Add TARGET_AAPT_CHARACTERISTICS values to AAPT link flags if they exist and --product flags were not provided.
 	autogenerateRRO := proptools.Bool(a.appProperties.Generate_product_characteristics_rro)
 	hasProduct := android.PrefixInList(a.aaptProperties.Aaptflags, "--product")
-	if !autogenerateRRO && !hasProduct && len(ctx.Config().ProductAAPTCharacteristics()) > 0 {
-		aaptLinkFlags = append(aaptLinkFlags, "--product", ctx.Config().ProductAAPTCharacteristics())
+	characteristics := ctx.Config().ProductAAPTCharacteristics()
+	if !autogenerateRRO && !hasProduct && len(characteristics) > 0 && characteristics != "default" {
+		aaptLinkFlags = append(aaptLinkFlags, "--product", characteristics)
 	}
 
 	if !Bool(a.aaptProperties.Aapt_include_all_resources) {
@@ -515,7 +509,7 @@
 
 	var aconfigTextFilePaths android.Paths
 	ctx.VisitDirectDepsWithTag(aconfigDeclarationTag, func(dep android.Module) {
-		if provider, ok := ctx.OtherModuleProvider(dep, aconfig.DeclarationsProviderKey).(aconfig.DeclarationsProviderData); ok {
+		if provider, ok := android.OtherModuleProvider(ctx, dep, android.AconfigDeclarationsProviderKey); ok {
 			aconfigTextFilePaths = append(aconfigTextFilePaths, provider.IntermediateDumpOutputPath)
 		} else {
 			ctx.ModuleErrorf("Only aconfig_declarations module type is allowed for "+
@@ -543,7 +537,7 @@
 func (a *AndroidApp) proguardBuildActions(ctx android.ModuleContext) {
 	var staticLibProguardFlagFiles android.Paths
 	ctx.VisitDirectDeps(func(m android.Module) {
-		depProguardInfo := ctx.OtherModuleProvider(m, ProguardSpecInfoProvider).(ProguardSpecInfo)
+		depProguardInfo, _ := android.OtherModuleProvider(ctx, m, ProguardSpecInfoProvider)
 		staticLibProguardFlagFiles = append(staticLibProguardFlagFiles, depProguardInfo.UnconditionallyExportedProguardFlags.ToList()...)
 		if ctx.OtherModuleDependencyTag(m) == staticLibTag {
 			staticLibProguardFlagFiles = append(staticLibProguardFlagFiles, depProguardInfo.ProguardFlagsFiles.ToList()...)
@@ -595,7 +589,7 @@
 		var extraSrcJars android.Paths
 		var extraClasspathJars android.Paths
 		var extraCombinedJars android.Paths
-		if a.useResourceProcessorBusyBox() {
+		if a.useResourceProcessorBusyBox(ctx) {
 			// When building an app with ResourceProcessorBusyBox enabled ResourceProcessorBusyBox has already
 			// created R.class files that provide IDs for resources in busybox/R.jar.  Pass that file in the
 			// classpath when compiling everything else, and add it to the final classes jar.
@@ -751,7 +745,8 @@
 func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
 	var apkDeps android.Paths
 
-	if !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform() {
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	if !apexInfo.IsForPlatform() {
 		a.hideApexVariantFromMake = true
 	}
 
@@ -783,6 +778,9 @@
 	a.onDeviceDir = android.InstallPathToOnDevicePath(ctx, a.installDir)
 
 	a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
+	if a.usesLibrary.shouldDisableDexpreopt {
+		a.dexpreopter.disableDexpreopt()
+	}
 
 	var noticeAssetPath android.WritablePath
 	if Bool(a.appProperties.Embed_notices) || ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") {
@@ -824,7 +822,6 @@
 	a.linter.mergedManifest = a.aapt.mergedManifestFile
 	a.linter.manifest = a.aapt.manifestPath
 	a.linter.resources = a.aapt.resourceFiles
-	a.linter.compile_data = android.PathsForModuleSrc(ctx, a.appProperties.Compile_data)
 	a.linter.buildModuleReportZip = ctx.Config().UnbundledBuildApps()
 
 	dexJarFile, packageResources := a.dexBuildActions(ctx)
@@ -897,8 +894,6 @@
 		a.privAppAllowlist = android.OptionalPathForPath(allowlist)
 	}
 
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
-
 	// Install the app package.
 	shouldInstallAppPackage := (Bool(a.Module.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() && !a.appProperties.PreventInstall
 	if shouldInstallAppPackage {
@@ -929,15 +924,39 @@
 	shouldCollectRecursiveNativeDeps bool,
 	checkNativeSdkVersion bool) ([]jniLib, android.Paths, []Certificate) {
 
-	var jniLibs []jniLib
-	var prebuiltJniPackages android.Paths
-	var certificates []Certificate
-	seenModulePaths := make(map[string]bool)
-
 	if checkNativeSdkVersion {
 		checkNativeSdkVersion = app.SdkVersion(ctx).Specified() &&
 			app.SdkVersion(ctx).Kind != android.SdkCorePlatform && !app.RequiresStableAPIs(ctx)
 	}
+	jniLib, prebuiltJniPackages := collectJniDeps(ctx, shouldCollectRecursiveNativeDeps,
+		checkNativeSdkVersion, func(dep cc.LinkableInterface) bool {
+			return !dep.IsNdk(ctx.Config()) && !dep.IsStubs()
+		})
+
+	var certificates []Certificate
+
+	ctx.VisitDirectDeps(func(module android.Module) {
+		otherName := ctx.OtherModuleName(module)
+		tag := ctx.OtherModuleDependencyTag(module)
+
+		if tag == certificateTag {
+			if dep, ok := module.(*AndroidAppCertificate); ok {
+				certificates = append(certificates, dep.Certificate)
+			} else {
+				ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", otherName)
+			}
+		}
+	})
+	return jniLib, prebuiltJniPackages, certificates
+}
+
+func collectJniDeps(ctx android.ModuleContext,
+	shouldCollectRecursiveNativeDeps bool,
+	checkNativeSdkVersion bool,
+	filter func(cc.LinkableInterface) bool) ([]jniLib, android.Paths) {
+	var jniLibs []jniLib
+	var prebuiltJniPackages android.Paths
+	seenModulePaths := make(map[string]bool)
 
 	ctx.WalkDeps(func(module android.Module, parent android.Module) bool {
 		otherName := ctx.OtherModuleName(module)
@@ -945,7 +964,7 @@
 
 		if IsJniDepTag(tag) || cc.IsSharedDepTag(tag) {
 			if dep, ok := module.(cc.LinkableInterface); ok {
-				if dep.IsNdk(ctx.Config()) || dep.IsStubs() {
+				if filter != nil && !filter(dep) {
 					return false
 				}
 
@@ -982,22 +1001,14 @@
 			return shouldCollectRecursiveNativeDeps
 		}
 
-		if info, ok := ctx.OtherModuleProvider(module, JniPackageProvider).(JniPackageInfo); ok {
+		if info, ok := android.OtherModuleProvider(ctx, module, JniPackageProvider); ok {
 			prebuiltJniPackages = append(prebuiltJniPackages, info.JniPackages...)
 		}
 
-		if tag == certificateTag {
-			if dep, ok := module.(*AndroidAppCertificate); ok {
-				certificates = append(certificates, dep.Certificate)
-			} else {
-				ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", otherName)
-			}
-		}
-
 		return false
 	})
 
-	return jniLibs, prebuiltJniPackages, certificates
+	return jniLibs, prebuiltJniPackages
 }
 
 func (a *AndroidApp) WalkPayloadDeps(ctx android.ModuleContext, do android.PayloadDepsCallback) {
@@ -1105,6 +1116,8 @@
 		if a.rJar != nil {
 			return []android.Path{a.rJar}, nil
 		}
+	case ".apk":
+		return []android.Path{a.outputFile}, nil
 	case ".export-package.apk":
 		return []android.Path{a.exportPackage}, nil
 	case ".manifest.xml":
@@ -1117,7 +1130,7 @@
 	return Bool(a.appProperties.Privileged)
 }
 
-func (a *AndroidApp) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
+func (a *AndroidApp) IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool {
 	return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
 }
 
@@ -1199,10 +1212,10 @@
 			Cmd   *string
 		}{
 			Name:  proptools.StringPtr(rroManifestName),
-			Tools: []string{"characteristics_rro_generator"},
+			Tools: []string{"characteristics_rro_generator", "aapt2"},
 			Out:   []string{"AndroidManifest.xml"},
-			Srcs:  []string{":" + a.Name() + "{.manifest.xml}"},
-			Cmd:   proptools.StringPtr("$(location characteristics_rro_generator) $(in) $(out)"),
+			Srcs:  []string{":" + a.Name() + "{.apk}"},
+			Cmd:   proptools.StringPtr("$(location characteristics_rro_generator) $$($(location aapt2) dump packagename $(in)) $(out)"),
 		}
 		ctx.CreateModule(genrule.GenRuleFactory, &rroManifestProperties)
 
@@ -1302,7 +1315,7 @@
 	a.testConfig = a.FixTestConfig(ctx, testConfig)
 	a.extraTestConfigs = android.PathsForModuleSrc(ctx, a.testProperties.Test_options.Extra_test_configs)
 	a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
-	ctx.SetProvider(testing.TestModuleProviderKey, testing.TestModuleProviderData{})
+	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 }
 
 func (a *AndroidTest) FixTestConfig(ctx android.ModuleContext, testConfig android.Path) android.Path {
@@ -1556,6 +1569,9 @@
 
 	// Whether to enforce verify_uses_library check.
 	enforce bool
+
+	// Whether dexpreopt should be disabled
+	shouldDisableDexpreopt bool
 }
 
 func (u *usesLibrary) addLib(lib string, optional bool) {
@@ -1637,6 +1653,15 @@
 			}
 		}
 
+		// Skip java_sdk_library dependencies that provide stubs, but not an implementation.
+		// This will be restricted to optional_uses_libs
+		if sdklib, ok := m.(SdkLibraryDependency); ok {
+			if tag == usesLibOptTag && sdklib.DexJarBuildPath(ctx).PathOrNil() == nil {
+				u.shouldDisableDexpreopt = true
+				return
+			}
+		}
+
 		if lib, ok := m.(UsesLibraryDependency); ok {
 			libName := dep
 			if ulib, ok := m.(ProvidesUsesLib); ok && ulib.ProvidesUsesLib() != nil {
@@ -1648,7 +1673,7 @@
 				replaceInList(u.usesLibraryProperties.Optional_uses_libs, dep, libName)
 			}
 			clcMap.AddContext(ctx, tag.sdkVersion, libName, tag.optional,
-				lib.DexJarBuildPath().PathOrNil(), lib.DexJarInstallPath(),
+				lib.DexJarBuildPath(ctx).PathOrNil(), lib.DexJarInstallPath(),
 				lib.ClassLoaderContexts())
 		} else if ctx.Config().AllowMissingDependencies() {
 			ctx.AddMissingDependencies([]string{dep})
diff --git a/java/app_import.go b/java/app_import.go
index c5d09fd..dc84fc2 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -70,9 +70,10 @@
 	android.ApexModuleBase
 	prebuilt android.Prebuilt
 
-	properties   AndroidAppImportProperties
-	dpiVariants  interface{}
-	archVariants interface{}
+	properties       AndroidAppImportProperties
+	dpiVariants      interface{}
+	archVariants     interface{}
+	arch_dpiVariants interface{}
 
 	outputFile  android.Path
 	certificate Certificate
@@ -86,6 +87,9 @@
 	hideApexVariantFromMake bool
 
 	provenanceMetaDataFile android.OutputPath
+
+	// Single aconfig "cache file" merged from this module and all dependencies.
+	mergedAconfigFiles map[string]android.Paths
 }
 
 type AndroidAppImportProperties struct {
@@ -141,6 +145,11 @@
 	// Whether or not to skip checking the preprocessed apk for proper alignment and uncompressed
 	// JNI libs and dex files. Default is false
 	Skip_preprocessed_apk_checks *bool
+
+	// Name of the source soong module that gets shadowed by this prebuilt
+	// If unspecified, follows the naming convention that the source module of
+	// the prebuilt is Name() without "prebuilt_" prefix
+	Source_module_name *string
 }
 
 func (a *AndroidAppImport) IsInstallable() bool {
@@ -148,10 +157,12 @@
 }
 
 // Updates properties with variant-specific values.
-func (a *AndroidAppImport) processVariants(ctx android.LoadHookContext) {
+// This happens as a DefaultableHook instead of a LoadHook because we want to run it after
+// soong config variables are applied.
+func (a *AndroidAppImport) processVariants(ctx android.DefaultableHookContext) {
 	config := ctx.Config()
+	dpiProps := reflect.ValueOf(a.dpiVariants).Elem().FieldByName(DpiGroupName)
 
-	dpiProps := reflect.ValueOf(a.dpiVariants).Elem().FieldByName("Dpi_variants")
 	// Try DPI variant matches in the reverse-priority order so that the highest priority match
 	// overwrites everything else.
 	// TODO(jungjw): Can we optimize this by making it priority order?
@@ -161,11 +172,27 @@
 	if config.ProductAAPTPreferredConfig() != "" {
 		MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPreferredConfig())
 	}
-
-	archProps := reflect.ValueOf(a.archVariants).Elem().FieldByName("Arch")
+	archProps := reflect.ValueOf(a.archVariants).Elem().FieldByName(ArchGroupName)
 	archType := ctx.Config().AndroidFirstDeviceTarget.Arch.ArchType
 	MergePropertiesFromVariant(ctx, &a.properties, archProps, archType.Name)
 
+	// Process "arch" includes "dpi_variants"
+	archStructPtr := reflect.ValueOf(a.arch_dpiVariants).Elem().FieldByName(ArchGroupName)
+	if archStruct := archStructPtr.Elem(); archStruct.IsValid() {
+		archPartPropsPtr := archStruct.FieldByName(proptools.FieldNameForProperty(archType.Name))
+		if archPartProps := archPartPropsPtr.Elem(); archPartProps.IsValid() {
+			archDpiPropsPtr := archPartProps.FieldByName(DpiGroupName)
+			if archDpiProps := archDpiPropsPtr.Elem(); archDpiProps.IsValid() {
+				for i := len(config.ProductAAPTPrebuiltDPI()) - 1; i >= 0; i-- {
+					MergePropertiesFromVariant(ctx, &a.properties, archDpiProps, config.ProductAAPTPrebuiltDPI()[i])
+				}
+				if config.ProductAAPTPreferredConfig() != "" {
+					MergePropertiesFromVariant(ctx, &a.properties, archDpiProps, config.ProductAAPTPreferredConfig())
+				}
+			}
+		}
+	}
+
 	if String(a.properties.Apk) == "" {
 		// Disable this module since the apk property is still empty after processing all matching
 		// variants. This likely means there is no matching variant, and the default variant doesn't
@@ -241,7 +268,7 @@
 		return ctx.Config().UncompressPrivAppDex()
 	}
 
-	return shouldUncompressDex(ctx, &a.dexpreopter)
+	return shouldUncompressDex(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), &a.dexpreopter)
 }
 
 func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -252,12 +279,16 @@
 	return a.BaseModuleName()
 }
 
+func (a *AndroidAppImport) BaseModuleName() string {
+	return proptools.StringDefault(a.properties.Source_module_name, a.ModuleBase.Name())
+}
+
 func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext) {
 	if a.Name() == "prebuilt_framework-res" {
 		ctx.ModuleErrorf("prebuilt_framework-res found. This used to have special handling in soong, but was removed due to prebuilt_framework-res no longer existing. This check is to ensure it doesn't come back without readding the special handling.")
 	}
 
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	if !apexInfo.IsForPlatform() {
 		a.hideApexVariantFromMake = true
 	}
@@ -314,12 +345,15 @@
 
 	a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
 	a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
+	if a.usesLibrary.shouldDisableDexpreopt {
+		a.dexpreopter.disableDexpreopt()
+	}
 
 	if a.usesLibrary.enforceUsesLibraries() {
 		a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk)
 	}
 
-	a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
+	a.dexpreopter.dexpreopt(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), jnisUncompressed)
 	if a.dexpreopter.uncompressedDex {
 		dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk")
 		ctx.Build(pctx, android.BuildParams{
@@ -377,6 +411,7 @@
 		artifactPath := android.PathForModuleSrc(ctx, *a.properties.Apk)
 		a.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, artifactPath, a.installPath)
 	}
+	android.CollectDependencyAconfigFiles(ctx, &a.mergedAconfigFiles)
 
 	// TODO: androidmk converter jni libs
 }
@@ -442,18 +477,25 @@
 	return android.OptionalPath{}
 }
 
+const (
+	ArchGroupName = "Arch"
+	DpiGroupName  = "Dpi_variants"
+)
+
 var dpiVariantGroupType reflect.Type
 var archVariantGroupType reflect.Type
+var archdpiVariantGroupType reflect.Type
 var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"}
 
 func initAndroidAppImportVariantGroupTypes() {
-	dpiVariantGroupType = createVariantGroupType(supportedDpis, "Dpi_variants")
+	dpiVariantGroupType = createVariantGroupType(supportedDpis, DpiGroupName)
 
 	archNames := make([]string, len(android.ArchTypeList()))
 	for i, archType := range android.ArchTypeList() {
 		archNames[i] = archType.Name
 	}
-	archVariantGroupType = createVariantGroupType(archNames, "Arch")
+	archVariantGroupType = createVariantGroupType(archNames, ArchGroupName)
+	archdpiVariantGroupType = createArchDpiVariantGroupType(archNames, supportedDpis)
 }
 
 // Populates all variant struct properties at creation time.
@@ -463,6 +505,9 @@
 
 	a.archVariants = reflect.New(archVariantGroupType).Interface()
 	a.AddProperties(a.archVariants)
+
+	a.arch_dpiVariants = reflect.New(archdpiVariantGroupType).Interface()
+	a.AddProperties(a.arch_dpiVariants)
 }
 
 func (a *AndroidAppImport) Privileged() bool {
@@ -517,6 +562,42 @@
 	})
 }
 
+func createArchDpiVariantGroupType(archNames []string, dpiNames []string) reflect.Type {
+	props := reflect.TypeOf((*AndroidAppImportProperties)(nil))
+
+	dpiVariantFields := make([]reflect.StructField, len(dpiNames))
+	for i, variant_dpi := range dpiNames {
+		dpiVariantFields[i] = reflect.StructField{
+			Name: proptools.FieldNameForProperty(variant_dpi),
+			Type: props,
+		}
+	}
+	dpiVariantGroupStruct := reflect.StructOf(dpiVariantFields)
+	dpi_struct := reflect.StructOf([]reflect.StructField{
+		{
+			Name: DpiGroupName,
+			Type: reflect.PointerTo(dpiVariantGroupStruct),
+		},
+	})
+
+	archVariantFields := make([]reflect.StructField, len(archNames))
+	for i, variant_arch := range archNames {
+		archVariantFields[i] = reflect.StructField{
+			Name: proptools.FieldNameForProperty(variant_arch),
+			Type: reflect.PointerTo(dpi_struct),
+		}
+	}
+	archVariantGroupStruct := reflect.StructOf(archVariantFields)
+
+	return_struct := reflect.StructOf([]reflect.StructField{
+		{
+			Name: ArchGroupName,
+			Type: reflect.PointerTo(archVariantGroupStruct),
+		},
+	})
+	return return_struct
+}
+
 // android_app_import imports a prebuilt apk with additional processing specified in the module.
 // DPI-specific apk source files can be specified using dpi_variants. Example:
 //
@@ -539,7 +620,7 @@
 	module.AddProperties(&module.dexpreoptProperties)
 	module.AddProperties(&module.usesLibrary.usesLibraryProperties)
 	module.populateAllVariantStructs()
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+	module.SetDefaultableHook(func(ctx android.DefaultableHookContext) {
 		module.processVariants(ctx)
 	})
 
@@ -590,7 +671,7 @@
 	module.AddProperties(&module.dexpreoptProperties)
 	module.AddProperties(&module.testProperties)
 	module.populateAllVariantStructs()
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+	module.SetDefaultableHook(func(ctx android.DefaultableHookContext) {
 		module.processVariants(ctx)
 	})
 
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 8f29bb3..5de50e7 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -40,8 +40,8 @@
 	variant := ctx.ModuleForTests("foo", "android_common")
 
 	// Check dexpreopt outputs.
-	if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
-		variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
+	if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule == nil ||
+		variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.odex").Rule == nil {
 		t.Errorf("can't find dexpreopt outputs")
 	}
 
@@ -74,8 +74,8 @@
 	variant := ctx.ModuleForTests("foo", "android_common")
 
 	// Check dexpreopt outputs. They shouldn't exist.
-	if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule != nil ||
-		variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule != nil {
+	if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule != nil ||
+		variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.odex").Rule != nil {
 		t.Errorf("dexpreopt shouldn't have run.")
 	}
 
@@ -101,8 +101,8 @@
 	variant := ctx.ModuleForTests("foo", "android_common")
 
 	// Check dexpreopt outputs.
-	if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
-		variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
+	if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule == nil ||
+		variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.odex").Rule == nil {
 		t.Errorf("can't find dexpreopt outputs")
 	}
 	// Make sure signing was skipped and aligning was done.
@@ -210,8 +210,8 @@
 	variant := ctx.ModuleForTests("foo", "android_common")
 
 	// Check dexpreopt outputs.
-	if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
-		variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
+	if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule == nil ||
+		variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.odex").Rule == nil {
 		t.Errorf("can't find dexpreopt outputs")
 	}
 
@@ -411,6 +411,27 @@
 			installPath:  "/system/app/foo/foo.apk",
 		},
 		{
+			name: "matching arch without default",
+			bp: `
+				android_app_import {
+					name: "foo",
+					apk: "prebuilts/apk/app.apk",
+					arch: {
+						arm64: {
+							apk: "prebuilts/apk/app_arm64.apk",
+						},
+					},
+					presigned: true,
+					dex_preopt: {
+						enabled: true,
+					},
+				}
+			`,
+			expected:     "verify_uses_libraries/apk/app_arm64.apk",
+			artifactPath: "prebuilts/apk/app_arm64.apk",
+			installPath:  "/system/app/foo/foo.apk",
+		},
+		{
 			name: "no matching arch",
 			bp: `
 				android_app_import {
@@ -451,29 +472,137 @@
 			artifactPath: "prebuilts/apk/app_arm.apk",
 			installPath:  "/system/app/foo/foo.apk",
 		},
+		{
+			name: "matching arch and dpi_variants",
+			bp: `
+				android_app_import {
+					name: "foo",
+					apk: "prebuilts/apk/app.apk",
+					arch: {
+						arm64: {
+							apk: "prebuilts/apk/app_arm64.apk",
+							dpi_variants: {
+								mdpi: {
+									apk: "prebuilts/apk/app_arm64_mdpi.apk",
+								},
+								xhdpi: {
+									apk: "prebuilts/apk/app_arm64_xhdpi.apk",
+								},
+							},
+						},
+					},
+					presigned: true,
+					dex_preopt: {
+						enabled: true,
+					},
+				}
+			`,
+			expected:     "verify_uses_libraries/apk/app_arm64_xhdpi.apk",
+			artifactPath: "prebuilts/apk/app_arm64_xhdpi.apk",
+			installPath:  "/system/app/foo/foo.apk",
+		},
 	}
 
 	for _, test := range testCases {
-		ctx, _ := testJava(t, test.bp)
+		t.Run(test.name, func(t *testing.T) {
+			ctx, _ := testJava(t, test.bp)
 
-		variant := ctx.ModuleForTests("foo", "android_common")
-		if test.expected == "" {
-			if variant.Module().Enabled() {
-				t.Error("module should have been disabled, but wasn't")
+			variant := ctx.ModuleForTests("foo", "android_common")
+			if test.expected == "" {
+				if variant.Module().Enabled() {
+					t.Error("module should have been disabled, but wasn't")
+				}
+				rule := variant.MaybeRule("genProvenanceMetaData")
+				android.AssertDeepEquals(t, "Provenance metadata is not empty", android.TestingBuildParams{}, rule)
+				return
 			}
-			rule := variant.MaybeRule("genProvenanceMetaData")
-			android.AssertDeepEquals(t, "Provenance metadata is not empty", android.TestingBuildParams{}, rule)
-			continue
-		}
-		input := variant.Output("jnis-uncompressed/foo.apk").Input.String()
-		if strings.HasSuffix(input, test.expected) {
-			t.Errorf("wrong src apk, expected: %q got: %q", test.expected, input)
-		}
-		rule := variant.Rule("genProvenanceMetaData")
-		android.AssertStringEquals(t, "Invalid input", test.artifactPath, rule.Inputs[0].String())
-		android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
-		android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
-		android.AssertStringEquals(t, "Invalid args", test.installPath, rule.Args["install_path"])
+			input := variant.Output("jnis-uncompressed/foo.apk").Input.String()
+			if strings.HasSuffix(input, test.expected) {
+				t.Errorf("wrong src apk, expected: %q got: %q", test.expected, input)
+			}
+			rule := variant.Rule("genProvenanceMetaData")
+			android.AssertStringEquals(t, "Invalid input", test.artifactPath, rule.Inputs[0].String())
+			android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
+			android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
+			android.AssertStringEquals(t, "Invalid args", test.installPath, rule.Args["install_path"])
+		})
+	}
+}
+
+func TestAndroidAppImport_SoongConfigVariables(t *testing.T) {
+	testCases := []struct {
+		name         string
+		bp           string
+		expected     string
+		artifactPath string
+		metaDataPath string
+		installPath  string
+	}{
+		{
+			name: "matching arch",
+			bp: `
+				soong_config_module_type {
+					name: "my_android_app_import",
+					module_type: "android_app_import",
+					config_namespace: "my_namespace",
+					value_variables: ["my_apk_var"],
+					properties: ["apk"],
+				}
+				soong_config_value_variable {
+					name: "my_apk_var",
+				}
+				my_android_app_import {
+					name: "foo",
+					soong_config_variables: {
+						my_apk_var: {
+							apk: "prebuilts/apk/%s.apk",
+						},
+					},
+					presigned: true,
+					dex_preopt: {
+						enabled: true,
+					},
+				}
+			`,
+			expected:     "verify_uses_libraries/apk/name_from_soong_config.apk",
+			artifactPath: "prebuilts/apk/name_from_soong_config.apk",
+			installPath:  "/system/app/foo/foo.apk",
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.name, func(t *testing.T) {
+			ctx := android.GroupFixturePreparers(
+				prepareForJavaTest,
+				android.PrepareForTestWithSoongConfigModuleBuildComponents,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.VendorVars = map[string]map[string]string{
+						"my_namespace": {
+							"my_apk_var": "name_from_soong_config",
+						},
+					}
+				}),
+			).RunTestWithBp(t, test.bp).TestContext
+
+			variant := ctx.ModuleForTests("foo", "android_common")
+			if test.expected == "" {
+				if variant.Module().Enabled() {
+					t.Error("module should have been disabled, but wasn't")
+				}
+				rule := variant.MaybeRule("genProvenanceMetaData")
+				android.AssertDeepEquals(t, "Provenance metadata is not empty", android.TestingBuildParams{}, rule)
+				return
+			}
+			input := variant.Output("jnis-uncompressed/foo.apk").Input.String()
+			if strings.HasSuffix(input, test.expected) {
+				t.Errorf("wrong src apk, expected: %q got: %q", test.expected, input)
+			}
+			rule := variant.Rule("genProvenanceMetaData")
+			android.AssertStringEquals(t, "Invalid input", test.artifactPath, rule.Inputs[0].String())
+			android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
+			android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
+			android.AssertStringEquals(t, "Invalid args", test.installPath, rule.Args["install_path"])
+		})
 	}
 }
 
diff --git a/java/app_test.go b/java/app_test.go
index 0936b28..28bea0a 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -931,8 +931,8 @@
 				"out/soong/.intermediates/direct_import/android_common/aar/classes-combined.jar",
 			},
 			appCombined: []string{
-				"out/soong/.intermediates/app/android_common/busybox/R.jar",
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
+				"out/soong/.intermediates/app/android_common/busybox/R.jar",
 				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
 				"out/soong/.intermediates/direct_import/android_common/aar/classes-combined.jar",
 			},
@@ -1037,8 +1037,8 @@
 				"out/soong/.intermediates/direct_import/android_common/aar/classes-combined.jar",
 			},
 			appCombined: []string{
-				"out/soong/.intermediates/app/android_common/busybox/R.jar",
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
+				"out/soong/.intermediates/app/android_common/busybox/R.jar",
 				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
 				"out/soong/.intermediates/direct_import/android_common/aar/classes-combined.jar",
 			},
@@ -2109,7 +2109,7 @@
 		Output("libjni.so").Output.String()
 	sdkJNI := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared").
 		Output("libjni.so").Output.String()
-	vendorJNI := ctx.ModuleForTests("libvendorjni", "android_arm64_armv8-a_shared").
+	vendorJNI := ctx.ModuleForTests("libvendorjni", "android_vendor_arm64_armv8-a_shared").
 		Output("libvendorjni.so").Output.String()
 
 	for _, test := range testCases {
@@ -3357,7 +3357,7 @@
 	cmd := app.Rule("dexpreopt").RuleParams.Command
 	android.AssertStringDoesContain(t, "dexpreopt app cmd context", cmd, "--context-json=")
 	android.AssertStringDoesContain(t, "dexpreopt app cmd product_packages", cmd,
-		"--product-packages=out/soong/.intermediates/app/android_common/dexpreopt/product_packages.txt")
+		"--product-packages=out/soong/.intermediates/app/android_common/dexpreopt/app/product_packages.txt")
 }
 
 func TestDexpreoptBcp(t *testing.T) {
@@ -4347,14 +4347,14 @@
 		}
 		aconfig_declarations {
 			name: "bar",
-			package: "com.example.package",
+			package: "com.example.package.bar",
 			srcs: [
 				"bar.aconfig",
 			],
 		}
 		aconfig_declarations {
 			name: "baz",
-			package: "com.example.package",
+			package: "com.example.package.baz",
 			srcs: [
 				"baz.aconfig",
 			],
@@ -4378,3 +4378,26 @@
 		"--feature-flags @out/soong/.intermediates/bar/intermediate.txt --feature-flags @out/soong/.intermediates/baz/intermediate.txt",
 	)
 }
+
+// Test that dexpreopt is disabled if an optional_uses_libs exists, but does not provide an implementation.
+func TestNoDexpreoptOptionalUsesLibDoesNotHaveImpl(t *testing.T) {
+	bp := `
+		java_sdk_library_import {
+			name: "sdklib_noimpl",
+			public: {
+				jars: ["stub.jar"],
+			},
+		}
+		android_app {
+			name: "app",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			optional_uses_libs: [
+				"sdklib_noimpl",
+			],
+		}
+	`
+	result := prepareForJavaTest.RunTestWithBp(t, bp)
+	dexpreopt := result.ModuleForTests("app", "android_common").MaybeRule("dexpreopt").Rule
+	android.AssertBoolEquals(t, "dexpreopt should be disabled if optional_uses_libs does not have an implementation", true, dexpreopt == nil)
+}
diff --git a/java/base.go b/java/base.go
index cdb58a2..d8ccec6 100644
--- a/java/base.go
+++ b/java/base.go
@@ -17,6 +17,8 @@
 import (
 	"fmt"
 	"path/filepath"
+	"reflect"
+	"slices"
 	"strconv"
 	"strings"
 
@@ -90,6 +92,12 @@
 	// if not blank, run jarjar using the specified rules file
 	Jarjar_rules *string `android:"path,arch_variant"`
 
+	// if not blank, used as prefix to generate repackage rule
+	Jarjar_prefix *string
+
+	// if set to true, skip the jarjar repackaging
+	Skip_jarjar_repackage *bool
+
 	// If not blank, set the java version passed to javac as -source and -target
 	Java_version *string
 
@@ -195,6 +203,19 @@
 
 	// If true, then only the headers are built and not the implementation jar.
 	Headers_only *bool
+
+	// A list of files or dependencies to make available to the build sandbox. This is
+	// useful if source files are symlinks, the targets of the symlinks must be listed here.
+	// Note that currently not all actions implemented by android_apps are sandboxed, so you
+	// may only see this being necessary in lint builds.
+	Compile_data []string `android:"path"`
+
+	// Property signifying whether the module compiles stubs or not.
+	// Should be set to true when srcs of this module are stub files.
+	// This property does not need to be set to true when the module depends on
+	// the stubs via libs, but should be set to true when the module depends on
+	// the stubs via static libs.
+	Is_stubs_module *bool
 }
 
 // Properties that are specific to device modules. Host module factories should not add these when
@@ -420,6 +441,8 @@
 	// inserting into the bootclasspath/classpath of another compile
 	headerJarFile android.Path
 
+	repackagedHeaderJarFile android.Path
+
 	// jar file containing implementation classes including static library dependencies but no
 	// resources
 	implementationJarFile android.Path
@@ -484,6 +507,9 @@
 	// expanded Jarjar_rules
 	expandJarjarRules android.Path
 
+	// jarjar rule for inherited jarjar rules
+	repackageJarjarRules android.Path
+
 	// Extra files generated by the module type to be added as java resources.
 	extraResources android.Paths
 
@@ -513,6 +539,12 @@
 
 	// Single aconfig "cache file" merged from this module and all dependencies.
 	mergedAconfigFiles map[string]android.Paths
+
+	// Values that will be set in the JarJarProvider data for jarjar repackaging,
+	// and merged with our dependencies' rules.
+	jarjarRenameRules map[string]string
+
+	stubsLinkType StubsLinkType
 }
 
 func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error {
@@ -612,7 +644,7 @@
 	// Populate with package rules from the properties.
 	hiddenAPIInfo.extractPackageRulesFromProperties(&j.deviceProperties.HiddenAPIPackageProperties)
 
-	ctx.SetProvider(hiddenAPIPropertyInfoProvider, hiddenAPIInfo)
+	android.SetProvider(ctx, hiddenAPIPropertyInfoProvider, hiddenAPIInfo)
 }
 
 func (j *Module) OutputFiles(tag string) (android.Paths, error) {
@@ -679,7 +711,7 @@
 	// Force enable the instrumentation for java code that is built for APEXes ...
 	// except for the jacocoagent itself (because instrumenting jacocoagent using jacocoagent
 	// doesn't make sense) or framework libraries (e.g. libraries found in the InstrumentFrameworkModules list) unless EMMA_INSTRUMENT_FRAMEWORK is true.
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	isJacocoAgent := ctx.ModuleName() == "jacocoagent"
 	if j.DirectlyInAnyApex() && !isJacocoAgent && !apexInfo.IsForPlatform() {
 		if !inList(ctx.ModuleName(), config.InstrumentFrameworkModules) {
@@ -1067,6 +1099,21 @@
 }
 
 func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspathJars, extraCombinedJars android.Paths) {
+
+	// Auto-propagating jarjar rules
+	jarjarProviderData := j.collectJarJarRules(ctx)
+	if jarjarProviderData != nil {
+		android.SetProvider(ctx, JarJarProvider, *jarjarProviderData)
+		if !proptools.Bool(j.properties.Skip_jarjar_repackage) {
+			text := getJarJarRuleText(jarjarProviderData)
+			if text != "" {
+				ruleTextFile := android.PathForModuleOut(ctx, "repackaged-jarjar", "repackaging.txt")
+				android.WriteFileRule(ctx, ruleTextFile, text)
+				j.repackageJarjarRules = ruleTextFile
+			}
+		}
+	}
+
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)
 
 	deps := j.collectDeps(ctx)
@@ -1137,7 +1184,7 @@
 	uniqueSrcFiles = append(uniqueSrcFiles, uniqueJavaFiles...)
 	uniqueSrcFiles = append(uniqueSrcFiles, uniqueKtFiles...)
 	j.uniqueSrcFiles = uniqueSrcFiles
-	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: uniqueSrcFiles.Strings()})
+	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: uniqueSrcFiles.Strings()})
 
 	// We don't currently run annotation processors in turbine, which means we can't use turbine
 	// generated header jars when an annotation processor that generates API is enabled.  One
@@ -1165,14 +1212,14 @@
 			ctx.ModuleErrorf("headers_only is enabled but Turbine is disabled.")
 		}
 
-		_, j.headerJarFile =
+		_, j.headerJarFile, _ =
 			j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName,
 				extraCombinedJars)
 		if ctx.Failed() {
 			return
 		}
 
-		ctx.SetProvider(JavaInfoProvider, JavaInfo{
+		android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
 			HeaderJars:                     android.PathsIfNonNil(j.headerJarFile),
 			TransitiveLibsHeaderJars:       j.transitiveLibsHeaderJars,
 			TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars,
@@ -1180,6 +1227,7 @@
 			ExportedPlugins:                j.exportedPluginJars,
 			ExportedPluginClasses:          j.exportedPluginClasses,
 			ExportedPluginDisableTurbine:   j.exportedDisableTurbine,
+			StubsLinkType:                  j.stubsLinkType,
 		})
 
 		j.outputFile = j.headerJarFile
@@ -1264,7 +1312,7 @@
 		}
 	}
 
-	jars := append(android.Paths(nil), kotlinJars...)
+	jars := slices.Clone(kotlinJars)
 
 	j.compiledSrcJars = srcJars
 
@@ -1279,8 +1327,8 @@
 			// allow for the use of annotation processors that do function correctly
 			// with sharding enabled. See: b/77284273.
 		}
-		extraJars := append(android.CopyOf(extraCombinedJars), kotlinHeaderJars...)
-		headerJarFileWithoutDepsOrJarjar, j.headerJarFile =
+		extraJars := append(slices.Clone(kotlinHeaderJars), extraCombinedJars...)
+		headerJarFileWithoutDepsOrJarjar, j.headerJarFile, j.repackagedHeaderJarFile =
 			j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName, extraJars)
 		if ctx.Failed() {
 			return
@@ -1353,6 +1401,8 @@
 		}
 	}
 
+	jars = append(jars, extraCombinedJars...)
+
 	j.srcJarArgs, j.srcJarDeps = resourcePathsToJarArgs(srcFiles), srcFiles
 
 	var includeSrcJar android.WritablePath
@@ -1439,8 +1489,6 @@
 		jars = append(jars, servicesJar)
 	}
 
-	jars = append(android.CopyOf(extraCombinedJars), jars...)
-
 	// Combine the classes built from sources, any manifests, and any static libraries into
 	// classes.jar. If there is only one input jar this step will be skipped.
 	var outputFile android.OutputPath
@@ -1504,6 +1552,16 @@
 		}
 	}
 
+	// Automatic jarjar rules propagation
+	if j.repackageJarjarRules != nil {
+		repackagedJarjarFile := android.PathForModuleOut(ctx, "repackaged-jarjar", jarName).OutputPath
+		TransformJarJar(ctx, repackagedJarjarFile, outputFile, j.repackageJarjarRules)
+		outputFile = repackagedJarjarFile
+		if ctx.Failed() {
+			return
+		}
+	}
+
 	// Check package restrictions if necessary.
 	if len(j.properties.Permitted_packages) > 0 {
 		// Time stamp file created by the package check rule.
@@ -1566,7 +1624,7 @@
 
 	// Enable dex compilation for the APEX variants, unless it is disabled explicitly
 	compileDex := j.dexProperties.Compile_dex
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	if j.DirectlyInAnyApex() && !apexInfo.IsForPlatform() {
 		if compileDex == nil {
 			compileDex = proptools.BoolPtr(true)
@@ -1621,7 +1679,7 @@
 			j.dexJarFile = makeDexJarPathFromPath(dexOutputFile)
 
 			// Dexpreopting
-			j.dexpreopt(ctx, dexOutputFile)
+			j.dexpreopt(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), dexOutputFile)
 
 			outputFile = dexOutputFile
 		} else {
@@ -1639,30 +1697,11 @@
 	}
 
 	if ctx.Device() {
-		lintSDKVersion := func(apiLevel android.ApiLevel) int {
+		lintSDKVersion := func(apiLevel android.ApiLevel) android.ApiLevel {
 			if !apiLevel.IsPreview() {
-				return apiLevel.FinalInt()
+				return apiLevel
 			} else {
-				// When running metalava, we pass --version-codename. When that value
-				// is not REL, metalava will add 1 to the --current-version argument.
-				// On old branches, PLATFORM_SDK_VERSION is the latest version (for that
-				// branch) and the codename is REL, except potentially on the most
-				// recent non-master branch. On that branch, it goes through two other
-				// phases before it gets to the phase previously described:
-				//  - PLATFORM_SDK_VERSION has not been updated yet, and the codename
-				//    is not rel. This happens for most of the internal branch's life
-				//    while the branch has been cut but is still under active development.
-				//  - PLATFORM_SDK_VERSION has been set, but the codename is still not
-				//    REL. This happens briefly during the release process. During this
-				//    state the code to add --current-version is commented out, and then
-				//    that commenting out is reverted after the codename is set to REL.
-				// On the master branch, the PLATFORM_SDK_VERSION always represents a
-				// prior version and the codename is always non-REL.
-				//
-				// We need to add one here to match metalava adding 1. Technically
-				// this means that in the state described in the second bullet point
-				// above, this number is 1 higher than it should be.
-				return ctx.Config().PlatformSdkVersion().FinalInt() + 1
+				return ctx.Config().DefaultAppTargetSdk(ctx)
 			}
 		}
 
@@ -1677,6 +1716,7 @@
 		j.linter.compileSdkKind = j.SdkVersion(ctx).Kind
 		j.linter.javaLanguageLevel = flags.javaVersion.String()
 		j.linter.kotlinLanguageLevel = "1.3"
+		j.linter.compile_data = android.PathsForModuleSrc(ctx, j.properties.Compile_data)
 		if !apexInfo.IsForPlatform() && ctx.Config().UnbundledBuildApps() {
 			j.linter.buildModuleReportZip = true
 		}
@@ -1687,10 +1727,11 @@
 
 	ctx.CheckbuildFile(outputFile)
 
-	aconfig.CollectDependencyAconfigFiles(ctx, &j.mergedAconfigFiles)
+	android.CollectDependencyAconfigFiles(ctx, &j.mergedAconfigFiles)
 
-	ctx.SetProvider(JavaInfoProvider, JavaInfo{
+	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
 		HeaderJars:                     android.PathsIfNonNil(j.headerJarFile),
+		RepackagedHeaderJars:           android.PathsIfNonNil(j.repackagedHeaderJarFile),
 		TransitiveLibsHeaderJars:       j.transitiveLibsHeaderJars,
 		TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars,
 		ImplementationAndResourcesJars: android.PathsIfNonNil(j.implementationAndResourcesJar),
@@ -1704,6 +1745,7 @@
 		ExportedPluginClasses:          j.exportedPluginClasses,
 		ExportedPluginDisableTurbine:   j.exportedDisableTurbine,
 		JacocoReportClassesFile:        j.jacocoReportClassesFile,
+		StubsLinkType:                  j.stubsLinkType,
 	})
 
 	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
@@ -1719,7 +1761,7 @@
 	transitiveProguardFlags := []*android.DepSet[android.Path]{}
 
 	ctx.VisitDirectDeps(func(m android.Module) {
-		depProguardInfo := ctx.OtherModuleProvider(m, ProguardSpecInfoProvider).(ProguardSpecInfo)
+		depProguardInfo, _ := android.OtherModuleProvider(ctx, m, ProguardSpecInfoProvider)
 		depTag := ctx.OtherModuleDependencyTag(m)
 
 		if depProguardInfo.UnconditionallyExportedProguardFlags != nil {
@@ -1826,7 +1868,7 @@
 
 func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars android.Paths,
 	deps deps, flags javaBuilderFlags, jarName string,
-	extraJars android.Paths) (headerJar, jarjarAndDepsHeaderJar android.Path) {
+	extraJars android.Paths) (headerJar, jarjarAndDepsHeaderJar, jarjarAndDepsRepackagedHeaderJar android.Path) {
 
 	var jars android.Paths
 	if len(srcFiles) > 0 || len(srcJars) > 0 {
@@ -1834,7 +1876,7 @@
 		turbineJar := android.PathForModuleOut(ctx, "turbine", jarName)
 		TransformJavaToHeaderClasses(ctx, turbineJar, srcFiles, srcJars, flags)
 		if ctx.Failed() {
-			return nil, nil
+			return nil, nil, nil
 		}
 		jars = append(jars, turbineJar)
 		headerJar = turbineJar
@@ -1859,11 +1901,22 @@
 		TransformJarJar(ctx, jarjarFile, jarjarAndDepsHeaderJar, j.expandJarjarRules)
 		jarjarAndDepsHeaderJar = jarjarFile
 		if ctx.Failed() {
-			return nil, nil
+			return nil, nil, nil
 		}
 	}
 
-	return headerJar, jarjarAndDepsHeaderJar
+	if j.repackageJarjarRules != nil {
+		repackagedJarjarFile := android.PathForModuleOut(ctx, "repackaged-turbine-jarjar", jarName)
+		TransformJarJar(ctx, repackagedJarjarFile, jarjarAndDepsHeaderJar, j.repackageJarjarRules)
+		jarjarAndDepsRepackagedHeaderJar = repackagedJarjarFile
+		if ctx.Failed() {
+			return nil, nil, nil
+		}
+	} else {
+		jarjarAndDepsRepackagedHeaderJar = jarjarAndDepsHeaderJar
+	}
+
+	return headerJar, jarjarAndDepsHeaderJar, jarjarAndDepsRepackagedHeaderJar
 }
 
 func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags,
@@ -1905,7 +1958,7 @@
 			return
 		}
 
-		dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+		dep, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider)
 		tag := ctx.OtherModuleDependencyTag(module)
 		_, isUsesLibDep := tag.(usesLibraryDependencyTag)
 		if tag == libTag || tag == r8LibraryJarTag || isUsesLibDep {
@@ -1942,7 +1995,7 @@
 	return android.Paths{j.implementationJarFile}
 }
 
-func (j *Module) DexJarBuildPath() OptionalDexJarPath {
+func (j *Module) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
 	return j.dexJarFile
 }
 
@@ -2030,7 +2083,7 @@
 	ctx.VisitDirectDeps(func(module android.Module) {
 		tag := ctx.OtherModuleDependencyTag(module)
 		if tag == staticLibTag {
-			depInfo := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+			depInfo, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider)
 			if depInfo.TransitiveSrcFiles != nil {
 				fromDeps = append(fromDeps, depInfo.TransitiveSrcFiles)
 			}
@@ -2202,15 +2255,14 @@
 			case staticLibTag:
 				ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
 			}
-		} else if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
-			dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
-			if sdkLinkType != javaPlatform &&
-				ctx.OtherModuleHasProvider(module, SyspropPublicStubInfoProvider) {
-				// dep is a sysprop implementation library, but this module is not linking against
-				// the platform, so it gets the sysprop public stubs library instead.  Replace
-				// dep with the JavaInfo from the SyspropPublicStubInfoProvider.
-				syspropDep := ctx.OtherModuleProvider(module, SyspropPublicStubInfoProvider).(SyspropPublicStubInfo)
-				dep = syspropDep.JavaInfo
+		} else if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
+			if sdkLinkType != javaPlatform {
+				if syspropDep, ok := android.OtherModuleProvider(ctx, module, SyspropPublicStubInfoProvider); ok {
+					// dep is a sysprop implementation library, but this module is not linking against
+					// the platform, so it gets the sysprop public stubs library instead.  Replace
+					// dep with the JavaInfo from the SyspropPublicStubInfoProvider.
+					dep = syspropDep.JavaInfo
+				}
 			}
 			switch tag {
 			case bootClasspathTag:
@@ -2221,6 +2273,10 @@
 				}
 				deps.classpath = append(deps.classpath, dep.HeaderJars...)
 				deps.dexClasspath = append(deps.dexClasspath, dep.HeaderJars...)
+				if len(dep.RepackagedHeaderJars) == 1 && !slices.Contains(dep.HeaderJars, dep.RepackagedHeaderJars[0]) {
+					deps.classpath = append(deps.classpath, dep.RepackagedHeaderJars...)
+					deps.dexClasspath = append(deps.dexClasspath, dep.RepackagedHeaderJars...)
+				}
 				deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...)
 				addPlugins(&deps, dep.ExportedPlugins, dep.ExportedPluginClasses...)
 				deps.disableTurbine = deps.disableTurbine || dep.ExportedPluginDisableTurbine
@@ -2282,7 +2338,7 @@
 			case syspropPublicStubDepTag:
 				// This is a sysprop implementation library, forward the JavaInfoProvider from
 				// the corresponding sysprop public stub library as SyspropPublicStubInfoProvider.
-				ctx.SetProvider(SyspropPublicStubInfoProvider, SyspropPublicStubInfo{
+				android.SetProvider(ctx, SyspropPublicStubInfoProvider, SyspropPublicStubInfo{
 					JavaInfo: dep,
 				})
 			}
@@ -2325,6 +2381,304 @@
 	return deps
 }
 
+// Provider for jarjar renaming rules.
+//
+// Modules can set their jarjar renaming rules with addJarJarRenameRule, and those renamings will be
+// passed to all rdeps.  The typical way that these renamings will NOT be inherited is when a module
+// links against stubs -- these are not passed through stubs. The classes will remain unrenamed on
+// classes until a module with jarjar_prefix is reached, and all as yet unrenamed classes will then
+// be renamed from that module.
+// TODO: Add another property to suppress the forwarding of
+type DependencyUse int
+
+const (
+	RenameUseInvalid DependencyUse = iota
+	RenameUseInclude
+	RenameUseExclude
+)
+
+type RenameUseElement struct {
+	DepName   string
+	RenameUse DependencyUse
+	Why       string // token for determining where in the logic the decision was made.
+}
+
+type JarJarProviderData struct {
+	// Mapping of class names: original --> renamed.  If the value is "", the class will be
+	// renamed by the next rdep that has the jarjar_prefix attribute (or this module if it has
+	// attribute). Rdeps of that module will inherit the renaming.
+	Rename    map[string]string
+	RenameUse []RenameUseElement
+}
+
+func (this JarJarProviderData) GetDebugString() string {
+	result := ""
+	for _, k := range android.SortedKeys(this.Rename) {
+		v := this.Rename[k]
+		if strings.Contains(k, "android.companion.virtual.flags.FakeFeatureFlagsImpl") {
+			result += k + "--&gt;" + v + ";"
+		}
+	}
+	return result
+}
+
+var JarJarProvider = blueprint.NewProvider[JarJarProviderData]()
+
+var overridableJarJarPrefix = "com.android.internal.hidden_from_bootclasspath"
+
+func init() {
+	android.SetJarJarPrefixHandler(mergeJarJarPrefixes)
+}
+
+// BaseJarJarProviderData contains information that will propagate across dependencies regardless of
+// whether they are java modules or not.
+type BaseJarJarProviderData struct {
+	JarJarProviderData JarJarProviderData
+}
+
+func (this BaseJarJarProviderData) GetDebugString() string {
+	return this.JarJarProviderData.GetDebugString()
+}
+
+var BaseJarJarProvider = blueprint.NewProvider[BaseJarJarProviderData]()
+
+// mergeJarJarPrefixes is called immediately before module.GenerateAndroidBuildActions is called.
+// Since there won't be a JarJarProvider, we create the BaseJarJarProvider if any of our deps have
+// either JarJarProvider or BaseJarJarProvider.
+func mergeJarJarPrefixes(ctx android.ModuleContext) {
+	mod := ctx.Module()
+	// Explicitly avoid propagating into some module types.
+	switch reflect.TypeOf(mod).String() {
+	case "*java.Droidstubs":
+		return
+	}
+	jarJarData := collectDirectDepsProviders(ctx)
+	if jarJarData != nil {
+		providerData := BaseJarJarProviderData{
+			JarJarProviderData: *jarJarData,
+		}
+		android.SetProvider(ctx, BaseJarJarProvider, providerData)
+	}
+
+}
+
+// Add a jarjar renaming rule to this module, to be inherited to all dependent modules.
+func (module *Module) addJarJarRenameRule(original string, renamed string) {
+	if module.jarjarRenameRules == nil {
+		module.jarjarRenameRules = make(map[string]string)
+	}
+	module.jarjarRenameRules[original] = renamed
+}
+
+func collectDirectDepsProviders(ctx android.ModuleContext) (result *JarJarProviderData) {
+	// Gather repackage information from deps
+	// If the dep jas a JarJarProvider, it is used.  Otherwise, any BaseJarJarProvider is used.
+
+	module := ctx.Module()
+	moduleName := module.Name()
+
+	ctx.VisitDirectDepsIgnoreBlueprint(func(m android.Module) {
+		tag := ctx.OtherModuleDependencyTag(m)
+		// This logic mirrors that in (*Module).collectDeps above.  There are several places
+		// where we explicitly return RenameUseExclude, even though it is the default, to
+		// indicate that it has been verified to be the case.
+		//
+		// Note well: there are probably cases that are getting to the unconditional return
+		// and are therefore wrong.
+		shouldIncludeRenames := func() (DependencyUse, string) {
+			if moduleName == m.Name() {
+				return RenameUseInclude, "name" // If we have the same module name, include the renames.
+			}
+			if sc, ok := module.(android.SdkContext); ok {
+				if ctx.Device() {
+					sdkDep := decodeSdkDep(ctx, sc)
+					if !sdkDep.invalidVersion && sdkDep.useFiles {
+						return RenameUseExclude, "useFiles"
+					}
+				}
+			}
+			if IsJniDepTag(tag) || tag == certificateTag || tag == proguardRaiseTag {
+				return RenameUseExclude, "tags"
+			}
+			if _, ok := m.(SdkLibraryDependency); ok {
+				switch tag {
+				case sdkLibTag, libTag:
+					return RenameUseExclude, "sdklibdep" // matches collectDeps()
+				}
+				return RenameUseInvalid, "sdklibdep" // dep is not used in collectDeps()
+			} else if ji, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
+				switch ji.StubsLinkType {
+				case Stubs:
+					return RenameUseExclude, "info"
+				case Implementation:
+					return RenameUseInclude, "info"
+				default:
+					//fmt.Printf("LJ: %v -> %v StubsLinkType unknown\n", module, m)
+					// Fall through to the heuristic logic.
+				}
+				switch reflect.TypeOf(m).String() {
+				case "*java.GeneratedJavaLibraryModule":
+					// Probably a java_aconfig_library module.
+					// TODO: make this check better.
+					return RenameUseInclude, "reflect"
+				}
+				switch tag {
+				case bootClasspathTag:
+					return RenameUseExclude, "tagswitch"
+				case sdkLibTag, libTag, instrumentationForTag:
+					return RenameUseInclude, "tagswitch"
+				case java9LibTag:
+					return RenameUseExclude, "tagswitch"
+				case staticLibTag:
+					return RenameUseInclude, "tagswitch"
+				case pluginTag:
+					return RenameUseInclude, "tagswitch"
+				case errorpronePluginTag:
+					return RenameUseInclude, "tagswitch"
+				case exportedPluginTag:
+					return RenameUseInclude, "tagswitch"
+				case kotlinStdlibTag, kotlinAnnotationsTag:
+					return RenameUseExclude, "tagswitch"
+				case kotlinPluginTag:
+					return RenameUseInclude, "tagswitch"
+				default:
+					return RenameUseExclude, "tagswitch"
+				}
+			} else if _, ok := m.(android.SourceFileProducer); ok {
+				switch tag {
+				case sdkLibTag, libTag, staticLibTag:
+					return RenameUseInclude, "srcfile"
+				default:
+					return RenameUseExclude, "srcfile"
+				}
+			} else if _, ok := android.OtherModuleProvider(ctx, m, aconfig.CodegenInfoProvider); ok {
+				return RenameUseInclude, "aconfig_declarations_group"
+			} else {
+				switch tag {
+				case bootClasspathTag:
+					return RenameUseExclude, "else"
+				case systemModulesTag:
+					return RenameUseInclude, "else"
+				}
+			}
+			// If we got here, choose the safer option, which may lead to a build failure, rather
+			// than runtime failures on the device.
+			return RenameUseExclude, "end"
+		}
+
+		if result == nil {
+			result = &JarJarProviderData{
+				Rename:    make(map[string]string),
+				RenameUse: make([]RenameUseElement, 0),
+			}
+		}
+		how, why := shouldIncludeRenames()
+		result.RenameUse = append(result.RenameUse, RenameUseElement{DepName: m.Name(), RenameUse: how, Why: why})
+		if how != RenameUseInclude {
+			// Nothing to merge.
+			return
+		}
+
+		merge := func(theirs *JarJarProviderData) {
+			for orig, renamed := range theirs.Rename {
+				if preexisting, exists := (*result).Rename[orig]; !exists || preexisting == "" {
+					result.Rename[orig] = renamed
+				} else if preexisting != "" && renamed != "" && preexisting != renamed {
+					if strings.HasPrefix(preexisting, overridableJarJarPrefix) {
+						result.Rename[orig] = renamed
+					} else if !strings.HasPrefix(renamed, overridableJarJarPrefix) {
+						ctx.ModuleErrorf("1. Conflicting jarjar rules inherited for class: %s (%s and %s)", orig, renamed, preexisting, ctx.ModuleName(), m.Name())
+						continue
+					}
+				}
+			}
+		}
+		if theirs, ok := android.OtherModuleProvider(ctx, m, JarJarProvider); ok {
+			merge(&theirs)
+		} else if theirs, ok := android.OtherModuleProvider(ctx, m, BaseJarJarProvider); ok {
+			// TODO: if every java.Module should have a JarJarProvider, and we find only the
+			// BaseJarJarProvider, then there is a bug.  Consider seeing if m can be cast
+			// to java.Module.
+			merge(&theirs.JarJarProviderData)
+		}
+	})
+	return
+}
+
+func (this Module) GetDebugString() string {
+	return "sdk_version=" + proptools.String(this.deviceProperties.Sdk_version)
+}
+
+// Merge the jarjar rules we inherit from our dependencies, any that have been added directly to
+// us, and if it's been set, apply the jarjar_prefix property to rename them.
+func (module *Module) collectJarJarRules(ctx android.ModuleContext) *JarJarProviderData {
+	// Gather repackage information from deps
+	result := collectDirectDepsProviders(ctx)
+
+	// Update that with entries we've stored for ourself
+	for orig, renamed := range module.jarjarRenameRules {
+		if result == nil {
+			result = &JarJarProviderData{
+				Rename: make(map[string]string),
+			}
+		}
+		if renamed != "" {
+			if preexisting, exists := (*result).Rename[orig]; exists && preexisting != renamed {
+				ctx.ModuleErrorf("Conflicting jarjar rules inherited for class: %s (%s and %s)", orig, renamed, preexisting)
+				continue
+			}
+		}
+		(*result).Rename[orig] = renamed
+	}
+
+	// If there are no renamings, then jarjar_prefix does nothing, so skip the extra work.
+	if result == nil {
+		return nil
+	}
+
+	// If they've given us a jarjar_prefix property, then we will use that to rename any classes
+	// that have not yet been renamed.
+	prefix := proptools.String(module.properties.Jarjar_prefix)
+	if prefix != "" {
+		if prefix[0] == '.' {
+			ctx.PropertyErrorf("jarjar_prefix", "jarjar_prefix can not start with '.'")
+			return nil
+		}
+		if prefix[len(prefix)-1] == '.' {
+			ctx.PropertyErrorf("jarjar_prefix", "jarjar_prefix can not end with '.'")
+			return nil
+		}
+
+		var updated map[string]string
+		for orig, renamed := range (*result).Rename {
+			if renamed == "" {
+				if updated == nil {
+					updated = make(map[string]string)
+				}
+				updated[orig] = prefix + "." + orig
+			}
+		}
+		for orig, renamed := range updated {
+			(*result).Rename[orig] = renamed
+		}
+	}
+
+	return result
+}
+
+// Get the jarjar rule text for a given provider for the fully resolved rules. Classes that map
+// to "" won't be in this list because they shouldn't be renamed yet.
+func getJarJarRuleText(provider *JarJarProviderData) string {
+	result := ""
+	for _, orig := range android.SortedKeys(provider.Rename) {
+		renamed := provider.Rename[orig]
+		if renamed != "" {
+			result += "rule " + orig + " " + renamed + "\n"
+		}
+	}
+	return result
+}
+
 func addPlugins(deps *deps, pluginJars android.Paths, pluginClasses ...string) {
 	deps.processorPath = append(deps.processorPath, pluginJars...)
 	deps.processorClasses = append(deps.processorClasses, pluginClasses...)
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 191a65e..7c45d30 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -229,6 +229,7 @@
 
 type BootclasspathFragmentModule struct {
 	android.ModuleBase
+	android.DefaultableModuleBase
 	android.ApexModuleBase
 	ClasspathFragmentBase
 
@@ -240,7 +241,8 @@
 	sourceOnlyProperties SourceOnlyBootclasspathProperties
 
 	// Path to the boot image profile.
-	profilePath android.WritablePath
+	profilePath    android.WritablePath
+	profilePathErr error
 }
 
 // commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt
@@ -266,6 +268,7 @@
 	android.InitApexModule(m)
 	initClasspathFragment(m, BOOTCLASSPATH)
 	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(m)
 
 	android.AddLoadHook(m, func(ctx android.LoadHookContext) {
 		// If code coverage has been enabled for the framework then append the properties with
@@ -352,7 +355,7 @@
 	}
 }
 
-var BootclasspathFragmentApexContentInfoProvider = blueprint.NewProvider(BootclasspathFragmentApexContentInfo{})
+var BootclasspathFragmentApexContentInfoProvider = blueprint.NewProvider[BootclasspathFragmentApexContentInfo]()
 
 // BootclasspathFragmentApexContentInfo contains the bootclasspath_fragments contributions to the
 // apex contents.
@@ -384,6 +387,10 @@
 	}
 }
 
+func (i BootclasspathFragmentApexContentInfo) DexBootJarPathMap() bootDexJarByModule {
+	return i.contentModuleDexJarPaths
+}
+
 func (i BootclasspathFragmentApexContentInfo) ProfilePathOnHost() android.Path {
 	return i.profilePathOnHost
 }
@@ -394,6 +401,11 @@
 
 func (b *BootclasspathFragmentModule) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
 	tag := ctx.OtherModuleDependencyTag(dep)
+
+	// If the module is a default module, do not check the tag
+	if _, ok := dep.(*Defaults); ok {
+		return true
+	}
 	if IsBootclasspathFragmentContentDepTag(tag) {
 		// Boot image contents are automatically added to apex.
 		return true
@@ -500,7 +512,7 @@
 	if ctx.Module() != ctx.FinalModule() {
 		b.HideFromMake()
 	}
-	ctx.SetProvider(testing.TestModuleProviderKey, testing.TestModuleProviderData{})
+	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 }
 
 // getProfileProviderApex returns the name of the apex that provides a boot image profile, or an
@@ -512,7 +524,7 @@
 	}
 
 	// Bootclasspath fragment modules that are for the platform do not produce boot related files.
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	for _, apex := range apexInfo.InApexVariants {
 		if isProfileProviderApex(ctx, apex) {
 			return apex
@@ -533,11 +545,11 @@
 
 	if profile != nil {
 		info.profilePathOnHost = profile
-		info.profileInstallPathInApex = profileInstallPathInApex
+		info.profileInstallPathInApex = ProfileInstallPathInApex
 	}
 
 	// Make the apex content info available for other modules.
-	ctx.SetProvider(BootclasspathFragmentApexContentInfoProvider, info)
+	android.SetProvider(ctx, BootclasspathFragmentApexContentInfoProvider, info)
 }
 
 // generateClasspathProtoBuildActions generates all required build actions for classpath.proto config
@@ -623,7 +635,7 @@
 	hiddenAPIInfo.HiddenAPIFlagOutput = output.HiddenAPIFlagOutput
 
 	//  Provide it for use by other modules.
-	ctx.SetProvider(HiddenAPIInfoProvider, hiddenAPIInfo)
+	android.SetProvider(ctx, HiddenAPIInfoProvider, hiddenAPIInfo)
 
 	return output
 }
@@ -744,7 +756,7 @@
 	}
 
 	// Make the information available for the sdk snapshot.
-	ctx.SetProvider(HiddenAPIInfoForSdkProvider, HiddenAPIInfoForSdk{
+	android.SetProvider(ctx, HiddenAPIInfoForSdkProvider, HiddenAPIInfoForSdk{
 		FlagFilesByCategory: flagFilesByCategory,
 		HiddenAPIFlagOutput: flagOutput,
 	})
@@ -876,7 +888,7 @@
 
 	// Get the hidden API information from the module.
 	mctx := ctx.SdkModuleContext()
-	hiddenAPIInfo := mctx.OtherModuleProvider(module, HiddenAPIInfoForSdkProvider).(HiddenAPIInfoForSdk)
+	hiddenAPIInfo, _ := android.OtherModuleProvider(mctx, module, HiddenAPIInfoForSdkProvider)
 	b.Flag_files_by_category = hiddenAPIInfo.FlagFilesByCategory
 
 	// Copy all the generated file paths.
@@ -937,7 +949,7 @@
 					builder.CopyToSnapshot(p, dest)
 					dests = append(dests, dest)
 				}
-				hiddenAPISet.AddProperty(category.PropertyName, dests)
+				hiddenAPISet.AddProperty(category.PropertyName(), dests)
 			}
 		}
 	}
@@ -1033,10 +1045,6 @@
 		return android.PathForModuleSrc(ctx, *src)
 	}
 
-	// Retrieve the dex files directly from the content modules. They in turn should retrieve the
-	// encoded dex jars from the prebuilt .apex files.
-	encodedBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, contents)
-
 	output := HiddenAPIOutput{
 		HiddenAPIFlagOutput: HiddenAPIFlagOutput{
 			AnnotationFlagsPath:   pathForSrc("hidden_api.annotation_flags", module.prebuiltProperties.Hidden_api.Annotation_flags),
@@ -1047,8 +1055,6 @@
 			StubFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Stub_flags, nil),
 			AllFlagsPath:  pathForOptionalSrc(module.prebuiltProperties.Hidden_api.All_flags, nil),
 		},
-
-		EncodedBootDexFilesByModule: encodedBootDexJarsByModule,
 	}
 
 	// TODO: Temporarily fallback to stub_flags/all_flags properties until prebuilts have been updated.
@@ -1065,15 +1071,21 @@
 		return nil
 	}
 
-	di := android.FindDeapexerProviderForModule(ctx)
-	if di == nil {
+	di, err := android.FindDeapexerProviderForModule(ctx)
+	if err != nil {
+		// An error was found, possibly due to multiple apexes in the tree that export this library
+		// Defer the error till a client tries to call getProfilePath
+		module.profilePathErr = err
 		return nil // An error has been reported by FindDeapexerProviderForModule.
 	}
 
-	return di.PrebuiltExportPath(profileInstallPathInApex)
+	return di.PrebuiltExportPath(ProfileInstallPathInApex)
 }
 
 func (b *PrebuiltBootclasspathFragmentModule) getProfilePath() android.Path {
+	if b.profilePathErr != nil {
+		panic(b.profilePathErr.Error())
+	}
 	return b.profilePath
 }
 
@@ -1087,12 +1099,16 @@
 func (module *PrebuiltBootclasspathFragmentModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
 	for _, apex := range module.ApexProperties.Apex_available {
 		if isProfileProviderApex(ctx, apex) {
-			return []string{profileInstallPathInApex}
+			return []string{ProfileInstallPathInApex}
 		}
 	}
 	return nil
 }
 
+func (module *PrebuiltBootclasspathFragmentModule) UseProfileGuidedDexpreopt() bool {
+	return false
+}
+
 var _ android.RequiredFilesFromPrebuiltApex = (*PrebuiltBootclasspathFragmentModule)(nil)
 
 func prebuiltBootclasspathFragmentFactory() android.Module {
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 828de21..8bc0a7e 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -222,6 +222,11 @@
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary", "mycoreplatform"),
 		FixtureConfigureApexBootJars("someapex:mysdklibrary"),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+			}
+		}),
 	).RunTestWithBp(t, `
 		bootclasspath_fragment {
 			name: "myfragment",
@@ -272,16 +277,16 @@
 	`)
 
 	fragment := result.Module("myfragment", "android_common")
-	info := result.ModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo)
+	info, _ := android.SingletonModuleProvider(result, fragment, HiddenAPIInfoProvider)
 
 	stubsJar := "out/soong/.intermediates/mystublib/android_common/dex/mystublib.jar"
 
 	// Stubs jars for mysdklibrary
-	publicStubsJar := "out/soong/.intermediates/mysdklibrary.stubs/android_common/dex/mysdklibrary.stubs.jar"
-	systemStubsJar := "out/soong/.intermediates/mysdklibrary.stubs.system/android_common/dex/mysdklibrary.stubs.system.jar"
+	publicStubsJar := "out/soong/.intermediates/mysdklibrary.stubs.exportable/android_common/dex/mysdklibrary.stubs.exportable.jar"
+	systemStubsJar := "out/soong/.intermediates/mysdklibrary.stubs.exportable.system/android_common/dex/mysdklibrary.stubs.exportable.system.jar"
 
 	// Stubs jars for myothersdklibrary
-	otherPublicStubsJar := "out/soong/.intermediates/myothersdklibrary.stubs/android_common/dex/myothersdklibrary.stubs.jar"
+	otherPublicStubsJar := "out/soong/.intermediates/myothersdklibrary.stubs.exportable/android_common/dex/myothersdklibrary.stubs.exportable.jar"
 
 	// Check that SdkPublic uses public stubs for all sdk libraries.
 	android.AssertPathsRelativeToTopEquals(t, "public dex stubs jar", []string{otherPublicStubsJar, publicStubsJar, stubsJar}, info.TransitiveStubDexJarsByScope.StubDexJarsForScope(PublicHiddenAPIScope))
@@ -456,16 +461,16 @@
 
 	// Make sure that the library exports hidden API properties for use by the bootclasspath_fragment.
 	library := result.Module("mynewlibrary", "android_common")
-	info := result.ModuleProvider(library, hiddenAPIPropertyInfoProvider).(HiddenAPIPropertyInfo)
+	info, _ := android.SingletonModuleProvider(result, library, hiddenAPIPropertyInfoProvider)
 	android.AssertArrayString(t, "split packages", []string{"sdklibrary", "newlibrary"}, info.SplitPackages)
 	android.AssertArrayString(t, "package prefixes", []string{"newlibrary.all.mine"}, info.PackagePrefixes)
 	android.AssertArrayString(t, "single packages", []string{"newlibrary.mine"}, info.SinglePackages)
 	for _, c := range HiddenAPIFlagFileCategories {
 		expectedMaxTargetQPaths := []string(nil)
-		if c.PropertyName == "max_target_q" {
+		if c.PropertyName() == "max_target_q" {
 			expectedMaxTargetQPaths = []string{"my-new-max-target-q.txt"}
 		}
-		android.AssertPathsRelativeToTopEquals(t, c.PropertyName, expectedMaxTargetQPaths, info.FlagFilesByCategory[c])
+		android.AssertPathsRelativeToTopEquals(t, c.PropertyName(), expectedMaxTargetQPaths, info.FlagFilesByCategory[c])
 	}
 
 	// Make sure that the signature-patterns.csv is passed all the appropriate package properties
diff --git a/java/builder.go b/java/builder.go
index d03c8e5..b07a622 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -212,6 +212,14 @@
 			CommandDeps: []string{"${config.MergeZipsCmd}"},
 		},
 		"jarArgs")
+	combineJarRsp = pctx.AndroidStaticRule("combineJarRsp",
+		blueprint.RuleParams{
+			Command:        `${config.MergeZipsCmd} --ignore-duplicates -j $jarArgs $out @$out.rsp`,
+			CommandDeps:    []string{"${config.MergeZipsCmd}"},
+			Rspfile:        "$out.rsp",
+			RspfileContent: "$in",
+		},
+		"jarArgs")
 
 	jarjar = pctx.AndroidStaticRule("jarjar",
 		blueprint.RuleParams{
@@ -274,11 +282,30 @@
 				` cat $$f; ` +
 				`done > $out`,
 		})
+
+	gatherReleasedFlaggedApisRule = pctx.AndroidStaticRule("gatherReleasedFlaggedApisRule",
+		blueprint.RuleParams{
+			Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}={state:bool}' ` +
+				`--out ${out} ` +
+				`${flags_path} ` +
+				`${filter_args} `,
+			CommandDeps: []string{"${aconfig}"},
+			Description: "aconfig_bool",
+		}, "flags_path", "filter_args")
+
+	generateMetalavaRevertAnnotationsRule = pctx.AndroidStaticRule("generateMetalavaRevertAnnotationsRule",
+		blueprint.RuleParams{
+			Command:     `${keep-flagged-apis} ${in} > ${out}`,
+			CommandDeps: []string{"${keep-flagged-apis}"},
+		})
 )
 
 func init() {
 	pctx.Import("android/soong/android")
 	pctx.Import("android/soong/java/config")
+
+	pctx.HostBinToolVariable("aconfig", "aconfig")
+	pctx.HostBinToolVariable("keep-flagged-apis", "keep-flagged-apis")
 }
 
 type javaBuilderFlags struct {
@@ -399,7 +426,7 @@
 		})
 }
 
-func turbineFlags(ctx android.ModuleContext, flags javaBuilderFlags) (string, android.Paths) {
+func turbineFlags(ctx android.ModuleContext, flags javaBuilderFlags, dir string) (string, android.Paths) {
 	var deps android.Paths
 
 	classpath := flags.classpath
@@ -424,13 +451,21 @@
 	deps = append(deps, classpath...)
 	turbineFlags := bootClasspath + " " + classpath.FormTurbineClassPath("--classpath ")
 
+	const flagsLimit = 32 * 1024
+	if len(turbineFlags) > flagsLimit {
+		flagsRspFile := android.PathForModuleOut(ctx, dir, "turbine-flags.rsp")
+		android.WriteFileRule(ctx, flagsRspFile, turbineFlags)
+		turbineFlags = "@" + flagsRspFile.String()
+		deps = append(deps, flagsRspFile)
+	}
+
 	return turbineFlags, deps
 }
 
 func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath,
 	srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
 
-	turbineFlags, deps := turbineFlags(ctx, flags)
+	turbineFlags, deps := turbineFlags(ctx, flags, "turbine")
 
 	deps = append(deps, srcJars...)
 
@@ -462,7 +497,7 @@
 func TurbineApt(ctx android.ModuleContext, outputSrcJar, outputResJar android.WritablePath,
 	srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
 
-	turbineFlags, deps := turbineFlags(ctx, flags)
+	turbineFlags, deps := turbineFlags(ctx, flags, "kapt")
 
 	deps = append(deps, srcJars...)
 
@@ -515,14 +550,14 @@
 
 	deps = append(deps, srcJars...)
 
-	classpath := flags.classpath
+	javacClasspath := flags.classpath
 
 	var bootClasspath string
 	if flags.javaVersion.usesJavaModules() {
 		var systemModuleDeps android.Paths
 		bootClasspath, systemModuleDeps = flags.systemModules.FormJavaSystemModulesPath(ctx.Device())
 		deps = append(deps, systemModuleDeps...)
-		classpath = append(flags.java9Classpath, classpath...)
+		javacClasspath = append(flags.java9Classpath, javacClasspath...)
 	} else {
 		deps = append(deps, flags.bootClasspath...)
 		if len(flags.bootClasspath) == 0 && ctx.Device() {
@@ -534,7 +569,19 @@
 		}
 	}
 
-	deps = append(deps, classpath...)
+	classpathArg := javacClasspath.FormJavaClassPath("-classpath")
+
+	// Keep the command line under the MAX_ARG_STRLEN limit by putting the classpath argument into an rsp file
+	// if it is too long.
+	const classpathLimit = 64 * 1024
+	if len(classpathArg) > classpathLimit {
+		classpathRspFile := outputFile.ReplaceExtension(ctx, "classpath")
+		android.WriteFileRule(ctx, classpathRspFile, classpathArg)
+		deps = append(deps, classpathRspFile)
+		classpathArg = "@" + classpathRspFile.String()
+	}
+
+	deps = append(deps, javacClasspath...)
 	deps = append(deps, flags.processorPath...)
 
 	processor := "-proc:none"
@@ -565,7 +612,7 @@
 		Args: map[string]string{
 			"javacFlags":    flags.javacFlags,
 			"bootClasspath": bootClasspath,
-			"classpath":     classpath.FormJavaClassPath("-classpath"),
+			"classpath":     classpathArg,
 			"processorpath": flags.processorPath.FormJavaClassPath("-processorpath"),
 			"processor":     processor,
 			"srcJars":       strings.Join(srcJars.Strings(), " "),
@@ -624,8 +671,23 @@
 		jarArgs = append(jarArgs, "-D")
 	}
 
+	rule := combineJar
+	// Keep the command line under the MAX_ARG_STRLEN limit by putting the list of jars into an rsp file
+	// if it is too long.
+	const jarsLengthLimit = 64 * 1024
+	jarsLength := 0
+	for i, jar := range jars {
+		if i != 0 {
+			jarsLength += 1
+		}
+		jarsLength += len(jar.String())
+	}
+	if jarsLength > jarsLengthLimit {
+		rule = combineJarRsp
+	}
+
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        combineJar,
+		Rule:        rule,
 		Description: desc,
 		Output:      outputFile,
 		Inputs:      jars,
diff --git a/java/classpath_element.go b/java/classpath_element.go
index 4962916..abbcae7 100644
--- a/java/classpath_element.go
+++ b/java/classpath_element.go
@@ -21,7 +21,6 @@
 	"strings"
 
 	"android/soong/android"
-	"github.com/google/blueprint"
 )
 
 // Supports constructing a list of ClasspathElement from a set of fragments and modules.
@@ -72,8 +71,7 @@
 
 // ClasspathElementContext defines the context methods needed by CreateClasspathElements
 type ClasspathElementContext interface {
-	OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool
-	OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{}
+	android.OtherModuleProviderContext
 	ModuleErrorf(fmt string, args ...interface{})
 }
 
@@ -123,12 +121,12 @@
 	// associated with a particular apex.
 	apexToFragment := map[string]android.Module{}
 	for _, fragment := range fragments {
-		if !ctx.OtherModuleHasProvider(fragment, android.ApexInfoProvider) {
+		apexInfo, ok := android.OtherModuleProvider(ctx, fragment, android.ApexInfoProvider)
+		if !ok {
 			ctx.ModuleErrorf("fragment %s is not part of an apex", fragment)
 			continue
 		}
 
-		apexInfo := ctx.OtherModuleProvider(fragment, android.ApexInfoProvider).(android.ApexInfo)
 		for _, apex := range apexInfo.InApexVariants {
 			if existing, ok := apexToFragment[apex]; ok {
 				ctx.ModuleErrorf("apex %s has multiple fragments, %s and %s", apex, fragment, existing)
@@ -146,8 +144,7 @@
 	// Iterate over the libraries to construct the ClasspathElements list.
 	for _, library := range libraries {
 		var element ClasspathElement
-		if ctx.OtherModuleHasProvider(library, android.ApexInfoProvider) {
-			apexInfo := ctx.OtherModuleProvider(library, android.ApexInfoProvider).(android.ApexInfo)
+		if apexInfo, ok := android.OtherModuleProvider(ctx, library, android.ApexInfoProvider); ok {
 
 			var fragment android.Module
 
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
index bc9de50..0ebab4d 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -103,8 +103,8 @@
 func gatherPossibleApexModuleNamesAndStems(ctx android.ModuleContext, contents []string, tag blueprint.DependencyTag) []string {
 	set := map[string]struct{}{}
 	for _, name := range contents {
-		dep := ctx.GetDirectDepWithTag(name, tag)
-		set[name] = struct{}{}
+		dep, _ := ctx.GetDirectDepWithTag(name, tag).(android.Module)
+		set[ModuleStemForDeapexing(dep)] = struct{}{}
 		if m, ok := dep.(ModuleWithStem); ok {
 			set[m.Stem()] = struct{}{}
 		} else {
@@ -178,7 +178,7 @@
 		ClasspathFragmentProtoInstallDir: c.installDirPath,
 		ClasspathFragmentProtoOutput:     c.outputFilepath,
 	}
-	ctx.SetProvider(ClasspathFragmentProtoContentInfoProvider, classpathProtoInfo)
+	android.SetProvider(ctx, ClasspathFragmentProtoContentInfoProvider, classpathProtoInfo)
 }
 
 func writeClasspathsTextproto(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) {
@@ -211,7 +211,7 @@
 	}}
 }
 
-var ClasspathFragmentProtoContentInfoProvider = blueprint.NewProvider(ClasspathFragmentProtoContentInfo{})
+var ClasspathFragmentProtoContentInfoProvider = blueprint.NewProvider[ClasspathFragmentProtoContentInfo]()
 
 type ClasspathFragmentProtoContentInfo struct {
 	// Whether the classpaths.proto config is generated for the fragment.
diff --git a/java/code_metadata_test.go b/java/code_metadata_test.go
index 4b05d9e..0ef348a 100644
--- a/java/code_metadata_test.go
+++ b/java/code_metadata_test.go
@@ -25,14 +25,10 @@
 	}`
 	result := runCodeMetadataTest(t, android.FixtureExpectsNoErrors, bp)
 
-	module := result.ModuleForTests(
-		"module-name", "",
-	).Module().(*soongTesting.CodeMetadataModule)
+	module := result.ModuleForTests("module-name", "")
 
 	// Check that the provider has the right contents
-	data := result.ModuleProvider(
-		module, soongTesting.CodeMetadataProviderKey,
-	).(soongTesting.CodeMetadataProviderData)
+	data, _ := android.SingletonModuleProvider(result, module.Module(), soongTesting.CodeMetadataProviderKey)
 	if !strings.HasSuffix(
 		data.IntermediatePath.String(), "/intermediateCodeMetadata.pb",
 	) {
@@ -42,13 +38,8 @@
 		)
 	}
 
-	buildParamsSlice := module.BuildParamsForTests()
-	var metadata = ""
-	for _, params := range buildParamsSlice {
-		if params.Rule.String() == "android/soong/android.writeFile" {
-			metadata = params.Args["content"]
-		}
-	}
+	metadata := android.ContentFromFileRuleForTests(t, result.TestContext,
+		module.Output(data.IntermediatePath.String()))
 
 	metadataList := make([]*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership, 0, 2)
 	teamId := "12345"
@@ -65,9 +56,7 @@
 
 	CodeMetadataMetadata := code_metadata_internal_proto.CodeMetadataInternal{TargetOwnershipList: metadataList}
 	protoData, _ := proto.Marshal(&CodeMetadataMetadata)
-	rawData := string(protoData)
-	formattedData := strings.ReplaceAll(rawData, "\n", "\\n")
-	expectedMetadata := "'" + formattedData + "\\n'"
+	expectedMetadata := string(protoData)
 
 	if metadata != expectedMetadata {
 		t.Errorf(
@@ -114,7 +103,7 @@
 	}
 }
 func runCodeMetadataTest(
-		t *testing.T, errorHandler android.FixtureErrorHandler, bp string,
+	t *testing.T, errorHandler android.FixtureErrorHandler, bp string,
 ) *android.TestResult {
 	return android.GroupFixturePreparers(
 		soongTesting.PrepareForTestWithTestingBuildComponents, prepareForJavaTest,
diff --git a/java/config/config.go b/java/config/config.go
index d80ed41..6a945ac 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -95,13 +95,11 @@
 		"-JXX:TieredStopAtLevel=1",
 		"-JDcom.android.tools.r8.emitRecordAnnotationsInDex",
 		"-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex",
-		"-JDcom.android.tools.r8.emitRecordAnnotationsExInDex",
 	}, dexerJavaVmFlagsList...))
 	exportedVars.ExportStringListStaticVariable("R8Flags", append([]string{
 		"-JXmx4096M",
 		"-JDcom.android.tools.r8.emitRecordAnnotationsInDex",
 		"-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex",
-		"-JDcom.android.tools.r8.emitRecordAnnotationsExInDex",
 	}, dexerJavaVmFlagsList...))
 
 	exportedVars.ExportStringListStaticVariable("CommonJdkFlags", []string{
diff --git a/java/config/droidstubs.go b/java/config/droidstubs.go
index 5ee262f..39eec44 100644
--- a/java/config/droidstubs.go
+++ b/java/config/droidstubs.go
@@ -51,13 +51,6 @@
 	}
 
 	MetalavaAnnotationsWarningsFlags = strings.Join(metalavaAnnotationsWarningsFlags, " ")
-
-	metalavaHideFlaggedApis = []string{
-		"--revert-annotation",
-		"android.annotation.FlaggedApi",
-	}
-
-	MetalavaHideFlaggedApis = strings.Join(metalavaHideFlaggedApis, " ")
 )
 
 const (
@@ -69,8 +62,6 @@
 
 	exportedVars.ExportString("MetalavaAddOpens", MetalavaAddOpens)
 
-	exportedVars.ExportStringList("MetalavaHideFlaggedApis", metalavaHideFlaggedApis)
-
 	exportedVars.ExportStringListStaticVariable("MetalavaAnnotationsFlags", metalavaAnnotationsFlags)
 
 	exportedVars.ExportStringListStaticVariable("MetalavaAnnotationWarningsFlags", metalavaAnnotationsWarningsFlags)
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index c6ab561..ab72e8b 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -38,9 +38,6 @@
     visibility: ["//visibility:public"],
     sdk_version: "none",
     system_modules: "none",
-    dist: {
-        targets: dist_targets,
-    },
 }
 
 java_library {
@@ -67,6 +64,7 @@
         "stub-annotations",
     ],
     enable_validation: false,
+    stubs_type: "everything",
 }
 
 java_library {
@@ -89,6 +87,32 @@
     },
 }
 
+java_library {
+    name: "core.current.stubs.exportable.from-source",
+    defaults: [
+        "core.current.stubs.defaults",
+    ],
+    static_libs: [
+        "art.module.public.api.stubs.exportable",
+        "conscrypt.module.public.api.stubs.exportable",
+        "i18n.module.public.api.stubs.exportable",
+    ],
+    dist: {
+        targets: dist_targets,
+        dest: "core.current.stubs.jar",
+    },
+}
+
+java_library {
+    name: "core.current.stubs.exportable",
+    defaults: [
+        "core.current.stubs.defaults",
+    ],
+    static_libs: [
+        "core.current.stubs.exportable.from-source",
+    ],
+}
+
 // Distributed with the SDK for turning into system modules to compile apps
 // against.
 //
@@ -225,6 +249,7 @@
         "stub-annotations",
     ],
     visibility: ["//visibility:private"],
+    stubs_type: "everything",
 }
 
 // Produces a dist file that is used by the
@@ -298,6 +323,19 @@
     ],
 }
 
+java_library {
+    name: "legacy.core.platform.api.stubs.exportable.from-source",
+    visibility: core_platform_visibility,
+    defaults: [
+        "core.platform.api.stubs.defaults",
+    ],
+    static_libs: [
+        "art.module.public.api.stubs.exportable.module_lib",
+        "conscrypt.module.platform.api.stubs.exportable",
+        "legacy.i18n.module.platform.api.stubs.exportable",
+    ],
+}
+
 java_defaults {
     name: "android_core_platform_stubs_current_contributions",
     api_surface: "core_platform",
@@ -322,6 +360,7 @@
     libs: [
         "stub-annotations",
     ],
+    stubs_type: "everything",
 }
 
 java_library {
@@ -345,6 +384,17 @@
     },
 }
 
+java_library {
+    name: "legacy.core.platform.api.stubs.exportable",
+    visibility: core_platform_visibility,
+    defaults: [
+        "core.platform.api.stubs.defaults",
+    ],
+    static_libs: [
+        "legacy.core.platform.api.stubs.exportable.from-source",
+    ],
+}
+
 java_defaults {
     name: "core.platform.api.stubs.defaults",
     hostdex: true,
@@ -399,6 +449,7 @@
     libs: [
         "stub-annotations",
     ],
+    stubs_type: "everything",
 }
 
 java_library {
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index ee8a15a..3f8735c 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -97,8 +97,7 @@
 	}
 
 	ctx.VisitDirectDepsWithTag(deviceHostConverterDepTag, func(m android.Module) {
-		if ctx.OtherModuleHasProvider(m, JavaInfoProvider) {
-			dep := ctx.OtherModuleProvider(m, JavaInfoProvider).(JavaInfo)
+		if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
 			d.headerJars = append(d.headerJars, dep.HeaderJars...)
 			d.implementationJars = append(d.implementationJars, dep.ImplementationJars...)
 			d.implementationAndResourceJars = append(d.implementationAndResourceJars, dep.ImplementationAndResourcesJars...)
@@ -131,13 +130,14 @@
 		d.combinedHeaderJar = d.headerJars[0]
 	}
 
-	ctx.SetProvider(JavaInfoProvider, JavaInfo{
+	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
 		HeaderJars:                     d.headerJars,
 		ImplementationAndResourcesJars: d.implementationAndResourceJars,
 		ImplementationJars:             d.implementationJars,
 		ResourceJars:                   d.resourceJars,
 		SrcJarArgs:                     d.srcJarArgs,
 		SrcJarDeps:                     d.srcJarDeps,
+		StubsLinkType:                  Implementation,
 		// TODO: Not sure if aconfig flags that have been moved between device and host variants
 		// make sense.
 	})
@@ -152,7 +152,7 @@
 	return d.implementationAndResourceJars
 }
 
-func (d *DeviceHostConverter) DexJarBuildPath() android.Path {
+func (d *DeviceHostConverter) DexJarBuildPath(ctx android.ModuleErrorfContext) android.Path {
 	return nil
 }
 
diff --git a/java/dex.go b/java/dex.go
index 6f1c09d..4474c63 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -261,8 +261,8 @@
 	// See b/20667396
 	var proguardRaiseDeps classpath
 	ctx.VisitDirectDepsWithTag(proguardRaiseTag, func(m android.Module) {
-		dep := ctx.OtherModuleProvider(m, JavaInfoProvider).(JavaInfo)
-		proguardRaiseDeps = append(proguardRaiseDeps, dep.HeaderJars...)
+		dep, _ := android.OtherModuleProvider(ctx, m, JavaInfoProvider)
+		proguardRaiseDeps = append(proguardRaiseDeps, dep.RepackagedHeaderJars...)
 	})
 
 	r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index fe8c5fb..38ed856 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -29,7 +29,7 @@
 	IsInstallable() bool
 
 	// True if dexpreopt is disabled for the java module.
-	dexpreoptDisabled(ctx android.BaseModuleContext) bool
+	dexpreoptDisabled(ctx android.BaseModuleContext, libraryName string) bool
 
 	// If the java module is to be installed into an APEX, this list contains information about the
 	// dexpreopt outputs to be installed on devices. Note that these dexpreopt outputs are installed
@@ -79,22 +79,34 @@
 func (install dexpreopterInstall) ToMakeEntries() android.AndroidMkEntries {
 	return android.AndroidMkEntries{
 		Class:      "ETC",
-		SubName:    install.SubModuleName(),
 		OutputFile: android.OptionalPathForPath(install.outputPathOnHost),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE", install.FullModuleName())
 				entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice)
 				entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false")
+				// Unset LOCAL_SOONG_INSTALLED_MODULE so that this does not default to the primary .apex file
+				// Without this, installation of the dexpreopt artifacts get skipped
+				entries.SetString("LOCAL_SOONG_INSTALLED_MODULE", "")
 			},
 		},
 	}
 }
 
+type Dexpreopter struct {
+	dexpreopter
+}
+
 type dexpreopter struct {
 	dexpreoptProperties       DexpreoptProperties
 	importDexpreoptProperties ImportDexpreoptProperties
 
+	// If true, the dexpreopt rules will not be generated
+	// Unlike Dex_preopt.Enabled which is user-facing,
+	// shouldDisableDexpreopt is a mutated propery.
+	shouldDisableDexpreopt bool
+
 	installPath         android.InstallPath
 	uncompressedDex     bool
 	isSDKLibrary        bool
@@ -166,24 +178,45 @@
 }
 
 func isApexVariant(ctx android.BaseModuleContext) bool {
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	return !apexInfo.IsForPlatform()
 }
 
 func forPrebuiltApex(ctx android.BaseModuleContext) bool {
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	return apexInfo.ForPrebuiltApex
 }
 
-func moduleName(ctx android.BaseModuleContext) string {
-	// Remove the "prebuilt_" prefix if the module is from a prebuilt because the prefix is not
-	// expected by dexpreopter.
-	return android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName())
+// For apex variant of modules, this returns true on the source variant if the prebuilt apex
+// has been selected using apex_contributions.
+// The prebuilt apex will be responsible for generating the dexpreopt rules of the deapexed java lib.
+func disableSourceApexVariant(ctx android.BaseModuleContext) bool {
+	if !isApexVariant(ctx) {
+		return false // platform variant
+	}
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	psi := android.PrebuiltSelectionInfoMap{}
+	ctx.VisitDirectDepsWithTag(android.PrebuiltDepTag, func(am android.Module) {
+		psi, _ = android.OtherModuleProvider(ctx, am, android.PrebuiltSelectionInfoProvider)
+	})
+	// Find the apex variant for this module
+	_, apexVariantsWithoutTestApexes, _ := android.ListSetDifference(apexInfo.InApexVariants, apexInfo.TestApexes)
+	disableSource := false
+	// find the selected apexes
+	for _, apexVariant := range apexVariantsWithoutTestApexes {
+		for _, selected := range psi.GetSelectedModulesForApiDomain(apexVariant) {
+			// If the apex_contribution for this api domain contains a prebuilt apex, disable the source variant
+			if strings.HasPrefix(selected, "prebuilt_com.google.android") {
+				disableSource = true
+			}
+		}
+	}
+	return disableSource
 }
 
 // Returns whether dexpreopt is applicable to the module.
 // When it returns true, neither profile nor dexpreopt artifacts will be generated.
-func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool {
+func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext, libName string) bool {
 	if !ctx.Device() {
 		return true
 	}
@@ -196,6 +229,10 @@
 		return true
 	}
 
+	if d.shouldDisableDexpreopt {
+		return true
+	}
+
 	// If the module is from a prebuilt APEX, it shouldn't be installable, but it can still be
 	// dexpreopted.
 	if !ctx.Module().(DexpreopterInterface).IsInstallable() && !forPrebuiltApex(ctx) {
@@ -206,11 +243,24 @@
 		return true
 	}
 
+	if disableSourceApexVariant(ctx) {
+		return true
+	}
+
+	if _, isApex := android.ModuleProvider(ctx, android.ApexBundleInfoProvider); isApex {
+		// dexpreopt rules for system server jars can be generated in the ModuleCtx of prebuilt apexes
+		return false
+	}
+
 	global := dexpreopt.GetGlobalConfig(ctx)
 
-	isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx))
-	if isApexVariant(ctx) {
-		// Don't preopt APEX variant module unless the module is an APEX system server jar.
+	// Use the libName argument to determine if the library being dexpreopt'd is a system server jar
+	// ctx.ModuleName() is not safe. In case of prebuilt apexes, the dexpreopt rules of system server jars
+	// are created in the ctx object of the top-level prebuilt apex.
+	isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(libName)
+
+	if _, isApex := android.ModuleProvider(ctx, android.ApexBundleInfoProvider); isApex || isApexVariant(ctx) {
+		// dexpreopt rules for system server jars can be generated in the ModuleCtx of prebuilt apexes
 		if !isApexSystemServerJar {
 			return true
 		}
@@ -227,14 +277,20 @@
 }
 
 func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) {
-	if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) || !dexpreopt.IsDex2oatNeeded(ctx) {
+	if _, isApex := android.ModuleProvider(ctx, android.ApexBundleInfoProvider); isApex && dexpreopt.IsDex2oatNeeded(ctx) {
+		// prebuilt apexes can genererate rules to dexpreopt deapexed jars
+		// Add a dex2oat dep aggressively on _every_ apex module
+		dexpreopt.RegisterToolDeps(ctx)
+		return
+	}
+	if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName())) || !dexpreopt.IsDex2oatNeeded(ctx) {
 		return
 	}
 	dexpreopt.RegisterToolDeps(ctx)
 }
 
-func (d *dexpreopter) odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool {
-	return dexpreopt.OdexOnSystemOtherByName(moduleName(ctx), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
+func (d *dexpreopter) odexOnSystemOther(ctx android.ModuleContext, libName string, installPath android.InstallPath) bool {
+	return dexpreopt.OdexOnSystemOtherByName(libName, android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
 }
 
 // Returns the install path of the dex jar of a module.
@@ -245,20 +301,42 @@
 // This function is on a best-effort basis. It cannot handle the case where an APEX jar is not a
 // system server jar, which is fine because we currently only preopt system server jars for APEXes.
 func (d *dexpreopter) getInstallPath(
-	ctx android.ModuleContext, defaultInstallPath android.InstallPath) android.InstallPath {
+	ctx android.ModuleContext, libName string, defaultInstallPath android.InstallPath) android.InstallPath {
 	global := dexpreopt.GetGlobalConfig(ctx)
-	if global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) {
-		dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, moduleName(ctx))
+	if global.AllApexSystemServerJars(ctx).ContainsJar(libName) {
+		dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, libName)
 		return android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexLocation, "/"))
 	}
-	if !d.dexpreoptDisabled(ctx) && isApexVariant(ctx) &&
+	if !d.dexpreoptDisabled(ctx, libName) && isApexVariant(ctx) &&
 		filepath.Base(defaultInstallPath.PartitionDir()) != "apex" {
 		ctx.ModuleErrorf("unable to get the install path of the dex jar for dexpreopt")
 	}
 	return defaultInstallPath
 }
 
-func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.WritablePath) {
+// DexpreoptPrebuiltApexSystemServerJars generates the dexpreopt artifacts from a jar file that has been deapexed from a prebuilt apex
+func (d *Dexpreopter) DexpreoptPrebuiltApexSystemServerJars(ctx android.ModuleContext, libraryName string, di *android.DeapexerInfo) {
+	// A single prebuilt apex can have multiple apex system jars
+	// initialize the output path for this dex jar
+	dc := dexpreopt.GetGlobalConfig(ctx)
+	d.installPath = android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexpreopt.GetSystemServerDexLocation(ctx, dc, libraryName), "/"))
+	// generate the rules for creating the .odex and .vdex files for this system server jar
+	dexJarFile := di.PrebuiltExportPath(ApexRootRelativePathToJavaLib(libraryName))
+
+	d.inputProfilePathOnHost = nil // reset: TODO(spandandas): Make dexpreopter stateless
+	if android.InList(libraryName, di.GetDexpreoptProfileGuidedExportedModuleNames()) {
+		// Set the profile path to guide optimization
+		prof := di.PrebuiltExportPath(ApexRootRelativePathToJavaLib(libraryName) + ".prof")
+		if prof == nil {
+			ctx.ModuleErrorf("Could not find a .prof file in this prebuilt apex")
+		}
+		d.inputProfilePathOnHost = prof
+	}
+
+	d.dexpreopt(ctx, libraryName, dexJarFile)
+}
+
+func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, libName string, dexJarFile android.WritablePath) {
 	global := dexpreopt.GetGlobalConfig(ctx)
 
 	// TODO(b/148690468): The check on d.installPath is to bail out in cases where
@@ -271,7 +349,7 @@
 
 	dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
 
-	providesUsesLib := moduleName(ctx)
+	providesUsesLib := libName
 	if ulib, ok := ctx.Module().(ProvidesUsesLib); ok {
 		name := ulib.ProvidesUsesLib()
 		if name != nil {
@@ -281,11 +359,11 @@
 
 	// If it is test, make config files regardless of its dexpreopt setting.
 	// The config files are required for apps defined in make which depend on the lib.
-	if d.isTest && d.dexpreoptDisabled(ctx) {
+	if d.isTest && d.dexpreoptDisabled(ctx, libName) {
 		return
 	}
 
-	isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(moduleName(ctx))
+	isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(libName)
 
 	bootImage := defaultBootImageConfig(ctx)
 	// When `global.PreoptWithUpdatableBcp` is true, `bcpForDexpreopt` below includes the mainline
@@ -304,7 +382,7 @@
 				targets = append(targets, target)
 			}
 		}
-		if isSystemServerJar && moduleName(ctx) != "com.android.location.provider" {
+		if isSystemServerJar && libName != "com.android.location.provider" {
 			// If the module is a system server jar, only preopt for the primary arch because the jar can
 			// only be loaded by system server. "com.android.location.provider" is a special case because
 			// it's also used by apps as a shared library.
@@ -327,6 +405,7 @@
 	var profileClassListing android.OptionalPath
 	var profileBootListing android.OptionalPath
 	profileIsTextListing := false
+
 	if d.inputProfilePathOnHost != nil {
 		profileClassListing = android.OptionalPathForPath(d.inputProfilePathOnHost)
 	} else if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) && !forPrebuiltApex(ctx) {
@@ -340,17 +419,21 @@
 			profileIsTextListing = true
 		} else if global.ProfileDir != "" {
 			profileClassListing = android.ExistentPathForSource(ctx,
-				global.ProfileDir, moduleName(ctx)+".prof")
+				global.ProfileDir, libName+".prof")
 		}
 	}
 
 	d.dexpreoptProperties.Dex_preopt_result.Profile_guided = profileClassListing.Valid()
 
+	// A single apex can have multiple system server jars
+	// Use the dexJar to create a unique scope for each
+	dexJarStem := strings.TrimSuffix(dexJarFile.Base(), dexJarFile.Ext())
+
 	// Full dexpreopt config, used to create dexpreopt build rules.
 	dexpreoptConfig := &dexpreopt.ModuleConfig{
-		Name:            moduleName(ctx),
+		Name:            libName,
 		DexLocation:     dexLocation,
-		BuildPath:       android.PathForModuleOut(ctx, "dexpreopt", moduleName(ctx)+".jar").OutputPath,
+		BuildPath:       android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, libName+".jar").OutputPath,
 		DexPath:         dexJarFile,
 		ManifestPath:    android.OptionalPathForPath(d.manifestFile),
 		UncompressedDex: d.uncompressedDex,
@@ -380,10 +463,10 @@
 		PresignedPrebuilt: d.isPresignedPrebuilt,
 	}
 
-	d.configPath = android.PathForModuleOut(ctx, "dexpreopt", "dexpreopt.config")
+	d.configPath = android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, "dexpreopt.config")
 	dexpreopt.WriteModuleConfig(ctx, dexpreoptConfig, d.configPath)
 
-	if d.dexpreoptDisabled(ctx) {
+	if d.dexpreoptDisabled(ctx, libName) {
 		return
 	}
 
@@ -394,7 +477,7 @@
 	// dependencies to create a per-app list, and use `rsync --checksum` to prevent the file's mtime
 	// from being changed if the contents don't change. This avoids unnecessary dexpreopt reruns.
 	productPackages := android.PathForModuleInPartitionInstall(ctx, "", "product_packages.txt")
-	appProductPackages := android.PathForModuleOut(ctx, "dexpreopt", "product_packages.txt")
+	appProductPackages := android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, "product_packages.txt")
 	appProductPackagesStaging := appProductPackages.ReplaceExtension(ctx, "txt.tmp")
 	clcNames, _ := dexpreopt.ComputeClassLoaderContextDependencies(dexpreoptConfig.ClassLoaderContexts)
 	sort.Strings(clcNames) // The order needs to be deterministic.
@@ -416,7 +499,7 @@
 		Text("rsync --checksum").
 		Input(appProductPackagesStaging).
 		Output(appProductPackages)
-	productPackagesRule.Restat().Build("product_packages", "dexpreopt product_packages")
+	productPackagesRule.Restat().Build("product_packages."+dexJarStem, "dexpreopt product_packages")
 
 	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(
 		ctx, globalSoong, global, dexpreoptConfig, appProductPackages)
@@ -425,9 +508,11 @@
 		return
 	}
 
-	dexpreoptRule.Build("dexpreopt", "dexpreopt")
+	dexpreoptRule.Build("dexpreopt"+"."+dexJarStem, "dexpreopt")
 
-	isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx))
+	// The current ctx might be of a deapexer module created by a prebuilt apex
+	// Use the path of the dex file to determine the library name
+	isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(dexJarStem)
 
 	for _, install := range dexpreoptRule.Installs() {
 		// Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT.
@@ -452,7 +537,7 @@
 				// The installs will be handled by Make as sub-modules of the java library.
 				d.builtInstalledForApex = append(d.builtInstalledForApex, dexpreopterInstall{
 					name:                arch + "-" + installBase,
-					moduleName:          moduleName(ctx),
+					moduleName:          libName,
 					outputPathOnHost:    install.From,
 					installDirOnDevice:  installPath,
 					installFileOnDevice: installBase,
@@ -483,3 +568,7 @@
 func (d *dexpreopter) OutputProfilePathOnHost() android.Path {
 	return d.outputProfilePathOnHost
 }
+
+func (d *dexpreopter) disableDexpreopt() {
+	d.shouldDisableDexpreopt = true
+}
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 5fb36df..f7e3cb9 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -21,6 +21,7 @@
 	"android/soong/android"
 	"android/soong/dexpreopt"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -224,8 +225,9 @@
 }
 
 var (
-	dexpreoptBootJarDepTag  = bootclasspathDependencyTag{name: "dexpreopt-boot-jar"}
-	dexBootJarsFragmentsKey = android.NewOnceKey("dexBootJarsFragments")
+	dexpreoptBootJarDepTag          = bootclasspathDependencyTag{name: "dexpreopt-boot-jar"}
+	dexBootJarsFragmentsKey         = android.NewOnceKey("dexBootJarsFragments")
+	apexContributionsMetadataDepTag = dependencyTag{name: "all_apex_contributions"}
 )
 
 func init() {
@@ -236,8 +238,7 @@
 //
 // WARNING: All fields in this struct should be initialized in the genBootImageConfigs function.
 // Failure to do so can lead to data races if there is no synchronization enforced ordering between
-// the writer and the reader. Fields which break this rule are marked as deprecated and should be
-// removed and replaced with something else, e.g. providers.
+// the writer and the reader.
 type bootImageConfig struct {
 	// If this image is an extension, the image that it extends.
 	extends *bootImageConfig
@@ -277,16 +278,6 @@
 	// File path to a zip archive with all image files (or nil, if not needed).
 	zip android.WritablePath
 
-	// Rules which should be used in make to install the outputs.
-	//
-	// Deprecated: Not initialized correctly, see struct comment.
-	profileInstalls android.RuleBuilderInstalls
-
-	// Path to the license metadata file for the module that built the profile.
-	//
-	// Deprecated: Not initialized correctly, see struct comment.
-	profileLicenseMetadataFile android.OptionalPath
-
 	// Target-dependent fields.
 	variants []*bootImageVariant
 
@@ -502,6 +493,11 @@
 	dexpreoptConfigForMake android.WritablePath
 }
 
+func (dbj *dexpreoptBootJars) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// Create a dependency on all_apex_contributions to determine the selected mainline module
+	ctx.AddDependency(ctx.Module(), apexContributionsMetadataDepTag, "all_apex_contributions")
+}
+
 func DexpreoptBootJarsMutator(ctx android.BottomUpMutatorContext) {
 	if _, ok := ctx.Module().(*dexpreoptBootJars); !ok {
 		return
@@ -520,6 +516,14 @@
 		}
 		// For accessing the boot jars.
 		addDependenciesOntoBootImageModules(ctx, config.modules, dexpreoptBootJarDepTag)
+		// Create a dependency on the apex selected using RELEASE_APEX_CONTRIBUTIONS_*
+		// TODO: b/308174306 - Remove the direct depedendency edge to the java_library (source/prebuilt) once all mainline modules
+		// have been flagged using RELEASE_APEX_CONTRIBUTIONS_*
+		apexes := []string{}
+		for i := 0; i < config.modules.Len(); i++ {
+			apexes = append(apexes, config.modules.Apex(i))
+		}
+		addDependenciesOntoSelectedBootImageApexes(ctx, android.FirstUniqueStrings(apexes)...)
 	}
 
 	if ctx.OtherModuleExists("platform-bootclasspath") {
@@ -532,6 +536,28 @@
 	}
 }
 
+// Create a dependency from dex_bootjars to the specific apexes selected using all_apex_contributions
+// This dependency will be used to get the path to the deapexed dex boot jars and profile (via a provider)
+func addDependenciesOntoSelectedBootImageApexes(ctx android.BottomUpMutatorContext, apexes ...string) {
+	psi := android.PrebuiltSelectionInfoMap{}
+	ctx.VisitDirectDepsWithTag(apexContributionsMetadataDepTag, func(am android.Module) {
+		if info, exists := android.OtherModuleProvider(ctx, am, android.PrebuiltSelectionInfoProvider); exists {
+			psi = info
+		}
+	})
+	for _, apex := range apexes {
+		for _, selected := range psi.GetSelectedModulesForApiDomain(apex) {
+			// We need to add a dep on only the apex listed in `contents` of the selected apex_contributions module
+			// This is not available in a structured format in `apex_contributions`, so this hack adds a dep on all `contents`
+			// (some modules like art.module.public.api do not have an apex variation since it is a pure stub module that does not get installed)
+			apexVariationOfSelected := append(ctx.Target().Variations(), blueprint.Variation{Mutator: "apex", Variation: apex})
+			if ctx.OtherModuleDependencyVariantExists(apexVariationOfSelected, selected) {
+				ctx.AddFarVariationDependencies(apexVariationOfSelected, dexpreoptBootJarDepTag, selected)
+			}
+		}
+	}
+}
+
 func gatherBootclasspathFragments(ctx android.ModuleContext) map[string]android.Module {
 	return ctx.Config().Once(dexBootJarsFragmentsKey, func() interface{} {
 		fragments := make(map[string]android.Module)
@@ -544,7 +570,7 @@
 				return true
 			}
 			if tag == bootclasspathFragmentDepTag {
-				apexInfo := ctx.OtherModuleProvider(child, android.ApexInfoProvider).(android.ApexInfo)
+				apexInfo, _ := android.OtherModuleProvider(ctx, child, android.ApexInfoProvider)
 				for _, apex := range apexInfo.InApexVariants {
 					fragments[apex] = child
 				}
@@ -565,6 +591,7 @@
 	imageConfigs := genBootImageConfigs(ctx)
 	d.defaultBootImage = defaultBootImageConfig(ctx)
 	d.otherImages = make([]*bootImageConfig, 0, len(imageConfigs)-1)
+	var profileInstalls android.RuleBuilderInstalls
 	for _, name := range getImageNames() {
 		config := imageConfigs[name]
 		if config != d.defaultBootImage {
@@ -573,16 +600,25 @@
 		if !config.isEnabled(ctx) {
 			continue
 		}
-		generateBootImage(ctx, config)
+		installs := generateBootImage(ctx, config)
+		profileInstalls = append(profileInstalls, installs...)
 		if config == d.defaultBootImage {
-			bootFrameworkProfileRule(ctx, config)
+			_, installs := bootFrameworkProfileRule(ctx, config)
+			profileInstalls = append(profileInstalls, installs...)
 		}
 	}
+	if len(profileInstalls) > 0 {
+		android.SetProvider(ctx, profileInstallInfoProvider, profileInstallInfo{
+			profileInstalls:            profileInstalls,
+			profileLicenseMetadataFile: android.OptionalPathForPath(ctx.LicenseMetadataFile()),
+		})
+	}
 }
 
 // GenerateSingletonBuildActions generates build rules for the dexpreopt config for Make.
 func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonContext) {
-	d.dexpreoptConfigForMake = android.PathForOutput(ctx, getDexpreoptDirName(ctx), "dexpreopt.config")
+	d.dexpreoptConfigForMake =
+		android.PathForOutput(ctx, dexpreopt.GetDexpreoptDirName(ctx), "dexpreopt.config")
 	writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake)
 }
 
@@ -598,7 +634,7 @@
 	return true
 }
 
-func generateBootImage(ctx android.ModuleContext, imageConfig *bootImageConfig) {
+func generateBootImage(ctx android.ModuleContext, imageConfig *bootImageConfig) android.RuleBuilderInstalls {
 	apexJarModulePairs := getModulesForImage(ctx, imageConfig)
 
 	// Copy module dex jars to their predefined locations.
@@ -607,12 +643,12 @@
 
 	// Build a profile for the image config from the profile at the default path. The profile will
 	// then be used along with profiles imported from APEXes to build the boot image.
-	profile := bootImageProfileRule(ctx, imageConfig)
+	profile, profileInstalls := bootImageProfileRule(ctx, imageConfig)
 
 	// If dexpreopt of boot image jars should be skipped, stop after generating a profile.
 	global := dexpreopt.GetGlobalConfig(ctx)
 	if SkipDexpreoptBootJars(ctx) || (global.OnlyPreoptArtBootImage && imageConfig.name != "art") {
-		return
+		return profileInstalls
 	}
 
 	// Build boot image files for the android variants.
@@ -626,6 +662,8 @@
 
 	// Create a `dump-oat-<image-name>` rule that runs `oatdump` for debugging purposes.
 	dumpOatRules(ctx, imageConfig)
+
+	return profileInstalls
 }
 
 type apexJarModulePair struct {
@@ -662,38 +700,75 @@
 // extractEncodedDexJarsFromModulesOrBootclasspathFragments gets the hidden API encoded dex jars for
 // the given modules.
 func extractEncodedDexJarsFromModulesOrBootclasspathFragments(ctx android.ModuleContext, apexJarModulePairs []apexJarModulePair) bootDexJarByModule {
+	apexNameToApexExportInfoMap := getApexNameToApexExportsInfoMap(ctx)
 	encodedDexJarsByModuleName := bootDexJarByModule{}
 	for _, pair := range apexJarModulePairs {
-		var path android.Path
-		if android.IsConfiguredJarForPlatform(pair.apex) || android.IsModulePrebuilt(pair.jarModule) {
-			// This gives us the dex jar with the hidden API flags encoded from the monolithic hidden API
-			// files or the dex jar extracted from a prebuilt APEX. We can't use this for a boot jar for
-			// a source APEX because there is no guarantee that it is the same as the jar packed into the
-			// APEX. In practice, they are the same when we are building from a full source tree, but they
-			// are different when we are building from a thin manifest (e.g., master-art), where there is
-			// no monolithic hidden API files at all.
-			path = retrieveEncodedBootDexJarFromModule(ctx, pair.jarModule)
-		} else {
-			// Use exactly the same jar that is packed into the APEX.
-			fragment := getBootclasspathFragmentByApex(ctx, pair.apex)
-			if fragment == nil {
-				ctx.ModuleErrorf("Boot jar '%[1]s' is from APEX '%[2]s', but a bootclasspath_fragment for "+
-					"APEX '%[2]s' doesn't exist or is not added as a dependency of dex_bootjars",
-					pair.jarModule.Name(),
-					pair.apex)
-			}
-			bootclasspathFragmentInfo := ctx.OtherModuleProvider(fragment, BootclasspathFragmentApexContentInfoProvider).(BootclasspathFragmentApexContentInfo)
-			jar, err := bootclasspathFragmentInfo.DexBootJarPathForContentModule(pair.jarModule)
-			if err != nil {
-				ctx.ModuleErrorf("%s", err)
-			}
-			path = jar
-		}
-		encodedDexJarsByModuleName.addPath(pair.jarModule, path)
+		dexJarPath := getDexJarForApex(ctx, pair, apexNameToApexExportInfoMap)
+		encodedDexJarsByModuleName.addPath(pair.jarModule, dexJarPath)
 	}
 	return encodedDexJarsByModuleName
 }
 
+type apexNameToApexExportsInfoMap map[string]android.ApexExportsInfo
+
+// javaLibraryPathOnHost returns the path to the java library which is exported by the apex for hiddenapi and dexpreopt and a boolean indicating whether the java library exists
+// For prebuilt apexes, this is created by deapexing the prebuilt apex
+func (m *apexNameToApexExportsInfoMap) javaLibraryDexPathOnHost(ctx android.ModuleContext, apex string, javalib string) (android.Path, bool) {
+	if info, exists := (*m)[apex]; exists {
+		if dex, exists := info.LibraryNameToDexJarPathOnHost[javalib]; exists {
+			return dex, true
+		} else {
+			ctx.ModuleErrorf("Apex %s does not provide a dex boot jar for library %s\n", apex, javalib)
+		}
+	}
+	// An apex entry could not be found. Return false.
+	// TODO: b/308174306 - When all the mainline modules have been flagged, make this a hard error
+	return nil, false
+}
+
+// Returns the stem of an artifact inside a prebuilt apex
+func ModuleStemForDeapexing(m android.Module) string {
+	bmn, _ := m.(interface{ BaseModuleName() string })
+	return bmn.BaseModuleName()
+}
+
+// Returns the java libraries exported by the apex for hiddenapi and dexpreopt
+// This information can come from two mechanisms
+// 1. New: Direct deps to _selected_ apexes. The apexes return a ApexExportsInfo
+// 2. Legacy: An edge to java_library or java_import (java_sdk_library) module. For prebuilt apexes, this serves as a hook and is populated by deapexers of prebuilt apxes
+// TODO: b/308174306 - Once all mainline modules have been flagged, drop (2)
+func getDexJarForApex(ctx android.ModuleContext, pair apexJarModulePair, apexNameToApexExportsInfoMap apexNameToApexExportsInfoMap) android.Path {
+	if dex, found := apexNameToApexExportsInfoMap.javaLibraryDexPathOnHost(ctx, pair.apex, ModuleStemForDeapexing(pair.jarModule)); found {
+		return dex
+	}
+	// TODO: b/308174306 - Remove the legacy mechanism
+	if android.IsConfiguredJarForPlatform(pair.apex) || android.IsModulePrebuilt(pair.jarModule) {
+		// This gives us the dex jar with the hidden API flags encoded from the monolithic hidden API
+		// files or the dex jar extracted from a prebuilt APEX. We can't use this for a boot jar for
+		// a source APEX because there is no guarantee that it is the same as the jar packed into the
+		// APEX. In practice, they are the same when we are building from a full source tree, but they
+		// are different when we are building from a thin manifest (e.g., master-art), where there is
+		// no monolithic hidden API files at all.
+		return retrieveEncodedBootDexJarFromModule(ctx, pair.jarModule)
+	} else {
+		// Use exactly the same jar that is packed into the APEX.
+		fragment := getBootclasspathFragmentByApex(ctx, pair.apex)
+		if fragment == nil {
+			ctx.ModuleErrorf("Boot jar '%[1]s' is from APEX '%[2]s', but a bootclasspath_fragment for "+
+				"APEX '%[2]s' doesn't exist or is not added as a dependency of dex_bootjars",
+				pair.jarModule.Name(),
+				pair.apex)
+		}
+		bootclasspathFragmentInfo, _ := android.OtherModuleProvider(ctx, fragment, BootclasspathFragmentApexContentInfoProvider)
+		jar, err := bootclasspathFragmentInfo.DexBootJarPathForContentModule(pair.jarModule)
+		if err != nil {
+			ctx.ModuleErrorf("%s", err)
+		}
+		return jar
+	}
+	return nil
+}
+
 // copyBootJarsToPredefinedLocations generates commands that will copy boot jars to predefined
 // paths in the global config.
 func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, srcBootDexJarsByModule bootDexJarByModule, dstBootJarsByModule map[string]android.WritablePath) {
@@ -823,6 +898,37 @@
 	config *bootImageVariant
 }
 
+// Returns the profile file for an apex
+// This information can come from two mechanisms
+// 1. New: Direct deps to _selected_ apexes. The apexes return a BootclasspathFragmentApexContentInfo
+// 2. Legacy: An edge to bootclasspath_fragment module. For prebuilt apexes, this serves as a hook and is populated by deapexers of prebuilt apxes
+// TODO: b/308174306 - Once all mainline modules have been flagged, drop (2)
+func getProfilePathForApex(ctx android.ModuleContext, apexName string, apexNameToBcpInfoMap map[string]android.ApexExportsInfo) android.Path {
+	if info, exists := apexNameToBcpInfoMap[apexName]; exists {
+		return info.ProfilePathOnHost
+	}
+	// TODO: b/308174306 - Remove the legacy mechanism
+	fragment := getBootclasspathFragmentByApex(ctx, apexName)
+	if fragment == nil {
+		ctx.ModuleErrorf("Boot image config imports profile from '%[2]s', but a "+
+			"bootclasspath_fragment for APEX '%[2]s' doesn't exist or is not added as a "+
+			"dependency of dex_bootjars",
+			apexName)
+		return nil
+	}
+	return fragment.(commonBootclasspathFragment).getProfilePath()
+}
+
+func getApexNameToApexExportsInfoMap(ctx android.ModuleContext) apexNameToApexExportsInfoMap {
+	apexNameToApexExportsInfoMap := apexNameToApexExportsInfoMap{}
+	ctx.VisitDirectDepsWithTag(dexpreoptBootJarDepTag, func(am android.Module) {
+		if info, exists := android.OtherModuleProvider(ctx, am, android.ApexExportsInfoProvider); exists {
+			apexNameToApexExportsInfoMap[info.ApexName] = info
+		}
+	})
+	return apexNameToApexExportsInfoMap
+}
+
 // Generate boot image build rules for a specific target.
 func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) bootImageVariantOutputs {
 
@@ -865,6 +971,8 @@
 
 	invocationPath := outputPath.ReplaceExtension(ctx, "invocation")
 
+	apexNameToApexExportsInfoMap := getApexNameToApexExportsInfoMap(ctx)
+
 	cmd.Tool(globalSoong.Dex2oat).
 		Flag("--avoid-storing-invocation").
 		FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
@@ -877,16 +985,7 @@
 		}
 
 		for _, apex := range image.profileImports {
-			fragment := getBootclasspathFragmentByApex(ctx, apex)
-			if fragment == nil {
-				ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but a "+
-					"bootclasspath_fragment for APEX '%[2]s' doesn't exist or is not added as a "+
-					"dependency of dex_bootjars",
-					image.name,
-					apex)
-				return bootImageVariantOutputs{}
-			}
-			importedProfile := fragment.(commonBootclasspathFragment).getProfilePath()
+			importedProfile := getProfilePathForApex(ctx, apex, apexNameToApexExportsInfoMap)
 			if importedProfile == nil {
 				ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but '%[2]s' "+
 					"doesn't provide a profile",
@@ -974,8 +1073,8 @@
 		cmd.FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch])
 	}
 
-	if global.EnableUffdGc && image.target.Os == android.Android {
-		cmd.Flag("--runtime-arg").Flag("-Xgc:CMC")
+	if image.target.Os == android.Android {
+		cmd.Text("$(cat").Input(globalSoong.UffdGcFlag).Text(")")
 	}
 
 	if global.BootFlags != "" {
@@ -1085,9 +1184,19 @@
 	return profile
 }
 
-func bootImageProfileRule(ctx android.ModuleContext, image *bootImageConfig) android.WritablePath {
+type profileInstallInfo struct {
+	// Rules which should be used in make to install the outputs.
+	profileInstalls android.RuleBuilderInstalls
+
+	// Path to the license metadata file for the module that built the profile.
+	profileLicenseMetadataFile android.OptionalPath
+}
+
+var profileInstallInfoProvider = blueprint.NewProvider[profileInstallInfo]()
+
+func bootImageProfileRule(ctx android.ModuleContext, image *bootImageConfig) (android.WritablePath, android.RuleBuilderInstalls) {
 	if !image.isProfileGuided() {
-		return nil
+		return nil, nil
 	}
 
 	profile := bootImageProfileRuleCommon(ctx, image.name, image.dexPathsDeps.Paths(), image.getAnyAndroidVariant().dexLocationsDeps)
@@ -1095,21 +1204,19 @@
 	if image == defaultBootImageConfig(ctx) {
 		rule := android.NewRuleBuilder(pctx, ctx)
 		rule.Install(profile, "/system/etc/boot-image.prof")
-		image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
-		image.profileLicenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
+		return profile, rule.Installs()
 	}
-
-	return profile
+	return profile, nil
 }
 
 // bootFrameworkProfileRule generates the rule to create the boot framework profile and
 // returns a path to the generated file.
-func bootFrameworkProfileRule(ctx android.ModuleContext, image *bootImageConfig) android.WritablePath {
+func bootFrameworkProfileRule(ctx android.ModuleContext, image *bootImageConfig) (android.WritablePath, android.RuleBuilderInstalls) {
 	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
 	global := dexpreopt.GetGlobalConfig(ctx)
 
 	if global.DisableGenerateProfile || ctx.Config().UnbundledBuild() {
-		return nil
+		return nil, nil
 	}
 
 	defaultProfile := "frameworks/base/config/boot-profile.txt"
@@ -1129,16 +1236,13 @@
 
 	rule.Install(profile, "/system/etc/boot-image.bprof")
 	rule.Build("bootFrameworkProfile", "profile boot framework jars")
-	image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
-	image.profileLicenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
-
-	return profile
+	return profile, rule.Installs()
 }
 
 func dumpOatRules(ctx android.ModuleContext, image *bootImageConfig) {
 	var allPhonies android.Paths
 	name := image.name
-	global := dexpreopt.GetGlobalConfig(ctx)
+	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
 	for _, image := range image.variants {
 		arch := image.target.Arch.ArchType
 		suffix := arch.String()
@@ -1150,6 +1254,7 @@
 		output := android.PathForOutput(ctx, name+"."+suffix+".oatdump.txt")
 		rule := android.NewRuleBuilder(pctx, ctx)
 		imageLocationsOnHost, _ := image.imageLocations()
+
 		cmd := rule.Command().
 			BuiltTool("oatdump").
 			FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
@@ -1157,8 +1262,8 @@
 			FlagWithArg("--image=", strings.Join(imageLocationsOnHost, ":")).Implicits(image.imagesDeps.Paths()).
 			FlagWithOutput("--output=", output).
 			FlagWithArg("--instruction-set=", arch.String())
-		if global.EnableUffdGc && image.target.Os == android.Android {
-			cmd.Flag("--runtime-arg").Flag("-Xgc:CMC")
+		if image.target.Os == android.Android {
+			cmd.Text("$(cat").Input(globalSoong.UffdGcFlag).Text(")")
 		}
 		rule.Build("dump-oat-"+name+"-"+suffix, "dump oat "+name+" "+arch.String())
 
@@ -1200,9 +1305,11 @@
 
 	image := d.defaultBootImage
 	if image != nil {
-		ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", image.profileInstalls.String())
-		if image.profileLicenseMetadataFile.Valid() {
-			ctx.Strict("DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA", image.profileLicenseMetadataFile.String())
+		if profileInstallInfo, ok := android.SingletonModuleProvider(ctx, d, profileInstallInfoProvider); ok {
+			ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", profileInstallInfo.profileInstalls.String())
+			if profileInstallInfo.profileLicenseMetadataFile.Valid() {
+				ctx.Strict("DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA", profileInstallInfo.profileLicenseMetadataFile.String())
+			}
 		}
 
 		if SkipDexpreoptBootJars(ctx) {
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 2bd696c..dc0973c 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -45,7 +45,7 @@
 	frameworkBootImageName   = "boot"
 	mainlineBootImageName    = "mainline"
 	bootImageStem            = "boot"
-	profileInstallPathInApex = "etc/boot-image.prof"
+	ProfileInstallPathInApex = "etc/boot-image.prof"
 )
 
 // getImageNames returns an ordered list of image names. The order doesn't matter but needs to be
@@ -115,7 +115,7 @@
 func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
 	return ctx.Config().Once(bootImageConfigKey, func() interface{} {
 		targets := dexpreoptTargets(ctx)
-		deviceDir := android.PathForOutput(ctx, getDexpreoptDirName(ctx))
+		deviceDir := android.PathForOutput(ctx, dexpreopt.GetDexpreoptDirName(ctx))
 
 		configs := genBootImageConfigRaw(ctx)
 
@@ -234,12 +234,3 @@
 func dexpreoptConfigMakevars(ctx android.MakeVarsContext) {
 	ctx.Strict("DEXPREOPT_BOOT_JARS_MODULES", strings.Join(defaultBootImageConfig(ctx).modules.CopyOfApexJarPairs(), ":"))
 }
-
-func getDexpreoptDirName(ctx android.PathContext) string {
-	prefix := "dexpreopt_"
-	targets := ctx.Config().Targets[android.Android]
-	if len(targets) > 0 {
-		return prefix + targets[0].Arch.ArchType.String()
-	}
-	return prefix + "unknown_target"
-}
diff --git a/java/dexpreopt_config_testing.go b/java/dexpreopt_config_testing.go
index 41d4b72..104829f 100644
--- a/java/dexpreopt_config_testing.go
+++ b/java/dexpreopt_config_testing.go
@@ -523,7 +523,7 @@
 		},
 	}
 
-	checkBootImageConfig(t, imageConfig, mutated, expected)
+	checkBootImageConfig(t, result, imageConfig, mutated, expected)
 }
 
 // getFrameworkImageConfig gets the framework bootImageConfig that was created during the test.
@@ -904,7 +904,7 @@
 		profileLicenseMetadataFile: expectedLicenseMetadataFile,
 	}
 
-	checkBootImageConfig(t, imageConfig, mutated, expected)
+	checkBootImageConfig(t, result, imageConfig, mutated, expected)
 }
 
 // getMainlineImageConfig gets the framework bootImageConfig that was created during the test.
@@ -1183,7 +1183,7 @@
 		profileLicenseMetadataFile: expectedLicenseMetadataFile,
 	}
 
-	checkBootImageConfig(t, imageConfig, false, expected)
+	checkBootImageConfig(t, result, imageConfig, false, expected)
 }
 
 // clearMutatedFields clears fields in the expectedConfig that correspond to fields in the
@@ -1211,19 +1211,19 @@
 // zero value so that they will match the unmodified values in the boot image.
 //
 // It runs the checks in an image specific subtest of the current test.
-func checkBootImageConfig(t *testing.T, imageConfig *bootImageConfig, mutated bool, expected *expectedConfig) {
+func checkBootImageConfig(t *testing.T, result *android.TestResult, imageConfig *bootImageConfig, mutated bool, expected *expectedConfig) {
 	if !mutated {
 		clearMutatedFields(expected)
 	}
 
 	t.Run(imageConfig.name, func(t *testing.T) {
-		nestedCheckBootImageConfig(t, imageConfig, expected)
+		nestedCheckBootImageConfig(t, result, imageConfig, mutated, expected)
 	})
 }
 
 // nestedCheckBootImageConfig does the work of comparing the image against the expected values and
 // is run in an image specific subtest.
-func nestedCheckBootImageConfig(t *testing.T, imageConfig *bootImageConfig, expected *expectedConfig) {
+func nestedCheckBootImageConfig(t *testing.T, result *android.TestResult, imageConfig *bootImageConfig, mutated bool, expected *expectedConfig) {
 	android.AssertStringEquals(t, "name", expected.name, imageConfig.name)
 	android.AssertStringEquals(t, "stem", expected.stem, imageConfig.stem)
 	android.AssertPathRelativeToTopEquals(t, "dir", expected.dir, imageConfig.dir)
@@ -1234,8 +1234,13 @@
 	android.AssertPathsRelativeToTopEquals(t, "dexPathsDeps", expected.dexPathsDeps, imageConfig.dexPathsDeps.Paths())
 	// dexPathsByModule is just a different representation of the other information in the config.
 	android.AssertPathRelativeToTopEquals(t, "zip", expected.zip, imageConfig.zip)
-	assertInstallsEqual(t, "profileInstalls", expected.profileInstalls, imageConfig.profileInstalls)
-	android.AssertStringEquals(t, "profileLicenseMetadataFile", expected.profileLicenseMetadataFile, imageConfig.profileLicenseMetadataFile.RelativeToTop().String())
+
+	if !mutated {
+		dexBootJarModule := result.ModuleForTests("dex_bootjars", "android_common")
+		profileInstallInfo, _ := android.SingletonModuleProvider(result, dexBootJarModule.Module(), profileInstallInfoProvider)
+		assertInstallsEqual(t, "profileInstalls", expected.profileInstalls, profileInstallInfo.profileInstalls)
+		android.AssertStringEquals(t, "profileLicenseMetadataFile", expected.profileLicenseMetadataFile, profileInstallInfo.profileLicenseMetadataFile.RelativeToTop().String())
+	}
 
 	android.AssertIntEquals(t, "variant count", 4, len(imageConfig.variants))
 	for i, variant := range imageConfig.variants {
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index fedd564..73e33f4 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -410,7 +410,7 @@
 	verifyEntries(t,
 		"entriesList[0]",
 		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
-		"/dexpreopt/oat/arm64/javalib.odex",
+		"/dexpreopt/service-foo/oat/arm64/javalib.odex",
 		"/system/framework/oat/arm64",
 		"apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
 		entriesList[0])
@@ -418,7 +418,7 @@
 	verifyEntries(t,
 		"entriesList[1]",
 		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
-		"/dexpreopt/oat/arm64/javalib.vdex",
+		"/dexpreopt/service-foo/oat/arm64/javalib.vdex",
 		"/system/framework/oat/arm64",
 		"apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
 		entriesList[1])
@@ -459,7 +459,7 @@
 	ctx := result.TestContext
 	dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeRule("dexpreopt")
 
-	expected := []string{"out/soong/.intermediates/foo/android_common/dexpreopt/profile.prof"}
+	expected := []string{"out/soong/.intermediates/foo/android_common/dexpreopt/foo/profile.prof"}
 
 	android.AssertArrayString(t, "outputs", expected, dexpreopt.AllOutputs())
 }
diff --git a/java/droiddoc.go b/java/droiddoc.go
index b0d5376..6a66f45 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -21,6 +21,7 @@
 
 	"github.com/google/blueprint/proptools"
 
+	"android/soong/aconfig"
 	"android/soong/android"
 	"android/soong/java/config"
 )
@@ -182,6 +183,17 @@
 
 func apiCheckEnabled(ctx android.ModuleContext, apiToCheck ApiToCheck, apiVersionTag string) bool {
 	if ctx.Config().IsEnvTrue("WITHOUT_CHECK_API") {
+		if ctx.Config().BuildFromTextStub() {
+			ctx.ModuleErrorf("Generating stubs from api signature files is not available " +
+				"with WITHOUT_CHECK_API=true, as sync between the source Java files and the " +
+				"api signature files is not guaranteed.\n" +
+				"In order to utilize WITHOUT_CHECK_API, generate stubs from the source Java " +
+				"files with BUILD_FROM_SOURCE_STUB=true.\n" +
+				"However, the usage of WITHOUT_CHECK_API is not preferred as the incremental " +
+				"build is slower when generating stubs from the source Java files.\n" +
+				"Consider updating the api signature files and generating the stubs from " +
+				"them instead.")
+		}
 		return false
 	} else if String(apiToCheck.Api_file) != "" && String(apiToCheck.Removed_api_file) != "" {
 		return true
@@ -208,6 +220,8 @@
 
 	docZip      android.WritablePath
 	stubsSrcJar android.WritablePath
+
+	exportableStubsSrcJar android.WritablePath
 }
 
 func (j *Javadoc) OutputFiles(tag string) (android.Paths, error) {
@@ -363,8 +377,7 @@
 
 		switch tag {
 		case bootClasspathTag:
-			if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
-				dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+			if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 				deps.bootClasspath = append(deps.bootClasspath, dep.ImplementationJars...)
 			} else if sm, ok := module.(SystemModulesProvider); ok {
 				// A system modules dependency has been added to the bootclasspath
@@ -376,8 +389,7 @@
 		case libTag, sdkLibTag:
 			if dep, ok := module.(SdkLibraryDependency); ok {
 				deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
-			} else if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
-				dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+			} else if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 				deps.classpath = append(deps.classpath, dep.HeaderJars...)
 				deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...)
 			} else if dep, ok := module.(android.SourceFileProducer); ok {
@@ -387,8 +399,7 @@
 				ctx.ModuleErrorf("depends on non-java module %q", otherName)
 			}
 		case java9LibTag:
-			if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
-				dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+			if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 				deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars...)
 			} else {
 				ctx.ModuleErrorf("depends on non-java module %q", otherName)
@@ -400,6 +411,18 @@
 			sm := module.(SystemModulesProvider)
 			outputDir, outputDeps := sm.OutputDirAndDeps()
 			deps.systemModules = &systemModules{outputDir, outputDeps}
+		case aconfigDeclarationTag:
+			if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey); ok {
+				deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPath)
+			} else if dep, ok := android.OtherModuleProvider(ctx, module, aconfig.CodegenInfoProvider); ok {
+				deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPaths...)
+			} else {
+				ctx.ModuleErrorf("Only aconfig_declarations and aconfig_declarations_group "+
+					"module type is allowed for flags_packages property, but %s is neither "+
+					"of these supported module types",
+					module.Name(),
+				)
+			}
 		}
 	})
 	// do not pass exclude_srcs directly when expanding srcFiles since exclude_srcs
diff --git a/java/droidstubs.go b/java/droidstubs.go
index e7ccc1b..9556e95 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -30,6 +30,41 @@
 // The values allowed for Droidstubs' Api_levels_sdk_type
 var allowedApiLevelSdkTypes = []string{"public", "system", "module-lib", "system-server"}
 
+type StubsType int
+
+const (
+	Everything StubsType = iota
+	Runtime
+	Exportable
+	Unavailable
+)
+
+func (s StubsType) String() string {
+	switch s {
+	case Everything:
+		return "everything"
+	case Runtime:
+		return "runtime"
+	case Exportable:
+		return "exportable"
+	default:
+		return ""
+	}
+}
+
+func StringToStubsType(s string) StubsType {
+	switch strings.ToLower(s) {
+	case Everything.String():
+		return Everything
+	case Runtime.String():
+		return Runtime
+	case Exportable.String():
+		return Exportable
+	default:
+		return Unavailable
+	}
+}
+
 func init() {
 	RegisterStubsBuildComponents(android.InitRegistrationContext)
 }
@@ -43,15 +78,22 @@
 	ctx.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory)
 }
 
+type stubsArtifacts struct {
+	nullabilityWarningsFile android.WritablePath
+	annotationsZip          android.WritablePath
+	apiVersionsXml          android.WritablePath
+	metadataZip             android.WritablePath
+	metadataDir             android.WritablePath
+}
+
 // Droidstubs
 type Droidstubs struct {
 	Javadoc
 	embeddableInModuleAndImport
 
-	properties              DroidstubsProperties
-	apiFile                 android.Path
-	removedApiFile          android.Path
-	nullabilityWarningsFile android.WritablePath
+	properties     DroidstubsProperties
+	apiFile        android.Path
+	removedApiFile android.Path
 
 	checkCurrentApiTimestamp      android.WritablePath
 	updateCurrentApiTimestamp     android.WritablePath
@@ -61,11 +103,14 @@
 
 	checkNullabilityWarningsTimestamp android.WritablePath
 
-	annotationsZip android.WritablePath
-	apiVersionsXml android.WritablePath
+	everythingArtifacts stubsArtifacts
+	exportableArtifacts stubsArtifacts
 
-	metadataZip android.WritablePath
-	metadataDir android.WritablePath
+	// Single aconfig "cache file" merged from this module and all dependencies.
+	mergedAconfigFiles map[string]android.Paths
+
+	exportableApiFile        android.WritablePath
+	exportableRemovedApiFile android.WritablePath
 }
 
 type DroidstubsProperties struct {
@@ -151,22 +196,26 @@
 	// API surface of this module. If set, the module contributes to an API surface.
 	// For the full list of available API surfaces, refer to soong/android/sdk_version.go
 	Api_surface *string
+
+	// a list of aconfig_declarations module names that the stubs generated in this module
+	// depend on.
+	Aconfig_declarations []string
 }
 
 // Used by xsd_config
 type ApiFilePath interface {
-	ApiFilePath() android.Path
+	ApiFilePath(StubsType) (android.Path, error)
 }
 
 type ApiStubsSrcProvider interface {
-	StubsSrcJar() android.Path
+	StubsSrcJar(StubsType) (android.Path, error)
 }
 
 // Provider of information about API stubs, used by java_sdk_library.
 type ApiStubsProvider interface {
-	AnnotationsZip() android.Path
+	AnnotationsZip(StubsType) (android.Path, error)
 	ApiFilePath
-	RemovedApiFilePath() android.Path
+	RemovedApiFilePath(StubsType) (android.Path, error)
 
 	ApiStubsSrcProvider
 }
@@ -175,6 +224,36 @@
 	CurrentApiTimestamp() android.Path
 }
 
+type annotationFlagsParams struct {
+	migratingNullability    bool
+	validatingNullability   bool
+	nullabilityWarningsFile android.WritablePath
+	annotationsZip          android.WritablePath
+}
+type stubsCommandParams struct {
+	srcJarDir               android.ModuleOutPath
+	stubsDir                android.OptionalPath
+	stubsSrcJar             android.WritablePath
+	metadataZip             android.WritablePath
+	metadataDir             android.WritablePath
+	apiVersionsXml          android.WritablePath
+	nullabilityWarningsFile android.WritablePath
+	annotationsZip          android.WritablePath
+	stubConfig              stubsCommandConfigParams
+}
+type stubsCommandConfigParams struct {
+	stubsType             StubsType
+	javaVersion           javaVersion
+	deps                  deps
+	checkApi              bool
+	generateStubs         bool
+	doApiLint             bool
+	doCheckReleased       bool
+	writeSdkValues        bool
+	migratingNullability  bool
+	validatingNullability bool
+}
+
 // droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be
 // documented, filtering out hidden classes and methods.  The resulting .java files are intended to be passed to
 // a droiddoc module to generate documentation.
@@ -207,40 +286,149 @@
 	return module
 }
 
+func getStubsTypeAndTag(tag string) (StubsType, string, error) {
+	if len(tag) == 0 {
+		return Everything, "", nil
+	}
+	if tag[0] != '.' {
+		return Unavailable, "", fmt.Errorf("tag must begin with \".\"")
+	}
+
+	stubsType := Everything
+	// Check if the tag has a stubs type prefix (e.g. ".exportable")
+	for st := Everything; st <= Exportable; st++ {
+		if strings.HasPrefix(tag, "."+st.String()) {
+			stubsType = st
+		}
+	}
+
+	return stubsType, strings.TrimPrefix(tag, "."+stubsType.String()), nil
+}
+
+// Droidstubs' tag supports specifying with the stubs type.
+// While supporting the pre-existing tags, it also supports tags with
+// the stubs type prefix. Some examples are shown below:
+// {.annotations.zip} - pre-existing behavior. Returns the path to the
+// annotation zip.
+// {.exportable} - Returns the path to the exportable stubs src jar.
+// {.exportable.annotations.zip} - Returns the path to the exportable
+// annotations zip file.
+// {.runtime.api_versions.xml} - Runtime stubs does not generate api versions
+// xml file. For unsupported combinations, the default everything output file
+// is returned.
 func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
+	stubsType, prefixRemovedTag, err := getStubsTypeAndTag(tag)
+	if err != nil {
+		return nil, err
+	}
+	switch prefixRemovedTag {
 	case "":
-		return android.Paths{d.stubsSrcJar}, nil
+		stubsSrcJar, err := d.StubsSrcJar(stubsType)
+		return android.Paths{stubsSrcJar}, err
 	case ".docs.zip":
-		return android.Paths{d.docZip}, nil
+		docZip, err := d.DocZip(stubsType)
+		return android.Paths{docZip}, err
 	case ".api.txt", android.DefaultDistTag:
 		// This is the default dist path for dist properties that have no tag property.
-		return android.Paths{d.apiFile}, nil
+		apiFilePath, err := d.ApiFilePath(stubsType)
+		return android.Paths{apiFilePath}, err
 	case ".removed-api.txt":
-		return android.Paths{d.removedApiFile}, nil
+		removedApiFilePath, err := d.RemovedApiFilePath(stubsType)
+		return android.Paths{removedApiFilePath}, err
 	case ".annotations.zip":
-		return android.Paths{d.annotationsZip}, nil
+		annotationsZip, err := d.AnnotationsZip(stubsType)
+		return android.Paths{annotationsZip}, err
 	case ".api_versions.xml":
-		return android.Paths{d.apiVersionsXml}, nil
+		apiVersionsXmlFilePath, err := d.ApiVersionsXmlFilePath(stubsType)
+		return android.Paths{apiVersionsXmlFilePath}, err
 	default:
 		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
 	}
 }
 
-func (d *Droidstubs) AnnotationsZip() android.Path {
-	return d.annotationsZip
+func (d *Droidstubs) AnnotationsZip(stubsType StubsType) (ret android.Path, err error) {
+	switch stubsType {
+	case Everything:
+		ret, err = d.everythingArtifacts.annotationsZip, nil
+	case Exportable:
+		ret, err = d.exportableArtifacts.annotationsZip, nil
+	default:
+		ret, err = nil, fmt.Errorf("annotations zip not supported for the stub type %s", stubsType.String())
+	}
+	return ret, err
 }
 
-func (d *Droidstubs) ApiFilePath() android.Path {
-	return d.apiFile
+func (d *Droidstubs) ApiFilePath(stubsType StubsType) (ret android.Path, err error) {
+	switch stubsType {
+	case Everything:
+		ret, err = d.apiFile, nil
+	case Exportable:
+		ret, err = d.exportableApiFile, nil
+	default:
+		ret, err = nil, fmt.Errorf("api file path not supported for the stub type %s", stubsType.String())
+	}
+	if ret == nil && err == nil {
+		err = fmt.Errorf("api file is null for the stub type %s", stubsType.String())
+	}
+	return ret, err
 }
 
-func (d *Droidstubs) RemovedApiFilePath() android.Path {
-	return d.removedApiFile
+func (d *Droidstubs) ApiVersionsXmlFilePath(stubsType StubsType) (ret android.Path, err error) {
+	switch stubsType {
+	case Everything:
+		ret, err = d.everythingArtifacts.apiVersionsXml, nil
+	case Exportable:
+		ret, err = d.exportableArtifacts.apiVersionsXml, nil
+	default:
+		ret, err = nil, fmt.Errorf("api versions xml file path not supported for the stub type %s", stubsType.String())
+	}
+	if ret == nil && err == nil {
+		err = fmt.Errorf("api versions xml file is null for the stub type %s", stubsType.String())
+	}
+	return ret, err
 }
 
-func (d *Droidstubs) StubsSrcJar() android.Path {
-	return d.stubsSrcJar
+func (d *Droidstubs) DocZip(stubsType StubsType) (ret android.Path, err error) {
+	switch stubsType {
+	case Everything:
+		ret, err = d.docZip, nil
+	default:
+		ret, err = nil, fmt.Errorf("docs zip not supported for the stub type %s", stubsType.String())
+	}
+	if ret == nil && err == nil {
+		err = fmt.Errorf("docs zip is null for the stub type %s", stubsType.String())
+	}
+	return ret, err
+}
+
+func (d *Droidstubs) RemovedApiFilePath(stubsType StubsType) (ret android.Path, err error) {
+	switch stubsType {
+	case Everything:
+		ret, err = d.removedApiFile, nil
+	case Exportable:
+		ret, err = d.exportableRemovedApiFile, nil
+	default:
+		ret, err = nil, fmt.Errorf("removed api file path not supported for the stub type %s", stubsType.String())
+	}
+	if ret == nil && err == nil {
+		err = fmt.Errorf("removed api file is null for the stub type %s", stubsType.String())
+	}
+	return ret, err
+}
+
+func (d *Droidstubs) StubsSrcJar(stubsType StubsType) (ret android.Path, err error) {
+	switch stubsType {
+	case Everything:
+		ret, err = d.stubsSrcJar, nil
+	case Exportable:
+		ret, err = d.exportableStubsSrcJar, nil
+	default:
+		ret, err = nil, fmt.Errorf("stubs srcjar not supported for the stub type %s", stubsType.String())
+	}
+	if ret == nil && err == nil {
+		err = fmt.Errorf("stubs srcjar is null for the stub type %s", stubsType.String())
+	}
+	return ret, err
 }
 
 func (d *Droidstubs) CurrentApiTimestamp() android.Path {
@@ -274,39 +462,57 @@
 		}
 	}
 
+	if len(d.properties.Aconfig_declarations) != 0 {
+		for _, aconfigDeclarationModuleName := range d.properties.Aconfig_declarations {
+			ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfigDeclarationModuleName)
+		}
+	}
+
 	if d.properties.Api_levels_module != nil {
 		ctx.AddDependency(ctx.Module(), metalavaAPILevelsModuleTag, proptools.String(d.properties.Api_levels_module))
 	}
 }
 
-func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath) {
-	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
-		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
-		String(d.properties.Api_filename) != "" {
-		filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt")
-		uncheckedApiFile := android.PathForModuleOut(ctx, "metalava", filename)
-		cmd.FlagWithOutput("--api ", uncheckedApiFile)
-		d.apiFile = uncheckedApiFile
+func (d *Droidstubs) sdkValuesFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, metadataDir android.WritablePath) {
+	cmd.FlagWithArg("--sdk-values ", metadataDir.String())
+}
+
+func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath, stubsType StubsType, checkApi bool) {
+
+	apiFileName := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt")
+	uncheckedApiFile := android.PathForModuleOut(ctx, stubsType.String(), apiFileName)
+	cmd.FlagWithOutput("--api ", uncheckedApiFile)
+	if checkApi || String(d.properties.Api_filename) != "" {
+		if stubsType == Everything {
+			d.apiFile = uncheckedApiFile
+		} else if stubsType == Exportable {
+			d.exportableApiFile = uncheckedApiFile
+		}
 	} else if sourceApiFile := proptools.String(d.properties.Check_api.Current.Api_file); sourceApiFile != "" {
-		// If check api is disabled then make the source file available for export.
-		d.apiFile = android.PathForModuleSrc(ctx, sourceApiFile)
+		if stubsType == Everything {
+			// If check api is disabled then make the source file available for export.
+			d.apiFile = android.PathForModuleSrc(ctx, sourceApiFile)
+		} else if stubsType == Exportable {
+			d.exportableApiFile = uncheckedApiFile
+		}
 	}
 
-	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
-		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
-		String(d.properties.Removed_api_filename) != "" {
-		filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt")
-		uncheckedRemovedFile := android.PathForModuleOut(ctx, "metalava", filename)
-		cmd.FlagWithOutput("--removed-api ", uncheckedRemovedFile)
-		d.removedApiFile = uncheckedRemovedFile
+	removedApiFileName := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt")
+	uncheckedRemovedFile := android.PathForModuleOut(ctx, stubsType.String(), removedApiFileName)
+	cmd.FlagWithOutput("--removed-api ", uncheckedRemovedFile)
+	if checkApi || String(d.properties.Removed_api_filename) != "" {
+		if stubsType == Everything {
+			d.removedApiFile = uncheckedRemovedFile
+		} else if stubsType == Exportable {
+			d.exportableRemovedApiFile = uncheckedRemovedFile
+		}
 	} else if sourceRemovedApiFile := proptools.String(d.properties.Check_api.Current.Removed_api_file); sourceRemovedApiFile != "" {
-		// If check api is disabled then make the source removed api file available for export.
-		d.removedApiFile = android.PathForModuleSrc(ctx, sourceRemovedApiFile)
-	}
-
-	if Bool(d.properties.Write_sdk_values) {
-		d.metadataDir = android.PathForModuleOut(ctx, "metalava", "metadata")
-		cmd.FlagWithArg("--sdk-values ", d.metadataDir.String())
+		if stubsType == Everything {
+			// If check api is disabled then make the source removed api file available for export.
+			d.removedApiFile = android.PathForModuleSrc(ctx, sourceRemovedApiFile)
+		} else if stubsType == Exportable {
+			d.exportableRemovedApiFile = uncheckedRemovedFile
+		}
 	}
 
 	if stubsDir.Valid() {
@@ -321,16 +527,11 @@
 	}
 }
 
-func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
+func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, params annotationFlagsParams) {
 	if Bool(d.properties.Annotations_enabled) {
 		cmd.Flag(config.MetalavaAnnotationsFlags)
 
-		validatingNullability :=
-			strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") ||
-				String(d.properties.Validate_nullability_from_list) != ""
-
-		migratingNullability := String(d.properties.Previous_api) != ""
-		if migratingNullability {
+		if params.migratingNullability {
 			previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api))
 			cmd.FlagWithInput("--migrate-nullness ", previousApi)
 		}
@@ -339,13 +540,11 @@
 			cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s))
 		}
 
-		if validatingNullability {
-			d.nullabilityWarningsFile = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_nullability_warnings.txt")
-			cmd.FlagWithOutput("--nullability-warnings-txt ", d.nullabilityWarningsFile)
+		if params.validatingNullability {
+			cmd.FlagWithOutput("--nullability-warnings-txt ", params.nullabilityWarningsFile)
 		}
 
-		d.annotationsZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_annotations.zip")
-		cmd.FlagWithOutput("--extract-annotations ", d.annotationsZip)
+		cmd.FlagWithOutput("--extract-annotations ", params.annotationsZip)
 
 		if len(d.properties.Merge_annotations_dirs) != 0 {
 			d.mergeAnnoDirFlags(ctx, cmd)
@@ -377,15 +576,21 @@
 	})
 }
 
-func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
+func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, apiVersionsXml android.WritablePath) {
 	var apiVersions android.Path
 	if proptools.Bool(d.properties.Api_levels_annotations_enabled) {
-		d.apiLevelsGenerationFlags(ctx, cmd)
-		apiVersions = d.apiVersionsXml
+		d.apiLevelsGenerationFlags(ctx, cmd, stubsType, apiVersionsXml)
+		apiVersions = apiVersionsXml
 	} else {
 		ctx.VisitDirectDepsWithTag(metalavaAPILevelsModuleTag, func(m android.Module) {
 			if s, ok := m.(*Droidstubs); ok {
-				apiVersions = s.apiVersionsXml
+				if stubsType == Everything {
+					apiVersions = s.everythingArtifacts.apiVersionsXml
+				} else if stubsType == Exportable {
+					apiVersions = s.exportableArtifacts.apiVersionsXml
+				} else {
+					ctx.ModuleErrorf("%s stubs type does not generate api-versions.xml file", stubsType.String())
+				}
 			} else {
 				ctx.PropertyErrorf("api_levels_module",
 					"module %q is not a droidstubs module", ctx.OtherModuleName(m))
@@ -399,14 +604,13 @@
 	}
 }
 
-func (d *Droidstubs) apiLevelsGenerationFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
+func (d *Droidstubs) apiLevelsGenerationFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, apiVersionsXml android.WritablePath) {
 	if len(d.properties.Api_levels_annotations_dirs) == 0 {
 		ctx.PropertyErrorf("api_levels_annotations_dirs",
 			"has to be non-empty if api levels annotations was enabled!")
 	}
 
-	d.apiVersionsXml = android.PathForModuleOut(ctx, "metalava", "api-versions.xml")
-	cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml)
+	cmd.FlagWithOutput("--generate-api-levels ", apiVersionsXml)
 
 	filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar")
 
@@ -531,54 +735,101 @@
 	}
 
 	cmd.Flag(config.MetalavaFlags)
-	if ctx.DeviceConfig().HideFlaggedApis() {
-		cmd.Flag(config.MetalavaHideFlaggedApis)
-	}
 
 	return cmd
 }
 
-func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	deps := d.Javadoc.collectDeps(ctx)
+// Pass flagged apis related flags to metalava. When aconfig_declarations property is not
+// defined for a module, simply revert all flagged apis annotations. If aconfig_declarations
+// property is defined, apply transformations and only revert the flagged apis that are not
+// enabled via release configurations and are not specified in aconfig_declarations
+func generateRevertAnnotationArgs(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, aconfigFlagsPaths android.Paths) {
 
-	javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), android.SdkContext(d))
+	if len(aconfigFlagsPaths) == 0 {
+		cmd.Flag("--revert-annotation android.annotation.FlaggedApi")
+		return
+	}
 
-	// Create rule for metalava
+	releasedFlaggedApisFile := android.PathForModuleOut(ctx, fmt.Sprintf("released-flagged-apis-%s.txt", stubsType.String()))
+	revertAnnotationsFile := android.PathForModuleOut(ctx, fmt.Sprintf("revert-annotations-%s.txt", stubsType.String()))
 
-	srcJarDir := android.PathForModuleOut(ctx, "metalava", "srcjars")
+	var filterArgs string
+	switch stubsType {
+	// No flagged apis specific flags need to be passed to metalava when generating
+	// everything stubs
+	case Everything:
+		return
 
-	rule := android.NewRuleBuilder(pctx, ctx)
+	case Runtime:
+		filterArgs = "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'"
 
-	rule.Sbox(android.PathForModuleOut(ctx, "metalava"),
-		android.PathForModuleOut(ctx, "metalava.sbox.textproto")).
-		SandboxInputs()
+	case Exportable:
+		// When the build flag RELEASE_EXPORT_RUNTIME_APIS is set to true, apis marked with
+		// the flagged apis that have read_write permissions are exposed on top of the enabled
+		// and read_only apis. This is to support local override of flag values at runtime.
+		if ctx.Config().ReleaseExportRuntimeApis() {
+			filterArgs = "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'"
+		} else {
+			filterArgs = "--filter='state:ENABLED+permission:READ_ONLY'"
+		}
+	}
 
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        gatherReleasedFlaggedApisRule,
+		Inputs:      aconfigFlagsPaths,
+		Output:      releasedFlaggedApisFile,
+		Description: fmt.Sprintf("%s gather aconfig flags", stubsType),
+		Args: map[string]string{
+			"flags_path":  android.JoinPathsWithPrefix(aconfigFlagsPaths, "--cache "),
+			"filter_args": filterArgs,
+		},
+	})
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        generateMetalavaRevertAnnotationsRule,
+		Input:       releasedFlaggedApisFile,
+		Output:      revertAnnotationsFile,
+		Description: fmt.Sprintf("%s revert annotations", stubsType),
+	})
+
+	cmd.FlagWithInput("@", revertAnnotationsFile)
+}
+
+func (d *Droidstubs) commonMetalavaStubCmd(ctx android.ModuleContext, rule *android.RuleBuilder,
+	params stubsCommandParams) *android.RuleBuilderCommand {
 	if BoolDefault(d.properties.High_mem, false) {
 		// This metalava run uses lots of memory, restrict the number of metalava jobs that can run in parallel.
 		rule.HighMem()
 	}
 
-	generateStubs := BoolDefault(d.properties.Generate_stubs, true)
-	var stubsDir android.OptionalPath
-	if generateStubs {
-		d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-"+"stubs.srcjar")
-		stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, "metalava", "stubsDir"))
-		rule.Command().Text("rm -rf").Text(stubsDir.String())
-		rule.Command().Text("mkdir -p").Text(stubsDir.String())
+	if params.stubConfig.generateStubs {
+		rule.Command().Text("rm -rf").Text(params.stubsDir.String())
+		rule.Command().Text("mkdir -p").Text(params.stubsDir.String())
 	}
 
-	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
+	srcJarList := zipSyncCmd(ctx, rule, params.srcJarDir, d.Javadoc.srcJars)
 
-	homeDir := android.PathForModuleOut(ctx, "metalava", "home")
-	cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
-		deps.bootClasspath, deps.classpath, homeDir)
+	homeDir := android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "home")
+	cmd := metalavaCmd(ctx, rule, params.stubConfig.javaVersion, d.Javadoc.srcFiles, srcJarList,
+		params.stubConfig.deps.bootClasspath, params.stubConfig.deps.classpath, homeDir)
 	cmd.Implicits(d.Javadoc.implicits)
 
-	d.stubsFlags(ctx, cmd, stubsDir)
+	d.stubsFlags(ctx, cmd, params.stubsDir, params.stubConfig.stubsType, params.stubConfig.checkApi)
 
-	d.annotationsFlags(ctx, cmd)
+	if params.stubConfig.writeSdkValues {
+		d.sdkValuesFlags(ctx, cmd, params.metadataDir)
+	}
+
+	annotationParams := annotationFlagsParams{
+		migratingNullability:    params.stubConfig.migratingNullability,
+		validatingNullability:   params.stubConfig.validatingNullability,
+		nullabilityWarningsFile: params.nullabilityWarningsFile,
+		annotationsZip:          params.annotationsZip,
+	}
+
+	d.annotationsFlags(ctx, cmd, annotationParams)
 	d.inclusionAnnotationsFlags(ctx, cmd)
-	d.apiLevelsAnnotationsFlags(ctx, cmd)
+	d.apiLevelsAnnotationsFlags(ctx, cmd, params.stubConfig.stubsType, params.apiVersionsXml)
 
 	d.expandArgs(ctx, cmd)
 
@@ -586,24 +837,105 @@
 		cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
 	}
 
-	// Add options for the other optional tasks: API-lint and check-released.
-	// We generate separate timestamp files for them.
+	return cmd
+}
 
-	doApiLint := false
-	doCheckReleased := false
+// Sandbox rule for generating the everything stubs and other artifacts
+func (d *Droidstubs) everythingStubCmd(ctx android.ModuleContext, params stubsCommandConfigParams) {
+	srcJarDir := android.PathForModuleOut(ctx, Everything.String(), "srcjars")
+	rule := android.NewRuleBuilder(pctx, ctx)
+	rule.Sbox(android.PathForModuleOut(ctx, Everything.String()),
+		android.PathForModuleOut(ctx, "metalava.sbox.textproto")).
+		SandboxInputs()
+
+	var stubsDir android.OptionalPath
+	if params.generateStubs {
+		stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, Everything.String(), "stubsDir"))
+		d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"-"+"stubs.srcjar")
+	}
+
+	if params.writeSdkValues {
+		d.everythingArtifacts.metadataDir = android.PathForModuleOut(ctx, Everything.String(), "metadata")
+		d.everythingArtifacts.metadataZip = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"-metadata.zip")
+	}
+
+	if Bool(d.properties.Annotations_enabled) {
+		if params.validatingNullability {
+			d.everythingArtifacts.nullabilityWarningsFile = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"_nullability_warnings.txt")
+		}
+		d.everythingArtifacts.annotationsZip = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"_annotations.zip")
+	}
+	if Bool(d.properties.Api_levels_annotations_enabled) {
+		d.everythingArtifacts.apiVersionsXml = android.PathForModuleOut(ctx, Everything.String(), "api-versions.xml")
+	}
+
+	commonCmdParams := stubsCommandParams{
+		srcJarDir:               srcJarDir,
+		stubsDir:                stubsDir,
+		stubsSrcJar:             d.Javadoc.stubsSrcJar,
+		metadataDir:             d.everythingArtifacts.metadataDir,
+		apiVersionsXml:          d.everythingArtifacts.apiVersionsXml,
+		nullabilityWarningsFile: d.everythingArtifacts.nullabilityWarningsFile,
+		annotationsZip:          d.everythingArtifacts.annotationsZip,
+		stubConfig:              params,
+	}
+
+	cmd := d.commonMetalavaStubCmd(ctx, rule, commonCmdParams)
+
+	d.everythingOptionalCmd(ctx, cmd, params.doApiLint, params.doCheckReleased)
+
+	if params.generateStubs {
+		rule.Command().
+			BuiltTool("soong_zip").
+			Flag("-write_if_changed").
+			Flag("-jar").
+			FlagWithOutput("-o ", d.Javadoc.stubsSrcJar).
+			FlagWithArg("-C ", stubsDir.String()).
+			FlagWithArg("-D ", stubsDir.String())
+	}
+
+	if params.writeSdkValues {
+		rule.Command().
+			BuiltTool("soong_zip").
+			Flag("-write_if_changed").
+			Flag("-d").
+			FlagWithOutput("-o ", d.everythingArtifacts.metadataZip).
+			FlagWithArg("-C ", d.everythingArtifacts.metadataDir.String()).
+			FlagWithArg("-D ", d.everythingArtifacts.metadataDir.String())
+	}
+
+	// TODO: We don't really need two separate API files, but this is a reminiscence of how
+	// we used to run metalava separately for API lint and the "last_released" check. Unify them.
+	if params.doApiLint {
+		rule.Command().Text("touch").Output(d.apiLintTimestamp)
+	}
+	if params.doCheckReleased {
+		rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp)
+	}
+
+	// TODO(b/183630617): rewrapper doesn't support restat rules
+	if !metalavaUseRbe(ctx) {
+		rule.Restat()
+	}
+
+	zipSyncCleanupCmd(rule, srcJarDir)
+
+	rule.Build("metalava", "metalava merged")
+}
+
+// Sandbox rule for generating the everything artifacts that are not run by
+// default but only run based on the module configurations
+func (d *Droidstubs) everythingOptionalCmd(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, doApiLint bool, doCheckReleased bool) {
 
 	// Add API lint options.
-
-	if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) {
-		doApiLint = true
-
+	if doApiLint {
 		newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since)
 		if newSince.Valid() {
 			cmd.FlagWithInput("--api-lint ", newSince.Path())
 		} else {
 			cmd.Flag("--api-lint")
 		}
-		d.apiLintReport = android.PathForModuleOut(ctx, "metalava", "api_lint_report.txt")
+		d.apiLintReport = android.PathForModuleOut(ctx, Everything.String(), "api_lint_report.txt")
 		cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) // TODO:  Change to ":api-lint"
 
 		// TODO(b/154317059): Clean up this allowlist by baselining and/or checking in last-released.
@@ -614,8 +946,8 @@
 		}
 
 		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file)
-		updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "api_lint_baseline.txt")
-		d.apiLintTimestamp = android.PathForModuleOut(ctx, "metalava", "api_lint.timestamp")
+		updatedBaselineOutput := android.PathForModuleOut(ctx, Everything.String(), "api_lint_baseline.txt")
+		d.apiLintTimestamp = android.PathForModuleOut(ctx, Everything.String(), "api_lint.timestamp")
 
 		// Note this string includes a special shell quote $' ... ', which decodes the "\n"s.
 		//
@@ -656,10 +988,7 @@
 	}
 
 	// Add "check released" options. (Detect incompatible API changes from the last public release)
-
-	if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") {
-		doCheckReleased = true
-
+	if doCheckReleased {
 		if len(d.Javadoc.properties.Out) > 0 {
 			ctx.PropertyErrorf("out", "out property may not be combined with check_api")
 		}
@@ -667,9 +996,9 @@
 		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
 		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file))
 		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file)
-		updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "last_released_baseline.txt")
+		updatedBaselineOutput := android.PathForModuleOut(ctx, Everything.String(), "last_released_baseline.txt")
 
-		d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_last_released_api.timestamp")
+		d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_last_released_api.timestamp")
 
 		cmd.FlagWithInput("--check-compatibility:api:released ", apiFile)
 		cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile)
@@ -694,35 +1023,96 @@
 		currentApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
 		cmd.FlagWithInput("--use-same-format-as ", currentApiFile)
 	}
+}
 
-	if generateStubs {
+// Sandbox rule for generating exportable stubs and other artifacts
+func (d *Droidstubs) exportableStubCmd(ctx android.ModuleContext, params stubsCommandConfigParams) {
+	optionalCmdParams := stubsCommandParams{
+		stubConfig: params,
+	}
+
+	if params.generateStubs {
+		d.Javadoc.exportableStubsSrcJar = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"-"+"stubs.srcjar")
+		optionalCmdParams.stubsSrcJar = d.Javadoc.exportableStubsSrcJar
+	}
+
+	if params.writeSdkValues {
+		d.exportableArtifacts.metadataZip = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"-metadata.zip")
+		d.exportableArtifacts.metadataDir = android.PathForModuleOut(ctx, params.stubsType.String(), "metadata")
+		optionalCmdParams.metadataZip = d.exportableArtifacts.metadataZip
+		optionalCmdParams.metadataDir = d.exportableArtifacts.metadataDir
+	}
+
+	if Bool(d.properties.Annotations_enabled) {
+		if params.validatingNullability {
+			d.exportableArtifacts.nullabilityWarningsFile = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"_nullability_warnings.txt")
+			optionalCmdParams.nullabilityWarningsFile = d.exportableArtifacts.nullabilityWarningsFile
+		}
+		d.exportableArtifacts.annotationsZip = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"_annotations.zip")
+		optionalCmdParams.annotationsZip = d.exportableArtifacts.annotationsZip
+	}
+	if Bool(d.properties.Api_levels_annotations_enabled) {
+		d.exportableArtifacts.apiVersionsXml = android.PathForModuleOut(ctx, params.stubsType.String(), "api-versions.xml")
+		optionalCmdParams.apiVersionsXml = d.exportableArtifacts.apiVersionsXml
+	}
+
+	if params.checkApi || String(d.properties.Api_filename) != "" {
+		filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt")
+		d.exportableApiFile = android.PathForModuleOut(ctx, params.stubsType.String(), filename)
+	}
+
+	if params.checkApi || String(d.properties.Removed_api_filename) != "" {
+		filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_api.txt")
+		d.exportableRemovedApiFile = android.PathForModuleOut(ctx, params.stubsType.String(), filename)
+	}
+
+	d.optionalStubCmd(ctx, optionalCmdParams)
+}
+
+func (d *Droidstubs) optionalStubCmd(ctx android.ModuleContext, params stubsCommandParams) {
+
+	params.srcJarDir = android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "srcjars")
+	rule := android.NewRuleBuilder(pctx, ctx)
+	rule.Sbox(android.PathForModuleOut(ctx, params.stubConfig.stubsType.String()),
+		android.PathForModuleOut(ctx, fmt.Sprintf("metalava_%s.sbox.textproto", params.stubConfig.stubsType.String()))).
+		SandboxInputs()
+
+	if params.stubConfig.generateStubs {
+		params.stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "stubsDir"))
+	}
+
+	cmd := d.commonMetalavaStubCmd(ctx, rule, params)
+
+	generateRevertAnnotationArgs(ctx, cmd, params.stubConfig.stubsType, params.stubConfig.deps.aconfigProtoFiles)
+
+	if params.stubConfig.doApiLint {
+		// Pass the lint baseline file as an input to resolve the lint errors.
+		// The exportable stubs generation does not update the lint baseline file.
+		// Lint baseline file update is handled by the everything stubs
+		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file)
+		if baselineFile.Valid() {
+			cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path())
+		}
+	}
+
+	if params.stubConfig.generateStubs {
 		rule.Command().
 			BuiltTool("soong_zip").
 			Flag("-write_if_changed").
 			Flag("-jar").
-			FlagWithOutput("-o ", d.Javadoc.stubsSrcJar).
-			FlagWithArg("-C ", stubsDir.String()).
-			FlagWithArg("-D ", stubsDir.String())
+			FlagWithOutput("-o ", params.stubsSrcJar).
+			FlagWithArg("-C ", params.stubsDir.String()).
+			FlagWithArg("-D ", params.stubsDir.String())
 	}
 
-	if Bool(d.properties.Write_sdk_values) {
-		d.metadataZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-metadata.zip")
+	if params.stubConfig.writeSdkValues {
 		rule.Command().
 			BuiltTool("soong_zip").
 			Flag("-write_if_changed").
 			Flag("-d").
-			FlagWithOutput("-o ", d.metadataZip).
-			FlagWithArg("-C ", d.metadataDir.String()).
-			FlagWithArg("-D ", d.metadataDir.String())
-	}
-
-	// TODO: We don't really need two separate API files, but this is a reminiscence of how
-	// we used to run metalava separately for API lint and the "last_released" check. Unify them.
-	if doApiLint {
-		rule.Command().Text("touch").Output(d.apiLintTimestamp)
-	}
-	if doCheckReleased {
-		rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp)
+			FlagWithOutput("-o ", params.metadataZip).
+			FlagWithArg("-C ", params.metadataDir.String()).
+			FlagWithArg("-D ", params.metadataDir.String())
 	}
 
 	// TODO(b/183630617): rewrapper doesn't support restat rules
@@ -730,9 +1120,53 @@
 		rule.Restat()
 	}
 
-	zipSyncCleanupCmd(rule, srcJarDir)
+	zipSyncCleanupCmd(rule, params.srcJarDir)
 
-	rule.Build("metalava", "metalava merged")
+	rule.Build(fmt.Sprintf("metalava_%s", params.stubConfig.stubsType.String()), "metalava merged")
+}
+
+func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	deps := d.Javadoc.collectDeps(ctx)
+
+	javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), android.SdkContext(d))
+	generateStubs := BoolDefault(d.properties.Generate_stubs, true)
+
+	// Add options for the other optional tasks: API-lint and check-released.
+	// We generate separate timestamp files for them.
+	doApiLint := BoolDefault(d.properties.Check_api.Api_lint.Enabled, false)
+	doCheckReleased := apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released")
+
+	writeSdkValues := Bool(d.properties.Write_sdk_values)
+
+	annotationsEnabled := Bool(d.properties.Annotations_enabled)
+
+	migratingNullability := annotationsEnabled && String(d.properties.Previous_api) != ""
+	validatingNullability := annotationsEnabled && (strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") ||
+		String(d.properties.Validate_nullability_from_list) != "")
+
+	checkApi := apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
+		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released")
+
+	stubCmdParams := stubsCommandConfigParams{
+		javaVersion:           javaVersion,
+		deps:                  deps,
+		checkApi:              checkApi,
+		generateStubs:         generateStubs,
+		doApiLint:             doApiLint,
+		doCheckReleased:       doCheckReleased,
+		writeSdkValues:        writeSdkValues,
+		migratingNullability:  migratingNullability,
+		validatingNullability: validatingNullability,
+	}
+	stubCmdParams.stubsType = Everything
+	// Create default (i.e. "everything" stubs) rule for metalava
+	d.everythingStubCmd(ctx, stubCmdParams)
+
+	// The module generates "exportable" (and "runtime" eventually) stubs regardless of whether
+	// aconfig_declarations property is defined or not. If the property is not defined, the module simply
+	// strips all flagged apis to generate the "exportable" stubs
+	stubCmdParams.stubsType = Exportable
+	d.exportableStubCmd(ctx, stubCmdParams)
 
 	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
 
@@ -748,7 +1182,7 @@
 			ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName())
 		}
 
-		d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_current_api.timestamp")
+		d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_current_api.timestamp")
 
 		rule := android.NewRuleBuilder(pctx, ctx)
 
@@ -779,7 +1213,8 @@
 			`either of the two choices above and try re-building the target.\n`+
 			`If the mismatch between the stubs and the current.txt is intended,\n`+
 			`you can try re-building the target by executing the following command:\n`+
-			`m DISABLE_STUB_VALIDATION=true <your build target>\n`+
+			`m DISABLE_STUB_VALIDATION=true <your build target>.\n`+
+			`Note that DISABLE_STUB_VALIDATION=true does not bypass checkapi.\n`+
 			`******************************\n`, ctx.ModuleName())
 
 		rule.Command().
@@ -791,7 +1226,7 @@
 
 		rule.Build("metalavaCurrentApiCheck", "check current API")
 
-		d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "update_current_api.timestamp")
+		d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, Everything.String(), "update_current_api.timestamp")
 
 		// update API rule
 		rule = android.NewRuleBuilder(pctx, ctx)
@@ -819,14 +1254,14 @@
 	}
 
 	if String(d.properties.Check_nullability_warnings) != "" {
-		if d.nullabilityWarningsFile == nil {
+		if d.everythingArtifacts.nullabilityWarningsFile == nil {
 			ctx.PropertyErrorf("check_nullability_warnings",
 				"Cannot specify check_nullability_warnings unless validating nullability")
 		}
 
 		checkNullabilityWarnings := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings))
 
-		d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, "metalava", "check_nullability_warnings.timestamp")
+		d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_nullability_warnings.timestamp")
 
 		msg := fmt.Sprintf(`\n******************************\n`+
 			`The warnings encountered during nullability annotation validation did\n`+
@@ -836,13 +1271,13 @@
 			`   2. Update the file of expected warnings by running:\n`+
 			`         cp %s %s\n`+
 			`       and submitting the updated file as part of your change.`,
-			d.nullabilityWarningsFile, checkNullabilityWarnings)
+			d.everythingArtifacts.nullabilityWarningsFile, checkNullabilityWarnings)
 
 		rule := android.NewRuleBuilder(pctx, ctx)
 
 		rule.Command().
 			Text("(").
-			Text("diff").Input(checkNullabilityWarnings).Input(d.nullabilityWarningsFile).
+			Text("diff").Input(checkNullabilityWarnings).Input(d.everythingArtifacts.nullabilityWarningsFile).
 			Text("&&").
 			Text("touch").Output(d.checkNullabilityWarningsTimestamp).
 			Text(") || (").
@@ -852,6 +1287,7 @@
 
 		rule.Build("nullabilityWarningsCheck", "nullability warnings check")
 	}
+	android.CollectDependencyAconfigFiles(ctx, &d.mergedAconfigFiles)
 }
 
 func (d *Droidstubs) createApiContribution(ctx android.DefaultableHookContext) {
@@ -910,6 +1346,24 @@
 
 type PrebuiltStubsSourcesProperties struct {
 	Srcs []string `android:"path"`
+
+	// Name of the source soong module that gets shadowed by this prebuilt
+	// If unspecified, follows the naming convention that the source module of
+	// the prebuilt is Name() without "prebuilt_" prefix
+	Source_module_name *string
+
+	// Non-nil if this prebuilt stub srcs  module was dynamically created by a java_sdk_library_import
+	// The name is the undecorated name of the java_sdk_library as it appears in the blueprint file
+	// (without any prebuilt_ prefix)
+	Created_by_java_sdk_library_name *string `blueprint:"mutated"`
+}
+
+func (j *PrebuiltStubsSources) BaseModuleName() string {
+	return proptools.StringDefault(j.properties.Source_module_name, j.ModuleBase.Name())
+}
+
+func (j *PrebuiltStubsSources) CreatedByJavaSdkLibraryName() *string {
+	return j.properties.Created_by_java_sdk_library_name
 }
 
 type PrebuiltStubsSources struct {
@@ -926,15 +1380,17 @@
 
 func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) {
 	switch tag {
-	case "":
+	// prebuilt droidstubs does not output "exportable" stubs.
+	// Output the "everything" stubs srcjar file if the tag is ".exportable".
+	case "", ".exportable":
 		return android.Paths{p.stubsSrcJar}, nil
 	default:
 		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
 	}
 }
 
-func (d *PrebuiltStubsSources) StubsSrcJar() android.Path {
-	return d.stubsSrcJar
+func (d *PrebuiltStubsSources) StubsSrcJar(_ StubsType) (android.Path, error) {
+	return d.stubsSrcJar, nil
 }
 
 func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) {
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index 17cad89..8da695f 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -213,7 +213,7 @@
 	}
 
 	manifest := android.RuleBuilderSboxProtoForTests(t, ctx, m.Output("metalava.sbox.textproto"))
-	if g, w := manifest.Commands[0].GetCommand(), "reference __SBOX_SANDBOX_DIR__/out/.intermediates/foo/gen/foo.txt"; !strings.Contains(g, w) {
+	if g, w := manifest.Commands[0].GetCommand(), "reference __SBOX_SANDBOX_DIR__/out/soong/.intermediates/foo/gen/foo.txt"; !strings.Contains(g, w) {
 		t.Errorf("Expected command to contain %q, got %q", w, g)
 	}
 }
@@ -337,6 +337,7 @@
 			name: "bar",
 			api_surface: "public",
 			api_contributions: ["foo.api.contribution"],
+			stubs_type: "everything",
 		}
 	`
 	ctx, _ := testJavaWithFS(t, `
@@ -364,12 +365,10 @@
 	ctx.ModuleForTests("bar", "android_common")
 }
 
-func TestDroidstubsHideFlaggedApi(t *testing.T) {
+func TestAconfigDeclarations(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.NextReleaseHideFlaggedApi = proptools.BoolPtr(true)
-			variables.Release_expose_flagged_api = proptools.BoolPtr(false)
 		}),
 		android.FixtureMergeMockFs(map[string][]byte{
 			"a/A.java":      nil,
@@ -377,6 +376,13 @@
 			"a/removed.txt": nil,
 		}),
 	).RunTestWithBp(t, `
+	aconfig_declarations {
+		name: "bar",
+		package: "com.example.package",
+		srcs: [
+			"bar.aconfig",
+		],
+	}
 	droidstubs {
 		name: "foo",
 		srcs: ["a/A.java"],
@@ -387,11 +393,70 @@
 				removed_api_file: "a/removed.txt",
 			}
 		},
+		aconfig_declarations: [
+			"bar",
+		],
+	}
+	`)
+
+	// Check that droidstubs depend on aconfig_declarations
+	android.AssertBoolEquals(t, "foo expected to depend on bar",
+		CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar"), true)
+
+	m := result.ModuleForTests("foo", "android_common")
+	android.AssertStringDoesContain(t, "foo generates revert annotations file",
+		strings.Join(m.AllOutputs(), ""), "revert-annotations-exportable.txt")
+
+	// revert-annotations.txt passed to exportable stubs generation metalava command
+	manifest := m.Output("metalava_exportable.sbox.textproto")
+	cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command)
+	android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "revert-annotations-exportable.txt")
+
+	android.AssertStringDoesContain(t, "foo generates exportable stubs jar",
+		strings.Join(m.AllOutputs(), ""), "exportable/foo-stubs.srcjar")
+}
+
+func TestReleaseExportRuntimeApis(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+			}
+			variables.ExportRuntimeApis = proptools.BoolPtr(true)
+		}),
+		android.FixtureMergeMockFs(map[string][]byte{
+			"a/A.java":      nil,
+			"a/current.txt": nil,
+			"a/removed.txt": nil,
+		}),
+	).RunTestWithBp(t, `
+	aconfig_declarations {
+		name: "bar",
+		package: "com.example.package",
+		srcs: [
+			"bar.aconfig",
+		],
+	}
+	droidstubs {
+		name: "foo",
+		srcs: ["a/A.java"],
+		api_surface: "public",
+		check_api: {
+			current: {
+				api_file: "a/current.txt",
+				removed_api_file: "a/removed.txt",
+			}
+		},
+		aconfig_declarations: [
+			"bar",
+		],
 	}
 	`)
 
 	m := result.ModuleForTests("foo", "android_common")
-	manifest := m.Output("metalava.sbox.textproto")
-	cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command)
-	android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "--revert-annotation android.annotation.FlaggedApi")
+
+	rule := m.Output("released-flagged-apis-exportable.txt")
+	exposeWritableApisFilter := "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'"
+	android.AssertStringEquals(t, "Filter argument expected to contain READ_WRITE permissions", exposeWritableApisFilter, rule.Args["filter_args"])
 }
diff --git a/java/fuzz.go b/java/fuzz.go
index b3c2fd4..dc4c6be 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -121,7 +121,7 @@
 
 	_, sharedDeps := cc.CollectAllSharedDependencies(ctx)
 	for _, dep := range sharedDeps {
-		sharedLibInfo := ctx.OtherModuleProvider(dep, cc.SharedLibraryInfoProvider).(cc.SharedLibraryInfo)
+		sharedLibInfo, _ := android.OtherModuleProvider(ctx, dep, cc.SharedLibraryInfoProvider)
 		if sharedLibInfo.SharedLibrary != nil {
 			arch := "lib"
 			if sharedLibInfo.Target.Arch.ArchType.Multilib == "lib64" {
diff --git a/java/generated_java_library.go b/java/generated_java_library.go
index 40f780c..e8316cc 100644
--- a/java/generated_java_library.go
+++ b/java/generated_java_library.go
@@ -107,3 +107,8 @@
 	module.Library.properties.Generated_srcjars = append(module.Library.properties.Generated_srcjars, srcJarPath)
 	module.Library.GenerateAndroidBuildActions(ctx)
 }
+
+// Add a rule to the jarjar renaming rules.  See RepackageProviderData.
+func (module *GeneratedJavaLibraryModule) AddJarJarRenameRule(original string, renamed string) {
+	module.addJarJarRenameRule(original, renamed)
+}
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index fe3fe7b..5441a3b 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -44,7 +44,8 @@
 	//
 	// This must be the path to the unencoded dex jar as the encoded dex jar indirectly depends on
 	// this file so using the encoded dex jar here would result in a cycle in the ninja rules.
-	bootDexJarPath OptionalDexJarPath
+	bootDexJarPath    OptionalDexJarPath
+	bootDexJarPathErr error
 
 	// The paths to the classes jars that contain classes and class members annotated with
 	// the UnsupportedAppUsage annotation that need to be extracted as part of the hidden API
@@ -56,7 +57,10 @@
 	uncompressDexState *bool
 }
 
-func (h *hiddenAPI) bootDexJar() OptionalDexJarPath {
+func (h *hiddenAPI) bootDexJar(ctx android.ModuleErrorfContext) OptionalDexJarPath {
+	if h.bootDexJarPathErr != nil {
+		ctx.ModuleErrorf(h.bootDexJarPathErr.Error())
+	}
 	return h.bootDexJarPath
 }
 
@@ -77,7 +81,7 @@
 }
 
 type hiddenAPIIntf interface {
-	bootDexJar() OptionalDexJarPath
+	bootDexJar(ctx android.ModuleErrorfContext) OptionalDexJarPath
 	classesJars() android.Paths
 	uncompressDex() *bool
 }
@@ -94,7 +98,7 @@
 	// processing.
 	classesJars := android.Paths{classesJar}
 	ctx.VisitDirectDepsWithTag(hiddenApiAnnotationsTag, func(dep android.Module) {
-		javaInfo := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
+		javaInfo, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider)
 		classesJars = append(classesJars, javaInfo.ImplementationJars...)
 	})
 	h.classesJarPaths = classesJars
@@ -126,6 +130,11 @@
 	h.active = isModuleInBootClassPath(ctx, module)
 }
 
+// Store any error encountered during the initialization of hiddenapi structure (e.g. unflagged co-existing prebuilt apexes)
+func (h *hiddenAPI) initHiddenAPIError(err error) {
+	h.bootDexJarPathErr = err
+}
+
 func isModuleInBootClassPath(ctx android.BaseModuleContext, module android.Module) bool {
 	// Get the configured platform and apex boot jars.
 	nonApexBootJars := ctx.Config().NonApexBootJars()
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index fbc0197..e4beb5e 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -19,6 +19,7 @@
 	"strings"
 
 	"android/soong/android"
+	"android/soong/dexpreopt"
 
 	"github.com/google/blueprint"
 )
@@ -244,12 +245,22 @@
 		testStubModules = append(testStubModules, "sdk_test_current_android")
 	} else {
 		// Use stub modules built from source
-		publicStubModules = append(publicStubModules, android.SdkPublic.DefaultJavaLibraryName())
-		systemStubModules = append(systemStubModules, android.SdkSystem.DefaultJavaLibraryName())
-		testStubModules = append(testStubModules, android.SdkTest.DefaultJavaLibraryName())
+		if config.ReleaseHiddenApiExportableStubs() {
+			publicStubModules = append(publicStubModules, android.SdkPublic.DefaultExportableJavaLibraryName())
+			systemStubModules = append(systemStubModules, android.SdkSystem.DefaultExportableJavaLibraryName())
+			testStubModules = append(testStubModules, android.SdkTest.DefaultExportableJavaLibraryName())
+		} else {
+			publicStubModules = append(publicStubModules, android.SdkPublic.DefaultJavaLibraryName())
+			systemStubModules = append(systemStubModules, android.SdkSystem.DefaultJavaLibraryName())
+			testStubModules = append(testStubModules, android.SdkTest.DefaultJavaLibraryName())
+		}
 	}
 	// We do not have prebuilts of the core platform api yet
-	corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs")
+	if config.ReleaseHiddenApiExportableStubs() {
+		corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs.exportable")
+	} else {
+		corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs")
+	}
 
 	// Allow products to define their own stubs for custom product jars that apps can use.
 	publicStubModules = append(publicStubModules, config.ProductHiddenAPIStubs()...)
@@ -288,9 +299,14 @@
 func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android.Module, kind android.SdkKind) android.Path {
 	var dexJar OptionalDexJarPath
 	if sdkLibrary, ok := module.(SdkLibraryDependency); ok {
-		dexJar = sdkLibrary.SdkApiStubDexJar(ctx, kind)
+		if ctx.Config().ReleaseHiddenApiExportableStubs() {
+			dexJar = sdkLibrary.SdkApiExportableStubDexJar(ctx, kind)
+		} else {
+			dexJar = sdkLibrary.SdkApiStubDexJar(ctx, kind)
+		}
+
 	} else if j, ok := module.(UsesLibraryDependency); ok {
-		dexJar = j.DexJarBuildPath()
+		dexJar = j.DexJarBuildPath(ctx)
 	} else {
 		ctx.ModuleErrorf("dependency %s of module type %s does not support providing a dex jar", module, ctx.OtherModuleType(module))
 		return nil
@@ -419,122 +435,118 @@
 	}
 }
 
-type hiddenAPIFlagFileCategory struct {
-	// PropertyName is the name of the property for this category.
-	PropertyName string
+type hiddenAPIFlagFileCategory int
 
-	// propertyValueReader retrieves the value of the property for this category from the set of
-	// properties.
-	propertyValueReader func(properties *HiddenAPIFlagFileProperties) []string
+const (
+	// The flag file category for removed members of the API.
+	//
+	// This is extracted from HiddenAPIFlagFileCategories as it is needed to add the dex signatures
+	// list of removed API members that are generated automatically from the removed.txt files provided
+	// by API stubs.
+	hiddenAPIFlagFileCategoryRemoved hiddenAPIFlagFileCategory = iota
+	hiddenAPIFlagFileCategoryUnsupported
+	hiddenAPIFlagFileCategoryMaxTargetRLowPriority
+	hiddenAPIFlagFileCategoryMaxTargetQ
+	hiddenAPIFlagFileCategoryMaxTargetP
+	hiddenAPIFlagFileCategoryMaxTargetOLowPriority
+	hiddenAPIFlagFileCategoryBlocked
+	hiddenAPIFlagFileCategoryUnsupportedPackages
+)
 
-	// commandMutator adds the appropriate command line options for this category to the supplied
-	// command
-	commandMutator func(command *android.RuleBuilderCommand, path android.Path)
-}
-
-// The flag file category for removed members of the API.
-//
-// This is extracted from HiddenAPIFlagFileCategories as it is needed to add the dex signatures
-// list of removed API members that are generated automatically from the removed.txt files provided
-// by API stubs.
-var hiddenAPIRemovedFlagFileCategory = &hiddenAPIFlagFileCategory{
-	// See HiddenAPIFlagFileProperties.Removed
-	PropertyName: "removed",
-	propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
-		return properties.Hidden_api.Removed
-	},
-	commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
-		command.FlagWithInput("--unsupported ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed")
-	},
-}
-
-type hiddenAPIFlagFileCategories []*hiddenAPIFlagFileCategory
-
-func (c hiddenAPIFlagFileCategories) byProperty(name string) *hiddenAPIFlagFileCategory {
-	for _, category := range c {
-		if category.PropertyName == name {
-			return category
-		}
+func (c hiddenAPIFlagFileCategory) PropertyName() string {
+	switch c {
+	case hiddenAPIFlagFileCategoryRemoved:
+		return "removed"
+	case hiddenAPIFlagFileCategoryUnsupported:
+		return "unsupported"
+	case hiddenAPIFlagFileCategoryMaxTargetRLowPriority:
+		return "max_target_r_low_priority"
+	case hiddenAPIFlagFileCategoryMaxTargetQ:
+		return "max_target_q"
+	case hiddenAPIFlagFileCategoryMaxTargetP:
+		return "max_target_p"
+	case hiddenAPIFlagFileCategoryMaxTargetOLowPriority:
+		return "max_target_o_low_priority"
+	case hiddenAPIFlagFileCategoryBlocked:
+		return "blocked"
+	case hiddenAPIFlagFileCategoryUnsupportedPackages:
+		return "unsupported_packages"
+	default:
+		panic(fmt.Sprintf("Unknown hidden api flag file category type: %d", c))
 	}
-	panic(fmt.Errorf("no category exists with property name %q in %v", name, c))
 }
 
+// propertyValueReader retrieves the value of the property for this category from the set of properties.
+func (c hiddenAPIFlagFileCategory) propertyValueReader(properties *HiddenAPIFlagFileProperties) []string {
+	switch c {
+	case hiddenAPIFlagFileCategoryRemoved:
+		return properties.Hidden_api.Removed
+	case hiddenAPIFlagFileCategoryUnsupported:
+		return properties.Hidden_api.Unsupported
+	case hiddenAPIFlagFileCategoryMaxTargetRLowPriority:
+		return properties.Hidden_api.Max_target_r_low_priority
+	case hiddenAPIFlagFileCategoryMaxTargetQ:
+		return properties.Hidden_api.Max_target_q
+	case hiddenAPIFlagFileCategoryMaxTargetP:
+		return properties.Hidden_api.Max_target_p
+	case hiddenAPIFlagFileCategoryMaxTargetOLowPriority:
+		return properties.Hidden_api.Max_target_o_low_priority
+	case hiddenAPIFlagFileCategoryBlocked:
+		return properties.Hidden_api.Blocked
+	case hiddenAPIFlagFileCategoryUnsupportedPackages:
+		return properties.Hidden_api.Unsupported_packages
+	default:
+		panic(fmt.Sprintf("Unknown hidden api flag file category type: %d", c))
+	}
+}
+
+// commandMutator adds the appropriate command line options for this category to the supplied command
+func (c hiddenAPIFlagFileCategory) commandMutator(command *android.RuleBuilderCommand, path android.Path) {
+	switch c {
+	case hiddenAPIFlagFileCategoryRemoved:
+		command.FlagWithInput("--unsupported ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed")
+	case hiddenAPIFlagFileCategoryUnsupported:
+		command.FlagWithInput("--unsupported ", path)
+	case hiddenAPIFlagFileCategoryMaxTargetRLowPriority:
+		command.FlagWithInput("--max-target-r ", path).FlagWithArg("--tag ", "lo-prio")
+	case hiddenAPIFlagFileCategoryMaxTargetQ:
+		command.FlagWithInput("--max-target-q ", path)
+	case hiddenAPIFlagFileCategoryMaxTargetP:
+		command.FlagWithInput("--max-target-p ", path)
+	case hiddenAPIFlagFileCategoryMaxTargetOLowPriority:
+		command.FlagWithInput("--max-target-o ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio")
+	case hiddenAPIFlagFileCategoryBlocked:
+		command.FlagWithInput("--blocked ", path)
+	case hiddenAPIFlagFileCategoryUnsupportedPackages:
+		command.FlagWithInput("--unsupported ", path).Flag("--packages ")
+	default:
+		panic(fmt.Sprintf("Unknown hidden api flag file category type: %d", c))
+	}
+}
+
+type hiddenAPIFlagFileCategories []hiddenAPIFlagFileCategory
+
 var HiddenAPIFlagFileCategories = hiddenAPIFlagFileCategories{
 	// See HiddenAPIFlagFileProperties.Unsupported
-	{
-		PropertyName: "unsupported",
-		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
-			return properties.Hidden_api.Unsupported
-		},
-		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
-			command.FlagWithInput("--unsupported ", path)
-		},
-	},
-	hiddenAPIRemovedFlagFileCategory,
+	hiddenAPIFlagFileCategoryUnsupported,
+	// See HiddenAPIFlagFileProperties.Removed
+	hiddenAPIFlagFileCategoryRemoved,
 	// See HiddenAPIFlagFileProperties.Max_target_r_low_priority
-	{
-		PropertyName: "max_target_r_low_priority",
-		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
-			return properties.Hidden_api.Max_target_r_low_priority
-		},
-		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
-			command.FlagWithInput("--max-target-r ", path).FlagWithArg("--tag ", "lo-prio")
-		},
-	},
+	hiddenAPIFlagFileCategoryMaxTargetRLowPriority,
 	// See HiddenAPIFlagFileProperties.Max_target_q
-	{
-		PropertyName: "max_target_q",
-		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
-			return properties.Hidden_api.Max_target_q
-		},
-		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
-			command.FlagWithInput("--max-target-q ", path)
-		},
-	},
+	hiddenAPIFlagFileCategoryMaxTargetQ,
 	// See HiddenAPIFlagFileProperties.Max_target_p
-	{
-		PropertyName: "max_target_p",
-		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
-			return properties.Hidden_api.Max_target_p
-		},
-		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
-			command.FlagWithInput("--max-target-p ", path)
-		},
-	},
+	hiddenAPIFlagFileCategoryMaxTargetP,
 	// See HiddenAPIFlagFileProperties.Max_target_o_low_priority
-	{
-		PropertyName: "max_target_o_low_priority",
-		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
-			return properties.Hidden_api.Max_target_o_low_priority
-		},
-		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
-			command.FlagWithInput("--max-target-o ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio")
-		},
-	},
+	hiddenAPIFlagFileCategoryMaxTargetOLowPriority,
 	// See HiddenAPIFlagFileProperties.Blocked
-	{
-		PropertyName: "blocked",
-		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
-			return properties.Hidden_api.Blocked
-		},
-		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
-			command.FlagWithInput("--blocked ", path)
-		},
-	},
+	hiddenAPIFlagFileCategoryBlocked,
 	// See HiddenAPIFlagFileProperties.Unsupported_packages
-	{
-		PropertyName: "unsupported_packages",
-		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
-			return properties.Hidden_api.Unsupported_packages
-		},
-		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
-			command.FlagWithInput("--unsupported ", path).Flag("--packages ")
-		},
-	},
+	hiddenAPIFlagFileCategoryUnsupportedPackages,
 }
 
 // FlagFilesByCategory maps a hiddenAPIFlagFileCategory to the paths to the files in that category.
-type FlagFilesByCategory map[*hiddenAPIFlagFileCategory]android.Paths
+type FlagFilesByCategory map[hiddenAPIFlagFileCategory]android.Paths
 
 // append the supplied flags files to the corresponding category in this map.
 func (s FlagFilesByCategory) append(other FlagFilesByCategory) {
@@ -579,8 +591,7 @@
 	// Merge all the information from the fragments. The fragments form a DAG so it is possible that
 	// this will introduce duplicates so they will be resolved after processing all the fragments.
 	for _, fragment := range fragments {
-		if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) {
-			info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo)
+		if info, ok := android.OtherModuleProvider(ctx, fragment, HiddenAPIInfoProvider); ok {
 			i.TransitiveStubDexJarsByScope.addStubDexJarsByModule(info.TransitiveStubDexJarsByScope)
 		}
 	}
@@ -600,7 +611,7 @@
 	return SignatureCsvSubset{i.FilteredFlagsPath, i.SignaturePatternsPath}
 }
 
-var HiddenAPIInfoProvider = blueprint.NewProvider(HiddenAPIInfo{})
+var HiddenAPIInfoProvider = blueprint.NewProvider[HiddenAPIInfo]()
 
 // HiddenAPIInfoForSdk contains information provided by the hidden API processing for use
 // by the sdk snapshot.
@@ -617,7 +628,7 @@
 }
 
 // Provides hidden API info for the sdk snapshot.
-var HiddenAPIInfoForSdkProvider = blueprint.NewProvider(HiddenAPIInfoForSdk{})
+var HiddenAPIInfoForSdkProvider = blueprint.NewProvider[HiddenAPIInfoForSdk]()
 
 // ModuleStubDexJars contains the stub dex jars provided by a single module.
 //
@@ -749,7 +760,7 @@
 	SplitPackages []string
 }
 
-var hiddenAPIPropertyInfoProvider = blueprint.NewProvider(HiddenAPIPropertyInfo{})
+var hiddenAPIPropertyInfoProvider = blueprint.NewProvider[HiddenAPIPropertyInfo]()
 
 // newHiddenAPIPropertyInfo creates a new initialized HiddenAPIPropertyInfo struct.
 func newHiddenAPIPropertyInfo() HiddenAPIPropertyInfo {
@@ -777,8 +788,7 @@
 
 func (i *HiddenAPIPropertyInfo) gatherPropertyInfo(ctx android.ModuleContext, contents []android.Module) {
 	for _, module := range contents {
-		if ctx.OtherModuleHasProvider(module, hiddenAPIPropertyInfoProvider) {
-			info := ctx.OtherModuleProvider(module, hiddenAPIPropertyInfoProvider).(HiddenAPIPropertyInfo)
+		if info, ok := android.OtherModuleProvider(ctx, module, hiddenAPIPropertyInfoProvider); ok {
 			i.FlagFilesByCategory.append(info.FlagFilesByCategory)
 			i.PackagePrefixes = append(i.PackagePrefixes, info.PackagePrefixes...)
 			i.SinglePackages = append(i.SinglePackages, info.SinglePackages...)
@@ -949,6 +959,7 @@
 	HiddenAPIFlagOutput
 
 	// The map from base module name to the path to the encoded boot dex file.
+	// This field is not available in prebuilt apexes
 	EncodedBootDexFilesByModule bootDexJarByModule
 }
 
@@ -999,7 +1010,7 @@
 	// If available then pass the automatically generated file containing dex signatures of removed
 	// API members to the rule so they can be marked as removed.
 	if generatedRemovedDexSignatures.Valid() {
-		hiddenAPIRemovedFlagFileCategory.commandMutator(command, generatedRemovedDexSignatures.Path())
+		hiddenAPIFlagFileCategoryRemoved.commandMutator(command, generatedRemovedDexSignatures.Path())
 	}
 
 	commitChangeForRestat(rule, tempPath, outputPath)
@@ -1251,9 +1262,27 @@
 }
 
 // extractBootDexJarsFromModules extracts the boot dex jars from the supplied modules.
+// This information can come from two mechanisms
+// 1. New: Direct deps to _selected_ apexes. The apexes contain a ApexExportsInfo
+// 2. Legacy: An edge to java_sdk_library(_import) module. For prebuilt apexes, this serves as a hook and is populated by deapexers of prebuilt apxes
+// TODO: b/308174306 - Once all mainline modules have been flagged, drop (2)
 func extractBootDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule {
 	bootDexJars := bootDexJarByModule{}
+
+	apexNameToApexExportsInfoMap := getApexNameToApexExportsInfoMap(ctx)
+	// For ART and mainline module jars, query apexNameToApexExportsInfoMap to get the dex file
+	apexJars := dexpreopt.GetGlobalConfig(ctx).ArtApexJars.AppendList(&dexpreopt.GetGlobalConfig(ctx).ApexBootJars)
+	for i := 0; i < apexJars.Len(); i++ {
+		if dex, found := apexNameToApexExportsInfoMap.javaLibraryDexPathOnHost(ctx, apexJars.Apex(i), apexJars.Jar(i)); found {
+			bootDexJars[apexJars.Jar(i)] = dex
+		}
+	}
+
+	// TODO - b/308174306: Drop the legacy mechanism
 	for _, module := range contents {
+		if _, exists := bootDexJars[android.RemoveOptionalPrebuiltPrefix(module.Name())]; exists {
+			continue
+		}
 		hiddenAPIModule := hiddenAPIModuleFromModule(ctx, module)
 		if hiddenAPIModule == nil {
 			continue
@@ -1324,7 +1353,7 @@
 // invalid, then create a fake path and either report an error immediately or defer reporting of the
 // error until the path is actually used.
 func retrieveBootDexJarFromHiddenAPIModule(ctx android.ModuleContext, module hiddenAPIModule) android.Path {
-	bootDexJar := module.bootDexJar()
+	bootDexJar := module.bootDexJar(ctx)
 	if !bootDexJar.Valid() {
 		fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/boot-dex/%s.jar", module.Name()))
 		handleMissingDexBootFile(ctx, module, fake, bootDexJar.InvalidReason())
@@ -1404,7 +1433,7 @@
 		}
 
 		if am, ok := module.(android.ApexModule); ok && am.InAnyApex() {
-			apexInfo := ctx.OtherModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
+			apexInfo, _ := android.OtherModuleProvider(ctx, module, android.ApexInfoProvider)
 			if apexInfo.IsForPlatform() {
 				return true
 			}
@@ -1439,7 +1468,9 @@
 // However, under certain conditions, e.g. errors, or special build configurations it will return
 // a path to a fake file.
 func retrieveEncodedBootDexJarFromModule(ctx android.ModuleContext, module android.Module) android.Path {
-	bootDexJar := module.(interface{ DexJarBuildPath() OptionalDexJarPath }).DexJarBuildPath()
+	bootDexJar := module.(interface {
+		DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath
+	}).DexJarBuildPath(ctx)
 	if !bootDexJar.Valid() {
 		fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/encoded-dex/%s.jar", module.Name()))
 		handleMissingDexBootFile(ctx, module, fake, bootDexJar.InvalidReason())
diff --git a/java/hiddenapi_monolithic.go b/java/hiddenapi_monolithic.go
index 5956e3c..1e30c5f 100644
--- a/java/hiddenapi_monolithic.go
+++ b/java/hiddenapi_monolithic.go
@@ -67,9 +67,8 @@
 
 		case *ClasspathFragmentElement:
 			fragment := e.Module()
-			if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) {
-				info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo)
-				monolithicInfo.append(&info)
+			if info, ok := android.OtherModuleProvider(ctx, fragment, HiddenAPIInfoProvider); ok {
+				monolithicInfo.append(ctx, fragment, &info)
 			} else {
 				ctx.ModuleErrorf("%s does not provide hidden API information", fragment)
 			}
@@ -80,14 +79,25 @@
 }
 
 // append appends all the files from the supplied info to the corresponding files in this struct.
-func (i *MonolithicHiddenAPIInfo) append(other *HiddenAPIInfo) {
+func (i *MonolithicHiddenAPIInfo) append(ctx android.ModuleContext, otherModule android.Module, other *HiddenAPIInfo) {
 	i.FlagsFilesByCategory.append(other.FlagFilesByCategory)
 	i.AnnotationFlagsPaths = append(i.AnnotationFlagsPaths, other.AnnotationFlagsPath)
 	i.MetadataPaths = append(i.MetadataPaths, other.MetadataPath)
 	i.IndexPaths = append(i.IndexPaths, other.IndexPath)
 
-	i.StubFlagSubsets = append(i.StubFlagSubsets, other.StubFlagSubset())
-	i.FlagSubsets = append(i.FlagSubsets, other.FlagSubset())
+	apexInfo, ok := android.OtherModuleProvider(ctx, otherModule, android.ApexInfoProvider)
+	if !ok {
+		ctx.ModuleErrorf("Could not determine min_version_version of %s\n", otherModule.Name())
+		return
+	}
+	if apexInfo.MinSdkVersion.LessThanOrEqualTo(android.ApiLevelR) {
+		// Restrict verify_overlaps to R and older modules.
+		// The runtime in S does not have the same restriction that
+		// requires the hiddenapi flags to be generated in a monolithic
+		// invocation.
+		i.StubFlagSubsets = append(i.StubFlagSubsets, other.StubFlagSubset())
+		i.FlagSubsets = append(i.FlagSubsets, other.FlagSubset())
+	}
 }
 
-var MonolithicHiddenAPIInfoProvider = blueprint.NewProvider(MonolithicHiddenAPIInfo{})
+var MonolithicHiddenAPIInfoProvider = blueprint.NewProvider[MonolithicHiddenAPIInfo]()
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 8ec1797..8cb78cd 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -162,7 +162,7 @@
 		return false
 	}
 
-	apexInfo := ctx.OtherModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.OtherModuleProvider(ctx, module, android.ApexInfoProvider)
 
 	// Now match the apex part of the boot image configuration.
 	requiredApex := configuredBootJars.Apex(index)
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index ef792f9..c1fee21 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -177,10 +177,10 @@
 		{
 			name:             "testBundled",
 			unbundledBuild:   false,
-			publicStub:       "android_stubs_current",
-			systemStub:       "android_system_stubs_current",
-			testStub:         "android_test_stubs_current",
-			corePlatformStub: "legacy.core.platform.api.stubs",
+			publicStub:       "android_stubs_current_exportable",
+			systemStub:       "android_system_stubs_current_exportable",
+			testStub:         "android_test_stubs_current_exportable",
+			corePlatformStub: "legacy.core.platform.api.stubs.exportable",
 			preparer:         android.GroupFixturePreparers(),
 		}, {
 			name:             "testUnbundled",
@@ -188,7 +188,7 @@
 			publicStub:       "sdk_public_current_android",
 			systemStub:       "sdk_system_current_android",
 			testStub:         "sdk_test_current_android",
-			corePlatformStub: "legacy.core.platform.api.stubs",
+			corePlatformStub: "legacy.core.platform.api.stubs.exportable",
 			preparer:         PrepareForTestWithPrebuiltsOfCurrentApi,
 		},
 	}
@@ -200,6 +200,9 @@
 				prepareForTestWithDefaultPlatformBootclasspath,
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 					variables.Always_use_prebuilt_sdks = proptools.BoolPtr(tc.unbundledBuild)
+					variables.BuildFlags = map[string]string{
+						"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+					}
 				}),
 			).RunTest(t)
 
@@ -309,7 +312,8 @@
 		android.AssertStringEquals(t, "encode embedded java_library", unencodedDexJar, actualUnencodedDexJar.String())
 
 		// Make sure that the encoded dex jar is the exported one.
-		exportedDexJar := moduleForTests.Module().(UsesLibraryDependency).DexJarBuildPath().Path()
+		errCtx := moduleErrorfTestCtx{}
+		exportedDexJar := moduleForTests.Module().(UsesLibraryDependency).DexJarBuildPath(errCtx).Path()
 		android.AssertPathRelativeToTopEquals(t, "encode embedded java_library", encodedDexJar, exportedDexJar)
 	}
 
diff --git a/java/java.go b/java/java.go
index 9be8bfd..a9e6a39 100644
--- a/java/java.go
+++ b/java/java.go
@@ -24,8 +24,10 @@
 	"sort"
 	"strings"
 
+	"android/soong/aconfig"
 	"android/soong/remoteexec"
 	"android/soong/testing"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
@@ -86,6 +88,14 @@
 	android.RegisterSdkMemberType(javaTestSdkMemberType)
 }
 
+type StubsLinkType int
+
+const (
+	Unknown StubsLinkType = iota
+	Stubs
+	Implementation
+)
+
 var (
 	// Supports adding java header libraries to module_exports and sdk.
 	javaHeaderLibsSdkMemberType = &librarySdkMemberType{
@@ -239,7 +249,7 @@
 	UnconditionallyExportedProguardFlags *android.DepSet[android.Path]
 }
 
-var ProguardSpecInfoProvider = blueprint.NewProvider(ProguardSpecInfo{})
+var ProguardSpecInfoProvider = blueprint.NewProvider[ProguardSpecInfo]()
 
 // JavaInfo contains information about a java module for use by modules that depend on it.
 type JavaInfo struct {
@@ -247,6 +257,8 @@
 	// against this module.  If empty, ImplementationJars should be used instead.
 	HeaderJars android.Paths
 
+	RepackagedHeaderJars android.Paths
+
 	// set of header jars for all transitive libs deps
 	TransitiveLibsHeaderJars *android.DepSet[android.Path]
 
@@ -293,9 +305,14 @@
 	// JacocoReportClassesFile is the path to a jar containing uninstrumented classes that will be
 	// instrumented by jacoco.
 	JacocoReportClassesFile android.Path
+
+	// StubsLinkType provides information about whether the provided jars are stub jars or
+	// implementation jars. If the provider is set by java_sdk_library, the link type is "unknown"
+	// and selection between the stub jar vs implementation jar is deferred to SdkLibrary.sdkJars(...)
+	StubsLinkType StubsLinkType
 }
 
-var JavaInfoProvider = blueprint.NewProvider(JavaInfo{})
+var JavaInfoProvider = blueprint.NewProvider[JavaInfo]()
 
 // SyspropPublicStubInfo contains info about the sysprop public stub library that corresponds to
 // the sysprop implementation library.
@@ -305,7 +322,7 @@
 	JavaInfo JavaInfo
 }
 
-var SyspropPublicStubInfoProvider = blueprint.NewProvider(SyspropPublicStubInfo{})
+var SyspropPublicStubInfoProvider = blueprint.NewProvider[SyspropPublicStubInfo]()
 
 // Methods that need to be implemented for a module that is added to apex java_libs property.
 type ApexDependency interface {
@@ -315,7 +332,7 @@
 
 // Provides build path and install path to DEX jars.
 type UsesLibraryDependency interface {
-	DexJarBuildPath() OptionalDexJarPath
+	DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath
 	DexJarInstallPath() android.Path
 	ClassLoaderContexts() dexpreopt.ClassLoaderContextMap
 }
@@ -519,6 +536,7 @@
 	kotlinStdlib            android.Paths
 	kotlinAnnotations       android.Paths
 	kotlinPlugins           android.Paths
+	aconfigProtoFiles       android.Paths
 
 	disableTurbine bool
 }
@@ -562,9 +580,11 @@
 func (v javaVersion) String() string {
 	switch v {
 	case JAVA_VERSION_6:
-		return "1.6"
+		// Java version 1.6 no longer supported, bumping to 1.8
+		return "1.8"
 	case JAVA_VERSION_7:
-		return "1.7"
+		// Java version 1.7 no longer supported, bumping to 1.8
+		return "1.8"
 	case JAVA_VERSION_8:
 		return "1.8"
 	case JAVA_VERSION_9:
@@ -581,10 +601,12 @@
 func (v javaVersion) StringForKotlinc() string {
 	// $ ./external/kotlinc/bin/kotlinc -jvm-target foo
 	// error: unknown JVM target version: foo
-	// Supported versions: 1.6, 1.8, 9, 10, 11, 12, 13, 14, 15, 16, 17
+	// Supported versions: 1.8, 9, 10, 11, 12, 13, 14, 15, 16, 17
 	switch v {
+	case JAVA_VERSION_6:
+		return "1.8"
 	case JAVA_VERSION_7:
-		return "1.6"
+		return "1.8"
 	case JAVA_VERSION_9:
 		return "9"
 	default:
@@ -600,9 +622,11 @@
 func normalizeJavaVersion(ctx android.BaseModuleContext, javaVersion string) javaVersion {
 	switch javaVersion {
 	case "1.6", "6":
-		return JAVA_VERSION_6
+		// Java version 1.6 no longer supported, bumping to 1.8
+		return JAVA_VERSION_8
 	case "1.7", "7":
-		return JAVA_VERSION_7
+		// Java version 1.7 no longer supported, bumping to 1.8
+		return JAVA_VERSION_8
 	case "1.8", "8":
 		return JAVA_VERSION_8
 	case "1.9", "9":
@@ -645,9 +669,9 @@
 	return j.properties.Permitted_packages
 }
 
-func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bool {
+func shouldUncompressDex(ctx android.ModuleContext, libName string, dexpreopter *dexpreopter) bool {
 	// Store uncompressed (and aligned) any dex files from jars in APEXes.
-	if apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo); !apexInfo.IsForPlatform() {
+	if apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider); !apexInfo.IsForPlatform() {
 		return true
 	}
 
@@ -657,7 +681,7 @@
 	}
 
 	// Store uncompressed dex files that are preopted on /system.
-	if !dexpreopter.dexpreoptDisabled(ctx) && (ctx.Host() || !dexpreopter.odexOnSystemOther(ctx, dexpreopter.installPath)) {
+	if !dexpreopter.dexpreoptDisabled(ctx, libName) && (ctx.Host() || !dexpreopter.odexOnSystemOther(ctx, libName, dexpreopter.installPath)) {
 		return true
 	}
 	if ctx.Config().UncompressPrivAppDex() &&
@@ -672,22 +696,204 @@
 func setUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter, dexer *dexer) {
 	if dexer.dexProperties.Uncompress_dex == nil {
 		// If the value was not force-set by the user, use reasonable default based on the module.
-		dexer.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, dexpreopter))
+		dexer.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), dexpreopter))
+	}
+}
+
+// list of java_library modules that set platform_apis: true
+// this property is a no-op for java_library
+// TODO (b/215379393): Remove this allowlist
+var (
+	aospPlatformApiAllowlist = map[string]bool{
+		"adservices-test-scenarios":                         true,
+		"aidl-cpp-java-test-interface-java":                 true,
+		"aidl-test-extras-java":                             true,
+		"aidl-test-interface-java":                          true,
+		"aidl-test-interface-permission-java":               true,
+		"aidl_test_java_client_permission":                  true,
+		"aidl_test_java_client_sdk1":                        true,
+		"aidl_test_java_client_sdk29":                       true,
+		"aidl_test_java_client":                             true,
+		"aidl_test_java_service_permission":                 true,
+		"aidl_test_java_service_sdk1":                       true,
+		"aidl_test_java_service_sdk29":                      true,
+		"aidl_test_java_service":                            true,
+		"aidl_test_loggable_interface-java":                 true,
+		"aidl_test_nonvintf_parcelable-V1-java":             true,
+		"aidl_test_nonvintf_parcelable-V2-java":             true,
+		"aidl_test_unstable_parcelable-java":                true,
+		"aidl_test_vintf_parcelable-V1-java":                true,
+		"aidl_test_vintf_parcelable-V2-java":                true,
+		"android.aidl.test.trunk-V1-java":                   true,
+		"android.aidl.test.trunk-V2-java":                   true,
+		"android.frameworks.location.altitude-V1-java":      true,
+		"android.frameworks.location.altitude-V2-java":      true,
+		"android.frameworks.stats-V1-java":                  true,
+		"android.frameworks.stats-V2-java":                  true,
+		"android.frameworks.stats-V3-java":                  true,
+		"android.hardware.authsecret-V1-java":               true,
+		"android.hardware.authsecret-V2-java":               true,
+		"android.hardware.biometrics.common-V1-java":        true,
+		"android.hardware.biometrics.common-V2-java":        true,
+		"android.hardware.biometrics.common-V3-java":        true,
+		"android.hardware.biometrics.common-V4-java":        true,
+		"android.hardware.biometrics.face-V1-java":          true,
+		"android.hardware.biometrics.face-V2-java":          true,
+		"android.hardware.biometrics.face-V3-java":          true,
+		"android.hardware.biometrics.face-V4-java":          true,
+		"android.hardware.biometrics.fingerprint-V1-java":   true,
+		"android.hardware.biometrics.fingerprint-V2-java":   true,
+		"android.hardware.biometrics.fingerprint-V3-java":   true,
+		"android.hardware.biometrics.fingerprint-V4-java":   true,
+		"android.hardware.bluetooth.lmp_event-V1-java":      true,
+		"android.hardware.confirmationui-V1-java":           true,
+		"android.hardware.confirmationui-V2-java":           true,
+		"android.hardware.gatekeeper-V1-java":               true,
+		"android.hardware.gatekeeper-V2-java":               true,
+		"android.hardware.gnss-V1-java":                     true,
+		"android.hardware.gnss-V2-java":                     true,
+		"android.hardware.gnss-V3-java":                     true,
+		"android.hardware.gnss-V4-java":                     true,
+		"android.hardware.graphics.common-V1-java":          true,
+		"android.hardware.graphics.common-V2-java":          true,
+		"android.hardware.graphics.common-V3-java":          true,
+		"android.hardware.graphics.common-V4-java":          true,
+		"android.hardware.graphics.common-V5-java":          true,
+		"android.hardware.identity-V1-java":                 true,
+		"android.hardware.identity-V2-java":                 true,
+		"android.hardware.identity-V3-java":                 true,
+		"android.hardware.identity-V4-java":                 true,
+		"android.hardware.identity-V5-java":                 true,
+		"android.hardware.identity-V6-java":                 true,
+		"android.hardware.keymaster-V1-java":                true,
+		"android.hardware.keymaster-V2-java":                true,
+		"android.hardware.keymaster-V3-java":                true,
+		"android.hardware.keymaster-V4-java":                true,
+		"android.hardware.keymaster-V5-java":                true,
+		"android.hardware.oemlock-V1-java":                  true,
+		"android.hardware.oemlock-V2-java":                  true,
+		"android.hardware.power.stats-V1-java":              true,
+		"android.hardware.power.stats-V2-java":              true,
+		"android.hardware.power.stats-V3-java":              true,
+		"android.hardware.power-V1-java":                    true,
+		"android.hardware.power-V2-java":                    true,
+		"android.hardware.power-V3-java":                    true,
+		"android.hardware.power-V4-java":                    true,
+		"android.hardware.power-V5-java":                    true,
+		"android.hardware.rebootescrow-V1-java":             true,
+		"android.hardware.rebootescrow-V2-java":             true,
+		"android.hardware.security.authgraph-V1-java":       true,
+		"android.hardware.security.keymint-V1-java":         true,
+		"android.hardware.security.keymint-V2-java":         true,
+		"android.hardware.security.keymint-V3-java":         true,
+		"android.hardware.security.keymint-V4-java":         true,
+		"android.hardware.security.secretkeeper-V1-java":    true,
+		"android.hardware.security.secureclock-V1-java":     true,
+		"android.hardware.security.secureclock-V2-java":     true,
+		"android.hardware.thermal-V1-java":                  true,
+		"android.hardware.thermal-V2-java":                  true,
+		"android.hardware.threadnetwork-V1-java":            true,
+		"android.hardware.weaver-V1-java":                   true,
+		"android.hardware.weaver-V2-java":                   true,
+		"android.hardware.weaver-V3-java":                   true,
+		"android.security.attestationmanager-java":          true,
+		"android.security.authorization-java":               true,
+		"android.security.compat-java":                      true,
+		"android.security.legacykeystore-java":              true,
+		"android.security.maintenance-java":                 true,
+		"android.security.metrics-java":                     true,
+		"android.system.keystore2-V1-java":                  true,
+		"android.system.keystore2-V2-java":                  true,
+		"android.system.keystore2-V3-java":                  true,
+		"android.system.keystore2-V4-java":                  true,
+		"binderReadParcelIface-java":                        true,
+		"binderRecordReplayTestIface-java":                  true,
+		"car-experimental-api-static-lib":                   true,
+		"collector-device-lib-platform":                     true,
+		"com.android.car.oem":                               true,
+		"com.google.hardware.pixel.display-V10-java":        true,
+		"com.google.hardware.pixel.display-V1-java":         true,
+		"com.google.hardware.pixel.display-V2-java":         true,
+		"com.google.hardware.pixel.display-V3-java":         true,
+		"com.google.hardware.pixel.display-V4-java":         true,
+		"com.google.hardware.pixel.display-V5-java":         true,
+		"com.google.hardware.pixel.display-V6-java":         true,
+		"com.google.hardware.pixel.display-V7-java":         true,
+		"com.google.hardware.pixel.display-V8-java":         true,
+		"com.google.hardware.pixel.display-V9-java":         true,
+		"conscrypt-support":                                 true,
+		"cts-keystore-test-util":                            true,
+		"cts-keystore-user-auth-helper-library":             true,
+		"ctsmediautil":                                      true,
+		"CtsNetTestsNonUpdatableLib":                        true,
+		"DpmWrapper":                                        true,
+		"flickerlib-apphelpers":                             true,
+		"flickerlib-helpers":                                true,
+		"flickerlib-parsers":                                true,
+		"flickerlib":                                        true,
+		"hardware.google.bluetooth.ccc-V1-java":             true,
+		"hardware.google.bluetooth.sar-V1-java":             true,
+		"monet":                                             true,
+		"pixel-power-ext-V1-java":                           true,
+		"pixel-power-ext-V2-java":                           true,
+		"pixel_stateresidency_provider_aidl_interface-java": true,
+		"pixel-thermal-ext-V1-java":                         true,
+		"protolog-lib":                                      true,
+		"RkpRegistrationCheck":                              true,
+		"rotary-service-javastream-protos":                  true,
+		"service_based_camera_extensions":                   true,
+		"statsd-helper-test":                                true,
+		"statsd-helper":                                     true,
+		"test-piece-2-V1-java":                              true,
+		"test-piece-2-V2-java":                              true,
+		"test-piece-3-V1-java":                              true,
+		"test-piece-3-V2-java":                              true,
+		"test-piece-3-V3-java":                              true,
+		"test-piece-4-V1-java":                              true,
+		"test-piece-4-V2-java":                              true,
+		"test-root-package-V1-java":                         true,
+		"test-root-package-V2-java":                         true,
+		"test-root-package-V3-java":                         true,
+		"test-root-package-V4-java":                         true,
+		"testServiceIface-java":                             true,
+		"wm-flicker-common-app-helpers":                     true,
+		"wm-flicker-common-assertions":                      true,
+		"wm-shell-flicker-utils":                            true,
+		"wycheproof-keystore":                               true,
+	}
+
+	// Union of aosp and internal allowlists
+	PlatformApiAllowlist = map[string]bool{}
+)
+
+func init() {
+	for k, v := range aospPlatformApiAllowlist {
+		PlatformApiAllowlist[k] = v
 	}
 }
 
 func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-
 	j.provideHiddenAPIPropertyInfo(ctx)
 
 	j.sdkVersion = j.SdkVersion(ctx)
 	j.minSdkVersion = j.MinSdkVersion(ctx)
 	j.maxSdkVersion = j.MaxSdkVersion(ctx)
 
+	// SdkLibrary.GenerateAndroidBuildActions(ctx) sets the stubsLinkType to Unknown.
+	// If the stubsLinkType has already been set to Unknown, the stubsLinkType should
+	// not be overridden.
+	if j.stubsLinkType != Unknown {
+		if proptools.Bool(j.properties.Is_stubs_module) {
+			j.stubsLinkType = Stubs
+		} else {
+			j.stubsLinkType = Implementation
+		}
+	}
+
 	j.stem = proptools.StringDefault(j.overridableDeviceProperties.Stem, ctx.ModuleName())
 
 	proguardSpecInfo := j.collectProguardSpecInfo(ctx)
-	ctx.SetProvider(ProguardSpecInfoProvider, proguardSpecInfo)
+	android.SetProvider(ctx, ProguardSpecInfoProvider, proguardSpecInfo)
 	exportedProguardFlagsFiles := proguardSpecInfo.ProguardFlagsFiles.ToList()
 	j.extraProguardFlagsFiles = append(j.extraProguardFlagsFiles, exportedProguardFlagsFiles...)
 
@@ -695,7 +901,7 @@
 	writeCombinedProguardFlagsFile(ctx, combinedExportedProguardFlagFile, exportedProguardFlagsFiles)
 	j.combinedExportedProguardFlagsFile = combinedExportedProguardFlagFile
 
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	if !apexInfo.IsForPlatform() {
 		j.hideApexVariantFromMake = true
 	}
@@ -704,11 +910,14 @@
 	j.checkHeadersOnly(ctx)
 	if ctx.Device() {
 		j.dexpreopter.installPath = j.dexpreopter.getInstallPath(
-			ctx, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar"))
+			ctx, j.Name(), android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar"))
 		j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary
 		setUncompressDex(ctx, &j.dexpreopter, &j.dexer)
 		j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
 		j.classLoaderContexts = j.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
+		if j.usesLibrary.shouldDisableDexpreopt {
+			j.dexpreopter.disableDexpreopt()
+		}
 	}
 	j.compile(ctx, nil, nil, nil)
 
@@ -1076,7 +1285,7 @@
 	return true
 }
 
-func (j *TestHost) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
+func (j *TestHost) IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool {
 	return ctx.DeviceConfig().NativeCoverageEnabled()
 }
 
@@ -1216,12 +1425,12 @@
 	}
 
 	j.Test.generateAndroidBuildActionsWithConfig(ctx, configs)
-	ctx.SetProvider(testing.TestModuleProviderKey, testing.TestModuleProviderData{})
+	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 }
 
 func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	j.generateAndroidBuildActionsWithConfig(ctx, nil)
-	ctx.SetProvider(testing.TestModuleProviderKey, testing.TestModuleProviderData{})
+	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 }
 
 func (j *Test) generateAndroidBuildActionsWithConfig(ctx android.ModuleContext, configs []tradefed.Config) {
@@ -1257,7 +1466,7 @@
 	})
 
 	ctx.VisitDirectDepsWithTag(jniLibTag, func(dep android.Module) {
-		sharedLibInfo := ctx.OtherModuleProvider(dep, cc.SharedLibraryInfoProvider).(cc.SharedLibraryInfo)
+		sharedLibInfo, _ := android.OtherModuleProvider(ctx, dep, cc.SharedLibraryInfoProvider)
 		if sharedLibInfo.SharedLibrary != nil {
 			// Copy to an intermediate output directory to append "lib[64]" to the path,
 			// so that it's compatible with the default rpath values.
@@ -1624,7 +1833,7 @@
 	ApiSurface string
 }
 
-var JavaApiImportProvider = blueprint.NewProvider(JavaApiImportInfo{})
+var JavaApiImportProvider = blueprint.NewProvider[JavaApiImportInfo]()
 
 func (ap *JavaApiContribution) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	var apiFile android.Path = nil
@@ -1632,7 +1841,7 @@
 		apiFile = android.PathForModuleSrc(ctx, String(apiFileString))
 	}
 
-	ctx.SetProvider(JavaApiImportProvider, JavaApiImportInfo{
+	android.SetProvider(ctx, JavaApiImportProvider, JavaApiImportInfo{
 		ApiFile:    apiFile,
 		ApiSurface: proptools.String(ap.properties.Api_surface),
 	})
@@ -1656,6 +1865,10 @@
 	dexJarFile OptionalDexJarPath
 
 	validationPaths android.Paths
+
+	stubsType StubsType
+
+	aconfigProtoFiles android.Paths
 }
 
 type JavaApiLibraryProperties struct {
@@ -1697,6 +1910,20 @@
 	// in sync with the source Java files. However, the environment variable
 	// DISABLE_STUB_VALIDATION has precedence over this property.
 	Enable_validation *bool
+
+	// Type of stubs the module should generate. Must be one of "everything", "runtime" or
+	// "exportable". Defaults to "everything".
+	// - "everything" stubs include all non-flagged apis and flagged apis, regardless of the state
+	// of the flag.
+	// - "runtime" stubs include all non-flagged apis and flagged apis that are ENABLED or
+	// READ_WRITE, and all other flagged apis are stripped.
+	// - "exportable" stubs include all non-flagged apis and flagged apis that are ENABLED and
+	// READ_ONLY, and all other flagged apis are stripped.
+	Stubs_type *string
+
+	// List of aconfig_declarations module names that the stubs generated in this module
+	// depend on.
+	Aconfig_declarations []string
 }
 
 func ApiLibraryFactory() android.Module {
@@ -1829,6 +2056,7 @@
 func (al *ApiLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
 	apiContributions := al.properties.Api_contributions
 	addValidations := !ctx.Config().IsEnvTrue("DISABLE_STUB_VALIDATION") &&
+		!ctx.Config().IsEnvTrue("WITHOUT_CHECK_API") &&
 		proptools.BoolDefault(al.properties.Enable_validation, true)
 	for _, apiContributionName := range apiContributions {
 		ctx.AddDependency(ctx.Module(), javaApiContributionTag, apiContributionName)
@@ -1859,6 +2087,9 @@
 	if al.properties.System_modules != nil {
 		ctx.AddVariationDependencies(nil, systemModulesTag, String(al.properties.System_modules))
 	}
+	for _, aconfigDeclarationsName := range al.properties.Aconfig_declarations {
+		ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfigDeclarationsName)
+	}
 }
 
 // Map where key is the api scope name and value is the int value
@@ -1879,7 +2110,23 @@
 	return srcFilesInfo
 }
 
+var validstubsType = []StubsType{Everything, Runtime, Exportable}
+
+func (al *ApiLibrary) validateProperties(ctx android.ModuleContext) {
+	if al.properties.Stubs_type == nil {
+		ctx.ModuleErrorf("java_api_library module type must specify stubs_type property.")
+	} else {
+		al.stubsType = StringToStubsType(proptools.String(al.properties.Stubs_type))
+	}
+
+	if !android.InList(al.stubsType, validstubsType) {
+		ctx.PropertyErrorf("stubs_type", "%s is not a valid stubs_type property value. "+
+			"Must be one of %s.", proptools.String(al.properties.Stubs_type), validstubsType)
+	}
+}
+
 func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	al.validateProperties(ctx)
 
 	rule := android.NewRuleBuilder(pctx, ctx)
 
@@ -1902,19 +2149,19 @@
 		tag := ctx.OtherModuleDependencyTag(dep)
 		switch tag {
 		case javaApiContributionTag:
-			provider := ctx.OtherModuleProvider(dep, JavaApiImportProvider).(JavaApiImportInfo)
+			provider, _ := android.OtherModuleProvider(ctx, dep, JavaApiImportProvider)
 			if provider.ApiFile == nil && !ctx.Config().AllowMissingDependencies() {
 				ctx.ModuleErrorf("Error: %s has an empty api file.", dep.Name())
 			}
 			srcFilesInfo = append(srcFilesInfo, provider)
 		case libTag:
-			provider := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
+			provider, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider)
 			classPaths = append(classPaths, provider.HeaderJars...)
 		case staticLibTag:
-			provider := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
+			provider, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider)
 			staticLibs = append(staticLibs, provider.HeaderJars...)
 		case depApiSrcsTag:
-			provider := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
+			provider, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider)
 			depApiSrcsStubsJar = provider.HeaderJars[0]
 		case systemModulesTag:
 			module := dep.(SystemModulesProvider)
@@ -1923,6 +2170,18 @@
 			if currentApiTimestampProvider, ok := dep.(currentApiTimestampProvider); ok {
 				al.validationPaths = append(al.validationPaths, currentApiTimestampProvider.CurrentApiTimestamp())
 			}
+		case aconfigDeclarationTag:
+			if provider, ok := android.OtherModuleProvider(ctx, dep, android.AconfigDeclarationsProviderKey); ok {
+				al.aconfigProtoFiles = append(al.aconfigProtoFiles, provider.IntermediateCacheOutputPath)
+			} else if provider, ok := android.OtherModuleProvider(ctx, dep, aconfig.CodegenInfoProvider); ok {
+				al.aconfigProtoFiles = append(al.aconfigProtoFiles, provider.IntermediateCacheOutputPaths...)
+			} else {
+				ctx.ModuleErrorf("Only aconfig_declarations and aconfig_declarations_group "+
+					"module type is allowed for flags_packages property, but %s is neither "+
+					"of these supported module types",
+					dep.Name(),
+				)
+			}
 		}
 	})
 
@@ -1948,6 +2207,8 @@
 
 	al.addValidation(ctx, cmd, al.validationPaths)
 
+	generateRevertAnnotationArgs(ctx, cmd, al.stubsType, al.aconfigProtoFiles)
+
 	al.stubsSrcJar = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-"+"stubs.srcjar")
 	al.stubsJarWithoutStaticLibs = android.PathForModuleOut(ctx, "metalava", "stubs.jar")
 	al.stubsJar = android.PathForModuleOut(ctx, ctx.ModuleName(), fmt.Sprintf("%s.jar", ctx.ModuleName()))
@@ -2002,16 +2263,17 @@
 
 	ctx.Phony(ctx.ModuleName(), al.stubsJar)
 
-	ctx.SetProvider(JavaInfoProvider, JavaInfo{
+	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
 		HeaderJars:                     android.PathsIfNonNil(al.stubsJar),
 		ImplementationAndResourcesJars: android.PathsIfNonNil(al.stubsJar),
 		ImplementationJars:             android.PathsIfNonNil(al.stubsJar),
 		AidlIncludeDirs:                android.Paths{},
+		StubsLinkType:                  Stubs,
 		// No aconfig libraries on api libraries
 	})
 }
 
-func (al *ApiLibrary) DexJarBuildPath() OptionalDexJarPath {
+func (al *ApiLibrary) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
 	return al.dexJarFile
 }
 
@@ -2081,6 +2343,19 @@
 		// that depend on this module, as well as to aidl for this module.
 		Export_include_dirs []string
 	}
+
+	// Name of the source soong module that gets shadowed by this prebuilt
+	// If unspecified, follows the naming convention that the source module of
+	// the prebuilt is Name() without "prebuilt_" prefix
+	Source_module_name *string
+
+	// Non-nil if this java_import module was dynamically created by a java_sdk_library_import
+	// The name is the undecorated name of the java_sdk_library as it appears in the blueprint file
+	// (without any prebuilt_ prefix)
+	Created_by_java_sdk_library_name *string `blueprint:"mutated"`
+
+	// Property signifying whether the module provides stubs jar or not.
+	Is_stubs_module *bool
 }
 
 type Import struct {
@@ -2100,6 +2375,7 @@
 
 	// output file containing classes.dex and resources
 	dexJarFile        OptionalDexJarPath
+	dexJarFileErr     error
 	dexJarInstallFile android.Path
 
 	combinedClasspathFile android.Path
@@ -2110,6 +2386,8 @@
 
 	sdkVersion    android.SdkSpec
 	minSdkVersion android.ApiLevel
+
+	stubsLinkType StubsLinkType
 }
 
 var _ PermittedPackagesForUpdatableBootJars = (*Import)(nil)
@@ -2153,12 +2431,20 @@
 	return j.properties.Jars
 }
 
+func (j *Import) BaseModuleName() string {
+	return proptools.StringDefault(j.properties.Source_module_name, j.ModuleBase.Name())
+}
+
 func (j *Import) Name() string {
 	return j.prebuilt.Name(j.ModuleBase.Name())
 }
 
 func (j *Import) Stem() string {
-	return proptools.StringDefault(j.properties.Stem, j.ModuleBase.Name())
+	return proptools.StringDefault(j.properties.Stem, j.BaseModuleName())
+}
+
+func (j *Import) CreatedByJavaSdkLibraryName() *string {
+	return j.properties.Created_by_java_sdk_library_name
 }
 
 func (a *Import) JacocoReportClassesFile() android.Path {
@@ -2188,13 +2474,20 @@
 	j.sdkVersion = j.SdkVersion(ctx)
 	j.minSdkVersion = j.MinSdkVersion(ctx)
 
-	if !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform() {
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	if !apexInfo.IsForPlatform() {
 		j.hideApexVariantFromMake = true
 	}
 
 	if ctx.Windows() {
 		j.HideFromMake()
 	}
+
+	if proptools.Bool(j.properties.Is_stubs_module) {
+		j.stubsLinkType = Stubs
+	} else {
+		j.stubsLinkType = Implementation
+	}
 }
 
 func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -2219,8 +2512,7 @@
 	j.collectTransitiveHeaderJars(ctx)
 	ctx.VisitDirectDeps(func(module android.Module) {
 		tag := ctx.OtherModuleDependencyTag(module)
-		if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
-			dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 			switch tag {
 			case libTag, sdkLibTag:
 				flags.classpath = append(flags.classpath, dep.HeaderJars...)
@@ -2247,21 +2539,25 @@
 	if ctx.Device() {
 		// If this is a variant created for a prebuilt_apex then use the dex implementation jar
 		// obtained from the associated deapexer module.
-		ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+		ai, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 		if ai.ForPrebuiltApex {
 			// Get the path of the dex implementation jar from the `deapexer` module.
-			di := android.FindDeapexerProviderForModule(ctx)
-			if di == nil {
-				return // An error has been reported by FindDeapexerProviderForModule.
+			di, err := android.FindDeapexerProviderForModule(ctx)
+			if err != nil {
+				// An error was found, possibly due to multiple apexes in the tree that export this library
+				// Defer the error till a client tries to call DexJarBuildPath
+				j.dexJarFileErr = err
+				j.initHiddenAPIError(err)
+				return
 			}
-			dexJarFileApexRootRelative := apexRootRelativePathToJavaLib(j.BaseModuleName())
+			dexJarFileApexRootRelative := ApexRootRelativePathToJavaLib(j.BaseModuleName())
 			if dexOutputPath := di.PrebuiltExportPath(dexJarFileApexRootRelative); dexOutputPath != nil {
 				dexJarFile := makeDexJarPathFromPath(dexOutputPath)
 				j.dexJarFile = dexJarFile
-				installPath := android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(j.BaseModuleName()))
+				installPath := android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, ApexRootRelativePathToJavaLib(j.BaseModuleName()))
 				j.dexJarInstallFile = installPath
 
-				j.dexpreopter.installPath = j.dexpreopter.getInstallPath(ctx, installPath)
+				j.dexpreopter.installPath = j.dexpreopter.getInstallPath(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), installPath)
 				setUncompressDex(ctx, &j.dexpreopter, &j.dexer)
 				j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
 
@@ -2269,8 +2565,6 @@
 					j.dexpreopter.inputProfilePathOnHost = profilePath
 				}
 
-				j.dexpreopt(ctx, dexOutputPath)
-
 				// Initialize the hiddenapi structure.
 				j.initHiddenAPI(ctx, dexJarFile, outputFile, j.dexProperties.Uncompress_dex)
 			} else {
@@ -2291,7 +2585,7 @@
 			// Dex compilation
 
 			j.dexpreopter.installPath = j.dexpreopter.getInstallPath(
-				ctx, android.PathForModuleInstall(ctx, "framework", jarName))
+				ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), android.PathForModuleInstall(ctx, "framework", jarName))
 			setUncompressDex(ctx, &j.dexpreopter, &j.dexer)
 			j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
 
@@ -2320,13 +2614,14 @@
 		}
 	}
 
-	ctx.SetProvider(JavaInfoProvider, JavaInfo{
+	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
 		HeaderJars:                     android.PathsIfNonNil(j.combinedClasspathFile),
 		TransitiveLibsHeaderJars:       j.transitiveLibsHeaderJars,
 		TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars,
 		ImplementationAndResourcesJars: android.PathsIfNonNil(j.combinedClasspathFile),
 		ImplementationJars:             android.PathsIfNonNil(j.combinedClasspathFile),
 		AidlIncludeDirs:                j.exportAidlIncludeDirs,
+		StubsLinkType:                  j.stubsLinkType,
 		// TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
 	})
 }
@@ -2374,7 +2669,10 @@
 	return android.Paths{j.combinedClasspathFile}
 }
 
-func (j *Import) DexJarBuildPath() OptionalDexJarPath {
+func (j *Import) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
+	if j.dexJarFileErr != nil {
+		ctx.ModuleErrorf(j.dexJarFileErr.Error())
+	}
 	return j.dexJarFile
 }
 
@@ -2415,7 +2713,7 @@
 // java_sdk_library_import with the specified base module name requires to be exported from a
 // prebuilt_apex/apex_set.
 func requiredFilesFromPrebuiltApexForImport(name string, d *dexpreopter) []string {
-	dexJarFileApexRootRelative := apexRootRelativePathToJavaLib(name)
+	dexJarFileApexRootRelative := ApexRootRelativePathToJavaLib(name)
 	// Add the dex implementation jar to the set of exported files.
 	files := []string{
 		dexJarFileApexRootRelative,
@@ -2426,9 +2724,9 @@
 	return files
 }
 
-// apexRootRelativePathToJavaLib returns the path, relative to the root of the apex's contents, for
+// ApexRootRelativePathToJavaLib returns the path, relative to the root of the apex's contents, for
 // the java library with the specified name.
-func apexRootRelativePathToJavaLib(name string) string {
+func ApexRootRelativePathToJavaLib(name string) string {
 	return filepath.Join("javalib", name+".jar")
 }
 
@@ -2439,6 +2737,10 @@
 	return requiredFilesFromPrebuiltApexForImport(name, &j.dexpreopter)
 }
 
+func (j *Import) UseProfileGuidedDexpreopt() bool {
+	return proptools.Bool(j.importDexpreoptProperties.Dex_preopt.Profile_guided)
+}
+
 // Add compile time check for interface implementation
 var _ android.IDEInfo = (*Import)(nil)
 var _ android.IDECustomizedModuleName = (*Import)(nil)
@@ -2570,14 +2872,14 @@
 		ctx.PropertyErrorf("jars", "exactly one jar must be provided")
 	}
 
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	if !apexInfo.IsForPlatform() {
 		j.hideApexVariantFromMake = true
 	}
 
 	j.dexpreopter.installPath = j.dexpreopter.getInstallPath(
-		ctx, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar"))
-	j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter)
+		ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar"))
+	j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), &j.dexpreopter)
 
 	inputJar := ctx.ExpandSource(j.properties.Jars[0], "jars")
 	dexOutputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".jar")
@@ -2616,7 +2918,7 @@
 
 	j.dexJarFile = makeDexJarPathFromPath(dexOutputFile)
 
-	j.dexpreopt(ctx, dexOutputFile)
+	j.dexpreopt(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), dexOutputFile)
 
 	if apexInfo.IsForPlatform() {
 		ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
@@ -2624,7 +2926,7 @@
 	}
 }
 
-func (j *DexImport) DexJarBuildPath() OptionalDexJarPath {
+func (j *DexImport) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
 	return j.dexJarFile
 }
 
@@ -2717,6 +3019,8 @@
 		&LintProperties{},
 		&appTestHelperAppProperties{},
 		&JavaApiLibraryProperties{},
+		&bootclasspathFragmentProperties{},
+		&SourceOnlyBootclasspathProperties{},
 	)
 
 	android.InitDefaultsModule(module)
@@ -2792,7 +3096,7 @@
 	// from its CLC should be added to the current CLC.
 	if sdkLib != nil {
 		clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, false,
-			dep.DexJarBuildPath().PathOrNil(), dep.DexJarInstallPath(), dep.ClassLoaderContexts())
+			dep.DexJarBuildPath(ctx).PathOrNil(), dep.DexJarInstallPath(), dep.ClassLoaderContexts())
 	} else {
 		clcMap.AddContextMap(dep.ClassLoaderContexts(), depName)
 	}
@@ -2801,7 +3105,20 @@
 type JavaApiContributionImport struct {
 	JavaApiContribution
 
-	prebuilt android.Prebuilt
+	prebuilt           android.Prebuilt
+	prebuiltProperties javaApiContributionImportProperties
+}
+
+type javaApiContributionImportProperties struct {
+	// Name of the source soong module that gets shadowed by this prebuilt
+	// If unspecified, follows the naming convention that the source module of
+	// the prebuilt is Name() without "prebuilt_" prefix
+	Source_module_name *string
+
+	// Non-nil if this java_import module was dynamically created by a java_sdk_library_import
+	// The name is the undecorated name of the java_sdk_library as it appears in the blueprint file
+	// (without any prebuilt_ prefix)
+	Created_by_java_sdk_library_name *string `blueprint:"mutated"`
 }
 
 func ApiContributionImportFactory() android.Module {
@@ -2809,7 +3126,7 @@
 	android.InitAndroidModule(module)
 	android.InitDefaultableModule(module)
 	android.InitPrebuiltModule(module, &[]string{""})
-	module.AddProperties(&module.properties)
+	module.AddProperties(&module.properties, &module.prebuiltProperties)
 	module.AddProperties(&module.sdkLibraryComponentProperties)
 	return module
 }
@@ -2822,6 +3139,14 @@
 	return module.prebuilt.Name(module.ModuleBase.Name())
 }
 
+func (j *JavaApiContributionImport) BaseModuleName() string {
+	return proptools.StringDefault(j.prebuiltProperties.Source_module_name, j.ModuleBase.Name())
+}
+
+func (j *JavaApiContributionImport) CreatedByJavaSdkLibraryName() *string {
+	return j.prebuiltProperties.Created_by_java_sdk_library_name
+}
+
 func (ap *JavaApiContributionImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	ap.JavaApiContribution.GenerateAndroidBuildActions(ctx)
 }
diff --git a/java/java_test.go b/java/java_test.go
index 8e83fc4..2f3ccb9 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -24,6 +24,7 @@
 	"strings"
 	"testing"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/aconfig"
@@ -528,6 +529,15 @@
 	}
 }
 
+// A minimal context object for use with DexJarBuildPath
+type moduleErrorfTestCtx struct {
+}
+
+func (ctx moduleErrorfTestCtx) ModuleErrorf(format string, args ...interface{}) {
+}
+
+var _ android.ModuleErrorfContext = (*moduleErrorfTestCtx)(nil)
+
 func TestPrebuilts(t *testing.T) {
 	ctx, _ := testJava(t, `
 		java_library {
@@ -595,7 +605,8 @@
 		t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], barJar.String())
 	}
 
-	barDexJar := barModule.Module().(*Import).DexJarBuildPath()
+	errCtx := moduleErrorfTestCtx{}
+	barDexJar := barModule.Module().(*Import).DexJarBuildPath(errCtx)
 	if barDexJar.IsSet() {
 		t.Errorf("bar dex jar build path expected to be set, got %s", barDexJar)
 	}
@@ -608,7 +619,7 @@
 		t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, bazJar.String())
 	}
 
-	bazDexJar := bazModule.Module().(*Import).DexJarBuildPath().Path()
+	bazDexJar := bazModule.Module().(*Import).DexJarBuildPath(errCtx).Path()
 	expectedDexJar := "out/soong/.intermediates/baz/android_common/dex/baz.jar"
 	android.AssertPathRelativeToTopEquals(t, "baz dex jar build path", expectedDexJar, bazDexJar)
 
@@ -618,8 +629,6 @@
 	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "java_library", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
 	entries = android.AndroidMkEntriesForTest(t, ctx, barModule.Module())[0]
 	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "java_import", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
-	entries = android.AndroidMkEntriesForTest(t, ctx, ctx.ModuleForTests("sdklib", "android_common").Module())[0]
-	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "java_sdk_library_import", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
 }
 
 func assertDeepEquals(t *testing.T, message string, expected interface{}, actual interface{}) {
@@ -1747,6 +1756,7 @@
 			name: "bar",
 			api_surface: "public",
 			api_contributions: ["foo"],
+			stubs_type: "everything",
 		}
 	`)
 }
@@ -1783,12 +1793,14 @@
 			name: "bar1",
 			api_surface: "public",
 			api_contributions: ["foo1"],
+			stubs_type: "everything",
 		}
 
 		java_api_library {
 			name: "bar2",
 			api_surface: "system",
 			api_contributions: ["foo1", "foo2"],
+			stubs_type: "everything",
 		}
 	`)
 
@@ -1876,12 +1888,14 @@
 			name: "bar1",
 			api_surface: "public",
 			api_contributions: ["foo1"],
+			stubs_type: "everything",
 		}
 
 		java_api_library {
 			name: "bar2",
 			api_surface: "public",
 			defaults:["baz1"],
+			stubs_type: "everything",
 		}
 
 		java_api_library {
@@ -1889,6 +1903,7 @@
 			api_surface: "system",
 			defaults:["baz1", "baz2"],
 			api_contributions: ["foo4"],
+			stubs_type: "everything",
 		}
 	`)
 
@@ -1953,12 +1968,14 @@
 			name: "bar1",
 			api_surface: "public",
 			api_contributions: ["foo1"],
+			stubs_type: "everything",
 		}
 
 		java_api_library {
 			name: "bar2",
 			api_surface: "system",
 			api_contributions: ["foo1", "foo2"],
+			stubs_type: "everything",
 		}
 	`)
 
@@ -2035,6 +2052,7 @@
 			api_surface: "public",
 			api_contributions: ["foo1"],
 			libs: ["lib1"],
+			stubs_type: "everything",
 		}
 
 		java_api_library {
@@ -2042,6 +2060,7 @@
 			api_surface: "system",
 			api_contributions: ["foo1", "foo2"],
 			libs: ["lib1", "lib2", "bar1"],
+			stubs_type: "everything",
 		}
 	`)
 
@@ -2121,6 +2140,7 @@
 			api_surface: "public",
 			api_contributions: ["foo1"],
 			static_libs: ["lib1"],
+			stubs_type: "everything",
 		}
 
 		java_api_library {
@@ -2128,6 +2148,7 @@
 			api_surface: "system",
 			api_contributions: ["foo1", "foo2"],
 			static_libs: ["lib1", "lib2", "bar1"],
+			stubs_type: "everything",
 		}
 	`)
 
@@ -2175,6 +2196,7 @@
 		name: "lib1",
 		api_surface: "public",
 		api_contributions: ["foo1", "foo2"],
+		stubs_type: "everything",
 	}
 	`
 
@@ -2198,6 +2220,7 @@
 			api_surface: "public",
 			api_contributions: ["foo1"],
 			full_api_surface_stub: "lib1",
+			stubs_type: "everything",
 		}
 	`)
 
@@ -2226,7 +2249,8 @@
 		}
 	`)
 	c := ctx.ModuleForTests("c", "android_common").Module()
-	transitiveSrcFiles := android.Paths(ctx.ModuleProvider(c, JavaInfoProvider).(JavaInfo).TransitiveSrcFiles.ToList())
+	javaInfo, _ := android.SingletonModuleProvider(ctx, c, JavaInfoProvider)
+	transitiveSrcFiles := android.Paths(javaInfo.TransitiveSrcFiles.ToList())
 	android.AssertArrayString(t, "unexpected jar deps", []string{"b.java", "c.java"}, transitiveSrcFiles.Strings())
 }
 
@@ -2358,6 +2382,7 @@
 		java_api_library {
 			name: "foo",
 			api_contributions: ["bar"],
+			stubs_type: "everything",
 		}
 		java_api_contribution_import {
 			name: "bar",
@@ -2384,6 +2409,7 @@
 				"module-lib-api-stubs-docs-non-updatable.api.contribution",
 				"api-stubs-docs-non-updatable.api.contribution",
 			],
+			stubs_type: "everything",
 		}
 	`)
 	m := ctx.ModuleForTests("foo", "android_common")
@@ -2432,7 +2458,7 @@
 	manifest := m.Output("metalava.sbox.textproto")
 	sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest)
 	manifestCommand := sboxProto.Commands[0].GetCommand()
-	classPathFlag := "--classpath __SBOX_SANDBOX_DIR__/out/.intermediates/bar/android_common/turbine-combined/bar.jar"
+	classPathFlag := "--classpath __SBOX_SANDBOX_DIR__/out/soong/.intermediates/bar/android_common/turbine-combined/bar.jar"
 	android.AssertStringDoesContain(t, "command expected to contain classpath flag", manifestCommand, classPathFlag)
 }
 
@@ -2456,6 +2482,7 @@
 				"api-stubs-docs-non-updatable.api.contribution",
 			],
 			enable_validation: true,
+			stubs_type: "everything",
 		}
 		java_api_library {
 			name: "bar",
@@ -2463,10 +2490,11 @@
 				"api-stubs-docs-non-updatable.api.contribution",
 			],
 			enable_validation: false,
+			stubs_type: "everything",
 		}
 	`)
 
-	currentApiTimestampPath := "api-stubs-docs-non-updatable/android_common/metalava/check_current_api.timestamp"
+	currentApiTimestampPath := "api-stubs-docs-non-updatable/android_common/everything/check_current_api.timestamp"
 	foo := result.ModuleForTests("foo", "android_common").Module().(*ApiLibrary)
 	fooValidationPathsString := strings.Join(foo.validationPaths.Strings(), " ")
 	bar := result.ModuleForTests("bar", "android_common").Module().(*ApiLibrary)
@@ -2512,3 +2540,156 @@
 			apiScopePublic.stubsLibraryModuleName("foo"), "android_common",
 			apiScopePublic.apiLibraryModuleName("foo")))
 }
+
+func TestMultiplePrebuilts(t *testing.T) {
+	bp := `
+		// an rdep
+		java_library {
+			name: "foo",
+			libs: ["bar"],
+		}
+
+		// multiple variations of dep
+		// source
+		java_library {
+			name: "bar",
+			srcs: ["bar.java"],
+		}
+		// prebuilt "v1"
+		java_import {
+			name: "bar",
+			jars: ["bar.jar"],
+		}
+		// prebuilt "v2"
+		java_import {
+			name: "bar.v2",
+			source_module_name: "bar",
+			jars: ["bar.v1.jar"],
+		}
+
+		// selectors
+		apex_contributions {
+			name: "myapex_contributions",
+			contents: ["%v"],
+		}
+	`
+	hasDep := func(ctx *android.TestResult, m android.Module, wantDep android.Module) bool {
+		t.Helper()
+		var found bool
+		ctx.VisitDirectDeps(m, func(dep blueprint.Module) {
+			if dep == wantDep {
+				found = true
+			}
+		})
+		return found
+	}
+
+	hasFileWithStem := func(m android.TestingModule, stem string) bool {
+		t.Helper()
+		for _, o := range m.AllOutputs() {
+			_, file := filepath.Split(o)
+			if file == stem+".jar" {
+				return true
+			}
+		}
+		return false
+	}
+
+	testCases := []struct {
+		desc                   string
+		selectedDependencyName string
+		expectedDependencyName string
+	}{
+		{
+			desc:                   "Source library is selected using apex_contributions",
+			selectedDependencyName: "bar",
+			expectedDependencyName: "bar",
+		},
+		{
+			desc:                   "Prebuilt library v1 is selected using apex_contributions",
+			selectedDependencyName: "prebuilt_bar",
+			expectedDependencyName: "prebuilt_bar",
+		},
+		{
+			desc:                   "Prebuilt library v2 is selected using apex_contributions",
+			selectedDependencyName: "prebuilt_bar.v2",
+			expectedDependencyName: "prebuilt_bar.v2",
+		},
+	}
+
+	for _, tc := range testCases {
+		ctx := android.GroupFixturePreparers(
+			prepareForJavaTest,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.BuildFlags = map[string]string{
+					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
+				}
+			}),
+		).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName))
+
+		// check that rdep gets the correct variation of dep
+		foo := ctx.ModuleForTests("foo", "android_common")
+		expectedDependency := ctx.ModuleForTests(tc.expectedDependencyName, "android_common")
+		android.AssertBoolEquals(t, fmt.Sprintf("expected dependency from %s to %s\n", foo.Module().Name(), tc.expectedDependencyName), true, hasDep(ctx, foo.Module(), expectedDependency.Module()))
+
+		// check that output file of dep is always bar.jar
+		// The filename should be agnostic to source/prebuilt/prebuilt_version
+		android.AssertBoolEquals(t, fmt.Sprintf("could not find bar.jar in outputs of %s. All Outputs %v\n", tc.expectedDependencyName, expectedDependency.AllOutputs()), true, hasFileWithStem(expectedDependency, "bar"))
+
+		// check LOCAL_MODULE of the selected module name
+		// the prebuilt should have the same LOCAL_MODULE when exported to make
+		entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, expectedDependency.Module())[0]
+		android.AssertStringEquals(t, "unexpected LOCAL_MODULE", "bar", entries.EntryMap["LOCAL_MODULE"][0])
+	}
+}
+
+func TestApiLibraryAconfigDeclarations(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+		}),
+		android.FixtureMergeMockFs(map[string][]byte{
+			"a/A.java":      nil,
+			"a/current.txt": nil,
+			"a/removed.txt": nil,
+		}),
+	).RunTestWithBp(t, `
+	aconfig_declarations {
+		name: "bar",
+		package: "com.example.package",
+		srcs: [
+			"bar.aconfig",
+		],
+	}
+	java_api_contribution {
+		name: "baz",
+		api_file: "a/current.txt",
+		api_surface: "public",
+	}
+	java_api_library {
+		name: "foo",
+		api_surface: "public",
+		api_contributions: [
+			"baz",
+		],
+		aconfig_declarations: [
+			"bar",
+		],
+		stubs_type: "exportable",
+		enable_validation: false,
+	}
+	`)
+
+	// Check if java_api_library depends on aconfig_declarations
+	android.AssertBoolEquals(t, "foo expected to depend on bar",
+		CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar"), true)
+
+	m := result.ModuleForTests("foo", "android_common")
+	android.AssertStringDoesContain(t, "foo generates revert annotations file",
+		strings.Join(m.AllOutputs(), ""), "revert-annotations-exportable.txt")
+
+	// revert-annotations.txt passed to exportable stubs generation metalava command
+	manifest := m.Output("metalava.sbox.textproto")
+	cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command)
+	android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "revert-annotations-exportable.txt")
+}
diff --git a/java/jdeps.go b/java/jdeps.go
index 7e3a14f..91f7ce7 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -89,8 +89,7 @@
 			dpInfo.Classes = append(dpInfo.Classes, data.Class)
 		}
 
-		if ctx.ModuleHasProvider(module, JavaInfoProvider) {
-			dep := ctx.ModuleProvider(module, JavaInfoProvider).(JavaInfo)
+		if dep, ok := android.SingletonModuleProvider(ctx, module, JavaInfoProvider); ok {
 			dpInfo.Installed_paths = append(dpInfo.Installed_paths, dep.ImplementationJars.Strings()...)
 		}
 		dpInfo.Classes = android.FirstUniqueStrings(dpInfo.Classes)
diff --git a/java/lint.go b/java/lint.go
index eb46ea8..c79f6e7 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"sort"
-	"strconv"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
@@ -56,7 +55,8 @@
 		// Modules that provide extra lint checks
 		Extra_check_modules []string
 
-		// Name of the file that lint uses as the baseline. Defaults to "lint-baseline.xml".
+		// The lint baseline file to use. If specified, lint warnings listed in this file will be
+		// suppressed during lint checks.
 		Baseline_filename *string
 
 		// If true, baselining updatability lint checks (e.g. NewApi) is prohibited. Defaults to false.
@@ -84,9 +84,9 @@
 	classes                 android.Path
 	extraLintCheckJars      android.Paths
 	library                 bool
-	minSdkVersion           int
-	targetSdkVersion        int
-	compileSdkVersion       int
+	minSdkVersion           android.ApiLevel
+	targetSdkVersion        android.ApiLevel
+	compileSdkVersion       android.ApiLevel
 	compileSdkKind          android.SdkKind
 	javaLanguageLevel       string
 	kotlinLanguageLevel     string
@@ -319,25 +319,19 @@
 	cmd.FlagWithInput("@",
 		android.PathForSource(ctx, "build/soong/java/lint_defaults.txt"))
 
-	if l.compileSdkKind == android.SdkPublic {
-		cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors)
-	} else {
-		// TODO(b/268261262): Remove this branch. We're demoting NewApi to a warning due to pre-existing issues that need to be fixed.
-		cmd.FlagForEachArg("--warning_check ", l.extraMainlineLintErrors)
-	}
+	cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors)
 	cmd.FlagForEachArg("--disable_check ", l.properties.Lint.Disabled_checks)
 	cmd.FlagForEachArg("--warning_check ", l.properties.Lint.Warning_checks)
 	cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
 	cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
 
-	// TODO(b/193460475): Re-enable strict updatability linting
-	//if l.GetStrictUpdatabilityLinting() {
-	//	// Verify the module does not baseline issues that endanger safe updatability.
-	//	if baselinePath := l.getBaselineFilepath(ctx); baselinePath.Valid() {
-	//		cmd.FlagWithInput("--baseline ", baselinePath.Path())
-	//		cmd.FlagForEachArg("--disallowed_issues ", updatabilityChecks)
-	//	}
-	//}
+	if l.GetStrictUpdatabilityLinting() {
+		// Verify the module does not baseline issues that endanger safe updatability.
+		if l.properties.Lint.Baseline_filename != nil {
+			cmd.FlagWithInput("--baseline ", android.PathForModuleSrc(ctx, *l.properties.Lint.Baseline_filename))
+			cmd.FlagForEachArg("--disallowed_issues ", updatabilityChecks)
+		}
+	}
 
 	return lintPaths{
 		projectXML: projectXMLPath,
@@ -357,33 +351,26 @@
 		Text(`echo "<?xml version='1.0' encoding='utf-8'?>" &&`).
 		Text(`echo "<manifest xmlns:android='http://schemas.android.com/apk/res/android'" &&`).
 		Text(`echo "    android:versionCode='1' android:versionName='1' >" &&`).
-		Textf(`echo "  <uses-sdk android:minSdkVersion='%d' android:targetSdkVersion='%d'/>" &&`,
-			l.minSdkVersion, l.targetSdkVersion).
+		Textf(`echo "  <uses-sdk android:minSdkVersion='%s' android:targetSdkVersion='%s'/>" &&`,
+			l.minSdkVersion.String(), l.targetSdkVersion.String()).
 		Text(`echo "</manifest>"`).
 		Text(") >").Output(manifestPath)
 
 	return manifestPath
 }
 
-func (l *linter) getBaselineFilepath(ctx android.ModuleContext) android.OptionalPath {
-	var lintBaseline android.OptionalPath
-	if lintFilename := proptools.StringDefault(l.properties.Lint.Baseline_filename, "lint-baseline.xml"); lintFilename != "" {
-		if String(l.properties.Lint.Baseline_filename) != "" {
-			// if manually specified, we require the file to exist
-			lintBaseline = android.OptionalPathForPath(android.PathForModuleSrc(ctx, lintFilename))
-		} else {
-			lintBaseline = android.ExistentPathForSource(ctx, ctx.ModuleDir(), lintFilename)
-		}
-	}
-	return lintBaseline
-}
-
 func (l *linter) lint(ctx android.ModuleContext) {
 	if !l.enabled() {
 		return
 	}
 
-	if l.minSdkVersion != l.compileSdkVersion {
+	for _, flag := range l.properties.Lint.Flags {
+		if strings.Contains(flag, "--disable") || strings.Contains(flag, "--enable") || strings.Contains(flag, "--check") {
+			ctx.PropertyErrorf("lint.flags", "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")
+		}
+	}
+
+	if l.minSdkVersion.CompareTo(l.compileSdkVersion) == -1 {
 		l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, updatabilityChecks...)
 		// Skip lint warning checks for NewApi warnings for libcore where they come from source
 		// files that reference the API they are adding (b/208656169).
@@ -413,8 +400,7 @@
 
 	extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag)
 	for _, extraLintCheckModule := range extraLintCheckModules {
-		if ctx.OtherModuleHasProvider(extraLintCheckModule, JavaInfoProvider) {
-			dep := ctx.OtherModuleProvider(extraLintCheckModule, JavaInfoProvider).(JavaInfo)
+		if dep, ok := android.OtherModuleProvider(ctx, extraLintCheckModule, JavaInfoProvider); ok {
 			l.extraLintCheckJars = append(l.extraLintCheckJars, dep.ImplementationAndResourcesJars...)
 		} else {
 			ctx.PropertyErrorf("lint.extra_check_modules",
@@ -498,7 +484,7 @@
 		FlagWithOutput("--html ", html).
 		FlagWithOutput("--text ", text).
 		FlagWithOutput("--xml ", xml).
-		FlagWithArg("--compile-sdk-version ", strconv.Itoa(l.compileSdkVersion)).
+		FlagWithArg("--compile-sdk-version ", l.compileSdkVersion.String()).
 		FlagWithArg("--java-language-level ", l.javaLanguageLevel).
 		FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
 		FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
@@ -519,9 +505,8 @@
 		cmd.FlagWithArg("--check ", checkOnly)
 	}
 
-	lintBaseline := l.getBaselineFilepath(ctx)
-	if lintBaseline.Valid() {
-		cmd.FlagWithInput("--baseline ", lintBaseline.Path())
+	if l.properties.Lint.Baseline_filename != nil {
+		cmd.FlagWithInput("--baseline ", android.PathForModuleSrc(ctx, *l.properties.Lint.Baseline_filename))
 	}
 
 	cmd.FlagWithOutput("--write-reference-baseline ", referenceBaseline)
@@ -557,6 +542,10 @@
 	if l.buildModuleReportZip {
 		l.reports = BuildModuleLintReportZips(ctx, l.LintDepSets())
 	}
+
+	// Create a per-module phony target to run the lint check.
+	phonyName := ctx.ModuleName() + "-lint"
+	ctx.Phony(phonyName, xml)
 }
 
 func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets) android.Paths {
@@ -661,7 +650,7 @@
 		}
 
 		if apex, ok := m.(android.ApexModule); ok && apex.NotAvailableForPlatform() {
-			apexInfo := ctx.ModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo)
+			apexInfo, _ := android.SingletonModuleProvider(ctx, m, android.ApexInfoProvider)
 			if apexInfo.IsForPlatform() {
 				// There are stray platform variants of modules in apexes that are not available for
 				// the platform, and they sometimes can't be built.  Don't depend on them.
diff --git a/java/lint_test.go b/java/lint_test.go
index 5e6b8bc..b51753f 100644
--- a/java/lint_test.go
+++ b/java/lint_test.go
@@ -21,7 +21,7 @@
 	"android/soong/android"
 )
 
-func TestJavaLint(t *testing.T) {
+func TestJavaLintDoesntUseBaselineImplicitly(t *testing.T) {
 	ctx, _ := testJavaWithFS(t, `
 		java_library {
 			name: "foo",
@@ -40,30 +40,8 @@
 	foo := ctx.ModuleForTests("foo", "android_common")
 
 	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, foo.Output("lint.sbox.textproto"))
-	if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml") {
-		t.Error("did not pass --baseline flag")
-	}
-}
-
-func TestJavaLintWithoutBaseline(t *testing.T) {
-	ctx, _ := testJavaWithFS(t, `
-		java_library {
-			name: "foo",
-			srcs: [
-				"a.java",
-				"b.java",
-				"c.java",
-			],
-			min_sdk_version: "29",
-			sdk_version: "system_current",
-		}
-       `, map[string][]byte{})
-
-	foo := ctx.ModuleForTests("foo", "android_common")
-
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, foo.Output("lint.sbox.textproto"))
-	if strings.Contains(*sboxProto.Commands[0].Command, "--baseline") {
-		t.Error("passed --baseline flag for non existent file")
+	if strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml") {
+		t.Error("Passed --baseline flag when baseline_filename was not set")
 	}
 }
 
@@ -113,9 +91,8 @@
 		t.Error("did not use the correct file for baseline")
 	}
 
-	if !strings.Contains(*sboxProto.Commands[0].Command, "--warning_check NewApi") {
-		// TODO(b/268261262): Change this to check for --error_check
-		t.Error("should check NewApi warnings")
+	if !strings.Contains(*sboxProto.Commands[0].Command, "--error_check NewApi") {
+		t.Error("should check NewApi errors")
 	}
 
 	if !strings.Contains(*sboxProto.Commands[0].Command, "--error_check SomeCheck") {
@@ -175,52 +152,55 @@
 	}
 }
 
-// TODO(b/193460475): Re-enable this test
-//func TestJavaLintStrictUpdatabilityLinting(t *testing.T) {
-//	bp := `
-//		java_library {
-//			name: "foo",
-//			srcs: [
-//				"a.java",
-//			],
-//			static_libs: ["bar"],
-//			min_sdk_version: "29",
-//			sdk_version: "current",
-//			lint: {
-//				strict_updatability_linting: true,
-//			},
-//		}
-//
-//		java_library {
-//			name: "bar",
-//			srcs: [
-//				"a.java",
-//			],
-//			min_sdk_version: "29",
-//			sdk_version: "current",
-//		}
-//	`
-//	fs := android.MockFS{
-//		"lint-baseline.xml": nil,
-//	}
-//
-//	result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules, fs.AddToFixture()).
-//		RunTestWithBp(t, bp)
-//
-//	foo := result.ModuleForTests("foo", "android_common")
-//	sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
-//	if !strings.Contains(*sboxProto.Commands[0].Command,
-//		"--baseline lint-baseline.xml --disallowed_issues NewApi") {
-//		t.Error("did not restrict baselining NewApi")
-//	}
-//
-//	bar := result.ModuleForTests("bar", "android_common")
-//	sboxProto = android.RuleBuilderSboxProtoForTests(t, bar.Output("lint.sbox.textproto"))
-//	if !strings.Contains(*sboxProto.Commands[0].Command,
-//		"--baseline lint-baseline.xml --disallowed_issues NewApi") {
-//		t.Error("did not restrict baselining NewApi")
-//	}
-//}
+func TestJavaLintStrictUpdatabilityLinting(t *testing.T) {
+	bp := `
+		java_library {
+			name: "foo",
+			srcs: [
+				"a.java",
+			],
+			static_libs: ["bar"],
+			min_sdk_version: "29",
+			sdk_version: "current",
+			lint: {
+				strict_updatability_linting: true,
+				baseline_filename: "lint-baseline.xml",
+			},
+		}
+
+		java_library {
+			name: "bar",
+			srcs: [
+				"a.java",
+			],
+			min_sdk_version: "29",
+			sdk_version: "current",
+			lint: {
+				baseline_filename: "lint-baseline.xml",
+			}
+		}
+	`
+	fs := android.MockFS{
+		"lint-baseline.xml": nil,
+	}
+
+	result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules, fs.AddToFixture()).
+		RunTestWithBp(t, bp)
+
+	foo := result.ModuleForTests("foo", "android_common")
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, foo.Output("lint.sbox.textproto"))
+	if !strings.Contains(*sboxProto.Commands[0].Command,
+		"--baseline lint-baseline.xml --disallowed_issues NewApi") {
+		t.Error("did not restrict baselining NewApi")
+	}
+
+	bar := result.ModuleForTests("bar", "android_common")
+	sboxProto = android.RuleBuilderSboxProtoForTests(t, result.TestContext, bar.Output("lint.sbox.textproto"))
+	if !strings.Contains(*sboxProto.Commands[0].Command,
+		"--baseline lint-baseline.xml --disallowed_issues NewApi") {
+		t.Error("did not restrict baselining NewApi")
+	}
+}
 
 func TestJavaLintDatabaseSelectionFull(t *testing.T) {
 	testCases := []struct {
@@ -282,3 +262,22 @@
 		}
 	}
 }
+
+func TestCantControlCheckSeverityWithFlags(t *testing.T) {
+	bp := `
+		java_library {
+			name: "foo",
+			srcs: [
+				"a.java",
+			],
+			min_sdk_version: "29",
+			sdk_version: "current",
+			lint: {
+				flags: ["--disabled", "NewApi"],
+			},
+		}
+	`
+	PrepareForTestWithJavaDefaultModules.
+		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)
+}
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 0d52614..4db426e 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -106,6 +106,9 @@
 }
 
 func (b *platformBootclasspathModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// Create a dependency on all_apex_contributions to determine the selected mainline module
+	ctx.AddDependency(ctx.Module(), apexContributionsMetadataDepTag, "all_apex_contributions")
+
 	b.hiddenAPIDepsMutator(ctx)
 
 	if !dexpreopt.IsDex2oatNeeded(ctx) {
@@ -130,6 +133,8 @@
 func (b *platformBootclasspathModule) BootclasspathDepsMutator(ctx android.BottomUpMutatorContext) {
 	// Add dependencies on all the ART jars.
 	global := dexpreopt.GetGlobalConfig(ctx)
+	addDependenciesOntoSelectedBootImageApexes(ctx, "com.android.art")
+	// TODO: b/308174306 - Remove the mechanism of depending on the java_sdk_library(_import) directly
 	addDependenciesOntoBootImageModules(ctx, global.ArtApexJars, platformBootclasspathArtBootJarDepTag)
 
 	// Add dependencies on all the non-updatable jars, which are on the platform or in non-updatable
@@ -138,6 +143,12 @@
 
 	// Add dependencies on all the updatable jars, except the ART jars.
 	apexJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars
+	apexes := []string{}
+	for i := 0; i < apexJars.Len(); i++ {
+		apexes = append(apexes, apexJars.Apex(i))
+	}
+	addDependenciesOntoSelectedBootImageApexes(ctx, android.FirstUniqueStrings(apexes)...)
+	// TODO: b/308174306 - Remove the mechanism of depending on the java_sdk_library(_import) directly
 	addDependenciesOntoBootImageModules(ctx, apexJars, platformBootclasspathApexBootJarDepTag)
 
 	// Add dependencies on all the fragments.
@@ -180,7 +191,7 @@
 
 	var transitiveSrcFiles android.Paths
 	for _, module := range allModules {
-		depInfo := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+		depInfo, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider)
 		if depInfo.TransitiveSrcFiles != nil {
 			transitiveSrcFiles = append(transitiveSrcFiles, depInfo.TransitiveSrcFiles.ToList()...)
 		}
@@ -219,7 +230,7 @@
 	// Include jars from APEXes that don't populate their classpath proto config.
 	remainingJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars
 	for _, fragment := range b.fragments {
-		info := ctx.OtherModuleProvider(fragment, ClasspathFragmentProtoContentInfoProvider).(ClasspathFragmentProtoContentInfo)
+		info, _ := android.OtherModuleProvider(ctx, fragment, ClasspathFragmentProtoContentInfoProvider)
 		if info.ClasspathFragmentProtoGenerated {
 			remainingJars = remainingJars.RemoveList(info.ClasspathFragmentProtoContents)
 		}
@@ -241,7 +252,7 @@
 func (b *platformBootclasspathModule) checkPlatformModules(ctx android.ModuleContext, modules []android.Module) {
 	// TODO(satayev): change this check to only allow core-icu4j, all apex jars should not be here.
 	for _, m := range modules {
-		apexInfo := ctx.OtherModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo)
+		apexInfo, _ := android.OtherModuleProvider(ctx, m, android.ApexInfoProvider)
 		fromUpdatableApex := apexInfo.Updatable
 		if fromUpdatableApex {
 			// error: this jar is part of an updatable apex
@@ -255,7 +266,7 @@
 // checkApexModules ensures that the apex modules supplied are not from the platform.
 func (b *platformBootclasspathModule) checkApexModules(ctx android.ModuleContext, modules []android.Module) {
 	for _, m := range modules {
-		apexInfo := ctx.OtherModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo)
+		apexInfo, _ := android.OtherModuleProvider(ctx, m, android.ApexInfoProvider)
 		fromUpdatableApex := apexInfo.Updatable
 		if fromUpdatableApex {
 			// ok: this jar is part of an updatable apex
@@ -389,7 +400,7 @@
 	monolithicInfo := newMonolithicHiddenAPIInfo(ctx, temporaryInput.FlagFilesByCategory, classpathElements)
 
 	// Store the information for testing.
-	ctx.SetProvider(MonolithicHiddenAPIInfoProvider, monolithicInfo)
+	android.SetProvider(ctx, MonolithicHiddenAPIInfoProvider, monolithicInfo)
 	return monolithicInfo
 }
 
diff --git a/java/ravenwood.go b/java/ravenwood.go
index e362003..908619d 100644
--- a/java/ravenwood.go
+++ b/java/ravenwood.go
@@ -17,6 +17,7 @@
 	"android/soong/android"
 	"android/soong/tradefed"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -29,14 +30,36 @@
 	ctx.RegisterModuleType("android_ravenwood_libgroup", ravenwoodLibgroupFactory)
 }
 
-var ravenwoodTag = dependencyTag{name: "ravenwood"}
+var ravenwoodLibContentTag = dependencyTag{name: "ravenwoodlibcontent"}
+var ravenwoodUtilsTag = dependencyTag{name: "ravenwoodutils"}
+var ravenwoodRuntimeTag = dependencyTag{name: "ravenwoodruntime"}
 
 const ravenwoodUtilsName = "ravenwood-utils"
 const ravenwoodRuntimeName = "ravenwood-runtime"
 
+type ravenwoodLibgroupJniDepProviderInfo struct {
+	// All the jni_libs module names with transient dependencies.
+	names map[string]bool
+}
+
+var ravenwoodLibgroupJniDepProvider = blueprint.NewProvider[ravenwoodLibgroupJniDepProviderInfo]()
+
+func getLibPath(archType android.ArchType) string {
+	if archType.Multilib == "lib64" {
+		return "lib64"
+	}
+	return "lib"
+}
+
+type ravenwoodTestProperties struct {
+	Jni_libs []string
+}
+
 type ravenwoodTest struct {
 	Library
 
+	ravenwoodTestProperties ravenwoodTestProperties
+
 	testProperties testProperties
 	testConfig     android.Path
 
@@ -48,7 +71,7 @@
 	module := &ravenwoodTest{}
 
 	module.addHostAndDeviceProperties()
-	module.AddProperties(&module.testProperties)
+	module.AddProperties(&module.testProperties, &module.ravenwoodTestProperties)
 
 	module.Module.dexpreopter.isTest = true
 	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
@@ -77,15 +100,20 @@
 	r.Library.DepsMutator(ctx)
 
 	// Generically depend on the runtime so that it's installed together with us
-	ctx.AddVariationDependencies(nil, ravenwoodTag, ravenwoodRuntimeName)
+	ctx.AddVariationDependencies(nil, ravenwoodRuntimeTag, ravenwoodRuntimeName)
 
 	// Directly depend on any utils so that we link against them
-	utils := ctx.AddVariationDependencies(nil, ravenwoodTag, ravenwoodUtilsName)[0]
+	utils := ctx.AddVariationDependencies(nil, ravenwoodUtilsTag, ravenwoodUtilsName)[0]
 	if utils != nil {
 		for _, lib := range utils.(*ravenwoodLibgroup).ravenwoodLibgroupProperties.Libs {
 			ctx.AddVariationDependencies(nil, libTag, lib)
 		}
 	}
+
+	// Add jni libs
+	for _, lib := range r.ravenwoodTestProperties.Jni_libs {
+		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib)
+	}
 }
 
 func (r *ravenwoodTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -103,20 +131,51 @@
 
 	r.Library.GenerateAndroidBuildActions(ctx)
 
-	// Start by depending on all files installed by dependancies
+	// Start by depending on all files installed by dependencies
 	var installDeps android.InstallPaths
-	for _, dep := range ctx.GetDirectDepsWithTag(ravenwoodTag) {
-		for _, installFile := range dep.FilesToInstall() {
+
+	// All JNI libraries included in the runtime
+	var runtimeJniModuleNames map[string]bool
+
+	if utils := ctx.GetDirectDepsWithTag(ravenwoodUtilsTag)[0]; utils != nil {
+		for _, installFile := range utils.FilesToInstall() {
 			installDeps = append(installDeps, installFile)
 		}
+		jniDeps, ok := android.OtherModuleProvider(ctx, utils, ravenwoodLibgroupJniDepProvider)
+		if ok {
+			runtimeJniModuleNames = jniDeps.names
+		}
 	}
 
+	if runtime := ctx.GetDirectDepsWithTag(ravenwoodRuntimeTag)[0]; runtime != nil {
+		for _, installFile := range runtime.FilesToInstall() {
+			installDeps = append(installDeps, installFile)
+		}
+		jniDeps, ok := android.OtherModuleProvider(ctx, runtime, ravenwoodLibgroupJniDepProvider)
+		if ok {
+			runtimeJniModuleNames = jniDeps.names
+		}
+	}
+
+	// Also remember what JNI libs are in the runtime.
+
 	// Also depend on our config
 	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
 	installConfig := ctx.InstallFile(installPath, ctx.ModuleName()+".config", r.testConfig)
 	installDeps = append(installDeps, installConfig)
 
-	// Finally install our JAR with all dependencies
+	// Depend on the JNI libraries, but don't install the ones that the runtime already
+	// contains.
+	soInstallPath := installPath.Join(ctx, getLibPath(r.forceArchType))
+	for _, jniLib := range collectTransitiveJniDeps(ctx) {
+		if _, ok := runtimeJniModuleNames[jniLib.name]; ok {
+			continue // Runtime already includes it.
+		}
+		installJni := ctx.InstallFile(soInstallPath, jniLib.path.Base(), jniLib.path)
+		installDeps = append(installDeps, installJni)
+	}
+
+	// Install our JAR with all dependencies
 	ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.outputFile, installDeps...)
 }
 
@@ -137,6 +196,8 @@
 
 type ravenwoodLibgroupProperties struct {
 	Libs []string
+
+	Jni_libs []string
 }
 
 type ravenwoodLibgroup struct {
@@ -170,7 +231,10 @@
 func (r *ravenwoodLibgroup) DepsMutator(ctx android.BottomUpMutatorContext) {
 	// Always depends on our underlying libs
 	for _, lib := range r.ravenwoodLibgroupProperties.Libs {
-		ctx.AddVariationDependencies(nil, ravenwoodTag, lib)
+		ctx.AddVariationDependencies(nil, ravenwoodLibContentTag, lib)
+	}
+	for _, lib := range r.ravenwoodLibgroupProperties.Jni_libs {
+		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib)
 	}
 }
 
@@ -178,14 +242,37 @@
 	r.forceOSType = ctx.Config().BuildOS
 	r.forceArchType = ctx.Config().BuildArch
 
+	// Collect the JNI dependencies, including the transitive deps.
+	jniDepNames := make(map[string]bool)
+	jniLibs := collectTransitiveJniDeps(ctx)
+
+	for _, jni := range jniLibs {
+		jniDepNames[jni.name] = true
+	}
+	android.SetProvider(ctx, ravenwoodLibgroupJniDepProvider, ravenwoodLibgroupJniDepProviderInfo{
+		names: jniDepNames,
+	})
+
 	// Install our runtime into expected location for packaging
 	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
 	for _, lib := range r.ravenwoodLibgroupProperties.Libs {
-		libModule := ctx.GetDirectDepWithTag(lib, ravenwoodTag)
+		libModule := ctx.GetDirectDepWithTag(lib, ravenwoodLibContentTag)
 		libJar := android.OutputFileForModule(ctx, libModule, "")
 		ctx.InstallFile(installPath, lib+".jar", libJar)
 	}
+	soInstallPath := android.PathForModuleInstall(ctx, r.BaseModuleName()).Join(ctx, getLibPath(r.forceArchType))
+
+	for _, jniLib := range jniLibs {
+		ctx.InstallFile(soInstallPath, jniLib.path.Base(), jniLib.path)
+	}
 
 	// Normal build should perform install steps
 	ctx.Phony(r.BaseModuleName(), android.PathForPhony(ctx, r.BaseModuleName()+"-install"))
 }
+
+// collectTransitiveJniDeps returns all JNI dependencies, including transitive
+// ones, including NDK / stub libs. (Because Ravenwood has no "preinstalled" libraries)
+func collectTransitiveJniDeps(ctx android.ModuleContext) []jniLib {
+	libs, _ := collectJniDeps(ctx, true, false, nil)
+	return libs
+}
diff --git a/java/ravenwood_test.go b/java/ravenwood_test.go
index 2443ea8..5961264 100644
--- a/java/ravenwood_test.go
+++ b/java/ravenwood_test.go
@@ -26,6 +26,25 @@
 		RegisterRavenwoodBuildComponents(ctx)
 	}),
 	android.FixtureAddTextFile("ravenwood/Android.bp", `
+		cc_library_shared {
+			name: "ravenwood-runtime-jni1",
+			host_supported: true,
+			srcs: ["jni.cpp"],
+		}
+		cc_library_shared {
+			name: "ravenwood-runtime-jni2",
+			host_supported: true,
+			srcs: ["jni.cpp"],
+			stem: "libred",
+			shared_libs: [
+				"ravenwood-runtime-jni3",
+			],
+		}
+		cc_library_shared {
+			name: "ravenwood-runtime-jni3",
+			host_supported: true,
+			srcs: ["jni.cpp"],
+		}
 		java_library_static {
 			name: "framework-minus-apex.ravenwood",
 			srcs: ["Framework.java"],
@@ -44,6 +63,10 @@
 				"framework-minus-apex.ravenwood",
 				"framework-services.ravenwood",
 			],
+			jni_libs: [
+				"ravenwood-runtime-jni1",
+				"ravenwood-runtime-jni2",
+			],
 		}
 		android_ravenwood_libgroup {
 			name: "ravenwood-utils",
@@ -69,12 +92,16 @@
 	// Verify that our runtime depends on underlying libs
 	CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-runtime", "android_common", "framework-minus-apex.ravenwood")
 	CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-runtime", "android_common", "framework-services.ravenwood")
+	CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-runtime", "android_common", "ravenwood-runtime-jni")
 	CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-utils", "android_common", "framework-rules.ravenwood")
 
 	// Verify that we've emitted artifacts in expected location
 	runtime := ctx.ModuleForTests("ravenwood-runtime", "android_common")
 	runtime.Output(installPathPrefix + "/ravenwood-runtime/framework-minus-apex.ravenwood.jar")
 	runtime.Output(installPathPrefix + "/ravenwood-runtime/framework-services.ravenwood.jar")
+	runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/ravenwood-runtime-jni1.so")
+	runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/libred.so")
+	runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/ravenwood-runtime-jni3.so")
 	utils := ctx.ModuleForTests("ravenwood-utils", "android_common")
 	utils.Output(installPathPrefix + "/ravenwood-utils/framework-rules.ravenwood.jar")
 }
@@ -88,9 +115,34 @@
 		PrepareForIntegrationTestWithJava,
 		prepareRavenwoodRuntime,
 	).RunTestWithBp(t, `
-		android_ravenwood_test {
+	cc_library_shared {
+		name: "jni-lib1",
+		host_supported: true,
+		srcs: ["jni.cpp"],
+	}
+	cc_library_shared {
+		name: "jni-lib2",
+		host_supported: true,
+		srcs: ["jni.cpp"],
+		stem: "libblue",
+		shared_libs: [
+			"jni-lib3",
+		],
+	}
+	cc_library_shared {
+		name: "jni-lib3",
+		host_supported: true,
+		srcs: ["jni.cpp"],
+		stem: "libpink",
+	}
+	android_ravenwood_test {
 			name: "ravenwood-test",
 			srcs: ["Test.java"],
+			jni_libs: [
+				"jni-lib1",
+				"jni-lib2",
+				"ravenwood-runtime-jni2",
+			],
 			sdk_version: "test_current",
 		}
 	`)
@@ -98,6 +150,7 @@
 	// Verify that our test depends on underlying libs
 	CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-test", "android_common", "ravenwood-buildtime")
 	CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-test", "android_common", "ravenwood-utils")
+	CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-test", "android_common", "jni-lib")
 
 	module := ctx.ModuleForTests("ravenwood-test", "android_common")
 	classpath := module.Rule("javac").Args["classpath"]
@@ -113,10 +166,21 @@
 	// Verify that we've emitted test artifacts in expected location
 	outputJar := module.Output(installPathPrefix + "/ravenwood-test/ravenwood-test.jar")
 	module.Output(installPathPrefix + "/ravenwood-test/ravenwood-test.config")
+	module.Output(installPathPrefix + "/ravenwood-test/lib64/jni-lib1.so")
+	module.Output(installPathPrefix + "/ravenwood-test/lib64/libblue.so")
+	module.Output(installPathPrefix + "/ravenwood-test/lib64/libpink.so")
+
+	// ravenwood-runtime*.so are included in the runtime, so it shouldn't be emitted.
+	for _, o := range module.AllOutputs() {
+		android.AssertStringDoesNotContain(t, "runtime libs shouldn't be included", o, "/ravenwood-test/lib64/ravenwood-runtime")
+	}
 
 	// Verify that we're going to install underlying libs
 	orderOnly := outputJar.OrderOnly.Strings()
 	android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/framework-minus-apex.ravenwood.jar")
 	android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/framework-services.ravenwood.jar")
+	android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/lib64/ravenwood-runtime-jni1.so")
+	android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/lib64/libred.so")
+	android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/lib64/ravenwood-runtime-jni3.so")
 	android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-utils/framework-rules.ravenwood.jar")
 }
diff --git a/java/robolectric.go b/java/robolectric.go
index 45621fd..9e8850c 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -193,7 +193,7 @@
 	}
 
 	handleLibDeps := func(dep android.Module) {
-		m := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
+		m, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider)
 		r.libs = append(r.libs, ctx.OtherModuleName(dep))
 		if !android.InList(ctx.OtherModuleName(dep), config.FrameworkLibraries) {
 			combinedJarJars = append(combinedJarJars, m.ImplementationAndResourcesJars...)
@@ -254,7 +254,7 @@
 	}
 
 	r.installFile = ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.combinedJar, installDeps...)
-	ctx.SetProvider(testing.TestModuleProviderKey, testing.TestModuleProviderData{})
+	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 }
 
 func generateRoboTestConfig(ctx android.ModuleContext, outputFile android.WritablePath,
@@ -305,8 +305,7 @@
 	srcJarDeps := append(android.Paths(nil), instrumentedApp.srcJarDeps...)
 
 	for _, m := range ctx.GetDirectDepsWithTag(roboCoverageLibsTag) {
-		if ctx.OtherModuleHasProvider(m, JavaInfoProvider) {
-			dep := ctx.OtherModuleProvider(m, JavaInfoProvider).(JavaInfo)
+		if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
 			srcJarArgs = append(srcJarArgs, dep.SrcJarArgs...)
 			srcJarDeps = append(srcJarDeps, dep.SrcJarDeps...)
 		}
diff --git a/java/sdk.go b/java/sdk.go
index ad71fb2..3591ccd 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -33,13 +33,26 @@
 var nonUpdatableFrameworkAidlPathKey = android.NewOnceKey("nonUpdatableFrameworkAidlPathKey")
 var apiFingerprintPathKey = android.NewOnceKey("apiFingerprintPathKey")
 
-func UseApiFingerprint(ctx android.BaseModuleContext) bool {
-	if ctx.Config().UnbundledBuild() &&
-		!ctx.Config().AlwaysUsePrebuiltSdks() &&
-		ctx.Config().IsEnvTrue("UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT") {
-		return true
+func UseApiFingerprint(ctx android.BaseModuleContext) (useApiFingerprint bool, fingerprintSdkVersion string, fingerprintDeps android.OutputPath) {
+	if ctx.Config().UnbundledBuild() && !ctx.Config().AlwaysUsePrebuiltSdks() {
+		apiFingerprintTrue := ctx.Config().IsEnvTrue("UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT")
+		dessertShaIsSet := ctx.Config().Getenv("UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA") != ""
+
+		// Error when both UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT and UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA are set
+		if apiFingerprintTrue && dessertShaIsSet {
+			ctx.ModuleErrorf("UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT=true cannot be set alongside with UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA")
+		}
+
+		useApiFingerprint = apiFingerprintTrue || dessertShaIsSet
+		if apiFingerprintTrue {
+			fingerprintSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
+			fingerprintDeps = ApiFingerprintPath(ctx)
+		}
+		if dessertShaIsSet {
+			fingerprintSdkVersion = ctx.Config().Getenv("UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA")
+		}
 	}
-	return false
+	return useApiFingerprint, fingerprintSdkVersion, fingerprintDeps
 }
 
 func defaultJavaLanguageVersion(ctx android.EarlyModuleContext, s android.SdkSpec) javaVersion {
@@ -47,9 +60,7 @@
 	if err != nil {
 		ctx.PropertyErrorf("sdk_version", "%s", err)
 	}
-	if sdk.FinalOrFutureInt() <= 23 {
-		return JAVA_VERSION_7
-	} else if sdk.FinalOrFutureInt() <= 29 {
+	if sdk.FinalOrFutureInt() <= 29 {
 		return JAVA_VERSION_8
 	} else if sdk.FinalOrFutureInt() <= 31 {
 		return JAVA_VERSION_9
@@ -262,8 +273,7 @@
 
 	ctx.VisitAllModules(func(module android.Module) {
 		// Collect dex jar paths for the modules listed above.
-		if ctx.ModuleHasProvider(module, JavaInfoProvider) {
-			j := ctx.ModuleProvider(module, JavaInfoProvider).(JavaInfo)
+		if j, ok := android.SingletonModuleProvider(ctx, module, JavaInfoProvider); ok {
 			name := ctx.ModuleName(module)
 			if i := android.IndexList(name, stubsModules); i != -1 {
 				stubsJars[i] = j.HeaderJars
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 176bda0..cdd0448 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -15,6 +15,7 @@
 package java
 
 import (
+	"errors"
 	"fmt"
 	"path"
 	"path/filepath"
@@ -104,8 +105,14 @@
 	// The name of the property in the java_sdk_library_import
 	propertyName string
 
-	// The tag to use to depend on the stubs library module.
-	stubsTag scopeDependencyTag
+	// The tag to use to depend on the prebuilt stubs library module
+	prebuiltStubsTag scopeDependencyTag
+
+	// The tag to use to depend on the everything stubs library module.
+	everythingStubsTag scopeDependencyTag
+
+	// The tag to use to depend on the exportable stubs library module.
+	exportableStubsTag scopeDependencyTag
 
 	// The tag to use to depend on the stubs source module (if separate from the API module).
 	stubsSourceTag scopeDependencyTag
@@ -167,11 +174,21 @@
 	allScopeNames = append(allScopeNames, name)
 	scope.propertyName = strings.ReplaceAll(name, "-", "_")
 	scope.fieldName = proptools.FieldNameForProperty(scope.propertyName)
-	scope.stubsTag = scopeDependencyTag{
+	scope.prebuiltStubsTag = scopeDependencyTag{
 		name:             name + "-stubs",
 		apiScope:         scope,
 		depInfoExtractor: (*scopePaths).extractStubsLibraryInfoFromDependency,
 	}
+	scope.everythingStubsTag = scopeDependencyTag{
+		name:             name + "-stubs-everything",
+		apiScope:         scope,
+		depInfoExtractor: (*scopePaths).extractEverythingStubsLibraryInfoFromDependency,
+	}
+	scope.exportableStubsTag = scopeDependencyTag{
+		name:             name + "-stubs-exportable",
+		apiScope:         scope,
+		depInfoExtractor: (*scopePaths).extractExportableStubsLibraryInfoFromDependency,
+	}
 	scope.stubsSourceTag = scopeDependencyTag{
 		name:             name + "-stubs-source",
 		apiScope:         scope,
@@ -231,6 +248,10 @@
 	return ".stubs" + scope.moduleSuffix
 }
 
+func (scope *apiScope) exportableStubsLibraryModuleNameSuffix() string {
+	return ".stubs.exportable" + scope.moduleSuffix
+}
+
 func (scope *apiScope) apiLibraryModuleName(baseName string) string {
 	return scope.stubsLibraryModuleName(baseName) + ".from-text"
 }
@@ -239,10 +260,18 @@
 	return scope.stubsLibraryModuleName(baseName) + ".from-source"
 }
 
+func (scope *apiScope) exportableSourceStubsLibraryModuleName(baseName string) string {
+	return scope.exportableStubsLibraryModuleName(baseName) + ".from-source"
+}
+
 func (scope *apiScope) stubsLibraryModuleName(baseName string) string {
 	return baseName + scope.stubsLibraryModuleNameSuffix()
 }
 
+func (scope *apiScope) exportableStubsLibraryModuleName(baseName string) string {
+	return baseName + scope.exportableStubsLibraryModuleNameSuffix()
+}
+
 func (scope *apiScope) stubsSourceModuleName(baseName string) string {
 	return baseName + ".stubs.source" + scope.moduleSuffix
 }
@@ -628,6 +657,10 @@
 	// Defaults to false.
 	Contribute_to_android_api *bool
 
+	// a list of aconfig_declarations module names that the stubs generated in this module
+	// depend on.
+	Aconfig_declarations []string
+
 	// TODO: determines whether to create HTML doc or not
 	// Html_doc *bool
 }
@@ -653,6 +686,11 @@
 	// This is not the implementation jar, it still only contains stubs.
 	stubsDexJarPath OptionalDexJarPath
 
+	// The exportable dex jar for the stubs.
+	// This is not the implementation jar, it still only contains stubs.
+	// Includes unflagged apis and flagged apis enabled by release configurations.
+	exportableStubsDexJarPath OptionalDexJarPath
+
 	// The API specification file, e.g. system_current.txt.
 	currentApiFilePath android.OptionalPath
 
@@ -673,63 +711,120 @@
 }
 
 func (paths *scopePaths) extractStubsLibraryInfoFromDependency(ctx android.ModuleContext, dep android.Module) error {
-	if ctx.OtherModuleHasProvider(dep, JavaInfoProvider) {
-		lib := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
+	if lib, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
 		paths.stubsHeaderPath = lib.HeaderJars
 		paths.stubsImplPath = lib.ImplementationJars
 
 		libDep := dep.(UsesLibraryDependency)
-		paths.stubsDexJarPath = libDep.DexJarBuildPath()
+		paths.stubsDexJarPath = libDep.DexJarBuildPath(ctx)
+		paths.exportableStubsDexJarPath = libDep.DexJarBuildPath(ctx)
 		return nil
 	} else {
 		return fmt.Errorf("expected module that has JavaInfoProvider, e.g. java_library")
 	}
 }
 
-func (paths *scopePaths) treatDepAsApiStubsProvider(dep android.Module, action func(provider ApiStubsProvider)) error {
-	if apiStubsProvider, ok := dep.(ApiStubsProvider); ok {
-		action(apiStubsProvider)
+func (paths *scopePaths) extractEverythingStubsLibraryInfoFromDependency(ctx android.ModuleContext, dep android.Module) error {
+	if lib, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
+		paths.stubsHeaderPath = lib.HeaderJars
+		if !ctx.Config().ReleaseHiddenApiExportableStubs() {
+			paths.stubsImplPath = lib.ImplementationJars
+		}
+
+		libDep := dep.(UsesLibraryDependency)
+		paths.stubsDexJarPath = libDep.DexJarBuildPath(ctx)
 		return nil
 	} else {
-		return fmt.Errorf("expected module that implements ApiStubsProvider, e.g. droidstubs")
+		return fmt.Errorf("expected module that has JavaInfoProvider, e.g. java_library")
 	}
 }
 
-func (paths *scopePaths) treatDepAsApiStubsSrcProvider(dep android.Module, action func(provider ApiStubsSrcProvider)) error {
+func (paths *scopePaths) extractExportableStubsLibraryInfoFromDependency(ctx android.ModuleContext, dep android.Module) error {
+	if lib, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
+		if ctx.Config().ReleaseHiddenApiExportableStubs() {
+			paths.stubsImplPath = lib.ImplementationJars
+		}
+
+		libDep := dep.(UsesLibraryDependency)
+		paths.exportableStubsDexJarPath = libDep.DexJarBuildPath(ctx)
+		return nil
+	} else {
+		return fmt.Errorf("expected module that has JavaInfoProvider, e.g. java_library")
+	}
+}
+
+func (paths *scopePaths) treatDepAsApiStubsProvider(dep android.Module, action func(provider ApiStubsProvider) error) error {
+	if apiStubsProvider, ok := dep.(ApiStubsProvider); ok {
+		err := action(apiStubsProvider)
+		if err != nil {
+			return err
+		}
+		return nil
+	} else {
+		return fmt.Errorf("expected module that implements ExportableApiStubsSrcProvider, e.g. droidstubs")
+	}
+}
+
+func (paths *scopePaths) treatDepAsApiStubsSrcProvider(dep android.Module, action func(provider ApiStubsSrcProvider) error) error {
 	if apiStubsProvider, ok := dep.(ApiStubsSrcProvider); ok {
-		action(apiStubsProvider)
+		err := action(apiStubsProvider)
+		if err != nil {
+			return err
+		}
 		return nil
 	} else {
 		return fmt.Errorf("expected module that implements ApiStubsSrcProvider, e.g. droidstubs")
 	}
 }
 
-func (paths *scopePaths) extractApiInfoFromApiStubsProvider(provider ApiStubsProvider) {
-	paths.annotationsZip = android.OptionalPathForPath(provider.AnnotationsZip())
-	paths.currentApiFilePath = android.OptionalPathForPath(provider.ApiFilePath())
-	paths.removedApiFilePath = android.OptionalPathForPath(provider.RemovedApiFilePath())
+func (paths *scopePaths) extractApiInfoFromApiStubsProvider(provider ApiStubsProvider, stubsType StubsType) error {
+	var annotationsZip, currentApiFilePath, removedApiFilePath android.Path
+	annotationsZip, annotationsZipErr := provider.AnnotationsZip(stubsType)
+	currentApiFilePath, currentApiFilePathErr := provider.ApiFilePath(stubsType)
+	removedApiFilePath, removedApiFilePathErr := provider.RemovedApiFilePath(stubsType)
+
+	combinedError := errors.Join(annotationsZipErr, currentApiFilePathErr, removedApiFilePathErr)
+
+	if combinedError == nil {
+		paths.annotationsZip = android.OptionalPathForPath(annotationsZip)
+		paths.currentApiFilePath = android.OptionalPathForPath(currentApiFilePath)
+		paths.removedApiFilePath = android.OptionalPathForPath(removedApiFilePath)
+	}
+	return combinedError
 }
 
 func (paths *scopePaths) extractApiInfoFromDep(ctx android.ModuleContext, dep android.Module) error {
-	return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) {
-		paths.extractApiInfoFromApiStubsProvider(provider)
+	return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) error {
+		return paths.extractApiInfoFromApiStubsProvider(provider, Everything)
 	})
 }
 
-func (paths *scopePaths) extractStubsSourceInfoFromApiStubsProviders(provider ApiStubsSrcProvider) {
-	paths.stubsSrcJar = android.OptionalPathForPath(provider.StubsSrcJar())
+func (paths *scopePaths) extractStubsSourceInfoFromApiStubsProviders(provider ApiStubsSrcProvider, stubsType StubsType) error {
+	stubsSrcJar, err := provider.StubsSrcJar(stubsType)
+	if err == nil {
+		paths.stubsSrcJar = android.OptionalPathForPath(stubsSrcJar)
+	}
+	return err
 }
 
 func (paths *scopePaths) extractStubsSourceInfoFromDep(ctx android.ModuleContext, dep android.Module) error {
-	return paths.treatDepAsApiStubsSrcProvider(dep, func(provider ApiStubsSrcProvider) {
-		paths.extractStubsSourceInfoFromApiStubsProviders(provider)
+	return paths.treatDepAsApiStubsSrcProvider(dep, func(provider ApiStubsSrcProvider) error {
+		return paths.extractStubsSourceInfoFromApiStubsProviders(provider, Everything)
 	})
 }
 
 func (paths *scopePaths) extractStubsSourceAndApiInfoFromApiStubsProvider(ctx android.ModuleContext, dep android.Module) error {
-	return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) {
-		paths.extractApiInfoFromApiStubsProvider(provider)
-		paths.extractStubsSourceInfoFromApiStubsProviders(provider)
+	if ctx.Config().ReleaseHiddenApiExportableStubs() {
+		return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) error {
+			extractApiInfoErr := paths.extractApiInfoFromApiStubsProvider(provider, Exportable)
+			extractStubsSourceInfoErr := paths.extractStubsSourceInfoFromApiStubsProviders(provider, Exportable)
+			return errors.Join(extractApiInfoErr, extractStubsSourceInfoErr)
+		})
+	}
+	return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) error {
+		extractApiInfoErr := paths.extractApiInfoFromApiStubsProvider(provider, Everything)
+		extractStubsSourceInfoErr := paths.extractStubsSourceInfoFromApiStubsProviders(provider, Everything)
+		return errors.Join(extractApiInfoErr, extractStubsSourceInfoErr)
 	})
 }
 
@@ -812,7 +907,30 @@
 type commonSdkLibraryAndImportModule interface {
 	android.Module
 
-	BaseModuleName() string
+	// Returns the name of the root java_sdk_library that creates the child stub libraries
+	// This is the `name` as it appears in Android.bp, and not the name in Soong's build graph
+	// (with the prebuilt_ prefix)
+	//
+	// e.g. in the following java_sdk_library_import
+	// java_sdk_library_import {
+	//    name: "framework-foo.v1",
+	//    source_module_name: "framework-foo",
+	// }
+	// the values returned by
+	// 1. Name(): prebuilt_framework-foo.v1 # unique
+	// 2. BaseModuleName(): framework-foo # the source
+	// 3. RootLibraryName: framework-foo.v1 # the undecordated `name` from Android.bp
+	RootLibraryName() string
+}
+
+func (m *SdkLibrary) RootLibraryName() string {
+	return m.BaseModuleName()
+}
+
+func (m *SdkLibraryImport) RootLibraryName() string {
+	// m.BaseModuleName refers to the source of the import
+	// use moduleBase.Name to get the name of the module as it appears in the .bp file
+	return m.ModuleBase.Name()
 }
 
 // Common code between sdk library and sdk library import
@@ -851,7 +969,7 @@
 		return false
 	}
 
-	namePtr := proptools.StringPtr(c.module.BaseModuleName())
+	namePtr := proptools.StringPtr(c.module.RootLibraryName())
 	c.sdkLibraryComponentProperties.SdkLibraryName = namePtr
 
 	// Only track this sdk library if this can be used as a shared library.
@@ -878,39 +996,52 @@
 
 // Module name of the runtime implementation library
 func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string {
-	return c.module.BaseModuleName() + ".impl"
+	return c.module.RootLibraryName() + ".impl"
 }
 
 // Module name of the XML file for the lib
 func (c *commonToSdkLibraryAndImport) xmlPermissionsModuleName() string {
-	return c.module.BaseModuleName() + sdkXmlFileSuffix
+	return c.module.RootLibraryName() + sdkXmlFileSuffix
 }
 
 // Name of the java_library module that compiles the stubs source.
 func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string {
-	baseName := c.module.BaseModuleName()
+	baseName := c.module.RootLibraryName()
 	return c.namingScheme.stubsLibraryModuleName(apiScope, baseName)
 }
 
+// Name of the java_library module that compiles the exportable stubs source.
+func (c *commonToSdkLibraryAndImport) exportableStubsLibraryModuleName(apiScope *apiScope) string {
+	baseName := c.module.RootLibraryName()
+	return c.namingScheme.exportableStubsLibraryModuleName(apiScope, baseName)
+}
+
 // Name of the droidstubs module that generates the stubs source and may also
 // generate/check the API.
 func (c *commonToSdkLibraryAndImport) stubsSourceModuleName(apiScope *apiScope) string {
-	baseName := c.module.BaseModuleName()
+	baseName := c.module.RootLibraryName()
 	return c.namingScheme.stubsSourceModuleName(apiScope, baseName)
 }
 
 // Name of the java_api_library module that generates the from-text stubs source
 // and compiles to a jar file.
 func (c *commonToSdkLibraryAndImport) apiLibraryModuleName(apiScope *apiScope) string {
-	baseName := c.module.BaseModuleName()
+	baseName := c.module.RootLibraryName()
 	return c.namingScheme.apiLibraryModuleName(apiScope, baseName)
 }
 
 // Name of the java_library module that compiles the stubs
 // generated from source Java files.
-func (c *commonToSdkLibraryAndImport) sourceStubLibraryModuleName(apiScope *apiScope) string {
-	baseName := c.module.BaseModuleName()
-	return c.namingScheme.sourceStubLibraryModuleName(apiScope, baseName)
+func (c *commonToSdkLibraryAndImport) sourceStubsLibraryModuleName(apiScope *apiScope) string {
+	baseName := c.module.RootLibraryName()
+	return c.namingScheme.sourceStubsLibraryModuleName(apiScope, baseName)
+}
+
+// Name of the java_library module that compiles the exportable stubs
+// generated from source Java files.
+func (c *commonToSdkLibraryAndImport) exportableSourceStubsLibraryModuleName(apiScope *apiScope) string {
+	baseName := c.module.RootLibraryName()
+	return c.namingScheme.exportableSourceStubsLibraryModuleName(apiScope, baseName)
 }
 
 // The component names for different outputs of the java_sdk_library.
@@ -959,7 +1090,7 @@
 		if scope, ok := scopeByName[scopeName]; ok {
 			paths := c.findScopePaths(scope)
 			if paths == nil {
-				return nil, fmt.Errorf("%q does not provide api scope %s", c.module.BaseModuleName(), scopeName)
+				return nil, fmt.Errorf("%q does not provide api scope %s", c.module.RootLibraryName(), scopeName)
 			}
 
 			switch component {
@@ -995,7 +1126,7 @@
 			if c.doctagPaths != nil {
 				return c.doctagPaths, nil
 			} else {
-				return nil, fmt.Errorf("no doctag_files specified on %s", c.module.BaseModuleName())
+				return nil, fmt.Errorf("no doctag_files specified on %s", c.module.RootLibraryName())
 			}
 		}
 		return nil, nil
@@ -1041,7 +1172,7 @@
 
 	// If a specific numeric version has been requested then use prebuilt versions of the sdk.
 	if !sdkVersion.ApiLevel.IsPreview() {
-		return PrebuiltJars(ctx, c.module.BaseModuleName(), sdkVersion)
+		return PrebuiltJars(ctx, c.module.RootLibraryName(), sdkVersion)
 	}
 
 	paths := c.selectScopePaths(ctx, sdkVersion.Kind)
@@ -1068,7 +1199,7 @@
 				scopes = append(scopes, s.name)
 			}
 		}
-		ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.module.BaseModuleName(), scopes)
+		ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.module.RootLibraryName(), scopes)
 		return nil
 	}
 
@@ -1104,6 +1235,16 @@
 }
 
 // to satisfy SdkLibraryDependency interface
+func (c *commonToSdkLibraryAndImport) SdkApiExportableStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath {
+	paths := c.selectScopePaths(ctx, kind)
+	if paths == nil {
+		return makeUnsetDexJarPath()
+	}
+
+	return paths.exportableStubsDexJarPath
+}
+
+// to satisfy SdkLibraryDependency interface
 func (c *commonToSdkLibraryAndImport) SdkRemovedTxtFile(ctx android.BaseModuleContext, kind android.SdkKind) android.OptionalPath {
 	apiScope := sdkKindToApiScope(kind)
 	paths := c.findScopePaths(apiScope)
@@ -1120,7 +1261,7 @@
 		SdkLibraryToImplicitlyTrack *string
 	}{}
 
-	namePtr := proptools.StringPtr(c.module.BaseModuleName())
+	namePtr := proptools.StringPtr(c.module.RootLibraryName())
 	componentProps.SdkLibraryName = namePtr
 
 	if c.sharedLibrary() {
@@ -1221,10 +1362,16 @@
 	// they are identical to the corresponding header jars.
 	SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths
 
-	// SdkApiStubDexJar returns the dex jar for the stubs. It is needed by the hiddenapi processing
-	// tool which processes dex files.
+	// SdkApiStubDexJar returns the dex jar for the stubs for the prebuilt
+	// java_sdk_library_import module. It is needed by the hiddenapi processing tool which
+	// processes dex files.
 	SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath
 
+	// SdkApiExportableStubDexJar returns the exportable dex jar for the stubs for
+	// java_sdk_library module. It is needed by the hiddenapi processing tool which processes
+	// dex files.
+	SdkApiExportableStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath
+
 	// SdkRemovedTxtFile returns the optional path to the removed.txt file for the specified sdk kind.
 	SdkRemovedTxtFile(ctx android.BaseModuleContext, kind android.SdkKind) android.OptionalPath
 
@@ -1337,8 +1484,10 @@
 	for _, apiScope := range module.getGeneratedApiScopes(ctx) {
 		// Add dependencies to the stubs library
 		stubModuleName := module.stubsLibraryModuleName(apiScope)
+		ctx.AddVariationDependencies(nil, apiScope.everythingStubsTag, stubModuleName)
 
-		ctx.AddVariationDependencies(nil, apiScope.stubsTag, stubModuleName)
+		exportableStubModuleName := module.exportableStubsLibraryModuleName(apiScope)
+		ctx.AddVariationDependencies(nil, apiScope.exportableStubsTag, exportableStubModuleName)
 
 		// Add a dependency on the stubs source in order to access both stubs source and api information.
 		ctx.AddVariationDependencies(nil, apiScope.stubsSourceAndApiTag, module.stubsSourceModuleName(apiScope))
@@ -1423,6 +1572,8 @@
 
 	// Only build an implementation library if required.
 	if module.requiresRuntimeImplementationLibrary() {
+		// stubsLinkType must be set before calling Library.GenerateAndroidBuildActions
+		module.Library.stubsLinkType = Unknown
 		module.Library.GenerateAndroidBuildActions(ctx)
 	}
 
@@ -1451,7 +1602,7 @@
 
 	// Make the set of components exported by this module available for use elsewhere.
 	exportedComponentInfo := android.ExportedComponentsInfo{Components: android.SortedKeys(exportedComponents)}
-	ctx.SetProvider(android.ExportedComponentsInfoProvider, exportedComponentInfo)
+	android.SetProvider(ctx, android.ExportedComponentsInfoProvider, exportedComponentInfo)
 
 	// Provide additional information for inclusion in an sdk's generated .info file.
 	additionalSdkInfo := map[string]interface{}{}
@@ -1471,7 +1622,7 @@
 			scopeInfo["latest_removed_api"] = p.Path().String()
 		}
 	}
-	ctx.SetProvider(android.AdditionalSdkInfoProvider, android.AdditionalSdkInfo{additionalSdkInfo})
+	android.SetProvider(ctx, android.AdditionalSdkInfoProvider, android.AdditionalSdkInfo{additionalSdkInfo})
 }
 
 func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {
@@ -1626,36 +1777,35 @@
 	mctx.CreateModule(LibraryFactory, properties...)
 }
 
-// Creates a static java library that has API stubs
-func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
-	props := struct {
-		Name           *string
-		Visibility     []string
-		Srcs           []string
-		Installable    *bool
-		Sdk_version    *string
-		System_modules *string
-		Patch_module   *string
-		Libs           []string
-		Static_libs    []string
-		Compile_dex    *bool
-		Java_version   *string
-		Openjdk9       struct {
-			Srcs       []string
-			Javacflags []string
-		}
-		Dist struct {
-			Targets []string
-			Dest    *string
-			Dir     *string
-			Tag     *string
-		}
-	}{}
+type libraryProperties struct {
+	Name           *string
+	Visibility     []string
+	Srcs           []string
+	Installable    *bool
+	Sdk_version    *string
+	System_modules *string
+	Patch_module   *string
+	Libs           []string
+	Static_libs    []string
+	Compile_dex    *bool
+	Java_version   *string
+	Openjdk9       struct {
+		Srcs       []string
+		Javacflags []string
+	}
+	Dist struct {
+		Targets []string
+		Dest    *string
+		Dir     *string
+		Tag     *string
+	}
+	Is_stubs_module *bool
+}
 
-	props.Name = proptools.StringPtr(module.sourceStubLibraryModuleName(apiScope))
+func (module *SdkLibrary) stubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope) libraryProperties {
+	props := libraryProperties{}
 	props.Visibility = []string{"//visibility:override", "//visibility:private"}
 	// sources are generated from the droiddoc
-	props.Srcs = []string{":" + module.stubsSourceModuleName(apiScope)}
 	sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope)
 	props.Sdk_version = proptools.StringPtr(sdkVersion)
 	props.System_modules = module.deviceProperties.System_modules
@@ -1674,6 +1824,26 @@
 	// We compile the stubs for 1.8 in line with the main android.jar stubs, and potential
 	// interop with older developer tools that don't support 1.9.
 	props.Java_version = proptools.StringPtr("1.8")
+	props.Is_stubs_module = proptools.BoolPtr(true)
+
+	return props
+}
+
+// Creates a static java library that has API stubs
+func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
+
+	props := module.stubsLibraryProps(mctx, apiScope)
+	props.Name = proptools.StringPtr(module.sourceStubsLibraryModuleName(apiScope))
+	props.Srcs = []string{":" + module.stubsSourceModuleName(apiScope)}
+
+	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
+}
+
+// Create a static java library that compiles the "exportable" stubs
+func (module *SdkLibrary) createExportableStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
+	props := module.stubsLibraryProps(mctx, apiScope)
+	props.Name = proptools.StringPtr(module.exportableSourceStubsLibraryModuleName(apiScope))
+	props.Srcs = []string{":" + module.stubsSourceModuleName(apiScope) + "{.exportable}"}
 
 	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
 }
@@ -1699,6 +1869,7 @@
 		Merge_inclusion_annotations_dirs []string
 		Generate_stubs                   *bool
 		Previous_api                     *string
+		Aconfig_declarations             []string
 		Check_api                        struct {
 			Current       ApiToCheck
 			Last_released ApiToCheck
@@ -1743,6 +1914,7 @@
 	props.Annotations_enabled = module.sdkLibraryProperties.Annotations_enabled
 	props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs
 	props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs
+	props.Aconfig_declarations = module.sdkLibraryProperties.Aconfig_declarations
 
 	droidstubsArgs := []string{}
 	if len(module.sdkLibraryProperties.Api_packages) != 0 {
@@ -1825,8 +1997,10 @@
 			tag     string
 			pattern string
 		}{
-			{tag: ".api.txt", pattern: "%s.txt"},
-			{tag: ".removed-api.txt", pattern: "%s-removed.txt"},
+			// "exportable" api files are copied to the dist directory instead of the
+			// "everything" api files.
+			{tag: ".exportable.api.txt", pattern: "%s.txt"},
+			{tag: ".exportable.removed-api.txt", pattern: "%s-removed.txt"},
 		} {
 			props.Dists = append(props.Dists, android.Dist{
 				Targets: []string{"sdk", "win_sdk"},
@@ -1850,6 +2024,7 @@
 		Full_api_surface_stub *string
 		System_modules        *string
 		Enable_validation     *bool
+		Stubs_type            *string
 	}{}
 
 	props.Name = proptools.StringPtr(module.apiLibraryModuleName(apiScope))
@@ -1899,47 +2074,20 @@
 
 	props.System_modules = module.deviceProperties.System_modules
 	props.Enable_validation = proptools.BoolPtr(true)
+	props.Stubs_type = proptools.StringPtr("everything")
 
 	mctx.CreateModule(ApiLibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
 }
 
-func (module *SdkLibrary) createTopLevelStubsLibrary(
-	mctx android.DefaultableHookContext, apiScope *apiScope, contributesToApiSurface bool) {
-	props := struct {
-		Name           *string
-		Visibility     []string
-		Sdk_version    *string
-		Static_libs    []string
-		System_modules *string
-		Dist           struct {
-			Targets []string
-			Dest    *string
-			Dir     *string
-			Tag     *string
-		}
-		Compile_dex *bool
-	}{}
-	props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
+func (module *SdkLibrary) topLevelStubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope) libraryProperties {
+	props := libraryProperties{}
+
 	props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_library_visibility)
 	sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope)
 	props.Sdk_version = proptools.StringPtr(sdkVersion)
 
-	// Add the stub compiling java_library/java_api_library as static lib based on build config
-	staticLib := module.sourceStubLibraryModuleName(apiScope)
-	if mctx.Config().BuildFromTextStub() && contributesToApiSurface {
-		staticLib = module.apiLibraryModuleName(apiScope)
-	}
-	props.Static_libs = append(props.Static_libs, staticLib)
 	props.System_modules = module.deviceProperties.System_modules
 
-	// Dist the class jar artifact for sdk builds.
-	if !Bool(module.sdkLibraryProperties.No_dist) {
-		props.Dist.Targets = []string{"sdk", "win_sdk"}
-		props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.distStem()))
-		props.Dist.Dir = proptools.StringPtr(module.apiDistPath(apiScope))
-		props.Dist.Tag = proptools.StringPtr(".jar")
-	}
-
 	// The imports need to be compiled to dex if the java_sdk_library requests it.
 	compileDex := module.dexProperties.Compile_dex
 	if module.stubLibrariesCompiledForDex() {
@@ -1947,6 +2095,43 @@
 	}
 	props.Compile_dex = compileDex
 
+	return props
+}
+
+func (module *SdkLibrary) createTopLevelStubsLibrary(
+	mctx android.DefaultableHookContext, apiScope *apiScope, contributesToApiSurface bool) {
+
+	props := module.topLevelStubsLibraryProps(mctx, apiScope)
+	props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
+
+	// Add the stub compiling java_library/java_api_library as static lib based on build config
+	staticLib := module.sourceStubsLibraryModuleName(apiScope)
+	if mctx.Config().BuildFromTextStub() && contributesToApiSurface {
+		staticLib = module.apiLibraryModuleName(apiScope)
+	}
+	props.Static_libs = append(props.Static_libs, staticLib)
+
+	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
+}
+
+func (module *SdkLibrary) createTopLevelExportableStubsLibrary(
+	mctx android.DefaultableHookContext, apiScope *apiScope) {
+
+	props := module.topLevelStubsLibraryProps(mctx, apiScope)
+	props.Name = proptools.StringPtr(module.exportableStubsLibraryModuleName(apiScope))
+
+	// Dist the class jar artifact for sdk builds.
+	// "exportable" stubs are copied to dist for sdk builds instead of the "everything" stubs.
+	if !Bool(module.sdkLibraryProperties.No_dist) {
+		props.Dist.Targets = []string{"sdk", "win_sdk"}
+		props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.distStem()))
+		props.Dist.Dir = proptools.StringPtr(module.apiDistPath(apiScope))
+		props.Dist.Tag = proptools.StringPtr(".jar")
+	}
+
+	staticLib := module.exportableSourceStubsLibraryModuleName(apiScope)
+	props.Static_libs = append(props.Static_libs, staticLib)
+
 	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
 }
 
@@ -2036,8 +2221,8 @@
 // If either this or the other module are on the platform then this will return
 // false.
 func withinSameApexesAs(ctx android.BaseModuleContext, other android.Module) bool {
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
-	otherApexInfo := ctx.OtherModuleProvider(other, android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	otherApexInfo, _ := android.OtherModuleProvider(ctx, other, android.ApexInfoProvider)
 	return len(otherApexInfo.InApexVariants) > 0 && reflect.DeepEqual(apexInfo.InApexVariants, otherApexInfo.InApexVariants)
 }
 
@@ -2152,6 +2337,7 @@
 		module.createStubsSourcesAndApi(mctx, scope, module.stubsSourceModuleName(scope), scope.droidstubsArgs)
 
 		module.createStubsLibrary(mctx, scope)
+		module.createExportableStubsLibrary(mctx, scope)
 
 		alternativeFullApiSurfaceStubLib := ""
 		if scope == apiScopePublic {
@@ -2163,6 +2349,7 @@
 		}
 
 		module.createTopLevelStubsLibrary(mctx, scope, contributesToApiSurface)
+		module.createTopLevelExportableStubsLibrary(mctx, scope)
 	}
 
 	if module.requiresRuntimeImplementationLibrary() {
@@ -2218,7 +2405,11 @@
 
 	apiLibraryModuleName(scope *apiScope, baseName string) string
 
-	sourceStubLibraryModuleName(scope *apiScope, baseName string) string
+	sourceStubsLibraryModuleName(scope *apiScope, baseName string) string
+
+	exportableStubsLibraryModuleName(scope *apiScope, baseName string) string
+
+	exportableSourceStubsLibraryModuleName(scope *apiScope, baseName string) string
 }
 
 type defaultNamingScheme struct {
@@ -2236,34 +2427,47 @@
 	return scope.apiLibraryModuleName(baseName)
 }
 
-func (s *defaultNamingScheme) sourceStubLibraryModuleName(scope *apiScope, baseName string) string {
+func (s *defaultNamingScheme) sourceStubsLibraryModuleName(scope *apiScope, baseName string) string {
 	return scope.sourceStubLibraryModuleName(baseName)
 }
 
+func (s *defaultNamingScheme) exportableStubsLibraryModuleName(scope *apiScope, baseName string) string {
+	return scope.exportableStubsLibraryModuleName(baseName)
+}
+
+func (s *defaultNamingScheme) exportableSourceStubsLibraryModuleName(scope *apiScope, baseName string) string {
+	return scope.exportableSourceStubsLibraryModuleName(baseName)
+}
+
 var _ sdkLibraryComponentNamingScheme = (*defaultNamingScheme)(nil)
 
+func hasStubsLibrarySuffix(name string, apiScope *apiScope) bool {
+	return strings.HasSuffix(name, apiScope.stubsLibraryModuleNameSuffix()) ||
+		strings.HasSuffix(name, apiScope.exportableStubsLibraryModuleNameSuffix())
+}
+
 func moduleStubLinkType(name string) (stub bool, ret sdkLinkType) {
 	name = strings.TrimSuffix(name, ".from-source")
 
 	// This suffix-based approach is fragile and could potentially mis-trigger.
 	// TODO(b/155164730): Clean this up when modules no longer reference sdk_lib stubs directly.
-	if strings.HasSuffix(name, apiScopePublic.stubsLibraryModuleNameSuffix()) {
+	if hasStubsLibrarySuffix(name, apiScopePublic) {
 		if name == "hwbinder.stubs" || name == "libcore_private.stubs" {
 			// Due to a previous bug, these modules were not considered stubs, so we retain that.
 			return false, javaPlatform
 		}
 		return true, javaSdk
 	}
-	if strings.HasSuffix(name, apiScopeSystem.stubsLibraryModuleNameSuffix()) {
+	if hasStubsLibrarySuffix(name, apiScopeSystem) {
 		return true, javaSystem
 	}
-	if strings.HasSuffix(name, apiScopeModuleLib.stubsLibraryModuleNameSuffix()) {
+	if hasStubsLibrarySuffix(name, apiScopeModuleLib) {
 		return true, javaModule
 	}
-	if strings.HasSuffix(name, apiScopeTest.stubsLibraryModuleNameSuffix()) {
+	if hasStubsLibrarySuffix(name, apiScopeTest) {
 		return true, javaSystem
 	}
-	if strings.HasSuffix(name, apiScopeSystemServer.stubsLibraryModuleNameSuffix()) {
+	if hasStubsLibrarySuffix(name, apiScopeSystemServer) {
 		return true, javaSystemServer
 	}
 	return false, javaPlatform
@@ -2352,6 +2556,11 @@
 
 	// If not empty, classes are restricted to the specified packages and their sub-packages.
 	Permitted_packages []string
+
+	// Name of the source soong module that gets shadowed by this prebuilt
+	// If unspecified, follows the naming convention that the source module of
+	// the prebuilt is Name() without "prebuilt_" prefix
+	Source_module_name *string
 }
 
 type SdkLibraryImport struct {
@@ -2379,7 +2588,8 @@
 	xmlPermissionsFileModule *sdkLibraryXml
 
 	// Build path to the dex implementation jar obtained from the prebuilt_apex, if any.
-	dexJarFile OptionalDexJarPath
+	dexJarFile    OptionalDexJarPath
+	dexJarFileErr error
 
 	// Expected install file path of the source module(sdk_library)
 	// or dex implementation jar obtained from the prebuilt_apex, if any.
@@ -2464,6 +2674,10 @@
 	return module.prebuilt.Name(module.ModuleBase.Name())
 }
 
+func (module *SdkLibraryImport) BaseModuleName() string {
+	return proptools.StringDefault(module.properties.Source_module_name, module.ModuleBase.Name())
+}
+
 func (module *SdkLibraryImport) createInternalModules(mctx android.DefaultableHookContext) {
 
 	// If the build is configured to use prebuilts then force this to be preferred.
@@ -2496,15 +2710,20 @@
 func (module *SdkLibraryImport) createJavaImportForStubs(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
 	// Creates a java import for the jar with ".stubs" suffix
 	props := struct {
-		Name        *string
-		Sdk_version *string
-		Libs        []string
-		Jars        []string
-		Compile_dex *bool
+		Name                             *string
+		Source_module_name               *string
+		Created_by_java_sdk_library_name *string
+		Sdk_version                      *string
+		Libs                             []string
+		Jars                             []string
+		Compile_dex                      *bool
+		Is_stubs_module                  *bool
 
 		android.UserSuppliedPrebuiltProperties
 	}{}
 	props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
+	props.Source_module_name = proptools.StringPtr(apiScope.stubsLibraryModuleName(module.BaseModuleName()))
+	props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
 	props.Sdk_version = scopeProperties.Sdk_version
 	// Prepend any of the libs from the legacy public properties to the libs for each of the
 	// scopes to avoid having to duplicate them in each scope.
@@ -2520,18 +2739,23 @@
 		compileDex = proptools.BoolPtr(true)
 	}
 	props.Compile_dex = compileDex
+	props.Is_stubs_module = proptools.BoolPtr(true)
 
 	mctx.CreateModule(ImportFactory, &props, module.sdkComponentPropertiesForChildLibrary())
 }
 
 func (module *SdkLibraryImport) createPrebuiltStubsSources(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
 	props := struct {
-		Name *string
-		Srcs []string
+		Name                             *string
+		Source_module_name               *string
+		Created_by_java_sdk_library_name *string
+		Srcs                             []string
 
 		android.UserSuppliedPrebuiltProperties
 	}{}
 	props.Name = proptools.StringPtr(module.stubsSourceModuleName(apiScope))
+	props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()))
+	props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
 	props.Srcs = scopeProperties.Stub_srcs
 
 	// The stubs source is preferred if the java_sdk_library_import is preferred.
@@ -2545,13 +2769,17 @@
 	api_surface := &apiScope.name
 
 	props := struct {
-		Name        *string
-		Api_surface *string
-		Api_file    *string
-		Visibility  []string
+		Name                             *string
+		Source_module_name               *string
+		Created_by_java_sdk_library_name *string
+		Api_surface                      *string
+		Api_file                         *string
+		Visibility                       []string
 	}{}
 
 	props.Name = proptools.StringPtr(module.stubsSourceModuleName(apiScope) + ".api.contribution")
+	props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()) + ".api.contribution")
+	props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
 	props.Api_surface = api_surface
 	props.Api_file = api_file
 	props.Visibility = []string{"//visibility:override", "//visibility:public"}
@@ -2568,7 +2796,7 @@
 		}
 
 		// Add dependencies to the prebuilt stubs library
-		ctx.AddVariationDependencies(nil, apiScope.stubsTag, android.PrebuiltNameFromSource(module.stubsLibraryModuleName(apiScope)))
+		ctx.AddVariationDependencies(nil, apiScope.prebuiltStubsTag, android.PrebuiltNameFromSource(module.stubsLibraryModuleName(apiScope)))
 
 		if len(scopeProperties.Stub_srcs) > 0 {
 			// Add dependencies to the prebuilt stubs source library
@@ -2592,14 +2820,6 @@
 	}
 }
 
-func (module *SdkLibraryImport) AndroidMkEntries() []android.AndroidMkEntries {
-	// For an SDK library imported from a prebuilt APEX, we don't need a Make module for itself, as we
-	// don't need to install it. However, we need to add its dexpreopt outputs as sub-modules, if it
-	// is preopted.
-	dexpreoptEntries := module.dexpreopter.AndroidMkEntriesForApex()
-	return append(dexpreoptEntries, android.AndroidMkEntries{Disabled: true})
-}
-
 var _ android.ApexModule = (*SdkLibraryImport)(nil)
 
 // Implements android.ApexModule
@@ -2693,14 +2913,18 @@
 	if ctx.Device() {
 		// If this is a variant created for a prebuilt_apex then use the dex implementation jar
 		// obtained from the associated deapexer module.
-		ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+		ai, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 		if ai.ForPrebuiltApex {
 			// Get the path of the dex implementation jar from the `deapexer` module.
-			di := android.FindDeapexerProviderForModule(ctx)
-			if di == nil {
-				return // An error has been reported by FindDeapexerProviderForModule.
+			di, err := android.FindDeapexerProviderForModule(ctx)
+			if err != nil {
+				// An error was found, possibly due to multiple apexes in the tree that export this library
+				// Defer the error till a client tries to call DexJarBuildPath
+				module.dexJarFileErr = err
+				module.initHiddenAPIError(err)
+				return
 			}
-			dexJarFileApexRootRelative := apexRootRelativePathToJavaLib(module.BaseModuleName())
+			dexJarFileApexRootRelative := ApexRootRelativePathToJavaLib(module.BaseModuleName())
 			if dexOutputPath := di.PrebuiltExportPath(dexJarFileApexRootRelative); dexOutputPath != nil {
 				dexJarFile := makeDexJarPathFromPath(dexOutputPath)
 				module.dexJarFile = dexJarFile
@@ -2709,16 +2933,13 @@
 				module.installFile = installPath
 				module.initHiddenAPI(ctx, dexJarFile, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil)
 
-				module.dexpreopter.installPath = module.dexpreopter.getInstallPath(ctx, installPath)
+				module.dexpreopter.installPath = module.dexpreopter.getInstallPath(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), installPath)
 				module.dexpreopter.isSDKLibrary = true
-				module.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &module.dexpreopter)
+				module.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), &module.dexpreopter)
 
 				if profilePath := di.PrebuiltExportPath(dexJarFileApexRootRelative + ".prof"); profilePath != nil {
 					module.dexpreopter.inputProfilePathOnHost = profilePath
 				}
-
-				// Dexpreopting.
-				module.dexpreopt(ctx, dexOutputPath)
 			} else {
 				// This should never happen as a variant for a prebuilt_apex is only created if the
 				// prebuilt_apex has been configured to export the java library dex file.
@@ -2757,16 +2978,19 @@
 }
 
 // to satisfy UsesLibraryDependency interface
-func (module *SdkLibraryImport) DexJarBuildPath() OptionalDexJarPath {
+func (module *SdkLibraryImport) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
 	// The dex implementation jar extracted from the .apex file should be used in preference to the
 	// source.
+	if module.dexJarFileErr != nil {
+		ctx.ModuleErrorf(module.dexJarFileErr.Error())
+	}
 	if module.dexJarFile.IsSet() {
 		return module.dexJarFile
 	}
 	if module.implLibraryModule == nil {
 		return makeUnsetDexJarPath()
 	} else {
-		return module.implLibraryModule.DexJarBuildPath()
+		return module.implLibraryModule.DexJarBuildPath(ctx)
 	}
 }
 
@@ -2849,6 +3073,10 @@
 	return requiredFilesFromPrebuiltApexForImport(name, &module.dexpreopter)
 }
 
+func (j *SdkLibraryImport) UseProfileGuidedDexpreopt() bool {
+	return proptools.Bool(j.importDexpreoptProperties.Dex_preopt.Profile_guided)
+}
+
 // java_sdk_library_xml
 type sdkLibraryXml struct {
 	android.ModuleBase
@@ -2960,7 +3188,7 @@
 // File path to the runtime implementation library
 func (module *sdkLibraryXml) implPath(ctx android.ModuleContext) string {
 	implName := proptools.String(module.properties.Lib_name)
-	if apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo); !apexInfo.IsForPlatform() {
+	if apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider); !apexInfo.IsForPlatform() {
 		// TODO(b/146468504): ApexVariationName() is only a soong module name, not apex name.
 		// In most cases, this works fine. But when apex_name is set or override_apex is used
 		// this can be wrong.
@@ -3067,7 +3295,8 @@
 }
 
 func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	module.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	module.hideApexVariantFromMake = !apexInfo.IsForPlatform()
 
 	libName := proptools.String(module.properties.Lib_name)
 	module.selfValidate(ctx)
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index a136818..93ef408 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -38,6 +38,11 @@
 		android.FixtureModifyConfig(func(config android.Config) {
 			config.SetApiLibraries([]string{"foo"})
 		}),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+			}
+		}),
 	).RunTestWithBp(t, `
 		droiddoc_exported_dir {
 			name: "droiddoc-templates-sdk",
@@ -132,13 +137,16 @@
 	result.ModuleForTests("foo.api.system.28", "")
 	result.ModuleForTests("foo.api.test.28", "")
 
-	exportedComponentsInfo := result.ModuleProvider(foo.Module(), android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo)
+	exportedComponentsInfo, _ := android.SingletonModuleProvider(result, foo.Module(), android.ExportedComponentsInfoProvider)
 	expectedFooExportedComponents := []string{
 		"foo-removed.api.public.latest",
 		"foo-removed.api.system.latest",
 		"foo.api.public.latest",
 		"foo.api.system.latest",
 		"foo.stubs",
+		"foo.stubs.exportable",
+		"foo.stubs.exportable.system",
+		"foo.stubs.exportable.test",
 		"foo.stubs.source",
 		"foo.stubs.source.system",
 		"foo.stubs.source.test",
@@ -529,6 +537,11 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib"),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+			}
+		}),
 	).RunTestWithBp(t, `
 		java_sdk_library {
 			name: "sdklib",
@@ -547,6 +560,7 @@
 		`sdklib.api.public.latest`,
 		`sdklib.impl`,
 		`sdklib.stubs`,
+		`sdklib.stubs.exportable`,
 		`sdklib.stubs.source`,
 		`sdklib.xml`,
 	})
@@ -919,6 +933,11 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib"),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+			}
+		}),
 	).RunTestWithBp(t, `
 		java_sdk_library {
 			name: "sdklib",
@@ -945,11 +964,13 @@
 		`sdklib.api.public.latest`,
 		`sdklib.impl`,
 		`sdklib.stubs`,
+		`sdklib.stubs.exportable`,
 		`sdklib.stubs.source`,
 		`sdklib.xml`,
 	})
 
 	CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{
+		`all_apex_contributions`,
 		`prebuilt_sdklib.stubs`,
 		`sdklib.impl`,
 		// This should be prebuilt_sdklib.stubs but is set to sdklib.stubs because the
@@ -965,6 +986,11 @@
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib"),
 		preparer,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+			}
+		}),
 	).RunTestWithBp(t, `
 		java_sdk_library {
 			name: "sdklib",
@@ -1017,11 +1043,13 @@
 		`sdklib.api.public.latest`,
 		`sdklib.impl`,
 		`sdklib.stubs`,
+		`sdklib.stubs.exportable`,
 		`sdklib.stubs.source`,
 		`sdklib.xml`,
 	})
 
 	CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{
+		`all_apex_contributions`,
 		`dex2oatd`,
 		`prebuilt_sdklib.stubs`,
 		`prebuilt_sdklib.stubs.source`,
@@ -1085,9 +1113,6 @@
 				"prebuilt_sdklib.source_preferred_using_legacy_flags",
 			],
 		}
-		all_apex_contributions {
-			name: "all_apex_contributions",
-		}
 		java_sdk_library {
 			name: "sdklib.prebuilt_preferred_using_legacy_flags",
 			srcs: ["a.java"],
@@ -1169,9 +1194,6 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib.source_preferred_using_legacy_flags", "sdklib.prebuilt_preferred_using_legacy_flags"),
-		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
-			android.RegisterApexContributionsBuildComponents(ctx)
-		}),
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 			variables.BuildFlags = map[string]string{
 				"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions",
@@ -1427,7 +1449,7 @@
 
 	for _, tt := range testCases {
 		t.Run(tt.module, func(t *testing.T) {
-			m := result.ModuleForTests(tt.module+".stubs", "android_common").Module().(*Library)
+			m := result.ModuleForTests(apiScopePublic.exportableStubsLibraryModuleName(tt.module), "android_common").Module().(*Library)
 			dists := m.Dists()
 			if len(dists) != 1 {
 				t.Fatalf("expected exactly 1 dist entry, got %d", len(dists))
@@ -1697,3 +1719,204 @@
 
 	android.AssertStringDoesContain(t, "bar.xml java_sdk_xml command", barPermissions.RuleParams.Command, `dependency=\"foo\"`)
 }
+
+func TestSdkLibraryExportableStubsLibrary(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo"),
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.SetApiLibraries([]string{"foo"})
+		}),
+	).RunTestWithBp(t, `
+		aconfig_declarations {
+			name: "bar",
+			package: "com.example.package",
+			srcs: [
+				"bar.aconfig",
+			],
+		}
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java", "b.java"],
+			api_packages: ["foo"],
+			system: {
+				enabled: true,
+			},
+			module_lib: {
+				enabled: true,
+			},
+			test: {
+				enabled: true,
+			},
+			aconfig_declarations: [
+				"bar",
+			],
+		}
+	`)
+
+	exportableStubsLibraryModuleName := apiScopePublic.exportableStubsLibraryModuleName("foo")
+	exportableSourceStubsLibraryModuleName := apiScopePublic.exportableSourceStubsLibraryModuleName("foo")
+
+	// Check modules generation
+	topLevelModule := result.ModuleForTests(exportableStubsLibraryModuleName, "android_common")
+	result.ModuleForTests(exportableSourceStubsLibraryModuleName, "android_common")
+
+	// Check static lib dependency
+	android.AssertBoolEquals(t, "exportable top level stubs library module depends on the"+
+		"exportable source stubs library module", true,
+		CheckModuleHasDependency(t, result.TestContext, exportableStubsLibraryModuleName,
+			"android_common", exportableSourceStubsLibraryModuleName),
+	)
+	android.AssertArrayString(t, "exportable source stub library is a static lib of the"+
+		"top level exportable stubs library", []string{exportableSourceStubsLibraryModuleName},
+		topLevelModule.Module().(*Library).properties.Static_libs)
+}
+
+// For java libraries depending on java_sdk_library(_import) via libs, assert that
+// rdep gets stubs of source if source is listed in apex_contributions and prebuilt has prefer (legacy mechanism)
+func TestStubResolutionOfJavaSdkLibraryInLibs(t *testing.T) {
+	bp := `
+		apex_contributions {
+			name: "my_mainline_module_contributions",
+			api_domain: "my_mainline_module",
+			contents: ["sdklib"], // source is selected using apex_contributions, but prebuilt is selected using prefer
+		}
+		java_sdk_library {
+			name: "sdklib",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			public: {
+				enabled: true,
+			},
+		}
+		java_sdk_library_import {
+			name: "sdklib",
+			public: {
+				jars: ["a.jar"],
+				stub_srcs: ["a.java"],
+				current_api: "current.txt",
+				removed_api: "removed.txt",
+				annotations: "annotations.zip",
+			},
+			prefer: true, // Set prefer explicitly on the prebuilt. We will assert that rdep gets source in a test case.
+		}
+		// rdeps
+		java_library {
+			name: "mymodule",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			libs: ["sdklib",], // this should be dynamically resolved to sdklib.stubs (source) or prebuilt_sdklib.stubs (prebuilt)
+		}
+	`
+
+	fixture := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("sdklib"),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				// We can use any of the apex contribution build flags from build/soong/android/config.go#mainlineApexContributionBuildFlags here
+				"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions",
+			}
+		}),
+	)
+
+	result := fixture.RunTestWithBp(t, bp)
+	// Make sure that rdeps get the correct source vs prebuilt based on mainline_module_contributions
+	public := result.ModuleForTests("mymodule", "android_common")
+	rule := public.Output("javac/mymodule.jar")
+	inputs := rule.Implicits.Strings()
+	android.AssertStringListContains(t, "Could not find the expected stub on classpath", inputs, "out/soong/.intermediates/sdklib.stubs/android_common/turbine-combined/sdklib.stubs.jar")
+}
+
+// test that rdep gets resolved to the correct version of a java_sdk_library (source or a specific prebuilt)
+func TestMultipleSdkLibraryPrebuilts(t *testing.T) {
+	bp := `
+		apex_contributions {
+			name: "my_mainline_module_contributions",
+			api_domain: "my_mainline_module",
+			contents: ["%s"],
+		}
+		java_sdk_library {
+			name: "sdklib",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			public: {
+				enabled: true,
+			},
+		}
+		java_sdk_library_import {
+			name: "sdklib.v1", //prebuilt
+			source_module_name: "sdklib",
+			public: {
+				jars: ["a.jar"],
+				stub_srcs: ["a.java"],
+				current_api: "current.txt",
+				removed_api: "removed.txt",
+				annotations: "annotations.zip",
+			},
+		}
+		java_sdk_library_import {
+			name: "sdklib.v2", //prebuilt
+			source_module_name: "sdklib",
+			public: {
+				jars: ["a.jar"],
+				stub_srcs: ["a.java"],
+				current_api: "current.txt",
+				removed_api: "removed.txt",
+				annotations: "annotations.zip",
+			},
+		}
+		// rdeps
+		java_library {
+			name: "mymodule",
+			srcs: ["a.java"],
+			libs: ["sdklib.stubs",],
+		}
+	`
+	testCases := []struct {
+		desc                   string
+		selectedDependencyName string
+		expectedStubPath       string
+	}{
+		{
+			desc:                   "Source library is selected using apex_contributions",
+			selectedDependencyName: "sdklib",
+			expectedStubPath:       "out/soong/.intermediates/sdklib.stubs/android_common/turbine-combined/sdklib.stubs.jar",
+		},
+		{
+			desc:                   "Prebuilt library v1 is selected using apex_contributions",
+			selectedDependencyName: "prebuilt_sdklib.v1",
+			expectedStubPath:       "out/soong/.intermediates/prebuilt_sdklib.v1.stubs/android_common/combined/sdklib.stubs.jar",
+		},
+		{
+			desc:                   "Prebuilt library v2 is selected using apex_contributions",
+			selectedDependencyName: "prebuilt_sdklib.v2",
+			expectedStubPath:       "out/soong/.intermediates/prebuilt_sdklib.v2.stubs/android_common/combined/sdklib.stubs.jar",
+		},
+	}
+
+	fixture := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("sdklib", "sdklib.v1", "sdklib.v2"),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions",
+			}
+		}),
+	)
+
+	for _, tc := range testCases {
+		result := fixture.RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName))
+
+		// Make sure that rdeps get the correct source vs prebuilt based on mainline_module_contributions
+		public := result.ModuleForTests("mymodule", "android_common")
+		rule := public.Output("javac/mymodule.jar")
+		inputs := rule.Implicits.Strings()
+		android.AssertStringListContains(t, "Could not find the expected stub on classpath", inputs, tc.expectedStubPath)
+	}
+}
diff --git a/java/sdk_version_test.go b/java/sdk_version_test.go
new file mode 100644
index 0000000..88351d2
--- /dev/null
+++ b/java/sdk_version_test.go
@@ -0,0 +1,66 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"testing"
+
+	"android/soong/android"
+)
+
+func stringPtr(v string) *string {
+	return &v
+}
+
+func TestSystemSdkFromVendor(t *testing.T) {
+	fixtures := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.Platform_sdk_version = intPtr(34)
+			variables.Platform_sdk_codename = stringPtr("VanillaIceCream")
+			variables.Platform_version_active_codenames = []string{"VanillaIceCream"}
+			variables.Platform_systemsdk_versions = []string{"33", "34", "VanillaIceCream"}
+			variables.DeviceSystemSdkVersions = []string{"VanillaIceCream"}
+		}),
+		FixtureWithPrebuiltApis(map[string][]string{
+			"33": {},
+			"34": {},
+			"35": {},
+		}),
+	)
+
+	fixtures.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("incompatible sdk version")).
+		RunTestWithBp(t, `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			vendor: true,
+			sdk_version: "system_35",
+		}`)
+
+	result := fixtures.RunTestWithBp(t, `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			vendor: true,
+			sdk_version: "system_current",
+		}`)
+	fooModule := result.ModuleForTests("foo", "android_common")
+	fooClasspath := fooModule.Rule("javac").Args["classpath"]
+
+	android.AssertStringDoesContain(t, "foo classpath", fooClasspath, "prebuilts/sdk/34/system/android.jar")
+	android.AssertStringDoesNotContain(t, "foo classpath", fooClasspath, "prebuilts/sdk/35/system/android.jar")
+	android.AssertStringDoesNotContain(t, "foo classpath", fooClasspath, "prebuilts/sdk/current/system/android.jar")
+}
diff --git a/java/system_modules.go b/java/system_modules.go
index 0efa1a4..92e31cd 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -19,6 +19,7 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
 )
@@ -55,7 +56,8 @@
 			`${config.MergeZipsCmd} -j ${workDir}/module.jar ${workDir}/classes.jar $in && ` +
 			// Note: The version of the java.base module created must match the version
 			// of the jlink tool which consumes it.
-			`${config.JmodCmd} create --module-version ${config.JlinkVersion} --target-platform android ` +
+			// Use LINUX-OTHER to be compatible with JDK 21+ (b/294137077)
+			`${config.JmodCmd} create --module-version ${config.JlinkVersion} --target-platform LINUX-OTHER ` +
 			`  --class-path ${workDir}/module.jar ${workDir}/jmod/java.base.jmod && ` +
 			`${config.JlinkCmd} --module-path ${workDir}/jmod --add-modules java.base --output ${outDir} ` +
 			// Note: The system-modules jlink plugin is disabled because (a) it is not
@@ -159,7 +161,7 @@
 	var jars android.Paths
 
 	ctx.VisitDirectDepsWithTag(systemModulesLibsTag, func(module android.Module) {
-		dep, _ := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
+		dep, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider)
 		jars = append(jars, dep.HeaderJars...)
 	})
 
@@ -209,7 +211,7 @@
 // type and the one to use is selected at runtime.
 func systemModulesImportFactory() android.Module {
 	module := &systemModulesImport{}
-	module.AddProperties(&module.properties)
+	module.AddProperties(&module.properties, &module.prebuiltProperties)
 	android.InitPrebuiltModule(module, &module.properties.Libs)
 	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
@@ -218,13 +220,39 @@
 
 type systemModulesImport struct {
 	SystemModules
-	prebuilt android.Prebuilt
+	prebuilt           android.Prebuilt
+	prebuiltProperties prebuiltSystemModulesProperties
+}
+
+type prebuiltSystemModulesProperties struct {
+	// Name of the source soong module that gets shadowed by this prebuilt
+	// If unspecified, follows the naming convention that the source module of
+	// the prebuilt is Name() without "prebuilt_" prefix
+	Source_module_name *string
 }
 
 func (system *systemModulesImport) Name() string {
 	return system.prebuilt.Name(system.ModuleBase.Name())
 }
 
+// BaseModuleName returns the source module that will get shadowed by this prebuilt
+// e.g.
+//
+//	java_system_modules_import {
+//	   name: "my_system_modules.v1",
+//	   source_module_name: "my_system_modules",
+//	}
+//
+//	java_system_modules_import {
+//	   name: "my_system_modules.v2",
+//	   source_module_name: "my_system_modules",
+//	}
+//
+// `BaseModuleName` for both will return `my_system_modules`
+func (system *systemModulesImport) BaseModuleName() string {
+	return proptools.StringDefault(system.prebuiltProperties.Source_module_name, system.ModuleBase.Name())
+}
+
 func (system *systemModulesImport) Prebuilt() *android.Prebuilt {
 	return &system.prebuilt
 }
diff --git a/java/system_modules_test.go b/java/system_modules_test.go
index 7b5a386..336dd21 100644
--- a/java/system_modules_test.go
+++ b/java/system_modules_test.go
@@ -15,6 +15,7 @@
 package java
 
 import (
+	"fmt"
 	"testing"
 
 	"android/soong/android"
@@ -24,7 +25,7 @@
 	paths := []string{}
 	for _, moduleName := range moduleNames {
 		module := result.Module(moduleName, "android_common")
-		info := result.ModuleProvider(module, JavaInfoProvider).(JavaInfo)
+		info, _ := android.SingletonModuleProvider(result, module, JavaInfoProvider)
 		paths = append(paths, info.HeaderJars.RelativeToTop().Strings()...)
 	}
 	return paths
@@ -111,3 +112,85 @@
 	expectedPrebuiltPaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "prebuilt_system-module1", "prebuilt_system-module2")
 	android.AssertArrayString(t, "prebuilt system modules inputs", expectedPrebuiltPaths, prebuiltInputs.RelativeToTop().Strings())
 }
+
+func TestMultipleSystemModulesPrebuilts(t *testing.T) {
+	bp := `
+		// an rdep
+		java_library {
+			name: "foo",
+			sdk_version: "none",
+			system_modules: "my_system_modules",
+		}
+
+		// multiple variations of java_system_modules
+		// source
+		java_system_modules {
+			name: "my_system_modules",
+			libs: ["bar"],
+		}
+		java_library {
+			name: "bar",
+			srcs: ["bar.java"],
+		}
+		// prebuilt "v1"
+		java_system_modules_import {
+			name: "my_system_modules.v1",
+			source_module_name: "my_system_modules",
+			libs: ["bar.v1"],
+		}
+		java_import {
+			name: "bar.v1",
+			source_module_name: "bar",
+			jars: ["bar.v1.jar"],
+		}
+		// prebuilt "v2"
+		java_system_modules_import {
+			name: "my_system_modules.v2",
+			source_module_name: "my_system_modules",
+			libs: ["bar.v2"],
+		}
+		java_import {
+			name: "bar.v2",
+			source_module_name: "bar",
+			jars: ["bar.v2.jar"],
+		}
+
+		// selectors
+		apex_contributions {
+			name: "myapex_contributions",
+			contents: ["%v"],
+		}
+	`
+	testCases := []struct {
+		desc                   string
+		selectedDependencyName string
+	}{
+		{
+			desc:                   "Source system_modules is selected using apex_contributions",
+			selectedDependencyName: "my_system_modules",
+		},
+		{
+			desc:                   "Prebuilt system_modules v1 is selected using apex_contributions",
+			selectedDependencyName: "prebuilt_my_system_modules.v1",
+		},
+		{
+			desc:                   "Prebuilt system_modules v2 is selected using apex_contributions",
+			selectedDependencyName: "prebuilt_my_system_modules.v2",
+		},
+	}
+
+	for _, tc := range testCases {
+		res := android.GroupFixturePreparers(
+			prepareForJavaTest,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.BuildFlags = map[string]string{
+					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
+				}
+			}),
+		).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName))
+
+		// check that rdep gets the correct variation of system_modules
+		hasDep := CheckModuleHasDependency(t, res.TestContext, "foo", "android_common", tc.selectedDependencyName)
+		android.AssertBoolEquals(t, fmt.Sprintf("expected dependency from foo to %s\n", tc.selectedDependencyName), true, hasDep)
+	}
+}
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index 30dd55f..59c5466 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -313,6 +313,10 @@
 	return nil
 }
 
+func (module *prebuiltSystemServerClasspathModule) UseProfileGuidedDexpreopt() bool {
+	return false
+}
+
 var _ android.RequiredFilesFromPrebuiltApex = (*prebuiltSystemServerClasspathModule)(nil)
 
 func prebuiltSystemServerClasspathModuleFactory() android.Module {
diff --git a/java/test_spec_test.go b/java/test_spec_test.go
index 7f06785..4144dad 100644
--- a/java/test_spec_test.go
+++ b/java/test_spec_test.go
@@ -29,14 +29,10 @@
 	}`
 	result := runTestSpecTest(t, android.FixtureExpectsNoErrors, bp)
 
-	module := result.ModuleForTests(
-		"module-name", "",
-	).Module().(*soongTesting.TestSpecModule)
+	module := result.ModuleForTests("module-name", "")
 
 	// Check that the provider has the right contents
-	data := result.ModuleProvider(
-		module, soongTesting.TestSpecProviderKey,
-	).(soongTesting.TestSpecProviderData)
+	data, _ := android.SingletonModuleProvider(result, module.Module(), soongTesting.TestSpecProviderKey)
 	if !strings.HasSuffix(
 		data.IntermediatePath.String(), "/intermediateTestSpecMetadata.pb",
 	) {
@@ -46,13 +42,8 @@
 		)
 	}
 
-	buildParamsSlice := module.BuildParamsForTests()
-	var metadata = ""
-	for _, params := range buildParamsSlice {
-		if params.Rule.String() == "android/soong/android.writeFile" {
-			metadata = params.Args["content"]
-		}
-	}
+	metadata := android.ContentFromFileRuleForTests(t, result.TestContext,
+		module.Output(data.IntermediatePath.String()))
 
 	metadataList := make([]*test_spec_proto.TestSpec_OwnershipMetadata, 0, 2)
 	teamId := "12345"
@@ -72,9 +63,7 @@
 	}
 	testSpecMetadata := test_spec_proto.TestSpec{OwnershipMetadataList: metadataList}
 	protoData, _ := proto.Marshal(&testSpecMetadata)
-	rawData := string(protoData)
-	formattedData := strings.ReplaceAll(rawData, "\n", "\\n")
-	expectedMetadata := "'" + formattedData + "\\n'"
+	expectedMetadata := string(protoData)
 
 	if metadata != expectedMetadata {
 		t.Errorf(
@@ -122,7 +111,7 @@
 }
 
 func runTestSpecTest(
-		t *testing.T, errorHandler android.FixtureErrorHandler, bp string,
+	t *testing.T, errorHandler android.FixtureErrorHandler, bp string,
 ) *android.TestResult {
 	return android.GroupFixturePreparers(
 		soongTesting.PrepareForTestWithTestingBuildComponents,
diff --git a/java/testing.go b/java/testing.go
index e883bcb..631d516 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -383,6 +383,7 @@
 	RegisterSystemModulesBuildComponents(ctx)
 	registerSystemserverClasspathBuildComponents(ctx)
 	registerLintBuildComponents(ctx)
+	android.RegisterApexContributionsBuildComponents(ctx)
 }
 
 // gatherRequiredDepsForTest gathers the module definitions used by
@@ -406,6 +407,14 @@
 		"legacy.core.platform.api.stubs",
 		"stable.core.platform.api.stubs",
 
+		"android_stubs_current_exportable",
+		"android_system_stubs_current_exportable",
+		"android_test_stubs_current_exportable",
+		"android_module_lib_stubs_current_exportable",
+		"android_system_server_stubs_current_exportable",
+		"core.current.stubs.exportable",
+		"legacy.core.platform.api.stubs.exportable",
+
 		"kotlin-stdlib",
 		"kotlin-stdlib-jdk7",
 		"kotlin-stdlib-jdk8",
@@ -514,10 +523,11 @@
 
 	for libName, droidstubs := range extraApiLibraryModules {
 		bp += fmt.Sprintf(`
-            java_api_library {
-                name: "%s",
-                api_contributions: ["%s"],
-            }
+		java_api_library {
+			name: "%s",
+			api_contributions: ["%s"],
+			stubs_type: "everything",
+		}
         `, libName, droidstubs.name+".api.contribution")
 	}
 
@@ -570,6 +580,11 @@
 		}
 `
 
+	bp += `
+		all_apex_contributions {
+			name: "all_apex_contributions",
+		}
+`
 	return bp
 }
 
@@ -617,7 +632,7 @@
 func CheckClasspathFragmentProtoContentInfoProvider(t *testing.T, result *android.TestResult, generated bool, contents, outputFilename, installDir string) {
 	t.Helper()
 	p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
-	info := result.ModuleProvider(p, ClasspathFragmentProtoContentInfoProvider).(ClasspathFragmentProtoContentInfo)
+	info, _ := android.SingletonModuleProvider(result, p, ClasspathFragmentProtoContentInfoProvider)
 
 	android.AssertBoolEquals(t, "classpath proto generated", generated, info.ClasspathFragmentProtoGenerated)
 	android.AssertStringEquals(t, "classpath proto contents", contents, info.ClasspathFragmentProtoContents.String())
@@ -637,7 +652,7 @@
 func apexNamePairFromModule(ctx *android.TestContext, module android.Module) string {
 	name := module.Name()
 	var apex string
-	apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.SingletonModuleProvider(ctx, module, android.ApexInfoProvider)
 	if apexInfo.IsForPlatform() {
 		apex = "platform"
 	} else {
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index 8b52d86..78b62d9 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -140,7 +140,7 @@
 
 // linker_config generates protobuf file from json file. This protobuf file will be used from
 // linkerconfig while generating ld.config.txt. Format of this file can be found from
-// https://android.googlesource.com/platform/system/linkerconfig/+/master/README.md
+// https://android.googlesource.com/platform/system/linkerconfig/+/main/README.md
 func LinkerConfigFactory() android.Module {
 	m := &linkerConfig{}
 	m.AddProperties(&m.properties)
diff --git a/linkerconfig/proto/linker_config.proto b/linkerconfig/proto/linker_config.proto
index dccf311..fcbd1c5 100644
--- a/linkerconfig/proto/linker_config.proto
+++ b/linkerconfig/proto/linker_config.proto
@@ -16,7 +16,7 @@
 
 // This format file defines configuration file for linkerconfig. Details on this
 // format can be found from
-// https://android.googlesource.com/platform/system/linkerconfig/+/master/README.md
+// https://android.googlesource.com/platform/system/linkerconfig/+/main/README.md
 
 syntax = "proto3";
 
diff --git a/multitree/api_imports.go b/multitree/api_imports.go
index 07ec7bc..51b9e07 100644
--- a/multitree/api_imports.go
+++ b/multitree/api_imports.go
@@ -64,7 +64,7 @@
 	SharedLibs, HeaderLibs, ApexSharedLibs map[string]string
 }
 
-var ApiImportsProvider = blueprint.NewMutatorProvider(ApiImportInfo{}, "deps")
+var ApiImportsProvider = blueprint.NewMutatorProvider[ApiImportInfo]("deps")
 
 // Store module lists into ApiImportInfo and share it over mutator provider.
 func (imports *ApiImports) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -81,7 +81,7 @@
 	headerLibs := generateNameMapWithSuffix(imports.properties.Header_libs)
 	apexSharedLibs := generateNameMapWithSuffix(imports.properties.Apex_shared_libs)
 
-	ctx.SetProvider(ApiImportsProvider, ApiImportInfo{
+	android.SetProvider(ctx, ApiImportsProvider, ApiImportInfo{
 		SharedLibs:     sharedLibs,
 		HeaderLibs:     headerLibs,
 		ApexSharedLibs: apexSharedLibs,
diff --git a/phony/phony.go b/phony/phony.go
index a8b651a..b8dbd00 100644
--- a/phony/phony.go
+++ b/phony/phony.go
@@ -24,6 +24,7 @@
 
 func init() {
 	android.RegisterModuleType("phony", PhonyFactory)
+	android.RegisterModuleType("phony_rule", PhonyRuleFactory)
 }
 
 type phony struct {
@@ -67,7 +68,48 @@
 				fmt.Fprintln(w, "LOCAL_TARGET_REQUIRED_MODULES :=",
 					strings.Join(p.targetRequiredModuleNames, " "))
 			}
+			// AconfigUpdateAndroidMkData may have added elements to Extra.  Process them here.
+			for _, extra := range data.Extra {
+				extra(w, nil)
+			}
 			fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
 		},
 	}
 }
+
+type PhonyRule struct {
+	android.ModuleBase
+
+	properties PhonyProperties
+}
+
+type PhonyProperties struct {
+	// The Phony_deps is the set of all dependencies for this target,
+	// and it can function similarly to .PHONY in a makefile.
+	// Additionally, dependencies within it can even include genrule.
+	Phony_deps []string
+}
+
+// The phony_rule provides functionality similar to the .PHONY in a makefile.
+// It can create a phony target and include relevant dependencies associated with it.
+func PhonyRuleFactory() android.Module {
+	module := &PhonyRule{}
+	android.InitAndroidModule(module)
+	module.AddProperties(&module.properties)
+	return module
+}
+
+func (p *PhonyRule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+}
+
+func (p *PhonyRule) AndroidMk() android.AndroidMkData {
+	return android.AndroidMkData{
+		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+			if len(p.properties.Phony_deps) > 0 {
+				depModulesStr := strings.Join(p.properties.Phony_deps, " ")
+				fmt.Fprintln(w, ".PHONY:", name)
+				fmt.Fprintln(w, name, ":", depModulesStr)
+			}
+		},
+	}
+}
diff --git a/python/binary.go b/python/binary.go
index 85084a4..d6750c6 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -71,6 +71,9 @@
 	installedDest android.Path
 
 	androidMkSharedLibs []string
+
+	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
+	mergedAconfigFiles map[string]android.Paths
 }
 
 var _ android.AndroidMkEntriesProvider = (*PythonBinaryModule)(nil)
@@ -103,6 +106,7 @@
 	p.buildBinary(ctx)
 	p.installedDest = ctx.InstallFile(installDir(ctx, "bin", "", ""),
 		p.installSource.Base(), p.installSource)
+	android.CollectDependencyAconfigFiles(ctx, &p.mergedAconfigFiles)
 }
 
 func (p *PythonBinaryModule) buildBinary(ctx android.ModuleContext) {
@@ -166,6 +170,7 @@
 			entries.SetString("LOCAL_MODULE_STEM", stem)
 			entries.AddStrings("LOCAL_SHARED_LIBRARIES", p.androidMkSharedLibs...)
 			entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
+			android.SetAconfigFileMkEntries(&p.ModuleBase, entries, p.mergedAconfigFiles)
 		})
 
 	return []android.AndroidMkEntries{entries}
diff --git a/python/python.go b/python/python.go
index 79b1515..d3cbd76 100644
--- a/python/python.go
+++ b/python/python.go
@@ -420,7 +420,7 @@
 // GenerateAndroidBuildActions performs build actions common to all Python modules
 func (p *PythonLibraryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	expandedSrcs := android.PathsForModuleSrcExcludes(ctx, p.properties.Srcs, p.properties.Exclude_srcs)
-	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: expandedSrcs.Strings()})
+	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: expandedSrcs.Strings()})
 
 	// expand data files from "data" property.
 	expandedData := android.PathsForModuleSrc(ctx, p.properties.Data)
diff --git a/python/scripts/precompile_python.py b/python/scripts/precompile_python.py
index e12e7d2..aa1a5df 100644
--- a/python/scripts/precompile_python.py
+++ b/python/scripts/precompile_python.py
@@ -16,6 +16,7 @@
 import argparse
 import py_compile
 import os
+import sys
 import shutil
 import tempfile
 import zipfile
@@ -23,22 +24,34 @@
 # This file needs to support both python 2 and 3.
 
 
-def process_one_file(name, inf, outzip):
-    if not name.endswith('.py'):
-        outzip.writestr(name, inf.read())
+def process_one_file(name, infile, outzip):
+    # Create a ZipInfo instance with a fixed date to ensure a deterministic output.
+    # Date was chosen to be the same as
+    # https://cs.android.com/android/platform/superproject/main/+/main:build/soong/jar/jar.go;l=36;drc=2863e4535eb65e15f955dc8ed48fa99b1d2a1db5
+    info = zipfile.ZipInfo(filename=name, date_time=(2008, 1, 1, 0, 0, 0))
+
+    if not info.filename.endswith('.py'):
+        outzip.writestr(info, infile.read())
         return
 
     # Unfortunately py_compile requires the input/output files to be written
     # out to disk.
     with tempfile.NamedTemporaryFile(prefix="Soong_precompile_", delete=False) as tmp:
-        shutil.copyfileobj(inf, tmp)
+        shutil.copyfileobj(infile, tmp)
         in_name = tmp.name
     with tempfile.NamedTemporaryFile(prefix="Soong_precompile_", delete=False) as tmp:
         out_name = tmp.name
     try:
-        py_compile.compile(in_name, out_name, name, doraise=True)
+        # Ensure a deterministic .pyc output by using the hash rather than the timestamp.
+        # Only works on Python 3.7+
+        # See https://docs.python.org/3/library/py_compile.html#py_compile.PycInvalidationMode
+        if sys.version_info >= (3, 7):
+            py_compile.compile(in_name, out_name, info.filename, doraise=True, invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH)
+        else:
+            py_compile.compile(in_name, out_name, info.filename, doraise=True)
         with open(out_name, 'rb') as f:
-            outzip.writestr(name + 'c', f.read())
+            info.filename = info.filename + 'c'
+            outzip.writestr(info, f.read())
     finally:
         os.remove(in_name)
         os.remove(out_name)
diff --git a/python/test.go b/python/test.go
index f15a8fc..826f353 100644
--- a/python/test.go
+++ b/python/test.go
@@ -149,6 +149,7 @@
 	// just use buildBinary() so that the binary is not installed into the location
 	// it would be for regular binaries.
 	p.PythonLibraryModule.GenerateAndroidBuildActions(ctx)
+	android.CollectDependencyAconfigFiles(ctx, &p.mergedAconfigFiles)
 	p.buildBinary(ctx)
 
 	var configs []tradefed.Option
@@ -157,35 +158,25 @@
 	}
 
 	runner := proptools.StringDefault(p.testProperties.Test_options.Runner, "tradefed")
-	if runner == "tradefed" {
-		p.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
-			TestConfigProp:          p.testProperties.Test_config,
-			TestConfigTemplateProp:  p.testProperties.Test_config_template,
-			TestSuites:              p.binaryProperties.Test_suites,
-			OptionsForAutogenerated: configs,
-			AutoGenConfig:           p.binaryProperties.Auto_gen_config,
-			DeviceTemplate:          "${PythonBinaryHostTestConfigTemplate}",
-			HostTemplate:            "${PythonBinaryHostTestConfigTemplate}",
-		})
-	} else if runner == "mobly" {
-		if p.testProperties.Test_config != nil || p.testProperties.Test_config_template != nil || p.binaryProperties.Auto_gen_config != nil {
-			panic(fmt.Errorf("cannot set test_config, test_config_template or auto_gen_config for mobly test"))
+	template := "${PythonBinaryHostTestConfigTemplate}"
+	if runner == "mobly" {
+		// Add tag to enable Atest mobly runner
+		if !android.InList("mobly", p.testProperties.Test_options.Tags) {
+			p.testProperties.Test_options.Tags = append(p.testProperties.Test_options.Tags, "mobly")
 		}
-
-		for _, testSuite := range p.binaryProperties.Test_suites {
-			if testSuite == "cts" {
-				configs = append(configs, tradefed.Option{Name: "test-suite-tag", Value: "cts"})
-				break
-			}
-		}
-		p.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
-			OptionsForAutogenerated: configs,
-			DeviceTemplate:          "${PythonBinaryHostMoblyTestConfigTemplate}",
-			HostTemplate:            "${PythonBinaryHostMoblyTestConfigTemplate}",
-		})
-	} else {
+		template = "${PythonBinaryHostMoblyTestConfigTemplate}"
+	} else if runner != "tradefed" {
 		panic(fmt.Errorf("unknown python test runner '%s', should be 'tradefed' or 'mobly'", runner))
 	}
+	p.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
+		TestConfigProp:          p.testProperties.Test_config,
+		TestConfigTemplateProp:  p.testProperties.Test_config_template,
+		TestSuites:              p.binaryProperties.Test_suites,
+		OptionsForAutogenerated: configs,
+		AutoGenConfig:           p.binaryProperties.Auto_gen_config,
+		DeviceTemplate:          template,
+		HostTemplate:            template,
+	})
 
 	for _, dataSrcPath := range android.PathsForModuleSrc(ctx, p.testProperties.Data) {
 		p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath})
@@ -208,7 +199,7 @@
 	installedData := ctx.InstallTestData(installDir, p.data)
 	p.installedDest = ctx.InstallFile(installDir, p.installSource.Base(), p.installSource, installedData...)
 
-	ctx.SetProvider(testing.TestModuleProviderKey, testing.TestModuleProviderData{})
+	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 }
 
 func (p *PythonTestModule) AndroidMkEntries() []android.AndroidMkEntries {
@@ -227,7 +218,14 @@
 				entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
 			}
 
+			// ATS 2.0 is the test harness for mobly tests and the test config is for ATS 2.0.
+			// Add "v2" suffix to test config name to distinguish it from the config for TF.
+			if proptools.String(p.testProperties.Test_options.Runner) == "mobly" {
+				entries.SetString("LOCAL_TEST_CONFIG_SUFFIX", "v2")
+			}
+
 			entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
+			android.SetAconfigFileMkEntries(&p.ModuleBase, entries, p.mergedAconfigFiles)
 
 			p.testProperties.Test_options.SetAndroidMkEntries(entries)
 		})
diff --git a/rust/afdo.go b/rust/afdo.go
index 3534ee6..6116c5e 100644
--- a/rust/afdo.go
+++ b/rust/afdo.go
@@ -44,14 +44,14 @@
 		if err != nil {
 			ctx.ModuleErrorf("%s", err.Error())
 		}
-		if fdoProfileName != nil {
+		if fdoProfileName != "" {
 			actx.AddFarVariationDependencies(
 				[]blueprint.Variation{
 					{Mutator: "arch", Variation: actx.Target().ArchVariation()},
 					{Mutator: "os", Variation: "android"},
 				},
 				cc.FdoProfileTag,
-				[]string{*fdoProfileName}...,
+				[]string{fdoProfileName}...,
 			)
 		}
 	}
@@ -67,8 +67,7 @@
 	}
 
 	ctx.VisitDirectDepsWithTag(cc.FdoProfileTag, func(m android.Module) {
-		if ctx.OtherModuleHasProvider(m, cc.FdoProfileProvider) {
-			info := ctx.OtherModuleProvider(m, cc.FdoProfileProvider).(cc.FdoProfileInfo)
+		if info, ok := android.OtherModuleProvider(ctx, m, cc.FdoProfileProvider); ok {
 			path := info.Path
 			profileUseFlag := fmt.Sprintf(afdoFlagFormat, path.String())
 			flags.RustFlags = append(flags.RustFlags, profileUseFlag)
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 733ffc5..e0cb3ce 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -63,11 +63,12 @@
 				entries.AddStrings("LOCAL_SHARED_LIBRARIES", mod.transitiveAndroidMkSharedLibs.ToList()...)
 				entries.AddStrings("LOCAL_STATIC_LIBRARIES", mod.Properties.AndroidMkStaticLibs...)
 				entries.AddStrings("LOCAL_SOONG_LINK_TYPE", mod.makeLinkType)
-				if mod.UseVndk() {
-					entries.SetBool("LOCAL_USE_VNDK", true)
+				if mod.InVendor() {
+					entries.SetBool("LOCAL_IN_VENDOR", true)
+				} else if mod.InProduct() {
+					entries.SetBool("LOCAL_IN_PRODUCT", true)
 				}
-				// TODO(b/311155208): The container here should be system.
-				entries.SetPaths("LOCAL_ACONFIG_FILES", mod.mergedAconfigFiles[""])
+				android.SetAconfigFileMkEntries(mod.AndroidModuleBase(), entries, mod.mergedAconfigFiles)
 			},
 		},
 	}
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 1cc1574..454dd87 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -29,7 +29,7 @@
 	defaultBindgenFlags = []string{""}
 
 	// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
-	bindgenClangVersion = "clang-r498229b"
+	bindgenClangVersion = "clang-r510928"
 
 	_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
@@ -170,7 +170,7 @@
 	cflags = append(cflags, strings.ReplaceAll(ccToolchain.Cflags(), "${config.", "${cc_config."))
 	cflags = append(cflags, strings.ReplaceAll(ccToolchain.ToolchainCflags(), "${config.", "${cc_config."))
 
-	if ctx.RustModule().UseVndk() {
+	if ctx.RustModule().InVendorOrProduct() {
 		cflags = append(cflags, "-D__ANDROID_VNDK__")
 		if ctx.RustModule().InVendor() {
 			cflags = append(cflags, "-D__ANDROID_VENDOR__")
@@ -263,10 +263,9 @@
 	// clang: error: '-x c' after last input file has no effect [-Werror,-Wunused-command-line-argument]
 	cflags = append(cflags, "-Wno-unused-command-line-argument")
 
-	// LLVM_NEXT may contain flags that bindgen doesn't recognise. Turn off unknown flags warning.
-	if ctx.Config().IsEnvTrue("LLVM_NEXT") {
-		cflags = append(cflags, "-Wno-unknown-warning-option")
-	}
+	// The Clang version used by CXX can be newer than the one used by Bindgen, and uses warning related flags that
+	// it cannot recognize. Turn off unknown warning flags warning.
+	cflags = append(cflags, "-Wno-unknown-warning-option")
 
 	outputFile := android.PathForModuleOut(ctx, b.BaseSourceProvider.getStem(ctx)+".rs")
 
diff --git a/rust/compiler.go b/rust/compiler.go
index a8c5473..c1bdbeb 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -332,7 +332,7 @@
 }
 
 func (compiler *baseCompiler) cfgFlags(ctx ModuleContext, flags Flags) Flags {
-	if ctx.RustModule().UseVndk() {
+	if ctx.RustModule().InVendorOrProduct() {
 		compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_vndk")
 		if ctx.RustModule().InVendor() {
 			compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_vendor")
@@ -520,7 +520,7 @@
 		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
 	}
 
-	if compiler.location == InstallInData && ctx.RustModule().UseVndk() {
+	if compiler.location == InstallInData && ctx.RustModule().InVendorOrProduct() {
 		if ctx.RustModule().InProduct() {
 			dir = filepath.Join(dir, "product")
 		} else if ctx.RustModule().InVendor() {
diff --git a/rust/config/global.go b/rust/config/global.go
index 377ae58..418eca4 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -25,7 +25,7 @@
 	pctx         = android.NewPackageContext("android/soong/rust/config")
 	ExportedVars = android.NewExportedVariables(pctx)
 
-	RustDefaultVersion = "1.73.0"
+	RustDefaultVersion = "1.75.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
diff --git a/rust/config/lints.go b/rust/config/lints.go
index 9322981..735aa16 100644
--- a/rust/config/lints.go
+++ b/rust/config/lints.go
@@ -44,6 +44,7 @@
 	// Default Rust lints that applies to Google-authored modules.
 	defaultRustcLints = []string{
 		"-A deprecated",
+		"-A unknown_lints",
 		"-D missing-docs",
 		"-D warnings",
 		"-D unsafe_op_in_unsafe_fn",
@@ -52,7 +53,10 @@
 	// It should be assumed that any warning lint will be promoted to a
 	// deny.
 	defaultClippyLints = []string{
+		// Let people hack in peace. ;)
+		"-A clippy::disallowed_names",
 		"-A clippy::type-complexity",
+		"-A clippy::unnecessary_fallible_conversions",
 		"-A clippy::unnecessary-wraps",
 		"-A clippy::unusual-byte-groupings",
 		"-A clippy::upper-case-acronyms",
diff --git a/rust/config/x86_64_device.go b/rust/config/x86_64_device.go
index c797eef..49f7c77 100644
--- a/rust/config/x86_64_device.go
+++ b/rust/config/x86_64_device.go
@@ -28,15 +28,16 @@
 	x86_64LinkFlags            = []string{}
 
 	x86_64ArchVariantRustFlags = map[string][]string{
-		"":              []string{},
-		"broadwell":     []string{"-C target-cpu=broadwell"},
-		"goldmont":      []string{"-C target-cpu=goldmont"},
-		"goldmont-plus": []string{"-C target-cpu=goldmont-plus"},
-		"haswell":       []string{"-C target-cpu=haswell"},
-		"ivybridge":     []string{"-C target-cpu=ivybridge"},
-		"sandybridge":   []string{"-C target-cpu=sandybridge"},
-		"silvermont":    []string{"-C target-cpu=silvermont"},
-		"skylake":       []string{"-C target-cpu=skylake"},
+		"":                            []string{},
+		"broadwell":                   []string{"-C target-cpu=broadwell"},
+		"goldmont":                    []string{"-C target-cpu=goldmont"},
+		"goldmont-plus":               []string{"-C target-cpu=goldmont-plus"},
+		"goldmont-without-sha-xsaves": []string{"-C target-cpu=goldmont", "-C target-feature=-sha,-xsaves"},
+		"haswell":                     []string{"-C target-cpu=haswell"},
+		"ivybridge":                   []string{"-C target-cpu=ivybridge"},
+		"sandybridge":                 []string{"-C target-cpu=sandybridge"},
+		"silvermont":                  []string{"-C target-cpu=silvermont"},
+		"skylake":                     []string{"-C target-cpu=skylake"},
 		//TODO: Add target-cpu=stoneyridge when rustc supports it.
 		"stoneyridge": []string{""},
 		"tremont":     []string{"-C target-cpu=tremont"},
diff --git a/rust/config/x86_device.go b/rust/config/x86_device.go
index 822f281..2a57e73 100644
--- a/rust/config/x86_device.go
+++ b/rust/config/x86_device.go
@@ -26,16 +26,17 @@
 	x86LinkFlags            = []string{}
 
 	x86ArchVariantRustFlags = map[string][]string{
-		"":              []string{},
-		"atom":          []string{"-C target-cpu=atom"},
-		"broadwell":     []string{"-C target-cpu=broadwell"},
-		"goldmont":      []string{"-C target-cpu=goldmont"},
-		"goldmont-plus": []string{"-C target-cpu=goldmont-plus"},
-		"haswell":       []string{"-C target-cpu=haswell"},
-		"ivybridge":     []string{"-C target-cpu=ivybridge"},
-		"sandybridge":   []string{"-C target-cpu=sandybridge"},
-		"silvermont":    []string{"-C target-cpu=silvermont"},
-		"skylake":       []string{"-C target-cpu=skylake"},
+		"":                            []string{},
+		"atom":                        []string{"-C target-cpu=atom"},
+		"broadwell":                   []string{"-C target-cpu=broadwell"},
+		"goldmont":                    []string{"-C target-cpu=goldmont"},
+		"goldmont-plus":               []string{"-C target-cpu=goldmont-plus"},
+		"goldmont-without-sha-xsaves": []string{"-C target-cpu=goldmont", "-C target-feature=-sha,-xsaves"},
+		"haswell":                     []string{"-C target-cpu=haswell"},
+		"ivybridge":                   []string{"-C target-cpu=ivybridge"},
+		"sandybridge":                 []string{"-C target-cpu=sandybridge"},
+		"silvermont":                  []string{"-C target-cpu=silvermont"},
+		"skylake":                     []string{"-C target-cpu=skylake"},
 		//TODO: Add target-cpu=stoneyridge when rustc supports it.
 		"stoneyridge": []string{""},
 		"tremont":     []string{"-C target-cpu=tremont"},
diff --git a/rust/fuzz.go b/rust/fuzz.go
index bacfa2d..1770d2e 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -142,12 +142,12 @@
 
 		fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
 			cc.SharedLibraryInstallLocation(
-				install, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
+				install, ctx.Host(), ctx.InstallInVendor(), installBase, ctx.Arch().ArchType.String()))
 
 		// Also add the dependency on the shared library symbols dir.
 		if !ctx.Host() {
 			fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
-				cc.SharedLibrarySymbolsInstallLocation(install, installBase, ctx.Arch().ArchType.String()))
+				cc.SharedLibrarySymbolsInstallLocation(install, ctx.InstallInVendor(), installBase, ctx.Arch().ArchType.String()))
 		}
 	}
 
diff --git a/rust/image.go b/rust/image.go
index c2e250c..530c56e 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -117,20 +117,16 @@
 	return false
 }
 
-func (ctx *moduleContext) SocSpecific() bool {
+func (mod *Module) InstallInVendor() bool {
 	// Additionally check if this module is inVendor() that means it is a "vendor" variant of a
 	// module. As well as SoC specific modules, vendor variants must be installed to /vendor
 	// unless they have "odm_available: true".
-	return ctx.ModuleContext.SocSpecific() || (ctx.RustModule().InVendor() && !ctx.RustModule().VendorVariantToOdm())
+	return mod.InVendor() && !mod.VendorVariantToOdm()
 }
 
-func (ctx *moduleContext) DeviceSpecific() bool {
+func (mod *Module) InstallInOdm() bool {
 	// Some vendor variants want to be installed to /odm by setting "odm_available: true".
-	return ctx.ModuleContext.DeviceSpecific() || (ctx.RustModule().InVendor() && ctx.RustModule().VendorVariantToOdm())
-}
-
-func (ctx *moduleContext) SystemExtSpecific() bool {
-	return ctx.ModuleContext.SystemExtSpecific()
+	return mod.InVendor() && mod.VendorVariantToOdm()
 }
 
 // Returns true when this module creates a vendor variant and wants to install the vendor variant
@@ -188,12 +184,17 @@
 }
 
 func (mod *Module) InProduct() bool {
-	return mod.Properties.ImageVariationPrefix == cc.ProductVariationPrefix
+	return mod.Properties.ImageVariation == cc.ProductVariation
 }
 
 // Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
 func (mod *Module) InVendor() bool {
-	return mod.Properties.ImageVariationPrefix == cc.VendorVariationPrefix
+	return mod.Properties.ImageVariation == cc.VendorVariation
+}
+
+// Returns true if the module is "vendor" or "product" variant.
+func (mod *Module) InVendorOrProduct() bool {
+	return mod.InVendor() || mod.InProduct()
 }
 
 func (mod *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
@@ -202,9 +203,11 @@
 		m.MakeAsPlatform()
 	} else if variant == android.RecoveryVariation {
 		m.MakeAsPlatform()
-	} else if strings.HasPrefix(variant, cc.VendorVariationPrefix) {
-		m.Properties.ImageVariationPrefix = cc.VendorVariationPrefix
-		m.Properties.VndkVersion = strings.TrimPrefix(variant, cc.VendorVariationPrefix)
+	} else if strings.HasPrefix(variant, cc.VendorVariation) {
+		m.Properties.ImageVariation = cc.VendorVariation
+		if strings.HasPrefix(variant, cc.VendorVariationPrefix) {
+			m.Properties.VndkVersion = strings.TrimPrefix(variant, cc.VendorVariationPrefix)
+		}
 
 		// Makefile shouldn't know vendor modules other than BOARD_VNDK_VERSION.
 		// Hide other vendor variants to avoid collision.
@@ -213,9 +216,11 @@
 			m.Properties.HideFromMake = true
 			m.HideFromMake()
 		}
-	} else if strings.HasPrefix(variant, cc.ProductVariationPrefix) {
-		m.Properties.ImageVariationPrefix = cc.ProductVariationPrefix
-		m.Properties.VndkVersion = strings.TrimPrefix(variant, cc.ProductVariationPrefix)
+	} else if strings.HasPrefix(variant, cc.ProductVariation) {
+		m.Properties.ImageVariation = cc.ProductVariation
+		if strings.HasPrefix(variant, cc.ProductVariationPrefix) {
+			m.Properties.VndkVersion = strings.TrimPrefix(variant, cc.ProductVariationPrefix)
+		}
 	}
 }
 
diff --git a/rust/library.go b/rust/library.go
index 199ffbb..7f004fc 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -547,7 +547,7 @@
 	}
 
 	if library.static() || library.shared() {
-		ctx.SetProvider(cc.FlagExporterInfoProvider, cc.FlagExporterInfo{
+		android.SetProvider(ctx, cc.FlagExporterInfoProvider, cc.FlagExporterInfo{
 			IncludeDirs: library.includeDirs,
 		})
 	}
@@ -559,7 +559,7 @@
 		library.tocFile = android.OptionalPathForPath(tocFile)
 		cc.TransformSharedObjectToToc(ctx, outputFile, tocFile)
 
-		ctx.SetProvider(cc.SharedLibraryInfoProvider, cc.SharedLibraryInfo{
+		android.SetProvider(ctx, cc.SharedLibraryInfoProvider, cc.SharedLibraryInfo{
 			TableOfContents: android.OptionalPathForPath(tocFile),
 			SharedLibrary:   outputFile,
 			Target:          ctx.Target(),
@@ -568,7 +568,7 @@
 
 	if library.static() {
 		depSet := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(outputFile).Build()
-		ctx.SetProvider(cc.StaticLibraryInfoProvider, cc.StaticLibraryInfo{
+		android.SetProvider(ctx, cc.StaticLibraryInfoProvider, cc.StaticLibraryInfo{
 			StaticLibrary: outputFile,
 
 			TransitiveStaticLibrariesForOrdering: depSet,
diff --git a/rust/protobuf.go b/rust/protobuf.go
index c80e5f4..0b26b80 100644
--- a/rust/protobuf.go
+++ b/rust/protobuf.go
@@ -54,11 +54,6 @@
 	// List of libraries which export include paths required for this module
 	Header_libs []string `android:"arch_variant,variant_prepend"`
 
-	// Use protobuf version 3.x. This will be deleted once we migrate all current users
-	// of protobuf off of 2.x.
-	// ludovicb@: DEPRECATED, to be removed
-	Use_protobuf3 *bool
-
 	// List of exported include paths containing proto files for dependent rust_protobuf modules.
 	Exported_include_dirs []string
 }
@@ -178,7 +173,7 @@
 	// stemFile must be first here as the first path in BaseSourceProvider.OutputFiles is the library entry-point.
 	proto.BaseSourceProvider.OutputFiles = append(android.Paths{stemFile}, outputs.Paths()...)
 
-	ctx.SetProvider(cc.FlagExporterInfoProvider, cc.FlagExporterInfo{
+	android.SetProvider(ctx, cc.FlagExporterInfoProvider, cc.FlagExporterInfo{
 		IncludeDirs: android.PathsForModuleSrc(ctx, proto.Properties.Exported_include_dirs),
 	})
 
diff --git a/rust/protobuf_test.go b/rust/protobuf_test.go
index b375a64..cae071b 100644
--- a/rust/protobuf_test.go
+++ b/rust/protobuf_test.go
@@ -28,7 +28,6 @@
 			protos: ["buf.proto", "proto.proto"],
 			crate_name: "rust_proto",
 			source_stem: "buf",
-            use_protobuf3: true,
 			shared_libs: ["libfoo_shared"],
 			static_libs: ["libfoo_static"],
 		}
@@ -77,7 +76,6 @@
 			protos: ["proto.proto"],
 			crate_name: "rust_proto",
 			source_stem: "proto",
-			use_protobuf3: true,
 			rustlibs: ["librust_exported_proto", "libfoo"],
 		}
 		rust_protobuf {
@@ -85,7 +83,6 @@
 			protos: ["proto.proto"],
 			crate_name: "rust_exported_proto",
 			source_stem: "exported_proto",
-			use_protobuf3: true,
 			exported_include_dirs: ["proto"]
 		}
 		rust_library {
diff --git a/rust/rust.go b/rust/rust.go
index 02ec22d..245ed2e 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -20,10 +20,10 @@
 
 	"android/soong/bloaty"
 	"android/soong/testing"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
-	"android/soong/aconfig"
 	"android/soong/android"
 	"android/soong/cc"
 	cc_config "android/soong/cc/config"
@@ -70,9 +70,9 @@
 	AndroidMkProcMacroLibs []string `blueprint:"mutated"`
 	AndroidMkStaticLibs    []string `blueprint:"mutated"`
 
-	ImageVariationPrefix string `blueprint:"mutated"`
-	VndkVersion          string `blueprint:"mutated"`
-	SubName              string `blueprint:"mutated"`
+	ImageVariation string `blueprint:"mutated"`
+	VndkVersion    string `blueprint:"mutated"`
+	SubName        string `blueprint:"mutated"`
 
 	// SubName is used by CC for tracking image variants / SDK versions. RustSubName is used for Rust-specific
 	// subnaming which shouldn't be visible to CC modules (such as the rlib stdlinkage subname). This should be
@@ -509,7 +509,7 @@
 }
 
 func (flagExporter *flagExporter) setProvider(ctx ModuleContext) {
-	ctx.SetProvider(FlagExporterInfoProvider, FlagExporterInfo{
+	android.SetProvider(ctx, FlagExporterInfoProvider, FlagExporterInfo{
 		LinkDirs:    flagExporter.linkDirs,
 		LinkObjects: flagExporter.linkObjects,
 	})
@@ -527,7 +527,7 @@
 	LinkObjects []string // TODO: this should be android.Paths
 }
 
-var FlagExporterInfoProvider = blueprint.NewProvider(FlagExporterInfo{})
+var FlagExporterInfoProvider = blueprint.NewProvider[FlagExporterInfo]()
 
 func (mod *Module) isCoverageVariant() bool {
 	return mod.coverage.Properties.IsCoverageVariant
@@ -535,7 +535,7 @@
 
 var _ cc.Coverage = (*Module)(nil)
 
-func (mod *Module) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
+func (mod *Module) IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool {
 	return mod.coverage != nil && mod.coverage.Properties.NeedCoverageVariant
 }
 
@@ -747,7 +747,8 @@
 }
 
 func (ctx moduleContext) apexVariationName() string {
-	return ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).ApexVariationName
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	return apexInfo.ApexVariationName
 }
 
 var _ cc.LinkableInterface = (*Module)(nil)
@@ -897,7 +898,7 @@
 		ModuleContext: actx,
 	}
 
-	apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.ModuleProvider(actx, android.ApexInfoProvider)
 	if !apexInfo.IsForPlatform() {
 		mod.hideApexVariantFromMake = true
 	}
@@ -950,7 +951,7 @@
 			sourceLib := sourceMod.(*Module).compiler.(*libraryDecorator)
 			mod.sourceProvider.setOutputFiles(sourceLib.sourceProvider.Srcs())
 		}
-		ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: mod.sourceProvider.Srcs().Strings()})
+		android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: mod.sourceProvider.Srcs().Strings()})
 	}
 
 	if mod.compiler != nil && !mod.compiler.Disabled() {
@@ -978,7 +979,7 @@
 			}
 		}
 
-		apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+		apexInfo, _ := android.ModuleProvider(actx, android.ApexInfoProvider)
 		if !proptools.BoolDefault(mod.Installable(), mod.EverInstallable()) && !mod.ProcMacro() {
 			// If the module has been specifically configure to not be installed then
 			// hide from make as otherwise it will break when running inside make as the
@@ -1003,10 +1004,10 @@
 		ctx.Phony("rust", ctx.RustModule().OutputFile().Path())
 	}
 	if mod.testModule {
-		ctx.SetProvider(testing.TestModuleProviderKey, testing.TestModuleProviderData{})
+		android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 	}
 
-	aconfig.CollectDependencyAconfigFiles(ctx, &mod.mergedAconfigFiles)
+	android.CollectDependencyAconfigFiles(ctx, &mod.mergedAconfigFiles)
 }
 
 func (mod *Module) deps(ctx DepsContext) Deps {
@@ -1148,7 +1149,7 @@
 
 	// For the dependency from platform to apex, use the latest stubs
 	mod.apexSdkVersion = android.FutureApiLevel
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	if !apexInfo.IsForPlatform() {
 		mod.apexSdkVersion = apexInfo.MinSdkVersion
 	}
@@ -1167,7 +1168,7 @@
 
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		if dep.Name() == "api_imports" {
-			apiImportInfo = ctx.OtherModuleProvider(dep, multitree.ApiImportsProvider).(multitree.ApiImportInfo)
+			apiImportInfo, _ = android.OtherModuleProvider(ctx, dep, multitree.ApiImportsProvider)
 			hasApiImportInfo = true
 		}
 	})
@@ -1278,7 +1279,7 @@
 
 			//Append the dependencies exportedDirs, except for proc-macros which target a different arch/OS
 			if depTag != procMacroDepTag {
-				exportedInfo := ctx.OtherModuleProvider(dep, FlagExporterInfoProvider).(FlagExporterInfo)
+				exportedInfo, _ := android.OtherModuleProvider(ctx, dep, FlagExporterInfoProvider)
 				depPaths.linkDirs = append(depPaths.linkDirs, exportedInfo.LinkDirs...)
 				depPaths.depFlags = append(depPaths.depFlags, exportedInfo.Flags...)
 				depPaths.linkObjects = append(depPaths.linkObjects, exportedInfo.LinkObjects...)
@@ -1294,7 +1295,7 @@
 			if depTag == sourceDepTag {
 				if _, ok := mod.sourceProvider.(*protobufDecorator); ok && mod.Source() {
 					if _, ok := rustDep.sourceProvider.(*protobufDecorator); ok {
-						exportedInfo := ctx.OtherModuleProvider(dep, cc.FlagExporterInfoProvider).(cc.FlagExporterInfo)
+						exportedInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider)
 						depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
 					}
 				}
@@ -1347,7 +1348,7 @@
 				depPaths.linkObjects = append(depPaths.linkObjects, linkObject.String())
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
 
-				exportedInfo := ctx.OtherModuleProvider(dep, cc.FlagExporterInfoProvider).(cc.FlagExporterInfo)
+				exportedInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider)
 				depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
 				depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
 				depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
@@ -1391,7 +1392,7 @@
 				directAndroidMkSharedLibs = append(directAndroidMkSharedLibs, makeLibName)
 				exportDep = true
 			case cc.IsHeaderDepTag(depTag):
-				exportedInfo := ctx.OtherModuleProvider(dep, cc.FlagExporterInfoProvider).(cc.FlagExporterInfo)
+				exportedInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider)
 				depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
 				depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
 				depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
diff --git a/rust/sanitize.go b/rust/sanitize.go
index 0b10435..3c08cd8 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -56,6 +56,8 @@
 }
 
 var fuzzerFlags = []string{
+	"-Z external-clangrt=true",
+
 	"-C passes='sancov-module'",
 
 	"--cfg fuzzing",
@@ -73,11 +75,13 @@
 }
 
 var asanFlags = []string{
+	"-Z external-clangrt=true",
 	"-Z sanitizer=address",
 }
 
 // See cc/sanitize.go's hwasanGlobalOptions for global hwasan options.
 var hwasanFlags = []string{
+	"-Z external-clangrt=true",
 	"-Z sanitizer=hwaddress",
 	"-C target-feature=+tagged-globals",
 
@@ -203,11 +207,6 @@
 		s.Address = nil
 	}
 
-	// TODO: Remove once b/304507701 is resolved
-	if Bool(s.Address) && ctx.Host() {
-		s.Address = nil
-	}
-
 	// Memtag_heap is only implemented on AArch64.
 	if ctx.Arch().ArchType != android.Arm64 || !ctx.Os().Bionic() {
 		s.Memtag_heap = nil
@@ -270,7 +269,7 @@
 			}
 			// If we're using snapshots, redirect to snapshot whenever possible
 			// TODO(b/178470649): clean manual snapshot redirections
-			snapshot := mctx.Provider(cc.SnapshotInfoProvider).(cc.SnapshotInfo)
+			snapshot, _ := android.ModuleProvider(mctx, cc.SnapshotInfoProvider)
 			if lib, ok := snapshot.StaticLibs[noteDep]; ok {
 				noteDep = lib
 			}
diff --git a/rust/test.go b/rust/test.go
index 6619af6..2583893 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -117,7 +117,7 @@
 
 func (test *testDecorator) install(ctx ModuleContext) {
 	testInstallBase := "/data/local/tests/unrestricted"
-	if ctx.RustModule().InVendor() || ctx.RustModule().UseVndk() {
+	if ctx.RustModule().InVendorOrProduct() {
 		testInstallBase = "/data/local/tests/vendor"
 	}
 
diff --git a/rust/testing.go b/rust/testing.go
index 0b34c97..d9cacdc 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -75,6 +75,7 @@
 			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
 			min_sdk_version: "29",
 			vendor_available: true,
+			host_supported: true,
 			recovery_available: true,
 			llndk: {
 				symbol_file: "liblog.map.txt",
diff --git a/rust/vendor_snapshot_test.go b/rust/vendor_snapshot_test.go
index 4f45799..7ebe66b 100644
--- a/rust/vendor_snapshot_test.go
+++ b/rust/vendor_snapshot_test.go
@@ -553,6 +553,7 @@
 		vendor: true,
 		nocrt: true,
 		no_libcrt: true,
+		no_crt_pad_segment: true,
 		stl: "none",
 		system_shared_libs: [],
 	}
@@ -857,6 +858,7 @@
 		target_arch: "arm64",
 		compile_multilib: "32",
 		vendor: true,
+		no_crt_pad_segment: true,
 		arch: {
 			arm: {
 				src: "lib32.so",
@@ -870,6 +872,7 @@
 		target_arch: "arm64",
 		compile_multilib: "64",
 		vendor: true,
+		no_crt_pad_segment: true,
 		arch: {
 			arm64: {
 				src: "lib64.so",
@@ -882,6 +885,7 @@
 		target_arch: "arm64",
 		compile_multilib: "64",
 		vendor: true,
+		no_crt_pad_segment: true,
 		arch: {
 			arm64: {
 				src: "liblog.so",
@@ -913,6 +917,7 @@
 		target_arch: "arm64",
 		compile_multilib: "both",
 		vendor: true,
+		no_crt_pad_segment: true,
 		arch: {
 			arm64: {
 				src: "libvendor_available.so",
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 97f6ab4..e2fd59f 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -143,6 +143,39 @@
 }
 
 python_library_host {
+    name: "uffd_gc_utils",
+    srcs: [
+        "uffd_gc_utils.py",
+    ],
+    visibility: [
+        "//build/make/tools:__subpackages__",
+    ],
+}
+
+python_test_host {
+    name: "uffd_gc_utils_test",
+    main: "uffd_gc_utils_test.py",
+    srcs: [
+        "uffd_gc_utils_test.py",
+    ],
+    libs: [
+        "uffd_gc_utils",
+    ],
+    test_suites: ["general-tests"],
+}
+
+python_binary_host {
+    name: "construct_uffd_gc_flag",
+    main: "construct_uffd_gc_flag.py",
+    srcs: [
+        "construct_uffd_gc_flag.py",
+    ],
+    libs: [
+        "uffd_gc_utils",
+    ],
+}
+
+python_library_host {
     name: "ninja_rsp",
     srcs: ["ninja_rsp.py"],
 }
@@ -254,3 +287,8 @@
         "modify_permissions_allowlist.py",
     ],
 }
+
+sh_binary_host {
+    name: "keep-flagged-apis",
+    src: "keep-flagged-apis.sh",
+}
diff --git a/scripts/construct_uffd_gc_flag.py b/scripts/construct_uffd_gc_flag.py
new file mode 100644
index 0000000..f437961
--- /dev/null
+++ b/scripts/construct_uffd_gc_flag.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2024 The Android Open Source Project
+#
+# 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.
+#
+"""A tool for constructing UFFD GC flag."""
+
+import argparse
+import os
+
+from uffd_gc_utils import should_enable_uffd_gc
+
+
+def parse_args():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('kernel_version_file')
+  parser.add_argument('output')
+  return parser.parse_args()
+
+def main():
+  args = parse_args()
+  enable_uffd_gc = should_enable_uffd_gc(args.kernel_version_file)
+  flag = '--runtime-arg -Xgc:CMC' if enable_uffd_gc else ''
+  # Prevent the file's mtime from being changed if the contents don't change.
+  # This avoids unnecessary dexpreopt reruns.
+  if os.path.isfile(args.output):
+    with open(args.output, 'r') as f:
+      if f.read() == flag:
+        return
+  with open(args.output, 'w') as f:
+    f.write(flag)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/scripts/keep-flagged-apis.sh b/scripts/keep-flagged-apis.sh
new file mode 100755
index 0000000..9c48fdb
--- /dev/null
+++ b/scripts/keep-flagged-apis.sh
@@ -0,0 +1,46 @@
+#!/bin/bash -e
+#
+# Copyright 2023 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.
+
+# Convert a list of flags in the input file to a list of metalava options
+# that will keep the APIs for those flags will hiding all other flagged
+# APIs.
+
+FLAGS="$1"
+
+FLAGGED="android.annotation.FlaggedApi"
+
+# Convert the list of feature flags in the input file to Metalava options
+# of the form `--revert-annotation !android.annotation.FlaggedApi("<flag>")`
+# to prevent the annotated APIs from being hidden, i.e. include the annotated
+# APIs in the SDK snapshots. This also preserves the line comments, they will
+# be ignored by Metalava but might be useful when debugging.
+while read -r line; do
+  key=$(echo "$line" | cut -d= -f1)
+  value=$(echo "$line" | cut -d= -f2)
+
+  # Skip if value is not true and line does not start with '#'
+  if [[ ( $value != "true" ) && ( $line =~ ^[^#] )]]; then
+    continue
+  fi
+
+  # Escape and quote the key for sed
+  escaped_key=$(echo "$key" | sed "s/'/\\\'/g; s/ /\\ /g")
+
+  echo $line | sed "s|^[^#].*$|--revert-annotation '!$FLAGGED(\"$escaped_key\")'|"
+done < "$FLAGS"
+
+# Revert all flagged APIs, unless listed above.
+echo "--revert-annotation $FLAGGED"
diff --git a/scripts/lint_project_xml.py b/scripts/lint_project_xml.py
index 3b0158d..c40b07d 100755
--- a/scripts/lint_project_xml.py
+++ b/scripts/lint_project_xml.py
@@ -18,6 +18,7 @@
 """This file generates project.xml and lint.xml files used to drive the Android Lint CLI tool."""
 
 import argparse
+import sys
 from xml.dom import minidom
 
 from ninja_rsp import NinjaRspFileReader
@@ -159,8 +160,8 @@
   if args.baseline_path:
     baseline = minidom.parse(args.baseline_path)
     disallowed_issues = check_baseline_for_disallowed_issues(baseline, args.disallowed_issues)
-    if bool(disallowed_issues):
-      raise RuntimeError('disallowed issues %s found in lint baseline file %s for module %s'
+    if disallowed_issues:
+      sys.exit('disallowed issues %s found in lint baseline file %s for module %s'
                          % (disallowed_issues, args.baseline_path, args.name))
 
   if args.project_out:
diff --git a/scripts/run-soong-tests-with-go-tools.sh b/scripts/run-soong-tests-with-go-tools.sh
new file mode 100755
index 0000000..93c622e
--- /dev/null
+++ b/scripts/run-soong-tests-with-go-tools.sh
@@ -0,0 +1,79 @@
+#!/bin/bash -ex
+
+: "${OUT_DIR:?Must set OUT_DIR}"
+TOP=$(cd $(dirname $0)/../../..; pwd)
+cd ${TOP}
+
+UNAME="$(uname)"
+case "$UNAME" in
+Linux)
+    OS='linux'
+    ;;
+Darwin)
+    OS='darwin'
+    ;;
+*)
+    exit 1
+    ;;
+esac
+
+# Verify that go test and go build work on all the same projects that are parsed by
+# build/soong/build_kzip.bash
+declare -ar go_modules=(build/blueprint build/soong
+      build/make/tools/canoninja build/make/tools/compliance build/make/tools/rbcrun)
+export GOROOT=${TOP}/prebuilts/go/${OS}-x86
+export GOENV=off
+export GOPROXY=off
+abs_out_dir=$(cd ${OUT_DIR}; pwd)
+export GOPATH=${abs_out_dir}/gopath
+export GOCACHE=${abs_out_dir}/gocache
+export GOMODCACHE=${abs_out_dir}/gomodcache
+export TMPDIR=${abs_out_dir}/gotemp
+mkdir -p ${TMPDIR}
+${GOROOT}/bin/go env
+
+if [[ ${OS} = linux ]]; then
+    # Building with the race detector enabled uses the host linker, set the
+    # path to use the hermetic one.
+    CLANG_VERSION=$(build/soong/scripts/get_clang_version.py)
+    export CC="${TOP}/prebuilts/clang/host/${OS}-x86/${CLANG_VERSION}/bin/clang"
+    export CXX="${TOP}/prebuilts/clang/host/${OS}-x86/${CLANG_VERSION}/bin/clang++"
+fi
+
+# androidmk_test.go gets confused if ANDROID_BUILD_TOP is set.
+unset ANDROID_BUILD_TOP
+
+network_jail=""
+if [[ ${OS} = linux ]]; then
+    # The go tools often try to fetch dependencies from the network,
+    # wrap them in an nsjail to prevent network access.
+    network_jail=${TOP}/prebuilts/build-tools/linux-x86/bin/nsjail
+    # Quiet
+    network_jail="${network_jail} -q"
+    # No timeout
+    network_jail="${network_jail} -t 0"
+    # Set working directory
+    network_jail="${network_jail} --cwd=\$PWD"
+    # Pass environment variables through
+    network_jail="${network_jail} -e"
+    # Allow read-only access to everything
+    network_jail="${network_jail} -R /"
+    # Allow write access to the out directory
+    network_jail="${network_jail} -B ${abs_out_dir}"
+    # Allow write access to the /tmp directory
+    network_jail="${network_jail} -B /tmp"
+    # Set high values, as network_jail uses low defaults.
+    network_jail="${network_jail} --rlimit_as soft"
+    network_jail="${network_jail} --rlimit_core soft"
+    network_jail="${network_jail} --rlimit_cpu soft"
+    network_jail="${network_jail} --rlimit_fsize soft"
+    network_jail="${network_jail} --rlimit_nofile soft"
+fi
+
+for dir in "${go_modules[@]}"; do
+    (cd "$dir";
+     eval ${network_jail} -- ${GOROOT}/bin/go build ./...
+     eval ${network_jail} -- ${GOROOT}/bin/go test ./...
+     eval ${network_jail} -- ${GOROOT}/bin/go test -race -short ./...
+    )
+done
diff --git a/scripts/uffd_gc_utils.py b/scripts/uffd_gc_utils.py
new file mode 100644
index 0000000..2d35494
--- /dev/null
+++ b/scripts/uffd_gc_utils.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2024 The Android Open Source Project
+#
+# 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.
+#
+"""Utils to determine whether to enable UFFD GC."""
+
+import re
+import sys
+
+
+def should_enable_uffd_gc(kernel_version_file):
+  with open(kernel_version_file, 'r') as f:
+    kernel_version = f.read().strip()
+  return should_enable_uffd_gc_impl(kernel_version)
+
+def should_enable_uffd_gc_impl(kernel_version):
+  # See https://source.android.com/docs/core/architecture/kernel/gki-versioning#determine-release
+  p = r"^(?P<w>\d+)[.](?P<x>\d+)[.](?P<y>\d+)(-android(?P<z>\d+)-(?P<k>\d+).*$)?"
+  m = re.match(p, kernel_version)
+  if m is not None:
+    if m.group('z') is not None:
+      android_release = int(m.group('z'))
+      # No need to check w, x, y because all Android 12 kernels have backports.
+      return android_release >= 12
+    else:
+      # Old kernel or non-GKI kernel.
+      version = int(m.group('w'))
+      patch_level = int(m.group('x'))
+      if version < 5:
+        # Old kernel.
+        return False
+      elif (version == 5 and patch_level >= 7) or version >= 6:
+        # New non-GKI kernel. 5.7 supports MREMAP_DONTUNMAP without the need for
+        # backports.
+        return True
+      else:
+        # Non-GKI kernel between 5 and 5.6. It may have backports.
+        raise exit_with_error(kernel_version)
+  elif kernel_version == '<unknown-kernel>':
+    # The kernel information isn't available to the build system, probably
+    # because PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS is set to false. We
+    # assume that the kernel supports UFFD GC because it is the case for most of
+    # the products today and it is the future.
+    return True
+  else:
+    # Unrecognizable non-GKI kernel.
+    raise exit_with_error(kernel_version)
+
+def exit_with_error(kernel_version):
+  sys.exit(f"""
+Unable to determine UFFD GC flag for kernel version "{kernel_version}".
+You can fix this by explicitly setting PRODUCT_ENABLE_UFFD_GC to "true" or
+"false" based on the kernel version.
+1. Set PRODUCT_ENABLE_UFFD_GC to "true" if the kernel supports userfaultfd(2)
+   and MREMAP_DONTUNMAP.
+2. Set PRODUCT_ENABLE_UFFD_GC to "false" otherwise.""")
diff --git a/scripts/uffd_gc_utils_test.py b/scripts/uffd_gc_utils_test.py
new file mode 100644
index 0000000..c86ab4b
--- /dev/null
+++ b/scripts/uffd_gc_utils_test.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2024 The Android Open Source Project
+#
+# 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.
+#
+"""Unit tests for uffd_gc_utils.py."""
+
+import unittest
+
+from uffd_gc_utils import should_enable_uffd_gc_impl
+
+
+class UffdGcUtilsTest(unittest.TestCase):
+  def test_should_enable_uffd_gc_impl(self):
+    # GKI kernels in new format.
+    self.assertTrue(should_enable_uffd_gc_impl(
+        "6.1.25-android14-11-g34fde9ec08a3-ab10675345"))
+    self.assertTrue(should_enable_uffd_gc_impl(
+        "5.4.42-android12-0-something"))
+    self.assertFalse(should_enable_uffd_gc_impl(
+        "5.4.42-android11-0-something"))
+    # GKI kernels in old format.
+    self.assertFalse(should_enable_uffd_gc_impl(
+        "4.19.282-g4b749a433956-ab10893502"))
+    # Non GKI kernels.
+    self.assertTrue(should_enable_uffd_gc_impl(
+        "6.1.25-foo"))
+    self.assertTrue(should_enable_uffd_gc_impl(
+        "6.1.25"))
+    self.assertTrue(should_enable_uffd_gc_impl(
+        "5.10.19-foo"))
+    self.assertTrue(should_enable_uffd_gc_impl(
+        "5.10.19"))
+    with self.assertRaises(SystemExit):
+        should_enable_uffd_gc_impl("5.4.42-foo")
+    with self.assertRaises(SystemExit):
+        should_enable_uffd_gc_impl("5.4.42")
+    self.assertFalse(should_enable_uffd_gc_impl(
+        "4.19.282-foo"))
+    self.assertFalse(should_enable_uffd_gc_impl(
+        "4.19.282"))
+    with self.assertRaises(SystemExit):
+        should_enable_uffd_gc_impl("foo")
+    # No kernel.
+    self.assertTrue(should_enable_uffd_gc_impl(
+        "<unknown-kernel>"))
+
+
+if __name__ == '__main__':
+  unittest.main(verbosity=2)
diff --git a/sdk/Android.bp b/sdk/Android.bp
index f42b478..f436320 100644
--- a/sdk/Android.bp
+++ b/sdk/Android.bp
@@ -18,6 +18,7 @@
         "bp.go",
         "build_release.go",
         "exports.go",
+        "genrule.go",
         "member_trait.go",
         "member_type.go",
         "sdk.go",
@@ -30,6 +31,7 @@
         "cc_sdk_test.go",
         "compat_config_sdk_test.go",
         "exports_test.go",
+        "genrule_test.go",
         "java_sdk_test.go",
         "license_sdk_test.go",
         "member_trait_test.go",
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index ee1b5db..5d41958 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -206,8 +206,8 @@
 			checkBootJarsPackageCheckRule(t, result,
 				append(
 					[]string{
-						"out/soong/.intermediates/prebuilts/apex/com.android.art.deapexer/android_common/deapexer/javalib/core1.jar",
-						"out/soong/.intermediates/prebuilts/apex/com.android.art.deapexer/android_common/deapexer/javalib/core2.jar",
+						"out/soong/.intermediates/prebuilts/apex/prebuilt_com.android.art.deapexer/android_common/deapexer/javalib/core1.jar",
+						"out/soong/.intermediates/prebuilts/apex/prebuilt_com.android.art.deapexer/android_common/deapexer/javalib/core2.jar",
 						"out/soong/.intermediates/default/java/framework/android_common/aligned/framework.jar",
 					},
 					java.ApexBootJarDexJarPaths...,
@@ -270,6 +270,12 @@
 		// Add a platform_bootclasspath that depends on the fragment.
 		fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"),
 
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+			}
+		}),
+
 		android.FixtureWithRootAndroidBp(sdk+`
 			apex {
 				name: "myapex",
@@ -489,15 +495,15 @@
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
 .intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
-.intermediates/myothersdklibrary.stubs/android_common/combined/myothersdklibrary.stubs.jar -> sdk_library/public/myothersdklibrary-stubs.jar
-.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
-.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-removed.txt
-.intermediates/mysdklibrary.stubs/android_common/combined/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar
-.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
-.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
-.intermediates/mycoreplatform.stubs/android_common/combined/mycoreplatform.stubs.jar -> sdk_library/public/mycoreplatform-stubs.jar
-.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt
-.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
+.intermediates/myothersdklibrary.stubs.exportable/android_common/combined/myothersdklibrary.stubs.exportable.jar -> sdk_library/public/myothersdklibrary-stubs.jar
+.intermediates/myothersdklibrary.stubs.source/android_common/exportable/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
+.intermediates/myothersdklibrary.stubs.source/android_common/exportable/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-removed.txt
+.intermediates/mysdklibrary.stubs.exportable/android_common/combined/mysdklibrary.stubs.exportable.jar -> sdk_library/public/mysdklibrary-stubs.jar
+.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
+.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
+.intermediates/mycoreplatform.stubs.exportable/android_common/combined/mycoreplatform.stubs.exportable.jar -> sdk_library/public/mycoreplatform-stubs.jar
+.intermediates/mycoreplatform.stubs.source/android_common/exportable/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt
+.intermediates/mycoreplatform.stubs.source/android_common/exportable/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
 `)
 	})
 
@@ -509,15 +515,15 @@
 .intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
 .intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
 .intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
-.intermediates/myothersdklibrary.stubs/android_common/combined/myothersdklibrary.stubs.jar -> sdk_library/public/myothersdklibrary-stubs.jar
-.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
-.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-removed.txt
-.intermediates/mysdklibrary.stubs/android_common/combined/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar
-.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
-.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
-.intermediates/mycoreplatform.stubs/android_common/combined/mycoreplatform.stubs.jar -> sdk_library/public/mycoreplatform-stubs.jar
-.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt
-.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
+.intermediates/myothersdklibrary.stubs.exportable/android_common/combined/myothersdklibrary.stubs.exportable.jar -> sdk_library/public/myothersdklibrary-stubs.jar
+.intermediates/myothersdklibrary.stubs.source/android_common/exportable/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
+.intermediates/myothersdklibrary.stubs.source/android_common/exportable/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-removed.txt
+.intermediates/mysdklibrary.stubs.exportable/android_common/combined/mysdklibrary.stubs.exportable.jar -> sdk_library/public/mysdklibrary-stubs.jar
+.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
+.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
+.intermediates/mycoreplatform.stubs.exportable/android_common/combined/mycoreplatform.stubs.exportable.jar -> sdk_library/public/mycoreplatform-stubs.jar
+.intermediates/mycoreplatform.stubs.source/android_common/exportable/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt
+.intermediates/mycoreplatform.stubs.source/android_common/exportable/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
 `
 	t.Run("added-via-apex", func(t *testing.T) {
 		testSnapshotWithBootClasspathFragment_Contents(t, `
@@ -760,6 +766,12 @@
 		// Add a platform_bootclasspath that depends on the fragment.
 		fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"),
 
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+			}
+		}),
+
 		android.MockFS{
 			"my-blocked.txt":                   nil,
 			"my-max-target-o-low-priority.txt": nil,
@@ -963,12 +975,12 @@
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
 .intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
-.intermediates/mynewlibrary.stubs/android_common/combined/mynewlibrary.stubs.jar -> sdk_library/public/mynewlibrary-stubs.jar
-.intermediates/mynewlibrary.stubs.source/android_common/metalava/mynewlibrary.stubs.source_api.txt -> sdk_library/public/mynewlibrary.txt
-.intermediates/mynewlibrary.stubs.source/android_common/metalava/mynewlibrary.stubs.source_removed.txt -> sdk_library/public/mynewlibrary-removed.txt
-.intermediates/mysdklibrary.stubs/android_common/combined/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar
-.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
-.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
+.intermediates/mynewlibrary.stubs.exportable/android_common/combined/mynewlibrary.stubs.exportable.jar -> sdk_library/public/mynewlibrary-stubs.jar
+.intermediates/mynewlibrary.stubs.source/android_common/exportable/mynewlibrary.stubs.source_api.txt -> sdk_library/public/mynewlibrary.txt
+.intermediates/mynewlibrary.stubs.source/android_common/exportable/mynewlibrary.stubs.source_removed.txt -> sdk_library/public/mynewlibrary-removed.txt
+.intermediates/mysdklibrary.stubs.exportable/android_common/combined/mysdklibrary.stubs.exportable.jar -> sdk_library/public/mysdklibrary-stubs.jar
+.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
+.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
 `),
 		snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
 		snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
@@ -997,6 +1009,12 @@
 			"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": targetBuildRelease,
 		}),
 
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+			}
+		}),
+
 		android.FixtureWithRootAndroidBp(`
 			sdk {
 				name: "mysdk",
@@ -1095,15 +1113,15 @@
 .intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi-for-sdk-snapshot/index.csv -> hiddenapi/index.csv
 .intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi-for-sdk-snapshot/stub-flags.csv -> hiddenapi/stub-flags.csv
 .intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi-for-sdk-snapshot/all-flags.csv -> hiddenapi/all-flags.csv
-.intermediates/mysdklibrary.stubs/android_common/combined/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar
-.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
-.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
+.intermediates/mysdklibrary.stubs.exportable/android_common/combined/mysdklibrary.stubs.exportable.jar -> sdk_library/public/mysdklibrary-stubs.jar
+.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
+.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
 `
 
 		// On S the stub flags should only be generated from mysdklibrary as mynewsdklibrary is not part
 		// of the snapshot.
 		expectedStubFlagsInputs := []string{
-			"out/soong/.intermediates/mysdklibrary.stubs/android_common/dex/mysdklibrary.stubs.jar",
+			"out/soong/.intermediates/mysdklibrary.stubs.exportable/android_common/dex/mysdklibrary.stubs.exportable.jar",
 			"out/soong/.intermediates/mysdklibrary/android_common/aligned/mysdklibrary.jar",
 		}
 
@@ -1173,20 +1191,20 @@
 .intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
 .intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
 .intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
-.intermediates/mysdklibrary.stubs/android_common/combined/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar
-.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
-.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
-.intermediates/mynewsdklibrary.stubs/android_common/combined/mynewsdklibrary.stubs.jar -> sdk_library/public/mynewsdklibrary-stubs.jar
-.intermediates/mynewsdklibrary.stubs.source/android_common/metalava/mynewsdklibrary.stubs.source_api.txt -> sdk_library/public/mynewsdklibrary.txt
-.intermediates/mynewsdklibrary.stubs.source/android_common/metalava/mynewsdklibrary.stubs.source_removed.txt -> sdk_library/public/mynewsdklibrary-removed.txt
+.intermediates/mysdklibrary.stubs.exportable/android_common/combined/mysdklibrary.stubs.exportable.jar -> sdk_library/public/mysdklibrary-stubs.jar
+.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
+.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
+.intermediates/mynewsdklibrary.stubs.exportable/android_common/combined/mynewsdklibrary.stubs.exportable.jar -> sdk_library/public/mynewsdklibrary-stubs.jar
+.intermediates/mynewsdklibrary.stubs.source/android_common/exportable/mynewsdklibrary.stubs.source_api.txt -> sdk_library/public/mynewsdklibrary.txt
+.intermediates/mynewsdklibrary.stubs.source/android_common/exportable/mynewsdklibrary.stubs.source_removed.txt -> sdk_library/public/mynewsdklibrary-removed.txt
 `
 
 		// On tiramisu the stub flags should be generated from both mynewsdklibrary and mysdklibrary as
 		// they are both part of the snapshot.
 		expectedStubFlagsInputs := []string{
-			"out/soong/.intermediates/mynewsdklibrary.stubs/android_common/dex/mynewsdklibrary.stubs.jar",
+			"out/soong/.intermediates/mynewsdklibrary.stubs.exportable/android_common/dex/mynewsdklibrary.stubs.exportable.jar",
 			"out/soong/.intermediates/mynewsdklibrary/android_common/aligned/mynewsdklibrary.jar",
-			"out/soong/.intermediates/mysdklibrary.stubs/android_common/dex/mysdklibrary.stubs.jar",
+			"out/soong/.intermediates/mysdklibrary.stubs.exportable/android_common/dex/mysdklibrary.stubs.exportable.jar",
 			"out/soong/.intermediates/mysdklibrary/android_common/aligned/mysdklibrary.jar",
 		}
 
diff --git a/sdk/genrule.go b/sdk/genrule.go
new file mode 100644
index 0000000..347ab05
--- /dev/null
+++ b/sdk/genrule.go
@@ -0,0 +1,44 @@
+// Copyright 2023 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 sdk
+
+import (
+	"android/soong/android"
+	"android/soong/genrule"
+)
+
+func init() {
+	registerGenRuleBuildComponents(android.InitRegistrationContext)
+}
+
+func registerGenRuleBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("sdk_genrule", SdkGenruleFactory)
+}
+
+// sdk_genrule_host is a genrule that can depend on sdk and sdk_snapshot module types
+//
+// What this means is that it's a genrule with only the "common_os" variant.
+// sdk modules have 3 variants: host, android, and common_os. The common_os one depends
+// on the host/device ones and packages their result into a final snapshot zip.
+// Genrules probably want access to this snapshot zip when they depend on an sdk module,
+// which means they want to depend on the common_os variant and not the host/android
+// variants.
+func SdkGenruleFactory() android.Module {
+	module := genrule.NewGenRule()
+
+	android.InitCommonOSAndroidMultiTargetsArchModule(module, android.NeitherHostNorDeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+
+	return module
+}
diff --git a/sdk/genrule_test.go b/sdk/genrule_test.go
new file mode 100644
index 0000000..6e52a3d
--- /dev/null
+++ b/sdk/genrule_test.go
@@ -0,0 +1,52 @@
+// Copyright 2018 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 sdk
+
+import (
+	"testing"
+
+	"android/soong/android"
+	"android/soong/genrule"
+	"android/soong/java"
+)
+
+func TestSdkGenrule(t *testing.T) {
+	// Test that an sdk_genrule can depend on an sdk, and that a genrule can depend on an sdk_genrule
+	bp := `
+				sdk {
+					name: "my_sdk",
+				}
+				sdk_genrule {
+					name: "my_sdk_genrule",
+					tool_files: ["tool"],
+					cmd: "$(location tool) $(in) $(out)",
+					srcs: [":my_sdk"],
+					out: ["out"],
+				}
+				genrule {
+					name: "my_regular_genrule",
+					srcs: [":my_sdk_genrule"],
+					out: ["out"],
+					cmd: "cp $(in) $(out)",
+				}
+			`
+	android.GroupFixturePreparers(
+		// if java components aren't registered, the sdk module doesn't create a snapshot for some reason.
+		java.PrepareForTestWithJavaBuildComponents,
+		genrule.PrepareForTestWithGenRuleBuildComponents,
+		PrepareForTestWithSdkBuildComponents,
+		android.FixtureRegisterWithContext(registerGenRuleBuildComponents),
+	).RunTestWithBp(t, bp)
+}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 680494f..1b2b0f1 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -45,6 +45,11 @@
 	java.PrepareForTestWithJavaDefaultModules,
 	java.PrepareForTestWithJavaSdkLibraryFiles,
 	java.FixtureWithLastReleaseApis("myjavalib"),
+	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+		variables.BuildFlags = map[string]string{
+			"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+		}
+	}),
 )
 
 // Contains tests for SDK members provided by the java package.
@@ -608,6 +613,11 @@
 			"1": {"myjavalib"},
 			"2": {"myjavalib"},
 		}),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+			}
+		}),
 	).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
@@ -696,9 +706,9 @@
 		checkAllCopyRules(`
 .intermediates/exported-system-module/android_common/turbine-combined/exported-system-module.jar -> java/exported-system-module.jar
 .intermediates/system-module/android_common/turbine-combined/system-module.jar -> java/system-module.jar
-.intermediates/myjavalib.stubs/android_common/combined/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.exportable/android_common/combined/myjavalib.stubs.exportable.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
 `),
 		checkInfoContents(result.Config, `
 [
@@ -941,15 +951,15 @@
 }
 `),
 		checkAllCopyRules(`
-.intermediates/myjavalib.stubs/android_common/combined/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
-.intermediates/myjavalib.stubs.system/android_common/combined/myjavalib.stubs.system.jar -> sdk_library/system/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.system/android_common/metalava/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
-.intermediates/myjavalib.stubs.source.system/android_common/metalava/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
-.intermediates/myjavalib.stubs.test/android_common/combined/myjavalib.stubs.test.jar -> sdk_library/test/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.test/android_common/metalava/myjavalib.stubs.source.test_api.txt -> sdk_library/test/myjavalib.txt
-.intermediates/myjavalib.stubs.source.test/android_common/metalava/myjavalib.stubs.source.test_removed.txt -> sdk_library/test/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.exportable/android_common/combined/myjavalib.stubs.exportable.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.exportable.system/android_common/combined/myjavalib.stubs.exportable.system.jar -> sdk_library/system/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source.system/android_common/exportable/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
+.intermediates/myjavalib.stubs.source.system/android_common/exportable/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.exportable.test/android_common/combined/myjavalib.stubs.exportable.test.jar -> sdk_library/test/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source.test/android_common/exportable/myjavalib.stubs.source.test_api.txt -> sdk_library/test/myjavalib.txt
+.intermediates/myjavalib.stubs.source.test/android_common/exportable/myjavalib.stubs.source.test_removed.txt -> sdk_library/test/myjavalib-removed.txt
 `),
 		checkMergeZips(
 			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -999,9 +1009,9 @@
 }
 `),
 		checkAllCopyRules(`
-.intermediates/myjavalib-foo.stubs/android_common/combined/myjavalib-foo.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib-foo.stubs.source/android_common/metalava/myjavalib-foo.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib-foo.stubs.source/android_common/metalava/myjavalib-foo.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib-foo.stubs.exportable/android_common/combined/myjavalib-foo.stubs.exportable.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib-foo.stubs.source/android_common/exportable/myjavalib-foo.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib-foo.stubs.source/android_common/exportable/myjavalib-foo.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
 `),
 		checkMergeZips(
 			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1052,10 +1062,10 @@
 }
 		`),
 		checkAllCopyRules(`
-.intermediates/myjavalib.stubs/android_common/combined/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source-stubs.srcjar -> sdk_library/public/myjavalib.srcjar
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.exportable/android_common/combined/myjavalib.stubs.exportable.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source-stubs.srcjar -> sdk_library/public/myjavalib.srcjar
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
 		`),
 	)
 }
@@ -1100,10 +1110,10 @@
 }
 		`),
 		checkAllCopyRules(`
-.intermediates/myjavalib.stubs/android_common/combined/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_annotations.zip -> sdk_library/public/myjavalib_annotations.zip
+.intermediates/myjavalib.stubs.exportable/android_common/combined/myjavalib.stubs.exportable.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_annotations.zip -> sdk_library/public/myjavalib_annotations.zip
 		`),
 		checkMergeZips(".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip"),
 	)
@@ -1153,16 +1163,23 @@
 }
 		`),
 		checkAllCopyRules(`
-.intermediates/myjavalib.stubs/android_common/combined/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.exportable/android_common/combined/myjavalib.stubs.exportable.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
 		`),
 		checkMergeZips(".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip"),
 	)
 }
 
 func TestSnapshotWithJavaSdkLibrary_CompileDex(t *testing.T) {
-	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJavaSdkLibrary,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+			}
+		}),
+	).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			java_sdk_libs: ["myjavalib"],
@@ -1214,21 +1231,22 @@
 			ctx := android.ModuleInstallPathContextForTesting(result.Config)
 			dexJarBuildPath := func(name string, kind android.SdkKind) string {
 				dep := result.Module(name, "android_common").(java.SdkLibraryDependency)
-				path := dep.SdkApiStubDexJar(ctx, kind).Path()
+				path := dep.SdkApiExportableStubDexJar(ctx, kind).Path()
 				return path.RelativeToTop().String()
 			}
 
 			dexJarPath := dexJarBuildPath("myjavalib", android.SdkPublic)
-			android.AssertStringEquals(t, "source dex public stubs jar build path", "out/soong/.intermediates/myjavalib.stubs/android_common/dex/myjavalib.stubs.jar", dexJarPath)
+			android.AssertStringEquals(t, "source dex public stubs jar build path", "out/soong/.intermediates/myjavalib.stubs.exportable/android_common/dex/myjavalib.stubs.exportable.jar", dexJarPath)
 
 			dexJarPath = dexJarBuildPath("myjavalib", android.SdkSystem)
-			systemDexJar := "out/soong/.intermediates/myjavalib.stubs.system/android_common/dex/myjavalib.stubs.system.jar"
+			systemDexJar := "out/soong/.intermediates/myjavalib.stubs.exportable.system/android_common/dex/myjavalib.stubs.exportable.system.jar"
 			android.AssertStringEquals(t, "source dex system stubs jar build path", systemDexJar, dexJarPath)
 
 			// This should fall back to system as module is not available.
 			dexJarPath = dexJarBuildPath("myjavalib", android.SdkModule)
 			android.AssertStringEquals(t, "source dex module stubs jar build path", systemDexJar, dexJarPath)
 
+			// Prebuilt dex jar does not come from the exportable stubs.
 			dexJarPath = dexJarBuildPath(android.PrebuiltNameFromSource("myjavalib"), android.SdkPublic)
 			android.AssertStringEquals(t, "prebuilt dex public stubs jar build path", "out/soong/.intermediates/snapshot/prebuilt_myjavalib.stubs/android_common/dex/myjavalib.stubs.jar", dexJarPath)
 		}),
@@ -1270,9 +1288,9 @@
 }
 `),
 		checkAllCopyRules(`
-.intermediates/myjavalib.stubs/android_common/combined/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.exportable/android_common/combined/myjavalib.stubs.exportable.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
 `),
 		checkMergeZips(
 			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1318,9 +1336,9 @@
 }
 `),
 		checkAllCopyRules(`
-.intermediates/myjavalib.stubs/android_common/combined/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.exportable/android_common/combined/myjavalib.stubs.exportable.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
 `),
 		checkMergeZips(
 			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1376,12 +1394,12 @@
 }
 `),
 		checkAllCopyRules(`
-.intermediates/myjavalib.stubs/android_common/combined/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
-.intermediates/myjavalib.stubs.system/android_common/combined/myjavalib.stubs.system.jar -> sdk_library/system/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.system/android_common/metalava/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
-.intermediates/myjavalib.stubs.source.system/android_common/metalava/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.exportable/android_common/combined/myjavalib.stubs.exportable.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.exportable.system/android_common/combined/myjavalib.stubs.exportable.system.jar -> sdk_library/system/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source.system/android_common/exportable/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
+.intermediates/myjavalib.stubs.source.system/android_common/exportable/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
 `),
 		checkMergeZips(
 			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1448,15 +1466,15 @@
 }
 `),
 		checkAllCopyRules(`
-.intermediates/myjavalib.stubs/android_common/combined/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
-.intermediates/myjavalib.stubs.system/android_common/combined/myjavalib.stubs.system.jar -> sdk_library/system/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.system/android_common/metalava/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
-.intermediates/myjavalib.stubs.source.system/android_common/metalava/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
-.intermediates/myjavalib.stubs.module_lib/android_common/combined/myjavalib.stubs.module_lib.jar -> sdk_library/module-lib/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.module_lib/android_common/metalava/myjavalib.stubs.source.module_lib_api.txt -> sdk_library/module-lib/myjavalib.txt
-.intermediates/myjavalib.stubs.source.module_lib/android_common/metalava/myjavalib.stubs.source.module_lib_removed.txt -> sdk_library/module-lib/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.exportable/android_common/combined/myjavalib.stubs.exportable.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.exportable.system/android_common/combined/myjavalib.stubs.exportable.system.jar -> sdk_library/system/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source.system/android_common/exportable/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
+.intermediates/myjavalib.stubs.source.system/android_common/exportable/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.exportable.module_lib/android_common/combined/myjavalib.stubs.exportable.module_lib.jar -> sdk_library/module-lib/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source.module_lib/android_common/exportable/myjavalib.stubs.source.module_lib_api.txt -> sdk_library/module-lib/myjavalib.txt
+.intermediates/myjavalib.stubs.source.module_lib/android_common/exportable/myjavalib.stubs.source.module_lib_removed.txt -> sdk_library/module-lib/myjavalib-removed.txt
 `),
 		checkMergeZips(
 			".intermediates/mysdk/common_os/tmp/sdk_library/module-lib/myjavalib_stub_sources.zip",
@@ -1514,12 +1532,12 @@
 }
 `),
 		checkAllCopyRules(`
-.intermediates/myjavalib.stubs/android_common/combined/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
-.intermediates/myjavalib.stubs.system_server/android_common/combined/myjavalib.stubs.system_server.jar -> sdk_library/system-server/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.system_server/android_common/metalava/myjavalib.stubs.source.system_server_api.txt -> sdk_library/system-server/myjavalib.txt
-.intermediates/myjavalib.stubs.source.system_server/android_common/metalava/myjavalib.stubs.source.system_server_removed.txt -> sdk_library/system-server/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.exportable/android_common/combined/myjavalib.stubs.exportable.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.exportable.system_server/android_common/combined/myjavalib.stubs.exportable.system_server.jar -> sdk_library/system-server/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source.system_server/android_common/exportable/myjavalib.stubs.source.system_server_api.txt -> sdk_library/system-server/myjavalib.txt
+.intermediates/myjavalib.stubs.source.system_server/android_common/exportable/myjavalib.stubs.source.system_server_removed.txt -> sdk_library/system-server/myjavalib-removed.txt
 `),
 		checkMergeZips(
 			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1568,9 +1586,9 @@
 }
 `),
 		checkAllCopyRules(`
-.intermediates/myjavalib.stubs/android_common/combined/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.exportable/android_common/combined/myjavalib.stubs.exportable.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
 `),
 		checkMergeZips(
 			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1625,9 +1643,9 @@
 }
 `),
 		checkAllCopyRules(`
-.intermediates/myjavalib.stubs/android_common/combined/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.exportable/android_common/combined/myjavalib.stubs.exportable.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
 docs/known_doctags -> doctags/docs/known_doctags
 `),
 	)
diff --git a/sdk/sdk.go b/sdk/sdk.go
index 4d4a2a2..fd16ab6 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -222,6 +222,18 @@
 	}}
 }
 
+func (s *sdk) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		if s.snapshotFile.Valid() {
+			return []android.Path{s.snapshotFile.Path()}, nil
+		}
+		return nil, fmt.Errorf("snapshot file not defined. This is most likely because this isn't the common_os variant of this module")
+	default:
+		return nil, fmt.Errorf("unknown tag %q", tag)
+	}
+}
+
 // gatherTraits gathers the traits from the dynamically generated trait specific properties.
 //
 // Returns a map from member name to the set of required traits.
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index 5a25146..c4df146 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -442,6 +442,11 @@
 			android.FixtureMergeEnv(map[string]string{
 				"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": "S",
 			}),
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.BuildFlags = map[string]string{
+					"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+				}
+			}),
 		).RunTest(t)
 
 		CheckSnapshot(t, result, "mysdk", "",
@@ -487,9 +492,9 @@
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/stub-flags.csv -> hiddenapi/stub-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/all-flags.csv -> hiddenapi/all-flags.csv
-.intermediates/mysdklibrary.stubs/android_common/combined/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar
-.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
-.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
+.intermediates/mysdklibrary.stubs.exportable/android_common/combined/mysdklibrary.stubs.exportable.jar -> sdk_library/public/mysdklibrary-stubs.jar
+.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
+.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
 `),
 		)
 	})
diff --git a/sdk/update.go b/sdk/update.go
index 4c39fae..095e0c2 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -166,10 +166,7 @@
 			// Keep track of which multilib variants are used by the sdk.
 			s.multilibUsages = s.multilibUsages.addArchType(child.Target().Arch.ArchType)
 
-			var exportedComponentsInfo android.ExportedComponentsInfo
-			if ctx.OtherModuleHasProvider(child, android.ExportedComponentsInfoProvider) {
-				exportedComponentsInfo = ctx.OtherModuleProvider(child, android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo)
-			}
+			exportedComponentsInfo, _ := android.OtherModuleProvider(ctx, child, android.ExportedComponentsInfoProvider)
 
 			var container android.Module
 			if parent != ctx.Module() {
@@ -607,7 +604,7 @@
 				name:       name,
 			}
 
-			additionalSdkInfo := ctx.OtherModuleProvider(module, android.AdditionalSdkInfoProvider).(android.AdditionalSdkInfo)
+			additionalSdkInfo, _ := android.OtherModuleProvider(ctx, module, android.AdditionalSdkInfoProvider)
 			info.memberSpecific = additionalSdkInfo.Properties
 
 			name2Info[name] = info
@@ -1171,7 +1168,7 @@
 
 	// The licenses are the same for all variants.
 	mctx := s.ctx
-	licenseInfo := mctx.OtherModuleProvider(variant, android.LicenseInfoProvider).(android.LicenseInfo)
+	licenseInfo, _ := android.OtherModuleProvider(mctx, variant, android.LicenseInfoProvider)
 	if len(licenseInfo.Licenses) > 0 {
 		m.AddPropertyWithTag("licenses", licenseInfo.Licenses, s.OptionalSdkMemberReferencePropertyTag())
 	}
@@ -1417,7 +1414,7 @@
 		variantsByApex := make(map[string]android.Module)
 		conflictDetected := false
 		for _, variant := range list {
-			apexInfo := moduleCtx.OtherModuleProvider(variant, android.ApexInfoProvider).(android.ApexInfo)
+			apexInfo, _ := android.OtherModuleProvider(moduleCtx, variant, android.ApexInfoProvider)
 			apexVariationName := apexInfo.ApexVariationName
 			// If there are two variants for a specific APEX variation then there is conflict.
 			if _, ok := variantsByApex[apexVariationName]; ok {
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 1e27375..97adeed 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -270,7 +270,7 @@
 		Output: s.outputFilePath,
 		Input:  s.sourceFilePath,
 	})
-	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: []string{s.sourceFilePath.String()}})
+	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: []string{s.sourceFilePath.String()}})
 }
 
 func (s *ShBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -457,7 +457,7 @@
 	installedData := ctx.InstallTestData(s.installDir, s.data)
 	s.installedFile = ctx.InstallExecutable(s.installDir, s.outputFilePath.Base(), s.outputFilePath, installedData...)
 
-	ctx.SetProvider(testing.TestModuleProviderKey, testing.TestModuleProviderData{})
+	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 }
 
 func (s *ShTest) InstallInData() bool {
diff --git a/snapshot/host_fake_snapshot.go b/snapshot/host_fake_snapshot.go
index c4cfbb5..63cd4e1 100644
--- a/snapshot/host_fake_snapshot.go
+++ b/snapshot/host_fake_snapshot.go
@@ -119,7 +119,7 @@
 		if !module.Enabled() || module.IsHideFromMake() {
 			return
 		}
-		apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
+		apexInfo, _ := android.SingletonModuleProvider(ctx, module, android.ApexInfoProvider)
 		if !apexInfo.IsForPlatform() {
 			return
 		}
diff --git a/snapshot/recovery_snapshot.go b/snapshot/recovery_snapshot.go
index 8ff59cb..ab114b4 100644
--- a/snapshot/recovery_snapshot.go
+++ b/snapshot/recovery_snapshot.go
@@ -22,16 +22,14 @@
 	ExcludeFromRecoverySnapshot() bool
 }
 
-var recoverySnapshotSingleton = SnapshotSingleton{
-	"recovery",                     // name
-	"SOONG_RECOVERY_SNAPSHOT_ZIP",  // makeVar
-	android.OptionalPath{},         // snapshotZipFile
-	RecoverySnapshotImageSingleton, // Image
-	false,                          // Fake
-}
-
 func RecoverySnapshotSingleton() android.Singleton {
-	return &recoverySnapshotSingleton
+	return &SnapshotSingleton{
+		"recovery",                     // name
+		"SOONG_RECOVERY_SNAPSHOT_ZIP",  // makeVar
+		android.OptionalPath{},         // snapshotZipFile
+		RecoverySnapshotImageSingleton, // Image
+		false,                          // Fake
+	}
 }
 
 // Determine if a dir under source tree is an SoC-owned proprietary directory based
diff --git a/snapshot/vendor_snapshot.go b/snapshot/vendor_snapshot.go
index 4484c85..3e5f546 100644
--- a/snapshot/vendor_snapshot.go
+++ b/snapshot/vendor_snapshot.go
@@ -22,28 +22,24 @@
 	ExcludeFromVendorSnapshot() bool
 }
 
-var vendorSnapshotSingleton = SnapshotSingleton{
-	"vendor",                     // name
-	"SOONG_VENDOR_SNAPSHOT_ZIP",  // makeVar
-	android.OptionalPath{},       // snapshotZipFile
-	VendorSnapshotImageSingleton, // Image
-	false,                        // Fake
-}
-
-var vendorFakeSnapshotSingleton = SnapshotSingleton{
-	"vendor",                         // name
-	"SOONG_VENDOR_FAKE_SNAPSHOT_ZIP", // makeVar
-	android.OptionalPath{},           // snapshotZipFile
-	VendorSnapshotImageSingleton,     // Image
-	true,                             // Fake
-}
-
 func VendorSnapshotSingleton() android.Singleton {
-	return &vendorSnapshotSingleton
+	return &SnapshotSingleton{
+		"vendor",                     // name
+		"SOONG_VENDOR_SNAPSHOT_ZIP",  // makeVar
+		android.OptionalPath{},       // snapshotZipFile
+		VendorSnapshotImageSingleton, // Image
+		false,                        // Fake
+	}
 }
 
 func VendorFakeSnapshotSingleton() android.Singleton {
-	return &vendorFakeSnapshotSingleton
+	return &SnapshotSingleton{
+		"vendor",                         // name
+		"SOONG_VENDOR_FAKE_SNAPSHOT_ZIP", // makeVar
+		android.OptionalPath{},           // snapshotZipFile
+		VendorSnapshotImageSingleton,     // Image
+		true,                             // Fake
+	}
 }
 
 // Determine if a dir under source tree is an SoC-owned proprietary directory based
diff --git a/sysprop/Android.bp b/sysprop/Android.bp
index 1d5eb31..a00a5e4 100644
--- a/sysprop/Android.bp
+++ b/sysprop/Android.bp
@@ -11,6 +11,7 @@
         "soong-android",
         "soong-cc",
         "soong-java",
+        "soong-rust",
     ],
     srcs: [
         "sysprop_library.go",
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index c89c6b0..2258232 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -21,6 +21,7 @@
 	"io"
 	"os"
 	"path"
+	"strings"
 	"sync"
 
 	"github.com/google/blueprint"
@@ -29,6 +30,7 @@
 	"android/soong/android"
 	"android/soong/cc"
 	"android/soong/java"
+	"android/soong/rust"
 )
 
 type dependencyTag struct {
@@ -51,7 +53,16 @@
 	genSrcjars android.Paths
 }
 
+type syspropRustGenRule struct {
+	android.ModuleBase
+
+	properties syspropGenProperties
+
+	genSrcs android.Paths
+}
+
 var _ android.OutputFileProducer = (*syspropJavaGenRule)(nil)
+var _ android.OutputFileProducer = (*syspropRustGenRule)(nil)
 
 var (
 	syspropJava = pctx.AndroidStaticRule("syspropJava",
@@ -64,11 +75,20 @@
 				"$soongZipCmd",
 			},
 		}, "scope")
+	syspropRust = pctx.AndroidStaticRule("syspropRust",
+		blueprint.RuleParams{
+			Command: `rm -rf $out_dir && mkdir -p $out_dir && ` +
+				`$syspropRustCmd --scope $scope --rust-output-dir $out_dir $in`,
+			CommandDeps: []string{
+				"$syspropRustCmd",
+			},
+		}, "scope", "out_dir")
 )
 
 func init() {
 	pctx.HostBinToolVariable("soongZipCmd", "soong_zip")
 	pctx.HostBinToolVariable("syspropJavaCmd", "sysprop_java")
+	pctx.HostBinToolVariable("syspropRustCmd", "sysprop_rust")
 }
 
 // syspropJavaGenRule module generates srcjar containing generated java APIs.
@@ -122,6 +142,56 @@
 	return g
 }
 
+// syspropRustGenRule module generates rust source files containing generated rust APIs.
+// It also depends on check api rule, so api check has to pass to use sysprop_library.
+func (g *syspropRustGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	var checkApiFileTimeStamp android.WritablePath
+
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		if m, ok := dep.(*syspropLibrary); ok {
+			checkApiFileTimeStamp = m.checkApiFileTimeStamp
+		}
+	})
+
+	for _, syspropFile := range android.PathsForModuleSrc(ctx, g.properties.Srcs) {
+		syspropDir := strings.TrimSuffix(syspropFile.String(), syspropFile.Ext())
+		outputDir := android.PathForModuleGen(ctx, syspropDir, "src")
+		libPath := android.PathForModuleGen(ctx, syspropDir, "src", "lib.rs")
+		parsersPath := android.PathForModuleGen(ctx, syspropDir, "src", "gen_parsers_and_formatters.rs")
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        syspropRust,
+			Description: "sysprop_rust " + syspropFile.Rel(),
+			Outputs:     android.WritablePaths{libPath, parsersPath},
+			Input:       syspropFile,
+			Implicit:    checkApiFileTimeStamp,
+			Args: map[string]string{
+				"scope":   g.properties.Scope,
+				"out_dir": outputDir.String(),
+			},
+		})
+
+		g.genSrcs = append(g.genSrcs, libPath, parsersPath)
+	}
+}
+
+func (g *syspropRustGenRule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// Add a dependency from the stubs to sysprop library so that the generator rule can depend on
+	// the check API rule of the sysprop library.
+	ctx.AddFarVariationDependencies(nil, nil, proptools.String(g.properties.Check_api))
+}
+
+func (g *syspropRustGenRule) OutputFiles(_ string) (android.Paths, error) {
+	return g.genSrcs, nil
+}
+
+func syspropRustGenFactory() android.Module {
+	g := &syspropRustGenRule{}
+	g.AddProperties(&g.properties)
+	android.InitAndroidModule(g)
+	return g
+}
+
 type syspropLibrary struct {
 	android.ModuleBase
 	android.ApexModuleBase
@@ -180,6 +250,12 @@
 		// Forwarded to java_library.min_sdk_version
 		Min_sdk_version *string
 	}
+
+	Rust struct {
+		// Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
+		// Forwarded to rust_library.min_sdk_version
+		Min_sdk_version *string
+	}
 }
 
 var (
@@ -233,6 +309,21 @@
 	return m.BaseModuleName() + "_java_gen_public"
 }
 
+func (m *syspropLibrary) rustGenModuleName() string {
+	return m.rustCrateName() + "_rust_gen"
+}
+
+func (m *syspropLibrary) rustGenStubName() string {
+	return "lib" + m.rustCrateName() + "_rust"
+}
+
+func (m *syspropLibrary) rustCrateName() string {
+	moduleName := strings.ToLower(m.BaseModuleName())
+	moduleName = strings.ReplaceAll(moduleName, "-", "_")
+	moduleName = strings.ReplaceAll(moduleName, ".", "_")
+	return moduleName
+}
+
 func (m *syspropLibrary) BaseModuleName() string {
 	return m.ModuleBase.Name()
 }
@@ -251,7 +342,7 @@
 			ctx.PropertyErrorf("srcs", "srcs contains non-sysprop file %q", syspropFile.String())
 		}
 	}
-	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()})
+	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()})
 
 	if ctx.Failed() {
 		return
@@ -345,6 +436,10 @@
 			fmt.Fprintln(w, "LOCAL_MODULE :=", m.Name())
 			fmt.Fprintf(w, "LOCAL_MODULE_CLASS := FAKE\n")
 			fmt.Fprintf(w, "LOCAL_MODULE_TAGS := optional\n")
+			// AconfigUpdateAndroidMkData may have added elements to Extra.  Process them here.
+			for _, extra := range data.Extra {
+				extra(w, nil)
+			}
 			fmt.Fprintf(w, "include $(BUILD_SYSTEM)/base_rules.mk\n\n")
 			fmt.Fprintf(w, "$(LOCAL_BUILT_MODULE): %s\n", m.checkApiFileTimeStamp.String())
 			fmt.Fprintf(w, "\ttouch $@\n\n")
@@ -432,6 +527,18 @@
 	Min_sdk_version   *string
 }
 
+type rustLibraryProperties struct {
+	Name              *string
+	Srcs              []string
+	Installable       *bool
+	Crate_name        string
+	Rustlibs          []string
+	Vendor_available  *bool
+	Product_available *bool
+	Apex_available    []string
+	Min_sdk_version   *string
+}
+
 func syspropLibraryHook(ctx android.LoadHookContext, m *syspropLibrary) {
 	if len(m.properties.Srcs) == 0 {
 		ctx.PropertyErrorf("srcs", "sysprop_library must specify srcs")
@@ -560,6 +667,28 @@
 		})
 	}
 
+	// Generate a Rust implementation library.
+	ctx.CreateModule(syspropRustGenFactory, &syspropGenProperties{
+		Srcs:      m.properties.Srcs,
+		Scope:     scope,
+		Name:      proptools.StringPtr(m.rustGenModuleName()),
+		Check_api: proptools.StringPtr(ctx.ModuleName()),
+	})
+	rustProps := rustLibraryProperties{
+		Name:        proptools.StringPtr(m.rustGenStubName()),
+		Srcs:        []string{":" + m.rustGenModuleName()},
+		Installable: proptools.BoolPtr(false),
+		Crate_name:  m.rustCrateName(),
+		Rustlibs: []string{
+			"librustutils",
+		},
+		Vendor_available:  m.properties.Vendor_available,
+		Product_available: m.properties.Product_available,
+		Apex_available:    m.ApexProperties.Apex_available,
+		Min_sdk_version:   proptools.StringPtr("29"),
+	}
+	ctx.CreateModule(rust.RustLibraryFactory, &rustProps)
+
 	// syspropLibraries will be used by property_contexts to check types.
 	// Record absolute paths of sysprop_library to prevent soong_namespace problem.
 	if m.ExportedToMake() {
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index e51fe39..9dd696f 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -22,6 +22,7 @@
 	"android/soong/android"
 	"android/soong/cc"
 	"android/soong/java"
+	"android/soong/rust"
 
 	"github.com/google/blueprint/proptools"
 )
@@ -46,18 +47,6 @@
 			recovery_available: true,
 		}
 
-		cc_library {
-			name: "liblog",
-			no_libcrt: true,
-			nocrt: true,
-			system_shared_libs: [],
-			recovery_available: true,
-			host_supported: true,
-			llndk: {
-				symbol_file: "liblog.map.txt",
-			}
-		}
-
 		java_library {
 			name: "sysprop-library-stub-platform",
 			sdk_version: "core_current",
@@ -74,6 +63,15 @@
 			product_specific: true,
 			sdk_version: "core_current",
 		}
+
+		rust_library {
+			name: "librustutils",
+			crate_name: "rustutils",
+			srcs: ["librustutils/lib.rs"],
+			product_available: true,
+			vendor_available: true,
+			min_sdk_version: "29",
+		}
 	`
 
 	mockFS := android.MockFS{
@@ -115,16 +113,25 @@
 		"android/sysprop/PlatformProperties.sysprop": nil,
 		"com/android/VendorProperties.sysprop":       nil,
 		"com/android2/OdmProperties.sysprop":         nil,
+
+		"librustutils/lib.rs": nil,
 	}
 
 	result := android.GroupFixturePreparers(
 		cc.PrepareForTestWithCcDefaultModules,
 		java.PrepareForTestWithJavaDefaultModules,
+		rust.PrepareForTestWithRustDefaultModules,
 		PrepareForTestWithSyspropBuildComponents,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 			variables.DeviceSystemSdkVersions = []string{"28"}
 			variables.DeviceVndkVersion = proptools.StringPtr("current")
 			variables.Platform_vndk_version = proptools.StringPtr("29")
+			variables.DeviceCurrentApiLevelForVendorModules = proptools.StringPtr("28")
+		}),
+		java.FixtureWithPrebuiltApis(map[string][]string{
+			"28": {},
+			"29": {},
+			"30": {},
 		}),
 		mockFS.AddToFixture(),
 		android.FixtureWithRootAndroidBp(bp),
@@ -350,6 +357,10 @@
 	javaModule := result.ModuleForTests("sysprop-platform", "android_common").Module().(*java.Library)
 	propFromJava := javaModule.ApexProperties.Apex_available
 	android.AssertDeepEquals(t, "apex_available forwarding to java module", expected, propFromJava)
+
+	rustModule := result.ModuleForTests("libsysprop_platform_rust", "android_arm64_armv8-a_rlib_rlib-std").Module().(*rust.Module)
+	propFromRust := rustModule.ApexProperties.Apex_available
+	android.AssertDeepEquals(t, "apex_available forwarding to rust module", expected, propFromRust)
 }
 
 func TestMinSdkVersionIsForwarded(t *testing.T) {
@@ -365,6 +376,9 @@
 			java: {
 				min_sdk_version: "30",
 			},
+			rust: {
+				min_sdk_version: "29",
+			}
 		}
 	`)
 
@@ -375,4 +389,8 @@
 	javaModule := result.ModuleForTests("sysprop-platform", "android_common").Module().(*java.Library)
 	propFromJava := javaModule.MinSdkVersionString()
 	android.AssertStringEquals(t, "min_sdk_version forwarding to java module", "30", propFromJava)
+
+	rustModule := result.ModuleForTests("libsysprop_platform_rust", "android_arm64_armv8-a_rlib_rlib-std").Module().(*rust.Module)
+	propFromRust := proptools.String(rustModule.Properties.Min_sdk_version)
+	android.AssertStringEquals(t, "min_sdk_version forwarding to rust module", "29", propFromRust)
 }
diff --git a/testing/all_code_metadata.go b/testing/all_code_metadata.go
index 16d7aae..12aa7b5 100644
--- a/testing/all_code_metadata.go
+++ b/testing/all_code_metadata.go
@@ -21,14 +21,9 @@
 
 	ctx.VisitAllModules(
 		func(module android.Module) {
-			if !ctx.ModuleHasProvider(module, CodeMetadataProviderKey) {
-				return
+			if metadata, ok := android.SingletonModuleProvider(ctx, module, CodeMetadataProviderKey); ok {
+				intermediateMetadataPaths = append(intermediateMetadataPaths, metadata.IntermediatePath)
 			}
-			intermediateMetadataPaths = append(
-				intermediateMetadataPaths, ctx.ModuleProvider(
-					module, CodeMetadataProviderKey,
-				).(CodeMetadataProviderData).IntermediatePath,
-			)
 		},
 	)
 
diff --git a/testing/all_test_specs.go b/testing/all_test_specs.go
index 9d4645b..b035435 100644
--- a/testing/all_test_specs.go
+++ b/testing/all_test_specs.go
@@ -21,10 +21,9 @@
 	var intermediateMetadataPaths android.Paths
 
 	ctx.VisitAllModules(func(module android.Module) {
-		if !ctx.ModuleHasProvider(module, TestSpecProviderKey) {
-			return
+		if metadata, ok := android.SingletonModuleProvider(ctx, module, TestSpecProviderKey); ok {
+			intermediateMetadataPaths = append(intermediateMetadataPaths, metadata.IntermediatePath)
 		}
-		intermediateMetadataPaths = append(intermediateMetadataPaths, ctx.ModuleProvider(module, TestSpecProviderKey).(TestSpecProviderData).IntermediatePath)
 	})
 
 	rspFile := android.PathForOutput(ctx, fileContainingFilePaths)
diff --git a/testing/code_metadata.go b/testing/code_metadata.go
index 9e20ec6..11ba430 100644
--- a/testing/code_metadata.go
+++ b/testing/code_metadata.go
@@ -20,6 +20,7 @@
 	"android/soong/android"
 	"android/soong/testing/code_metadata_internal_proto"
 	"github.com/google/blueprint"
+
 	"google.golang.org/protobuf/proto"
 )
 
@@ -83,7 +84,7 @@
 	IntermediatePath android.WritablePath
 }
 
-var CodeMetadataProviderKey = blueprint.NewProvider(CodeMetadataProviderData{})
+var CodeMetadataProviderKey = blueprint.NewProvider[CodeMetadataProviderData]()
 
 func (module *CodeMetadataModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	metadataList := make(
@@ -95,10 +96,8 @@
 	for _, m := range ctx.GetDirectDepsWithTag(codeDepTag) {
 		targetName := m.Name()
 		var moduleSrcs []string
-		if ctx.OtherModuleHasProvider(m, blueprint.SrcsFileProviderKey) {
-			moduleSrcs = ctx.OtherModuleProvider(
-				m, blueprint.SrcsFileProviderKey,
-			).(blueprint.SrcsFileProviderData).SrcPaths
+		if srcsFileInfo, ok := android.OtherModuleProvider(ctx, m, blueprint.SrcsFileProviderKey); ok {
+			moduleSrcs = srcsFileInfo.SrcPaths
 		}
 		if module.properties.MultiOwnership {
 			metadata := &code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership{
@@ -129,9 +128,9 @@
 	intermediatePath := android.PathForModuleOut(
 		ctx, "intermediateCodeMetadata.pb",
 	)
-	android.WriteFileRule(ctx, intermediatePath, string(protoData))
+	android.WriteFileRuleVerbatim(ctx, intermediatePath, string(protoData))
 
-	ctx.SetProvider(
+	android.SetProvider(ctx,
 		CodeMetadataProviderKey,
 		CodeMetadataProviderData{IntermediatePath: intermediatePath},
 	)
diff --git a/testing/code_metadata_proto/Android.bp b/testing/code_metadata_proto/Android.bp
index 8fcca19..f07efff 100644
--- a/testing/code_metadata_proto/Android.bp
+++ b/testing/code_metadata_proto/Android.bp
@@ -20,10 +20,24 @@
     name: "soong-testing-code_metadata_proto",
     pkgPath: "android/soong/testing/code_metadata_proto",
     deps: [
-            "golang-protobuf-reflect-protoreflect",
-            "golang-protobuf-runtime-protoimpl",
-        ],
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+    ],
     srcs: [
         "code_metadata.pb.go",
     ],
 }
+
+python_library_host {
+    name: "code-metadata-proto-py",
+    pkg_path: "code_metadata",
+    srcs: [
+        "code_metadata.proto",
+    ],
+    libs: [
+        "libprotobuf-python",
+    ],
+    proto: {
+        canonical_path_from_root: false,
+    },
+}
diff --git a/testing/test_spec.go b/testing/test_spec.go
index 58d9c23..4d885c6 100644
--- a/testing/test_spec.go
+++ b/testing/test_spec.go
@@ -20,8 +20,9 @@
 
 	"android/soong/android"
 	"android/soong/testing/test_spec_proto"
-	"github.com/google/blueprint"
 	"google.golang.org/protobuf/proto"
+
+	"github.com/google/blueprint"
 )
 
 // ErrTestModuleDataNotFound is the error message for missing test module provider data.
@@ -81,16 +82,16 @@
 	IntermediatePath android.WritablePath
 }
 
-var TestSpecProviderKey = blueprint.NewProvider(TestSpecProviderData{})
+var TestSpecProviderKey = blueprint.NewProvider[TestSpecProviderData]()
 
 type TestModuleProviderData struct {
 }
 
-var TestModuleProviderKey = blueprint.NewProvider(TestModuleProviderData{})
+var TestModuleProviderKey = blueprint.NewProvider[TestModuleProviderData]()
 
 func (module *TestSpecModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	for _, m := range ctx.GetDirectDepsWithTag(testsDepTag) {
-		if !ctx.OtherModuleHasProvider(m, TestModuleProviderKey) {
+		if _, ok := android.OtherModuleProvider(ctx, m, TestModuleProviderKey); !ok {
 			ctx.ModuleErrorf(ErrTestModuleDataNotFound, m.Name())
 		}
 	}
@@ -116,9 +117,9 @@
 	if err != nil {
 		ctx.ModuleErrorf("Error: %s", err.Error())
 	}
-	android.WriteFileRule(ctx, intermediatePath, string(protoData))
+	android.WriteFileRuleVerbatim(ctx, intermediatePath, string(protoData))
 
-	ctx.SetProvider(
+	android.SetProvider(ctx,
 		TestSpecProviderKey, TestSpecProviderData{
 			IntermediatePath: intermediatePath,
 		},
diff --git a/testing/test_spec_proto/Android.bp b/testing/test_spec_proto/Android.bp
index 1cac492..d5ad70b 100644
--- a/testing/test_spec_proto/Android.bp
+++ b/testing/test_spec_proto/Android.bp
@@ -20,10 +20,24 @@
     name: "soong-testing-test_spec_proto",
     pkgPath: "android/soong/testing/test_spec_proto",
     deps: [
-            "golang-protobuf-reflect-protoreflect",
-            "golang-protobuf-runtime-protoimpl",
-        ],
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+    ],
     srcs: [
         "test_spec.pb.go",
     ],
 }
+
+python_library_host {
+    name: "test-spec-proto-py",
+    pkg_path: "test_spec",
+    srcs: [
+        "test_spec.proto",
+    ],
+    libs: [
+        "libprotobuf-python",
+    ],
+    proto: {
+        canonical_path_from_root: false,
+    },
+}
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index 21453ba..ee286f6 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -35,6 +35,7 @@
         "blueprint",
         "blueprint-bootstrap",
         "blueprint-microfactory",
+        "soong-android",
         "soong-finder",
         "soong-remoteexec",
         "soong-shared",
@@ -46,6 +47,7 @@
         "soong-ui-tracer",
     ],
     srcs: [
+        "androidmk_denylist.go",
         "build.go",
         "cleanbuild.go",
         "config.go",
diff --git a/ui/build/androidmk_denylist.go b/ui/build/androidmk_denylist.go
new file mode 100644
index 0000000..e7896ab
--- /dev/null
+++ b/ui/build/androidmk_denylist.go
@@ -0,0 +1,46 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+	"strings"
+)
+
+var androidmk_denylist []string = []string{
+	"chained_build_config/",
+	"cts/",
+	"dalvik/",
+	"developers/",
+	// Do not block other directories in kernel/, see b/319658303.
+	"kernel/configs/",
+	"kernel/prebuilts/",
+	"kernel/tests/",
+	"libcore/",
+	"libnativehelper/",
+	"pdk/",
+	// Add back toolchain/ once defensive Android.mk files are removed
+	//"toolchain/",
+}
+
+func blockAndroidMks(ctx Context, androidMks []string) {
+	for _, mkFile := range androidMks {
+		for _, d := range androidmk_denylist {
+			if strings.HasPrefix(mkFile, d) {
+				ctx.Fatalf("Found blocked Android.mk file: %s. "+
+					"Please see androidmk_denylist.go for the blocked directories and contact build system team if the file should not be blocked.", mkFile)
+			}
+		}
+	}
+}
diff --git a/ui/build/config.go b/ui/build/config.go
index 5085c68..1a25397 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -75,7 +75,6 @@
 	queryview                bool
 	reportMkMetrics          bool // Collect and report mk2bp migration progress metrics.
 	soongDocs                bool
-	multitreeBuild           bool // This is a multitree build.
 	skipConfig               bool
 	skipKati                 bool
 	skipKatiNinja            bool
@@ -115,6 +114,11 @@
 
 	// Data source to write ninja weight list
 	ninjaWeightListSource NinjaWeightListSource
+
+	// This file is a detailed dump of all soong-defined modules for debugging purposes.
+	// There's quite a bit of overlap with module-info.json and soong module graph. We
+	// could consider merging them.
+	moduleDebugFile string
 }
 
 type NinjaWeightListSource uint
@@ -273,6 +277,10 @@
 		ret.sandboxConfig.SetSrcDirIsRO(srcDirIsWritable == "false")
 	}
 
+	if os.Getenv("GENERATE_SOONG_DEBUG") == "true" {
+		ret.moduleDebugFile, _ = filepath.Abs(shared.JoinPath(ret.SoongOutDir(), "soong-debug-info.json"))
+	}
+
 	ret.environ.Unset(
 		// We're already using it
 		"USE_SOONG_UI",
@@ -325,6 +333,9 @@
 		"ANDROID_DEV_SCRIPTS",
 		"ANDROID_EMULATOR_PREBUILTS",
 		"ANDROID_PRE_BUILD_PATHS",
+
+		// We read it here already, don't let others share in the fun
+		"GENERATE_SOONG_DEBUG",
 	)
 
 	if ret.UseGoma() || ret.ForceUseGoma() {
@@ -412,10 +423,6 @@
 	// zip files produced by soong_zip.  Disable zipbomb detection.
 	ret.environ.Set("UNZIP_DISABLE_ZIPBOMB_DETECTION", "TRUE")
 
-	if ret.MultitreeBuild() {
-		ret.environ.Set("MULTITREE_BUILD", "true")
-	}
-
 	outDir := ret.OutDir()
 	buildDateTimeFile := filepath.Join(outDir, "build_date.txt")
 	if buildDateTime, ok := ret.environ.Get("BUILD_DATETIME"); ok && buildDateTime != "" {
@@ -777,8 +784,6 @@
 			c.skipMetricsUpload = true
 		} else if arg == "--mk-metrics" {
 			c.reportMkMetrics = true
-		} else if arg == "--multitree-build" {
-			c.multitreeBuild = true
 		} else if arg == "--search-api-dir" {
 			c.searchApiDir = true
 		} else if strings.HasPrefix(arg, "--ninja_weight_source=") {
@@ -1083,10 +1088,6 @@
 	return c.verbose
 }
 
-func (c *configImpl) MultitreeBuild() bool {
-	return c.multitreeBuild
-}
-
 func (c *configImpl) NinjaWeightListSource() NinjaWeightListSource {
 	return c.ninjaWeightListSource
 }
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index 5182b12..b1222fe 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -860,23 +860,24 @@
 }
 
 func TestGetConfigArgsBuildModulesInDirectory(t *testing.T) {
-	tests := []buildActionTestCase{{
-		description:  "normal execution in a directory",
-		dirsInTrees:  []string{"0/1/2"},
-		buildFiles:   []string{"0/1/2/Android.mk"},
-		args:         []string{"fake-module"},
-		curDir:       "0/1/2",
-		tidyOnly:     "",
-		expectedArgs: []string{"fake-module", "MODULES-IN-0-1-2"},
-	}, {
-		description:  "build file in parent directory",
-		dirsInTrees:  []string{"0/1/2"},
-		buildFiles:   []string{"0/1/Android.mk"},
-		args:         []string{},
-		curDir:       "0/1/2",
-		tidyOnly:     "",
-		expectedArgs: []string{"MODULES-IN-0-1"},
-	},
+	tests := []buildActionTestCase{
+		{
+			description:  "normal execution in a directory",
+			dirsInTrees:  []string{"0/1/2"},
+			buildFiles:   []string{"0/1/2/Android.mk"},
+			args:         []string{"fake-module"},
+			curDir:       "0/1/2",
+			tidyOnly:     "",
+			expectedArgs: []string{"fake-module", "MODULES-IN-0-1-2"},
+		}, {
+			description:  "build file in parent directory",
+			dirsInTrees:  []string{"0/1/2"},
+			buildFiles:   []string{"0/1/Android.mk"},
+			args:         []string{},
+			curDir:       "0/1/2",
+			tidyOnly:     "",
+			expectedArgs: []string{"MODULES-IN-0-1"},
+		},
 		{
 			description:  "build file in parent directory, multiple module names passed in",
 			dirsInTrees:  []string{"0/1/2"},
@@ -903,15 +904,6 @@
 			tidyOnly:     "",
 			expectedArgs: []string{},
 		}, {
-			description:  "multitree build action executed at root directory",
-			dirsInTrees:  []string{},
-			buildFiles:   []string{},
-			rootSymlink:  false,
-			args:         []string{"--multitree-build"},
-			curDir:       ".",
-			tidyOnly:     "",
-			expectedArgs: []string{"--multitree-build"},
-		}, {
 			description:  "build action executed at root directory in symlink",
 			dirsInTrees:  []string{},
 			buildFiles:   []string{},
@@ -953,7 +945,8 @@
 			curDir:       "",
 			tidyOnly:     "",
 			expectedArgs: []string{"-j", "-k", "fake_module"},
-		}}
+		},
+	}
 	for _, tt := range tests {
 		t.Run("build action BUILD_MODULES_IN_DIR, "+tt.description, func(t *testing.T) {
 			testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY)
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 32b0ef3..2576b09 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -240,7 +240,6 @@
 		"BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST",
 
 		// Not used, but useful to be in the soong.log
-		"BOARD_VNDK_VERSION",
 		"TARGET_BUILD_TYPE",
 		"HOST_ARCH",
 		"HOST_2ND_ARCH",
diff --git a/ui/build/finder.go b/ui/build/finder.go
index d0bcf40..573df21 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -128,6 +128,7 @@
 
 	// Stop searching a subdirectory recursively after finding an Android.mk.
 	androidMks := f.FindFirstNamedAt(".", "Android.mk")
+	blockAndroidMks(ctx, androidMks)
 	err := dumpListToFile(ctx, config, androidMks, filepath.Join(dumpDir, "Android.mk.list"))
 	if err != nil {
 		ctx.Fatalf("Could not export module list: %v", err)
diff --git a/ui/build/kati.go b/ui/build/kati.go
index 31e7440..d599c99 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -100,8 +100,6 @@
 		"--no_ninja_prelude",
 		// Support declaring phony outputs in AOSP Ninja.
 		"--use_ninja_phony_output",
-		// Support declaring symlink outputs in AOSP Ninja.
-		"--use_ninja_symlink_outputs",
 		// Regenerate the Ninja file if environment inputs have changed. e.g.
 		// CLI flags, .mk file timestamps, env vars, $(wildcard ..) and some
 		// $(shell ..) results.
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index b69e938..551b8ab 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -271,11 +271,13 @@
 		// The Ninja file hasn't been modified since the last time it was
 		// checked, so Ninja could be stuck. Output some diagnostics.
 		ctx.Verbosef("ninja may be stuck; last update to %v was %v. dumping process tree...", c.logPath, newModTime)
+		ctx.Printf("ninja may be stuck, check %v for list of running processes.",
+			filepath.Join(config.LogsDir(), config.logsPrefix+"soong.log"))
 
 		// The "pstree" command doesn't exist on Mac, but "pstree" on Linux
 		// gives more convenient output than "ps" So, we try pstree first, and
 		// ps second
-		commandText := fmt.Sprintf("pstree -pal %v || ps -ef", os.Getpid())
+		commandText := fmt.Sprintf("pstree -palT %v || ps -ef", os.Getpid())
 
 		cmd := Command(ctx, config, "dump process tree", "bash", "-c", commandText)
 		output := cmd.CombinedOutputOrFatal()
diff --git a/ui/build/path.go b/ui/build/path.go
index 29128d8..51ebff1 100644
--- a/ui/build/path.go
+++ b/ui/build/path.go
@@ -180,7 +180,7 @@
 			// Compute the error message along with the process tree, including
 			// parents, for this log line.
 			procPrints := []string{
-				"See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.",
+				"See https://android.googlesource.com/platform/build/+/main/Changes.md#PATH_Tools for more information.",
 			}
 			if len(log.Parents) > 0 {
 				procPrints = append(procPrints, "Process tree:")
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index 65e2c8e..2f25a8c 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -93,6 +93,7 @@
 	"fuser":          Allowed,
 	"gcert":          Allowed,
 	"gcertstatus":    Allowed,
+	"gcloud":         Allowed,
 	"getopt":         Allowed,
 	"git":            Allowed,
 	"hexdump":        Allowed,
@@ -101,7 +102,6 @@
 	"javap":          Allowed,
 	"lsof":           Allowed,
 	"openssl":        Allowed,
-	"prodcertstatus": Allowed,
 	"pstree":         Allowed,
 	"rsync":          Allowed,
 	"sh":             Allowed,
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 0bf8862..79584c6 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -36,6 +36,7 @@
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/microfactory"
+	"github.com/google/blueprint/pathtools"
 
 	"google.golang.org/protobuf/proto"
 )
@@ -181,20 +182,30 @@
 	return globPathName
 }
 
-func (pb PrimaryBuilderFactory) primaryBuilderInvocation() bootstrap.PrimaryBuilderInvocation {
+func getGlobPathNameFromPrimaryBuilderFactory(config Config, pb PrimaryBuilderFactory) string {
+	if pb.name == soongBuildTag {
+		// Glob path for soong build would be separated per product target
+		return getGlobPathName(config)
+	}
+	return pb.name
+}
+
+func (pb PrimaryBuilderFactory) primaryBuilderInvocation(config Config) bootstrap.PrimaryBuilderInvocation {
 	commonArgs := make([]string, 0, 0)
 
 	if !pb.config.skipSoongTests {
 		commonArgs = append(commonArgs, "-t")
 	}
 
-	if pb.config.multitreeBuild {
-		commonArgs = append(commonArgs, "--multitree-build")
-	}
 	if pb.config.buildFromSourceStub {
 		commonArgs = append(commonArgs, "--build-from-source-stub")
 	}
 
+	if pb.config.moduleDebugFile != "" {
+		commonArgs = append(commonArgs, "--soong_module_debug")
+		commonArgs = append(commonArgs, pb.config.moduleDebugFile)
+	}
+
 	commonArgs = append(commonArgs, "-l", filepath.Join(pb.config.FileListDir(), "Android.bp.list"))
 	invocationEnv := make(map[string]string)
 	if pb.debugPort != "" {
@@ -215,11 +226,7 @@
 
 	var allArgs []string
 	allArgs = append(allArgs, pb.specificArgs...)
-	globPathName := pb.name
-	// Glob path for soong build would be separated per product target
-	if pb.name == soongBuildTag {
-		globPathName = getGlobPathName(pb.config)
-	}
+	globPathName := getGlobPathNameFromPrimaryBuilderFactory(config, pb)
 	allArgs = append(allArgs,
 		"--globListDir", globPathName,
 		"--globFile", pb.config.NamedGlobFile(globPathName))
@@ -234,8 +241,11 @@
 	}
 	allArgs = append(allArgs, "Android.bp")
 
+	globfiles := bootstrap.GlobFileListFiles(bootstrap.GlobDirectory(config.SoongOutDir(), globPathName))
+
 	return bootstrap.PrimaryBuilderInvocation{
 		Inputs:      []string{"Android.bp"},
+		Implicits:   globfiles,
 		Outputs:     []string{pb.output},
 		Args:        allArgs,
 		Description: pb.description,
@@ -292,9 +302,6 @@
 	if config.EmptyNinjaFile() {
 		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--empty-ninja-file")
 	}
-	if config.MultitreeBuild() {
-		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--multitree-build")
-	}
 	if config.buildFromSourceStub {
 		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--build-from-source-stub")
 	}
@@ -376,17 +383,10 @@
 		if debuggedInvocations[pbf.name] {
 			pbf.debugPort = delvePort
 		}
-		pbi := pbf.primaryBuilderInvocation()
+		pbi := pbf.primaryBuilderInvocation(config)
 		invocations = append(invocations, pbi)
 	}
 
-	// The glob .ninja files are subninja'd. However, they are generated during
-	// the build itself so we write an empty file if the file does not exist yet
-	// so that the subninja doesn't fail on clean builds
-	for _, globFile := range bootstrapGlobFileList(config) {
-		writeEmptyFile(ctx, globFile)
-	}
-
 	blueprintArgs := bootstrap.Args{
 		ModuleListFile: filepath.Join(config.FileListDir(), "Android.bp.list"),
 		OutFile:        shared.JoinPath(config.SoongOutDir(), "bootstrap.ninja"),
@@ -408,6 +408,28 @@
 		primaryBuilderInvocations: invocations,
 	}
 
+	// The glob ninja files are generated during the main build phase. However, the
+	// primary buildifer invocation depends on all of its glob files, even before
+	// it's been run. Generate a "empty" glob ninja file on the first run,
+	// so that the files can be there to satisfy the dependency.
+	for _, pb := range pbfs {
+		globPathName := getGlobPathNameFromPrimaryBuilderFactory(config, pb)
+		globNinjaFile := config.NamedGlobFile(globPathName)
+		if _, err := os.Stat(globNinjaFile); os.IsNotExist(err) {
+			err := bootstrap.WriteBuildGlobsNinjaFile(&bootstrap.GlobSingleton{
+				GlobLister: func() pathtools.MultipleGlobResults { return nil },
+				GlobFile:   globNinjaFile,
+				GlobDir:    bootstrap.GlobDirectory(config.SoongOutDir(), globPathName),
+				SrcDir:     ".",
+			}, blueprintConfig)
+			if err != nil {
+				ctx.Fatal(err)
+			}
+		} else if err != nil {
+			ctx.Fatal(err)
+		}
+	}
+
 	// since `bootstrap.ninja` is regenerated unconditionally, we ignore the deps, i.e. little
 	// reason to write a `bootstrap.ninja.d` file
 	_, err := bootstrap.RunBlueprint(blueprintArgs, bootstrap.DoEverything, blueprintCtx, blueprintConfig)
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index c5dc4c5..3095139 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -63,6 +63,7 @@
 
 	outDir := config.OutDir()
 	modulePathsDir := filepath.Join(outDir, ".module_paths")
+	rawFilesDir := filepath.Join(outDir, "soong", "raw")
 	variablesFilePath := filepath.Join(outDir, "soong", "soong.variables")
 
 	// dexpreopt.config is an input to the soong_docs action, which runs the
@@ -88,6 +89,7 @@
 			continue
 		}
 		if strings.HasPrefix(line, modulePathsDir) ||
+			strings.HasPrefix(line, rawFilesDir) ||
 			line == variablesFilePath ||
 			line == dexpreoptConfigFilePath ||
 			line == buildDatetimeFilePath ||
diff --git a/ui/status/ninja_frontend/README b/ui/status/ninja_frontend/README
index 8c4b451..767bbf1 100644
--- a/ui/status/ninja_frontend/README
+++ b/ui/status/ninja_frontend/README
@@ -1,3 +1,3 @@
-This comes from https://android.googlesource.com/platform/external/ninja/+/master/src/frontend.proto
+This comes from https://android.googlesource.com/platform/external/ninja/+/main/src/frontend.proto
 
 The only difference is the specification of a go_package. To regenerate frontend.pb.go, run regen.sh.
diff --git a/ui/terminal/format.go b/ui/terminal/format.go
index 5391023..241a1dd 100644
--- a/ui/terminal/format.go
+++ b/ui/terminal/format.go
@@ -25,7 +25,6 @@
 type formatter struct {
 	format string
 	quiet  bool
-	smart  bool
 	start  time.Time
 }
 
@@ -33,11 +32,10 @@
 // the terminal in a format similar to Ninja.
 // format takes nearly all the same options as NINJA_STATUS.
 // %c is currently unsupported.
-func newFormatter(format string, quiet bool, smart bool) formatter {
+func newFormatter(format string, quiet bool) formatter {
 	return formatter{
 		format: format,
 		quiet:  quiet,
-		smart:  smart,
 		start:  time.Now(),
 	}
 }
@@ -63,9 +61,8 @@
 func (s formatter) progress(counts status.Counts) string {
 	if s.format == "" {
 		output := fmt.Sprintf("[%3d%% %d/%d", 100*counts.FinishedActions/counts.TotalActions, counts.FinishedActions, counts.TotalActions)
-		// Not to break parsing logic in the build bot
-		// TODO(b/313981966): make buildbot more flexible for output format
-		if s.smart && !counts.EstimatedTime.IsZero() {
+
+		if !counts.EstimatedTime.IsZero() {
 			output += fmt.Sprintf(" %s remaining", remainingTimeString(counts.EstimatedTime))
 		}
 		output += "] "
diff --git a/ui/terminal/status.go b/ui/terminal/status.go
index 810e3c9..2ad174f 100644
--- a/ui/terminal/status.go
+++ b/ui/terminal/status.go
@@ -27,10 +27,9 @@
 // statusFormat takes nearly all the same options as NINJA_STATUS.
 // %c is currently unsupported.
 func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild, forceKeepANSI bool) status.StatusOutput {
-	useSmartStatus := !forceSimpleOutput && isSmartTerminal(w)
-	formatter := newFormatter(statusFormat, quietBuild, useSmartStatus)
+	formatter := newFormatter(statusFormat, quietBuild)
 
-	if useSmartStatus {
+	if !forceSimpleOutput && isSmartTerminal(w) {
 		return NewSmartStatusOutput(w, formatter)
 	} else {
 		return NewSimpleStatusOutput(w, formatter, forceKeepANSI)