Merge "Sandbox chre_atoms_log.h" into main
diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go
index c4fc31a..b55d7bf 100644
--- a/aconfig/aconfig_declarations.go
+++ b/aconfig/aconfig_declarations.go
@@ -112,38 +112,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,7 +156,7 @@
 		Description: "aconfig_text",
 	})
 
-	ctx.SetProvider(DeclarationsProviderKey, DeclarationsProviderData{
+	android.SetProvider(ctx, android.AconfigDeclarationsProviderKey, android.AconfigDeclarationsProviderData{
 		Package:                     module.properties.Package,
 		Container:                   module.properties.Container,
 		IntermediateCacheOutputPath: intermediateCacheFilePath,
@@ -185,57 +164,18 @@
 	})
 
 }
-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}
-}
 
 func SetAconfigFileMkEntries(m *android.ModuleBase, entries *android.AndroidMkEntries, aconfigFiles map[string]android.Paths) {
-	if m.InstallInVendor() {
-		entries.SetPaths("LOCAL_ACONFIG_FILES", aconfigFiles["vendor"])
-	} else {
-		// TODO(b/311155208): The container here should be system.
-		entries.SetPaths("LOCAL_ACONFIG_FILES", aconfigFiles[""])
+	// TODO(b/311155208): The default container here should be system.
+	container := ""
+
+	if m.SocSpecific() {
+		container = "vendor"
+	} else if m.ProductSpecific() {
+		container = "product"
+	} else if m.SystemExtSpecific() {
+		container = "system_ext"
 	}
+
+	entries.SetPaths("LOCAL_ACONFIG_FILES", aconfigFiles[container])
 }
diff --git a/aconfig/aconfig_declarations_test.go b/aconfig/aconfig_declarations_test.go
index 9035c71..d508af7 100644
--- a/aconfig/aconfig_declarations_test.go
+++ b/aconfig/aconfig_declarations_test.go
@@ -38,7 +38,7 @@
 	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")
 	if !strings.HasSuffix(depData.IntermediateCacheOutputPath.String(), "/intermediate.pb") {
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..36bea0e 100644
--- a/aconfig/all_aconfig_declarations.go
+++ b/aconfig/all_aconfig_declarations.go
@@ -37,10 +37,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/codegen/cc_aconfig_library.go b/aconfig/codegen/cc_aconfig_library.go
index 30f6863..12c2dea 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"
 
@@ -92,7 +91,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 +121,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/java_aconfig_library.go b/aconfig/codegen/java_aconfig_library.go
index 202e358..c027815 100644
--- a/aconfig/codegen/java_aconfig_library.go
+++ b/aconfig/codegen/java_aconfig_library.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 
-	"android/soong/aconfig"
 	"android/soong/android"
 	"android/soong/java"
 
@@ -62,10 +61,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 +81,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")
diff --git a/aconfig/codegen/rust_aconfig_library.go b/aconfig/codegen/rust_aconfig_library.go
index e587056..73b6fec 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"
 
@@ -65,7 +64,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/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..04176ec 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -62,18 +62,21 @@
 			},
 		}, "cache_files")
 
-	mergeAconfigFilesRule = pctx.AndroidStaticRule("mergeAconfigFilesRule",
-		blueprint.RuleParams{
-			Command:     `${aconfig} dump --dedup --format protobuf --out $out $flags`,
-			CommandDeps: []string{"${aconfig}"},
-		}, "flags")
 	// 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 $$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..b359df9 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -27,6 +27,7 @@
         "androidmk-parser",
     ],
     srcs: [
+        "aconfig_providers.go",
         "androidmk.go",
         "apex.go",
         "apex_contributions.go",
@@ -79,6 +80,7 @@
         "prebuilt.go",
         "prebuilt_build_tool.go",
         "proto.go",
+        "provider.go",
         "register.go",
         "rule_builder.go",
         "sandbox.go",
diff --git a/android/aconfig_providers.go b/android/aconfig_providers.go
new file mode 100644
index 0000000..ddebec3
--- /dev/null
+++ b/android/aconfig_providers.go
@@ -0,0 +1,92 @@
+// 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 (
+	"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
+	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]()
+
+func CollectDependencyAconfigFiles(ctx ModuleContext, mergedAconfigFiles *map[string]Paths) {
+	if *mergedAconfigFiles == nil {
+		*mergedAconfigFiles = make(map[string]Paths)
+	}
+	ctx.VisitDirectDeps(func(module Module) {
+		if dep, _ := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); dep.IntermediateCacheOutputPath != nil {
+			(*mergedAconfigFiles)[dep.Container] = append((*mergedAconfigFiles)[dep.Container], dep.IntermediateCacheOutputPath)
+			return
+		}
+		if dep, _ := OtherModuleProvider(ctx, module, AconfigTransitiveDeclarationsInfoProvider); 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)
+	}
+
+	SetProvider(ctx, AconfigTransitiveDeclarationsInfoProvider, AconfigTransitiveDeclarationsInfo{
+		AconfigFiles: *mergedAconfigFiles,
+	})
+}
+
+func mergeAconfigFiles(ctx ModuleContext, inputs Paths) Paths {
+	inputs = LastUniquePaths(inputs)
+	if len(inputs) == 1 {
+		return Paths{inputs[0]}
+	}
+
+	output := PathForModuleOut(ctx, "aconfig_merged.pb")
+
+	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}
+}
diff --git a/android/androidmk.go b/android/androidmk.go
index c4b93c7..a0ed1e4 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -159,7 +159,7 @@
 }
 
 type AndroidMkExtraEntriesContext interface {
-	Provider(provider blueprint.ProviderKey) interface{}
+	Provider(provider blueprint.AnyProviderKey) (any, bool)
 }
 
 type androidMkExtraEntriesContext struct {
@@ -167,8 +167,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)
@@ -492,8 +492,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,8 +622,7 @@
 		}
 	}
 
-	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)
 	}
 
diff --git a/android/apex.go b/android/apex.go
index c6d9940..b4bb67c 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,7 @@
 	ApexContents []*ApexContents
 }
 
-var ApexTestForInfoProvider = blueprint.NewMutatorProvider(ApexTestForInfo{}, "apex_test_for")
+var ApexTestForInfoProvider = blueprint.NewMutatorProvider[ApexTestForInfo]("apex_test_for")
 
 // 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 +954,15 @@
 	// 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
+}
diff --git a/android/apex_contributions.go b/android/apex_contributions.go
index f13659a..a309640 100644
--- a/android/apex_contributions.go
+++ b/android/apex_contributions.go
@@ -119,12 +119,12 @@
 			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
 type PrebuiltSelectionInfoMap map[string]PrebuiltSelectionInfo
@@ -164,6 +164,18 @@
 	}
 }
 
+// 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.
 func (a *allApexContributions) GenerateAndroidBuildActions(ctx ModuleContext) {
 }
diff --git a/android/base_module_context.go b/android/base_module_context.go
index 4312e9b..2a4b12e 100644
--- a/android/base_module_context.go
+++ b/android/base_module_context.go
@@ -79,26 +79,30 @@
 	// 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{}
+	OtherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) any
 
 	// OtherModuleHasProvider returns true if the provider for the given module has been set.
-	OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool
+	OtherModuleHasProvider(m blueprint.Module, provider blueprint.AnyProviderKey) bool
+
+	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
 	// 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{}
+	Provider(provider blueprint.AnyProviderKey) any
 
 	// HasProvider returns true if the provider for the current module has been set.
-	HasProvider(provider blueprint.ProviderKey) bool
+	HasProvider(provider blueprint.AnyProviderKey) bool
+
+	provider(provider blueprint.AnyProviderKey) (any, bool)
 
 	// 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{})
+	SetProvider(provider blueprint.AnyProviderKey, value interface{})
 
 	GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module
 
@@ -260,19 +264,35 @@
 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 {
+	value, _ := b.bp.OtherModuleProvider(m, provider)
+	return value
+}
+
+func (b *baseModuleContext) OtherModuleHasProvider(m blueprint.Module, provider blueprint.AnyProviderKey) bool {
+	_, ok := b.bp.OtherModuleProvider(m, provider)
+	return ok
+}
+
+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.AnyProviderKey) any {
+	value, _ := b.bp.Provider(provider)
+	return value
 }
-func (b *baseModuleContext) Provider(provider blueprint.ProviderKey) interface{} {
+
+func (b *baseModuleContext) HasProvider(provider blueprint.AnyProviderKey) bool {
+	_, ok := b.bp.Provider(provider)
+	return ok
+}
+
+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)
 }
 
diff --git a/android/deapexer.go b/android/deapexer.go
index 6a93f60..fb2073d 100644
--- a/android/deapexer.go
+++ b/android/deapexer.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"fmt"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -98,7 +99,7 @@
 
 // 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.
@@ -146,11 +147,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 +166,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/filegroup.go b/android/filegroup.go
index 04bd8a8..0aabb68 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -92,7 +92,7 @@
 	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()})
 }
 
 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..d4cfd29 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -92,7 +92,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{})
diff --git a/android/module.go b/android/module.go
index f571157..1100fee 100644
--- a/android/module.go
+++ b/android/module.go
@@ -77,6 +77,8 @@
 	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
+	InstallInOdm() bool
+	InstallInProduct() bool
 	InstallInVendor() bool
 	InstallForceOS() (*OsType, *ArchType)
 	PartitionTag(DeviceConfig) string
@@ -1399,6 +1401,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)
 }
@@ -1648,7 +1658,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)
 	}
 
diff --git a/android/module_context.go b/android/module_context.go
index 39986df..81692d5 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -181,6 +181,8 @@
 	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
+	InstallInOdm() bool
+	InstallInProduct() bool
 	InstallInVendor() bool
 	InstallForceOS() (*OsType, *ArchType)
 
@@ -438,6 +440,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()
 }
diff --git a/android/mutator.go b/android/mutator.go
index 0d391a4..93c519d 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -325,7 +325,7 @@
 	// 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{})
 }
 
 type bottomUpMutatorContext struct {
@@ -746,6 +746,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/paths.go b/android/paths.go
index 3f35449..6aabe4f 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()
 }
@@ -1866,11 +1881,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 +2081,9 @@
 	inDebugRamdisk  bool
 	inRecovery      bool
 	inRoot          bool
+	inOdm           bool
+	inProduct       bool
+	inVendor        bool
 	forceOS         *OsType
 	forceArch       *ArchType
 }
@@ -2108,6 +2126,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/prebuilt.go b/android/prebuilt.go
index a32a37d..6a417a8 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -461,8 +461,8 @@
 		// 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 {
@@ -548,9 +548,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
diff --git a/android/provider.go b/android/provider.go
new file mode 100644
index 0000000..b2cc7c0
--- /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/rule_builder.go b/android/rule_builder.go
index 1a491f7..1454357 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -590,7 +590,7 @@
 							To:   proto.String(sboxOutSubDir),
 						},
 						{
-							From: proto.String(PathForOutput(r.ctx).String()),
+							From: proto.String(r.ctx.Config().OutDir()),
 							To:   proto.String(sboxOutSubDir),
 						},
 					},
@@ -891,7 +891,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
 		}
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index 63c3527..d659dcc 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -474,7 +474,7 @@
 		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 " +
+				"FlagWithRspFileInputList=__SBOX_SANDBOX_DIR__/out/soong/rsp Input __SBOX_SANDBOX_DIR__/out/Output " +
 				"__SBOX_SANDBOX_DIR__/out/SymlinkOutput 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",
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/singleton.go b/android/singleton.go
index 7c6cf4f..47cfb28 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)
 }
@@ -282,3 +280,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/testing.go b/android/testing.go
index fa4dffd..39a268b 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -203,7 +203,17 @@
 	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 {
+	value, _ := ctx.Context.ModuleProvider(m, p)
+	return value
+}
+
+func (ctx *TestContext) ModuleHasProvider(m blueprint.Module, p blueprint.AnyProviderKey) bool {
+	_, ok := ctx.Context.ModuleProvider(m, p)
+	return ok
+}
+
+func (ctx *TestContext) moduleProvider(m blueprint.Module, p blueprint.AnyProviderKey) (any, bool) {
 	return ctx.Context.ModuleProvider(m, p)
 }
 
@@ -225,6 +235,12 @@
 	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
 // runtime and provides support for reordering the components registered for a test in the same
 // way.
diff --git a/apex/apex.go b/apex/apex.go
index 38a166e..35a8782 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -24,7 +24,6 @@
 	"sort"
 	"strings"
 
-	"android/soong/aconfig"
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
@@ -912,13 +911,13 @@
 	ProvidedLibs []string
 }
 
-var DCLAInfoProvider = blueprint.NewMutatorProvider(DCLAInfo{}, "apex_info")
+var DCLAInfoProvider = blueprint.NewMutatorProvider[DCLAInfo]("apex_info")
 
 type ApexBundleInfo struct {
 	Contents *android.ApexContents
 }
 
-var ApexBundleInfoProvider = blueprint.NewMutatorProvider(ApexBundleInfo{}, "apex_info")
+var ApexBundleInfoProvider = blueprint.NewMutatorProvider[ApexBundleInfo]("apex_info")
 
 var _ ApexInfoMutator = (*apexBundle)(nil)
 
@@ -1020,7 +1019,7 @@
 
 	// The membership information is saved for later access
 	apexContents := android.NewApexContents(contents)
-	mctx.SetProvider(ApexBundleInfoProvider, ApexBundleInfo{
+	android.SetProvider(mctx, ApexBundleInfoProvider, ApexBundleInfo{
 		Contents: apexContents,
 	})
 
@@ -1058,7 +1057,7 @@
 	})
 
 	if a.dynamic_common_lib_apex() {
-		mctx.SetProvider(DCLAInfoProvider, DCLAInfo{
+		android.SetProvider(mctx, DCLAInfoProvider, DCLAInfo{
 			ProvidedLibs: a.properties.Native_shared_libs,
 		})
 	}
@@ -1201,10 +1200,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, ApexBundleInfoProvider)
 			contents = append(contents, abInfo.Contents)
 		}
-		mctx.SetProvider(android.ApexTestForInfoProvider, android.ApexTestForInfo{
+		android.SetProvider(mctx, android.ApexTestForInfoProvider, android.ApexTestForInfo{
 			ApexContents: contents,
 		})
 	}
@@ -1465,7 +1464,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{}
@@ -1783,7 +1782,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
@@ -2150,7 +2149,7 @@
 			af := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs)
 			af.transitiveDep = true
 
-			abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
+			abInfo, _ := android.ModuleProvider(ctx, 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.
@@ -2272,7 +2271,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()]...)
 	}
@@ -2371,12 +2370,30 @@
 	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(),
+			}
+			ctx.SetProvider(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 +2442,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 +2454,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.
@@ -2589,7 +2606,7 @@
 		return
 	}
 
-	abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
+	abInfo, _ := android.ModuleProvider(ctx, 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 +2667,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())
 			}
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..6c7dabc 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -3725,7 +3725,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())
@@ -8432,6 +8432,13 @@
 		prebuilt_bootclasspath_fragment {
 			name: "my-bootclasspath-fragment",
 			apex_available: ["com.android.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",
+			},
 			%s
 		}
 	`
@@ -8453,6 +8460,7 @@
 				public: {
 					jars: ["libbar.jar"],
 				},
+				shared_library: false,
 				apex_available: ["com.android.myapex"],
 			}
 		`)
@@ -8468,6 +8476,7 @@
 				public: {
 					jars: ["libbar.jar"],
 				},
+				shared_library: false,
 				apex_available: ["com.android.myapex"],
 			}
 		`)
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 43be310..159e9e1 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -152,7 +152,7 @@
 
 	// 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"
@@ -165,7 +165,7 @@
 
 	// 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"
@@ -530,6 +530,8 @@
 
 		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
 			`com.android.art.apex.selector`,
+			`com.android.art.deapexer`,
+			`dex2oatd`,
 			`prebuilt_art-bootclasspath-fragment`,
 		})
 
@@ -655,7 +657,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")
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..5aeea63 100644
--- a/apex/deapexer.go
+++ b/apex/deapexer.go
@@ -127,7 +127,7 @@
 	if len(exports) > 0 {
 		// Make the information available for other modules.
 		di := android.NewDeapexerInfo(apexModuleName(ctx.ModuleName()), exports)
-		ctx.SetProvider(android.DeapexerProvider, di)
+		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..9d74519 100644
--- a/apex/dexpreopt_bootjars_test.go
+++ b/apex/dexpreopt_bootjars_test.go
@@ -252,3 +252,153 @@
 
 	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.
+
+		prebuilt_bootclasspath_fragment {
+			name: "art-bootclasspath-fragment",
+			image_name: "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",
+			},
+			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/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/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..b741963 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -152,7 +152,7 @@
 	).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
@@ -234,7 +234,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())
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 7d339d5..37a9ff5 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.
@@ -170,50 +172,42 @@
 	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
+	}
+	// 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) {
@@ -248,6 +242,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 +427,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, ApexBundleInfoProvider, ApexBundleInfo{
 		Contents: apexContents,
 	})
 
@@ -756,12 +755,39 @@
 	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.BaseModuleName()))
+	}
+}
+
 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 {
+		exports := android.ApexExportsInfo{
+			ApexName:          p.ApexVariationName(),
+			ProfilePathOnHost: di.PrebuiltExportPath(java.ProfileInstallPathInApex),
+		}
+		ctx.SetProvider(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 +809,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)
 
@@ -999,6 +1031,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..90fd2ca 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -272,7 +272,9 @@
 	ctx := result.TestContext
 
 	java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex", []string{
+		`dex2oatd`,
 		`myapex.apex.selector`,
+		`myapex.deapexer`,
 		`prebuilt_mysystemserverclasspathfragment`,
 	})
 
diff --git a/bloaty/bloaty.go b/bloaty/bloaty.go
index 3cff60f..43fb71d 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,10 @@
 func (singleton *sizesSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var deps android.Paths
 	ctx.VisitAllModules(func(m android.Module) {
-		if !ctx.ModuleHasProvider(m, fileSizeMeasurerKey) {
+		filePaths, ok := android.SingletonModuleProvider(ctx, m, fileSizeMeasurerKey)
+		if !ok {
 			return
 		}
-		filePaths := ctx.ModuleProvider(m, fileSizeMeasurerKey).(measuredFiles)
 		for _, path := range filePaths.paths {
 			filePath := path.(android.ModuleOutPath)
 			sizeFile := filePath.InSameDir(ctx, filePath.Base()+bloatyDescriptorExt)
diff --git a/bpf/bpf.go b/bpf/bpf.go
index ce57b46..32d62b5 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -203,7 +203,7 @@
 		}
 
 	}
-	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()})
+	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()})
 }
 
 func (bpf *bpf) AndroidMk() android.AndroidMkData {
diff --git a/cc/afdo.go b/cc/afdo.go
index bad16a1..79fbae1 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -137,8 +137,7 @@
 	}
 
 	ctx.VisitDirectDepsWithTag(FdoProfileTag, func(m android.Module) {
-		if ctx.OtherModuleHasProvider(m, FdoProfileProvider) {
-			info := ctx.OtherModuleProvider(m, FdoProfileProvider).(FdoProfileInfo)
+		if info, ok := android.OtherModuleProvider(ctx, m, FdoProfileProvider); ok {
 			c.afdo.Properties.FdoProfilePath = proptools.StringPtr(info.Path.String())
 		}
 	})
diff --git a/cc/cc.go b/cc/cc.go
index 7a06128..9f32c44 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -28,7 +28,6 @@
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
-	"android/soong/aconfig"
 	"android/soong/aidl_library"
 	"android/soong/android"
 	"android/soong/cc/config"
@@ -1744,11 +1743,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 {
@@ -1991,7 +1992,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,11 +2132,11 @@
 		}
 	}
 	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)
 }
@@ -2368,9 +2369,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)
 			}
 		}
 	}
@@ -2391,10 +2392,10 @@
 			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 {
@@ -2982,7 +2983,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{}
@@ -2992,7 +2993,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
 		}
 	})
@@ -3043,12 +3044,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,
 				)
 			}
 		}
@@ -3113,10 +3112,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
@@ -3137,7 +3136,7 @@
 				return
 			}
 
-			depExporterInfo := ctx.OtherModuleProvider(dep, FlagExporterInfoProvider).(FlagExporterInfo)
+			depExporterInfo, _ := android.OtherModuleProvider(ctx, dep, FlagExporterInfoProvider)
 
 			var ptr *android.Paths
 			var depPtr *android.Paths
@@ -3146,7 +3145,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 {
@@ -3155,7 +3154,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 {
@@ -3192,7 +3191,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 {
@@ -3207,7 +3207,6 @@
 					break
 				}
 
-				staticLibraryInfo := ctx.OtherModuleProvider(dep, StaticLibraryInfoProvider).(StaticLibraryInfo)
 				linkFile = android.OptionalPathForPath(staticLibraryInfo.StaticLibrary)
 				if libDepTag.wholeStatic {
 					ptr = &depPaths.WholeStaticLibs
@@ -3317,7 +3316,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)
@@ -3402,7 +3401,7 @@
 		bootstrap = linkable.Bootstrap()
 	}
 
-	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 
 	useStubs := false
 
@@ -3436,7 +3435,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
@@ -3482,9 +3481,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.
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 3631f19..5c5275e 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -194,13 +194,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 +2544,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 +2580,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) {
@@ -2681,7 +2681,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 +4113,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 {
diff --git a/cc/fdo_profile.go b/cc/fdo_profile.go
index 02f68b5..0893da5 100644
--- a/cc/fdo_profile.go
+++ b/cc/fdo_profile.go
@@ -43,7 +43,7 @@
 }
 
 // FdoProfileProvider is used to provide path to an fdo profile
-var FdoProfileProvider = blueprint.NewMutatorProvider(FdoProfileInfo{}, "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
@@ -62,7 +62,7 @@
 func (fp *fdoProfile) fdoProfileMutator(ctx android.BottomUpMutatorContext) {
 	if fp.properties.Profile != nil {
 		path := android.PathForModuleSrc(ctx, *fp.properties.Profile)
-		ctx.SetProvider(FdoProfileProvider, FdoProfileInfo{
+		android.SetProvider(ctx, FdoProfileProvider, FdoProfileInfo{
 			Path: path,
 		})
 	}
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 6b3a739..f5642d6 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -544,7 +544,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 +554,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 +574,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/image.go b/cc/image.go
index 4f36111..4c0c722 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -51,18 +51,6 @@
 	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 +71,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()
 }
diff --git a/cc/library.go b/cc/library.go
index 4d5a254..4c8deef 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -341,7 +341,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
@@ -1071,7 +1071,7 @@
 	ctx.CheckbuildFile(outputFile)
 
 	if library.static() {
-		ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
+		android.SetProvider(ctx, StaticLibraryInfoProvider, StaticLibraryInfo{
 			StaticLibrary:                outputFile,
 			ReuseObjects:                 library.reuseObjects,
 			Objects:                      library.objects,
@@ -1085,7 +1085,7 @@
 	}
 
 	if library.header() {
-		ctx.SetProvider(HeaderLibraryInfoProvider, HeaderLibraryInfo{})
+		android.SetProvider(ctx, HeaderLibraryInfoProvider, HeaderLibraryInfo{})
 	}
 
 	return outputFile
@@ -1235,11 +1235,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,
@@ -1256,15 +1256,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(),
 		})
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..1183b29 100644
--- a/cc/library_stub.go
+++ b/cc/library_stub.go
@@ -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(),
diff --git a/cc/linkable.go b/cc/linkable.go
index 994517c..a009c6c 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -368,7 +368,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 +391,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 +410,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 +429,4 @@
 	GeneratedHeaders  android.Paths
 }
 
-var FlagExporterInfoProvider = blueprint.NewProvider(FlagExporterInfo{})
+var FlagExporterInfoProvider = blueprint.NewProvider[FlagExporterInfo]()
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/prebuilt.go b/cc/prebuilt.go
index f68fa24..e721c53 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -132,7 +132,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 +190,7 @@
 				},
 			})
 
-			ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
+			android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
 				SharedLibrary: outputFile,
 				Target:        ctx.Target(),
 
@@ -213,7 +213,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
diff --git a/cc/sabi.go b/cc/sabi.go
index 4cd776a..1310685 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -194,8 +194,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.
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/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/vndk.go b/cc/vndk.go
index a849455..b2c6e0d 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -772,7 +772,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 +797,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..3f3a025 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -171,7 +171,7 @@
 			p.androidMkSuffix = ""
 		}
 
-		ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
+		android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
 			SharedLibrary: in,
 			Target:        ctx.Target(),
 
diff --git a/genrule/allowlists.go b/genrule/allowlists.go
index 374e736..cbefa45 100644
--- a/genrule/allowlists.go
+++ b/genrule/allowlists.go
@@ -23,7 +23,6 @@
 
 	SandboxingDenyModuleList = []string{
 		// go/keep-sorted start
-		"CtsApkVerityTestDebugFiles",
 		"aidl_camera_build_version",
 		// go/keep-sorted end
 	}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 0512c15..87f6392 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -393,7 +393,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
diff --git a/java/aar.go b/java/aar.go
index af99cd0..2ad8fdf 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -805,7 +805,8 @@
 		},
 	)
 
-	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())
 
@@ -825,7 +826,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)
@@ -860,12 +861,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,
 		})
 	}
@@ -1069,7 +1070,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",
@@ -1106,7 +1107,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])
@@ -1123,7 +1125,7 @@
 	aarRTxt := 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},
@@ -1228,7 +1230,7 @@
 	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,
@@ -1256,11 +1258,11 @@
 				},
 			})
 		}
-
-		ctx.SetProvider(JniPackageProvider, JniPackageInfo{
-			JniPackages: a.jniPackages,
-		})
 	}
+
+	android.SetProvider(ctx, JniPackageProvider, JniPackageInfo{
+		JniPackages: a.jniPackages,
+	})
 }
 
 func (a *AARImport) HeaderJars() android.Paths {
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/androidmk.go b/java/androidmk.go
index 809f9b5..cbf9abb 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -207,11 +207,7 @@
 
 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",
diff --git a/java/app.go b/java/app.go
index ee82a32..8b28dac 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"
@@ -411,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()
 }
@@ -436,7 +435,7 @@
 }
 
 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
 }
@@ -509,7 +508,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 "+
@@ -537,7 +536,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()...)
@@ -745,7 +744,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
 	}
 
@@ -890,8 +890,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 {
@@ -975,7 +973,7 @@
 			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...)
 		}
 
@@ -1295,7 +1293,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 {
diff --git a/java/app_import.go b/java/app_import.go
index c5d09fd..ff0f5fe 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -257,7 +257,7 @@
 		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
 	}
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 8f29bb3..ef4626e 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")
 	}
 
diff --git a/java/app_test.go b/java/app_test.go
index 0936b28..861c047 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -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) {
diff --git a/java/base.go b/java/base.go
index 7cd2820..0d3e4db 100644
--- a/java/base.go
+++ b/java/base.go
@@ -24,7 +24,6 @@
 	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 
-	"android/soong/aconfig"
 	"android/soong/android"
 	"android/soong/dexpreopt"
 	"android/soong/java/config"
@@ -618,7 +617,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) {
@@ -685,7 +684,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) {
@@ -1143,7 +1142,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
@@ -1178,7 +1177,7 @@
 			return
 		}
 
-		ctx.SetProvider(JavaInfoProvider, JavaInfo{
+		android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
 			HeaderJars:                     android.PathsIfNonNil(j.headerJarFile),
 			TransitiveLibsHeaderJars:       j.transitiveLibsHeaderJars,
 			TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars,
@@ -1572,7 +1571,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)
@@ -1694,9 +1693,9 @@
 
 	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),
 		TransitiveLibsHeaderJars:       j.transitiveLibsHeaderJars,
 		TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars,
@@ -1726,7 +1725,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 {
@@ -1912,7 +1911,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 {
@@ -2037,7 +2036,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)
 			}
@@ -2209,15 +2208,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:
@@ -2289,7 +2287,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,
 				})
 			}
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 191a65e..010dbec 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -240,7 +240,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
@@ -352,7 +353,7 @@
 	}
 }
 
-var BootclasspathFragmentApexContentInfoProvider = blueprint.NewProvider(BootclasspathFragmentApexContentInfo{})
+var BootclasspathFragmentApexContentInfoProvider = blueprint.NewProvider[BootclasspathFragmentApexContentInfo]()
 
 // BootclasspathFragmentApexContentInfo contains the bootclasspath_fragments contributions to the
 // apex contents.
@@ -500,7 +501,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 +513,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 +534,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 +624,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 +745,7 @@
 	}
 
 	// Make the information available for the sdk snapshot.
-	ctx.SetProvider(HiddenAPIInfoForSdkProvider, HiddenAPIInfoForSdk{
+	android.SetProvider(ctx, HiddenAPIInfoForSdkProvider, HiddenAPIInfoForSdk{
 		FlagFilesByCategory: flagFilesByCategory,
 		HiddenAPIFlagOutput: flagOutput,
 	})
@@ -876,7 +877,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.
@@ -1065,15 +1066,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,7 +1094,7 @@
 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
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 828de21..216c3b3 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -272,7 +272,7 @@
 	`)
 
 	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"
 
@@ -456,7 +456,7 @@
 
 	// 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)
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..2017801 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -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..509e701 100644
--- a/java/code_metadata_test.go
+++ b/java/code_metadata_test.go
@@ -30,9 +30,7 @@
 	).Module().(*soongTesting.CodeMetadataModule)
 
 	// Check that the provider has the right contents
-	data := result.ModuleProvider(
-		module, soongTesting.CodeMetadataProviderKey,
-	).(soongTesting.CodeMetadataProviderData)
+	data, _ := android.SingletonModuleProvider(result, module, soongTesting.CodeMetadataProviderKey)
 	if !strings.HasSuffix(
 		data.IntermediatePath.String(), "/intermediateCodeMetadata.pb",
 	) {
@@ -114,7 +112,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/device_host_converter.go b/java/device_host_converter.go
index ee8a15a..834651f 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,7 +130,7 @@
 		d.combinedHeaderJar = d.headerJars[0]
 	}
 
-	ctx.SetProvider(JavaInfoProvider, JavaInfo{
+	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
 		HeaderJars:                     d.headerJars,
 		ImplementationAndResourcesJars: d.implementationAndResourceJars,
 		ImplementationJars:             d.implementationJars,
diff --git a/java/dex.go b/java/dex.go
index 6f1c09d..fbb8418 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -223,6 +223,13 @@
 	if err != nil {
 		ctx.PropertyErrorf("min_sdk_version", "%s", err)
 	}
+	if effectiveVersion.FinalOrFutureInt() >= 35 {
+		// V is 35, but we have not bumped the SDK version yet, so check for both.
+		if ctx.Config().PlatformSdkVersion().FinalInt() >= 35 ||
+			ctx.Config().PlatformSdkCodename() == "VanillaIceCream" {
+			flags = append([]string{"-JDcom.android.tools.r8.dexContainerExperiment"}, flags...)
+		}
+	}
 
 	// If the specified SDK level is 10000, then configure the compiler to use the
 	// current platform SDK level and to compile the build as a platform build.
@@ -261,7 +268,7 @@
 	// See b/20667396
 	var proguardRaiseDeps classpath
 	ctx.VisitDirectDepsWithTag(proguardRaiseTag, func(m android.Module) {
-		dep := ctx.OtherModuleProvider(m, JavaInfoProvider).(JavaInfo)
+		dep, _ := android.OtherModuleProvider(ctx, m, JavaInfoProvider)
 		proguardRaiseDeps = append(proguardRaiseDeps, dep.HeaderJars...)
 	})
 
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index fe8c5fb..1e289c5 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -79,18 +79,25 @@
 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
@@ -166,12 +173,12 @@
 }
 
 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
 }
 
@@ -258,6 +265,17 @@
 	return defaultInstallPath
 }
 
+// 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.dexpreopt(ctx, dexJarFile)
+}
+
 func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.WritablePath) {
 	global := dexpreopt.GetGlobalConfig(ctx)
 
@@ -346,11 +364,15 @@
 
 	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),
 		DexLocation:     dexLocation,
-		BuildPath:       android.PathForModuleOut(ctx, "dexpreopt", moduleName(ctx)+".jar").OutputPath,
+		BuildPath:       android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, moduleName(ctx)+".jar").OutputPath,
 		DexPath:         dexJarFile,
 		ManifestPath:    android.OptionalPathForPath(d.manifestFile),
 		UncompressedDex: d.uncompressedDex,
@@ -380,7 +402,7 @@
 		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) {
@@ -394,7 +416,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 +438,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 +447,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 +476,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:          dexJarStem,
 					outputPathOnHost:    install.From,
 					installDirOnDevice:  installPath,
 					installFileOnDevice: installBase,
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 5fb36df..e158ed3 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() {
@@ -502,6 +504,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 +527,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 +547,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 ctx.OtherModuleHasProvider(am, android.PrebuiltSelectionInfoProvider) {
+			psi = ctx.OtherModuleProvider(am, android.PrebuiltSelectionInfoProvider).(android.PrebuiltSelectionInfoMap)
+		}
+	})
+	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 +581,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
 				}
@@ -682,7 +719,7 @@
 					pair.jarModule.Name(),
 					pair.apex)
 			}
-			bootclasspathFragmentInfo := ctx.OtherModuleProvider(fragment, BootclasspathFragmentApexContentInfoProvider).(BootclasspathFragmentApexContentInfo)
+			bootclasspathFragmentInfo, _ := android.OtherModuleProvider(ctx, fragment, BootclasspathFragmentApexContentInfoProvider)
 			jar, err := bootclasspathFragmentInfo.DexBootJarPathForContentModule(pair.jarModule)
 			if err != nil {
 				ctx.ModuleErrorf("%s", err)
@@ -823,6 +860,27 @@
 	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()
+}
+
 // Generate boot image build rules for a specific target.
 func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) bootImageVariantOutputs {
 
@@ -865,6 +923,13 @@
 
 	invocationPath := outputPath.ReplaceExtension(ctx, "invocation")
 
+	apexNameToBcpInfoMap := map[string]android.ApexExportsInfo{}
+	ctx.VisitDirectDepsWithTag(dexpreoptBootJarDepTag, func(am android.Module) {
+		if info, exists := android.OtherModuleProvider(ctx, am, android.ApexExportsInfoProvider); exists {
+			apexNameToBcpInfoMap[info.ApexName] = info
+		}
+	})
+
 	cmd.Tool(globalSoong.Dex2oat).
 		Flag("--avoid-storing-invocation").
 		FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
@@ -877,16 +942,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, apexNameToBcpInfoMap)
 			if importedProfile == nil {
 				ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but '%[2]s' "+
 					"doesn't provide a profile",
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 2bd696c..254b2c1 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
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..138c9c3 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -363,8 +363,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 +375,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 +385,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)
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index 17cad89..7bcaca1 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)
 	}
 }
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/hiddenapi.go b/java/hiddenapi.go
index fe3fe7b..e9ee3a2 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -94,7 +94,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
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index fbc0197..8011f34 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -579,8 +579,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 +599,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 +616,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 +748,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 +776,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...)
@@ -1404,7 +1402,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
 			}
diff --git a/java/hiddenapi_monolithic.go b/java/hiddenapi_monolithic.go
index 5956e3c..a61018d 100644
--- a/java/hiddenapi_monolithic.go
+++ b/java/hiddenapi_monolithic.go
@@ -67,8 +67,7 @@
 
 		case *ClasspathFragmentElement:
 			fragment := e.Module()
-			if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) {
-				info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo)
+			if info, ok := android.OtherModuleProvider(ctx, fragment, HiddenAPIInfoProvider); ok {
 				monolithicInfo.append(&info)
 			} else {
 				ctx.ModuleErrorf("%s does not provide hidden API information", fragment)
@@ -90,4 +89,4 @@
 	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/java.go b/java/java.go
index 9be8bfd..51e8c34 100644
--- a/java/java.go
+++ b/java/java.go
@@ -239,7 +239,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 {
@@ -295,7 +295,7 @@
 	JacocoReportClassesFile android.Path
 }
 
-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 +305,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 {
@@ -647,7 +647,7 @@
 
 func shouldUncompressDex(ctx android.ModuleContext, 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
 	}
 
@@ -687,7 +687,7 @@
 	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 +695,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
 	}
@@ -1216,12 +1216,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 +1257,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 +1624,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 +1632,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),
 	})
@@ -1902,19 +1902,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)
@@ -2002,7 +2002,7 @@
 
 	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),
@@ -2100,6 +2100,7 @@
 
 	// output file containing classes.dex and resources
 	dexJarFile        OptionalDexJarPath
+	dexJarFileErr     error
 	dexJarInstallFile android.Path
 
 	combinedClasspathFile android.Path
@@ -2188,7 +2189,8 @@
 	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
 	}
 
@@ -2219,8 +2221,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,12 +2248,15 @@
 	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
+				return
 			}
 			dexJarFileApexRootRelative := apexRootRelativePathToJavaLib(j.BaseModuleName())
 			if dexOutputPath := di.PrebuiltExportPath(dexJarFileApexRootRelative); dexOutputPath != nil {
@@ -2320,7 +2324,7 @@
 		}
 	}
 
-	ctx.SetProvider(JavaInfoProvider, JavaInfo{
+	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
 		HeaderJars:                     android.PathsIfNonNil(j.combinedClasspathFile),
 		TransitiveLibsHeaderJars:       j.transitiveLibsHeaderJars,
 		TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars,
@@ -2375,6 +2379,9 @@
 }
 
 func (j *Import) DexJarBuildPath() OptionalDexJarPath {
+	if j.dexJarFileErr != nil {
+		panic(j.dexJarFileErr.Error())
+	}
 	return j.dexJarFile
 }
 
@@ -2570,7 +2577,7 @@
 		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
 	}
diff --git a/java/java_test.go b/java/java_test.go
index 8e83fc4..b9dc453 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -618,8 +618,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{}) {
@@ -2226,7 +2224,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())
 }
 
@@ -2432,7 +2431,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)
 }
 
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..5a684a8 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -413,8 +413,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",
@@ -661,7 +660,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/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 0d52614..88d1ae8 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -180,7 +180,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 +219,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 +241,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 +255,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 +389,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/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..352b243 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -262,8 +262,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..38bd301 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -673,8 +673,7 @@
 }
 
 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
 
@@ -1451,7 +1450,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 +1470,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 {
@@ -2036,8 +2035,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)
 }
 
@@ -2379,7 +2378,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.
@@ -2592,14 +2592,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,12 +2685,15 @@
 	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
+				return
 			}
 			dexJarFileApexRootRelative := apexRootRelativePathToJavaLib(module.BaseModuleName())
 			if dexOutputPath := di.PrebuiltExportPath(dexJarFileApexRootRelative); dexOutputPath != nil {
@@ -2760,6 +2755,9 @@
 func (module *SdkLibraryImport) DexJarBuildPath() OptionalDexJarPath {
 	// The dex implementation jar extracted from the .apex file should be used in preference to the
 	// source.
+	if module.dexJarFileErr != nil {
+		panic(module.dexJarFileErr.Error())
+	}
 	if module.dexJarFile.IsSet() {
 		return module.dexJarFile
 	}
@@ -2960,7 +2958,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 +3065,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..63419d6 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -132,7 +132,7 @@
 	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",
@@ -950,6 +950,7 @@
 	})
 
 	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
@@ -1022,6 +1023,7 @@
 	})
 
 	CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{
+		`all_apex_contributions`,
 		`dex2oatd`,
 		`prebuilt_sdklib.stubs`,
 		`prebuilt_sdklib.stubs.source`,
@@ -1085,9 +1087,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 +1168,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",
diff --git a/java/system_modules.go b/java/system_modules.go
index 0efa1a4..1c79171 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -159,7 +159,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...)
 	})
 
diff --git a/java/system_modules_test.go b/java/system_modules_test.go
index 7b5a386..2ceca5d 100644
--- a/java/system_modules_test.go
+++ b/java/system_modules_test.go
@@ -24,7 +24,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
diff --git a/java/test_spec_test.go b/java/test_spec_test.go
index 7f06785..f628b4b 100644
--- a/java/test_spec_test.go
+++ b/java/test_spec_test.go
@@ -34,9 +34,7 @@
 	).Module().(*soongTesting.TestSpecModule)
 
 	// Check that the provider has the right contents
-	data := result.ModuleProvider(
-		module, soongTesting.TestSpecProviderKey,
-	).(soongTesting.TestSpecProviderData)
+	data, _ := android.SingletonModuleProvider(result, module, soongTesting.TestSpecProviderKey)
 	if !strings.HasSuffix(
 		data.IntermediatePath.String(), "/intermediateTestSpecMetadata.pb",
 	) {
@@ -122,7 +120,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..5959c49 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
@@ -570,6 +571,11 @@
 		}
 `
 
+	bp += `
+		all_apex_contributions {
+			name: "all_apex_contributions",
+		}
+`
 	return bp
 }
 
@@ -617,7 +623,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 +643,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/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/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/test.go b/python/test.go
index f15a8fc..782f39f 100644
--- a/python/test.go
+++ b/python/test.go
@@ -208,7 +208,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 {
diff --git a/rust/afdo.go b/rust/afdo.go
index 3534ee6..323ee36 100644
--- a/rust/afdo.go
+++ b/rust/afdo.go
@@ -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/image.go b/rust/image.go
index c2e250c..d0218f0 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
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..d021076 100644
--- a/rust/protobuf.go
+++ b/rust/protobuf.go
@@ -178,7 +178,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/rust.go b/rust/rust.go
index 02ec22d..6f4631d 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -23,7 +23,6 @@
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
-	"android/soong/aconfig"
 	"android/soong/android"
 	"android/soong/cc"
 	cc_config "android/soong/cc/config"
@@ -509,7 +508,7 @@
 }
 
 func (flagExporter *flagExporter) setProvider(ctx ModuleContext) {
-	ctx.SetProvider(FlagExporterInfoProvider, FlagExporterInfo{
+	android.SetProvider(ctx, FlagExporterInfoProvider, FlagExporterInfo{
 		LinkDirs:    flagExporter.linkDirs,
 		LinkObjects: flagExporter.linkObjects,
 	})
@@ -527,7 +526,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
@@ -747,7 +746,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 +897,7 @@
 		ModuleContext: actx,
 	}
 
-	apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	apexInfo, _ := android.ModuleProvider(actx, android.ApexInfoProvider)
 	if !apexInfo.IsForPlatform() {
 		mod.hideApexVariantFromMake = true
 	}
@@ -950,7 +950,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 +978,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 +1003,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 +1148,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 +1167,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 +1278,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 +1294,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 +1347,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 +1391,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..9dda43f 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -270,7 +270,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/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/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index c89c6b0..4a0796b 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -251,7 +251,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
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..3cf7c59 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{
@@ -131,7 +130,7 @@
 	)
 	android.WriteFileRule(ctx, intermediatePath, string(protoData))
 
-	ctx.SetProvider(
+	android.SetProvider(ctx,
 		CodeMetadataProviderKey,
 		CodeMetadataProviderData{IntermediatePath: intermediatePath},
 	)
diff --git a/testing/test_spec.go b/testing/test_spec.go
index 58d9c23..d259612 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())
 		}
 	}
@@ -118,7 +119,7 @@
 	}
 	android.WriteFileRule(ctx, intermediatePath, string(protoData))
 
-	ctx.SetProvider(
+	android.SetProvider(ctx,
 		TestSpecProviderKey, TestSpecProviderData{
 			IntermediatePath: intermediatePath,
 		},
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)