Merge "Remove unused dist-related variables" into main
diff --git a/Android.bp b/Android.bp
index 523f55c..6c40661 100644
--- a/Android.bp
+++ b/Android.bp
@@ -147,16 +147,6 @@
 // Framework guests.
 cc_defaults {
     name: "cc_baremetal_defaults",
-    arch: {
-        arm64: {
-            cflags: [
-                // Prevent the compiler from optimizing code using SVE, as the
-                // baremetal environment might not have configured the hardware.
-                "-Xclang -target-feature",
-                "-Xclang -sve",
-            ],
-        },
-    },
     defaults_visibility: ["//visibility:public"],
 }
 
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index 6e2964a..40aeee4 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -17,6 +17,7 @@
         "aconfig_values.go",
         "aconfig_value_set.go",
         "all_aconfig_declarations.go",
+        "all_aconfig_declarations_extension.go",
         "exported_java_aconfig_library.go",
         "init.go",
         "testing.go",
@@ -25,6 +26,7 @@
         "aconfig_declarations_test.go",
         "aconfig_values_test.go",
         "aconfig_value_set_test.go",
+        "all_aconfig_declarations_extension_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/aconfig/all_aconfig_declarations.go b/aconfig/all_aconfig_declarations.go
index ec20099..b17820e 100644
--- a/aconfig/all_aconfig_declarations.go
+++ b/aconfig/all_aconfig_declarations.go
@@ -20,6 +20,7 @@
 
 	"android/soong/android"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -37,12 +38,18 @@
 	return module
 }
 
+type allAconfigDeclarationsInfo struct {
+	parsedFlagsFile android.Path
+}
+
+var allAconfigDeclarationsInfoProvider = blueprint.NewProvider[allAconfigDeclarationsInfo]()
+
 type allAconfigReleaseDeclarationsSingleton struct {
 	intermediateBinaryProtoPath android.OutputPath
 	intermediateTextProtoPath   android.OutputPath
 }
 
-type allAconfigReleaseDeclarationsProperties struct {
+type ApiSurfaceContributorProperties struct {
 	Api_signature_files  proptools.Configurable[[]string] `android:"arch_variant,path"`
 	Finalized_flags_file string                           `android:"arch_variant,path"`
 }
@@ -51,7 +58,9 @@
 	android.SingletonModuleBase
 
 	releaseMap map[string]allAconfigReleaseDeclarationsSingleton
-	properties allAconfigReleaseDeclarationsProperties
+	properties ApiSurfaceContributorProperties
+
+	finalizedFlags android.OutputPath
 }
 
 func (this *allAconfigDeclarationsSingleton) sortedConfigNames() []string {
@@ -63,29 +72,38 @@
 	return names
 }
 
-func (this *allAconfigDeclarationsSingleton) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func GenerateFinalizedFlagsForApiSurface(ctx android.ModuleContext, outputPath android.WritablePath,
+	parsedFlagsFile android.Path, apiSurface ApiSurfaceContributorProperties) {
+
 	apiSignatureFiles := android.Paths{}
-	for _, apiSignatureFile := range this.properties.Api_signature_files.GetOrDefault(ctx, nil) {
+	for _, apiSignatureFile := range apiSurface.Api_signature_files.GetOrDefault(ctx, nil) {
 		if path := android.PathForModuleSrc(ctx, apiSignatureFile); path != nil {
 			apiSignatureFiles = append(apiSignatureFiles, path)
 		}
 	}
-	finalizedFlagsFile := android.PathForModuleSrc(ctx, this.properties.Finalized_flags_file)
-	parsedFlagsFile := android.PathForIntermediates(ctx, "all_aconfig_declarations.pb")
-
-	output := android.PathForIntermediates(ctx, "finalized-flags.txt")
+	finalizedFlagsFile := android.PathForModuleSrc(ctx, apiSurface.Finalized_flags_file)
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   RecordFinalizedFlagsRule,
 		Inputs: append(apiSignatureFiles, finalizedFlagsFile, parsedFlagsFile),
-		Output: output,
+		Output: outputPath,
 		Args: map[string]string{
 			"api_signature_files":  android.JoinPathsWithPrefix(apiSignatureFiles, "--api-signature-file "),
 			"finalized_flags_file": "--finalized-flags-file " + finalizedFlagsFile.String(),
 			"parsed_flags_file":    "--parsed-flags-file " + parsedFlagsFile.String(),
 		},
 	})
-	ctx.Phony("all_aconfig_declarations", output)
+}
+
+func (this *allAconfigDeclarationsSingleton) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	parsedFlagsFile := android.PathForIntermediates(ctx, "all_aconfig_declarations.pb")
+	this.finalizedFlags = android.PathForIntermediates(ctx, "finalized-flags.txt")
+	GenerateFinalizedFlagsForApiSurface(ctx, this.finalizedFlags, parsedFlagsFile, this.properties)
+	ctx.Phony("all_aconfig_declarations", this.finalizedFlags)
+
+	android.SetProvider(ctx, allAconfigDeclarationsInfoProvider, allAconfigDeclarationsInfo{
+		parsedFlagsFile: parsedFlagsFile,
+	})
 }
 
 func (this *allAconfigDeclarationsSingleton) GenerateSingletonBuildActions(ctx android.SingletonContext) {
@@ -154,5 +172,5 @@
 			ctx.DistForGoalWithFilename(goal, this.releaseMap[rcName].intermediateTextProtoPath, assembleFileName(rcName, "flags.textproto"))
 		}
 	}
-	ctx.DistForGoalWithFilename("sdk", android.PathForIntermediates(ctx, "finalized-flags.txt"), "finalized-flags.txt")
+	ctx.DistForGoalWithFilename("sdk", this.finalizedFlags, "finalized-flags.txt")
 }
diff --git a/aconfig/all_aconfig_declarations_extension.go b/aconfig/all_aconfig_declarations_extension.go
new file mode 100644
index 0000000..44992cd
--- /dev/null
+++ b/aconfig/all_aconfig_declarations_extension.go
@@ -0,0 +1,90 @@
+// Copyright 2025 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aconfig
+
+import (
+	"android/soong/android"
+	"path"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+func AllAconfigDeclarationsExtensionFactory() android.Module {
+	module := &allAconfigDeclarationsExtension{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	return module
+}
+
+type allAconfigDeclarationsExtensionProperties struct {
+	// all_aconfig_declarations module that this module extends. Defaults to
+	// all_aconfig_declarations.
+	Base *string
+
+	// Directory where the dist artifact should be placed in.
+	Dist_dir *string
+
+	ApiSurfaceContributorProperties
+}
+
+type allAconfigDeclarationsExtension struct {
+	android.ModuleBase
+
+	properties allAconfigDeclarationsExtensionProperties
+
+	finalizedFlags android.ModuleOutPath
+}
+
+type allAconfigDeclarationsDependencyTagStruct struct {
+	blueprint.BaseDependencyTag
+}
+
+var allAconfigDeclarationsDependencyTag allAconfigDeclarationsDependencyTagStruct
+
+func (ext *allAconfigDeclarationsExtension) DepsMutator(ctx android.BottomUpMutatorContext) {
+	ctx.AddDependency(ctx.Module(), allAconfigDeclarationsDependencyTag, proptools.StringDefault(ext.properties.Base, "all_aconfig_declarations"))
+}
+
+func (ext *allAconfigDeclarationsExtension) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+
+	var parsedFlagsFile android.Path
+	ctx.VisitDirectDepsProxyWithTag(allAconfigDeclarationsDependencyTag, func(proxy android.ModuleProxy) {
+		if info, ok := android.OtherModuleProvider(ctx, proxy, allAconfigDeclarationsInfoProvider); ok {
+			parsedFlagsFile = info.parsedFlagsFile
+		} else {
+			ctx.PropertyErrorf("base", "base must provide allAconfigDeclarationsInfo")
+		}
+	})
+
+	ext.finalizedFlags = android.PathForModuleOut(ctx, "finalized-flags.txt")
+
+	GenerateFinalizedFlagsForApiSurface(ctx,
+		ext.finalizedFlags,
+		parsedFlagsFile,
+		ext.properties.ApiSurfaceContributorProperties,
+	)
+
+	ctx.Phony(ctx.ModuleName(), ext.finalizedFlags)
+
+	// This module must not set any provider or call `ctx.SetOutputFiles`!
+	// This module is only used to depend on the singleton module all_aconfig_declarations and
+	// generate the custom finalized-flags.txt file in dist builds, and should not be depended
+	// by other modules.
+}
+
+func (ext *allAconfigDeclarationsExtension) MakeVars(ctx android.MakeVarsContext) {
+	ctx.DistForGoalWithFilename("sdk", ext.finalizedFlags, path.Join(proptools.String(ext.properties.Dist_dir), "finalized-flags.txt"))
+}
diff --git a/aconfig/all_aconfig_declarations_extension_test.go b/aconfig/all_aconfig_declarations_extension_test.go
new file mode 100644
index 0000000..1207096
--- /dev/null
+++ b/aconfig/all_aconfig_declarations_extension_test.go
@@ -0,0 +1,50 @@
+// Copyright 2025 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aconfig
+
+import (
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestAllAconfigDeclarationsExtension(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithAconfigBuildComponents,
+		android.FixtureMergeMockFs(
+			android.MockFS{
+				"a.txt":     nil,
+				"flags.txt": nil,
+			},
+		),
+	).RunTestWithBp(t, `
+		all_aconfig_declarations {
+			name: "all_aconfig_declarations",
+		}
+
+		all_aconfig_declarations_extension {
+			name: "custom_aconfig_declarations",
+			base: "all_aconfig_declarations",
+			api_signature_files: [
+				"a.txt",
+			],
+			finalized_flags_file: "flags.txt",
+		}
+	`)
+
+	finalizedFlags := result.ModuleForTests("custom_aconfig_declarations", "").Output("finalized-flags.txt")
+	android.AssertStringContainsEquals(t, "must depend on all_aconfig_declarations", strings.Join(finalizedFlags.Inputs.Strings(), " "), "all_aconfig_declarations.pb", true)
+}
diff --git a/aconfig/build_flags/build_flags_singleton.go b/aconfig/build_flags/build_flags_singleton.go
index 3b40755..e76db49 100644
--- a/aconfig/build_flags/build_flags_singleton.go
+++ b/aconfig/build_flags/build_flags_singleton.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"fmt"
 )
 
 // A singleton module that collects all of the build flags declared in the
@@ -120,4 +121,26 @@
 		ctx.DistForGoalWithFilename(goal, this.configsBinaryProtoPath, "build_flags/all_release_config_contributions.pb")
 		ctx.DistForGoalWithFilename(goal, this.configsTextProtoPath, "build_flags/all_release_config_contributions.textproto")
 	}
+
+	if ctx.Config().HasDeviceProduct() {
+		flagsDir := android.PathForOutput(ctx, "release-config")
+		baseAllRelease := fmt.Sprintf("all_release_configs-%s", ctx.Config().DeviceProduct())
+
+		distAllReleaseConfigsArtifact := func(ext string) {
+			ctx.DistForGoalWithFilename(
+				"droid",
+				flagsDir.Join(ctx, fmt.Sprintf("%s.%s", baseAllRelease, ext)),
+				fmt.Sprintf("build_flags/all_release_configs.%s", ext),
+			)
+		}
+
+		distAllReleaseConfigsArtifact("pb")
+		distAllReleaseConfigsArtifact("textproto")
+		distAllReleaseConfigsArtifact("json")
+		ctx.DistForGoalWithFilename(
+			"droid",
+			flagsDir.Join(ctx, fmt.Sprintf("inheritance_graph-%s.dot", ctx.Config().DeviceProduct())),
+			fmt.Sprintf("build_flags/inheritance_graph-%s.dot", ctx.Config().DeviceProduct()),
+		)
+	}
 }
diff --git a/aconfig/init.go b/aconfig/init.go
index 3dcec5c..b2fe5a3 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -104,6 +104,7 @@
 				`        --mode=exported` +
 				`        --allow-instrumentation ${use_new_storage}` +
 				`        --new-exported ${use_new_exported}` +
+				`        --single-exported-file true` +
 				`        --check-api-level ${check_api_level}` +
 				`        --out ${out}.tmp; ` +
 				`  fi ` +
@@ -131,4 +132,5 @@
 	ctx.RegisterModuleType("aconfig_value_set", ValueSetFactory)
 	ctx.RegisterSingletonModuleType("all_aconfig_declarations", AllAconfigDeclarationsFactory)
 	ctx.RegisterParallelSingletonType("exported_java_aconfig_library", ExportedJavaDeclarationsLibraryFactory)
+	ctx.RegisterModuleType("all_aconfig_declarations_extension", AllAconfigDeclarationsExtensionFactory)
 }
diff --git a/android/module.go b/android/module.go
index 80275a3..3295e93 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1091,6 +1091,10 @@
 	InstallAlwaysNeededDependencyTag
 }{}
 
+func IsVintfDepTag(depTag blueprint.DependencyTag) bool {
+	return depTag == vintfDepTag
+}
+
 func addVintfFragmentDeps(ctx BottomUpMutatorContext) {
 	// Vintf manifests in the recovery partition will be ignored.
 	if !ctx.Device() || ctx.Module().InstallInRecovery() {
@@ -1109,7 +1113,7 @@
 			// of nil pointer dereference errors, but we should resolve the missing dependencies.
 			continue
 		}
-		if vintfModule, ok := vintf.(*vintfFragmentModule); ok {
+		if vintfModule, ok := vintf.(*VintfFragmentModule); ok {
 			vintfPartition := vintfModule.PartitionTag(deviceConfig)
 			if modPartition != vintfPartition {
 				ctx.ModuleErrorf("Module %q(%q) and Vintf_fragment %q(%q) are installed to different partitions.",
diff --git a/android/mutator.go b/android/mutator.go
index 76487fb..d6166d2 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -67,7 +67,6 @@
 }
 
 type RegisterMutatorsContext interface {
-	TopDown(name string, m TopDownMutator) MutatorHandle
 	BottomUp(name string, m BottomUpMutator) MutatorHandle
 	BottomUpBlueprint(name string, m blueprint.BottomUpMutator) MutatorHandle
 	Transition(name string, m VariationTransitionMutator) TransitionMutatorHandle
@@ -195,17 +194,6 @@
 	finalDeps = append(finalDeps, f)
 }
 
-type TopDownMutator func(TopDownMutatorContext)
-
-type TopDownMutatorContext interface {
-	BaseModuleContext
-}
-
-type topDownMutatorContext struct {
-	bp blueprint.TopDownMutatorContext
-	baseModuleContext
-}
-
 type BottomUpMutator func(BottomUpMutatorContext)
 
 type BottomUpMutatorContext interface {
@@ -281,8 +269,8 @@
 }
 
 // An outgoingTransitionContextImpl and incomingTransitionContextImpl is created for every dependency of every module
-// for each transition mutator.  bottomUpMutatorContext and topDownMutatorContext are created once for every module
-// for every BottomUp or TopDown mutator.  Use a global pool for each to avoid reallocating every time.
+// for each transition mutator.  bottomUpMutatorContext is created once for every module for every BottomUp mutator.
+// Use a global pool for each to avoid reallocating every time.
 var (
 	outgoingTransitionContextPool = sync.Pool{
 		New: func() any { return &outgoingTransitionContextImpl{} },
@@ -293,10 +281,6 @@
 	bottomUpMutatorContextPool = sync.Pool{
 		New: func() any { return &bottomUpMutatorContext{} },
 	}
-
-	topDownMutatorContextPool = sync.Pool{
-		New: func() any { return &topDownMutatorContext{} },
-	}
 )
 
 type bottomUpMutatorContext struct {
@@ -371,24 +355,6 @@
 	return name
 }
 
-func (x *registerMutatorsContext) TopDown(name string, m TopDownMutator) MutatorHandle {
-	f := func(ctx blueprint.TopDownMutatorContext) {
-		if a, ok := ctx.Module().(Module); ok {
-			moduleContext := a.base().baseModuleContextFactory(ctx)
-			actx := topDownMutatorContextPool.Get().(*topDownMutatorContext)
-			defer topDownMutatorContextPool.Put(actx)
-			*actx = topDownMutatorContext{
-				bp:                ctx,
-				baseModuleContext: moduleContext,
-			}
-			m(actx)
-		}
-	}
-	mutator := &mutator{name: x.mutatorName(name), topDownMutator: f}
-	x.mutators = append(x.mutators, mutator)
-	return mutator
-}
-
 func (mutator *mutator) componentName() string {
 	return mutator.name
 }
@@ -398,8 +364,6 @@
 	var handle blueprint.MutatorHandle
 	if mutator.bottomUpMutator != nil {
 		handle = blueprintCtx.RegisterBottomUpMutator(mutator.name, mutator.bottomUpMutator)
-	} else if mutator.topDownMutator != nil {
-		handle = blueprintCtx.RegisterTopDownMutator(mutator.name, mutator.topDownMutator)
 	} else if mutator.transitionMutator != nil {
 		handle := blueprintCtx.RegisterTransitionMutator(mutator.name, mutator.transitionMutator)
 		if mutator.neverFar {
@@ -529,11 +493,11 @@
 	ctx.BottomUp("deps", depsMutator).UsesReverseDependencies()
 }
 
-// android.topDownMutatorContext either has to embed blueprint.TopDownMutatorContext, in which case every method that
+// android.bottomUpMutatorContext either has to embed blueprint.BottomUpMutatorContext, in which case every method that
 // has an overridden version in android.BaseModuleContext has to be manually forwarded to BaseModuleContext to avoid
-// ambiguous method errors, or it has to store a blueprint.TopDownMutatorContext non-embedded, in which case every
+// ambiguous method errors, or it has to store a blueprint.BottomUpMutatorContext non-embedded, in which case every
 // non-overridden method has to be forwarded.  There are fewer non-overridden methods, so use the latter.  The following
-// methods forward to the identical blueprint versions for topDownMutatorContext and bottomUpMutatorContext.
+// methods forward to the identical blueprint versions for bottomUpMutatorContext.
 
 func (b *bottomUpMutatorContext) Rename(name string) {
 	b.bp.Rename(name)
diff --git a/android/mutator_test.go b/android/mutator_test.go
index 60a6119..123c6b2 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -54,7 +54,7 @@
 	ctx.AddDependency(ctx.Module(), nil, m.props.Deps_missing_deps...)
 }
 
-func addMissingDependenciesMutator(ctx TopDownMutatorContext) {
+func addMissingDependenciesMutator(ctx BottomUpMutatorContext) {
 	ctx.AddMissingDependencies(ctx.Module().(*mutatorTestModule).props.Mutator_missing_deps)
 }
 
@@ -72,7 +72,7 @@
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 			ctx.RegisterModuleType("test", mutatorTestModuleFactory)
 			ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-				ctx.TopDown("add_missing_dependencies", addMissingDependenciesMutator)
+				ctx.BottomUp("add_missing_dependencies", addMissingDependenciesMutator)
 			})
 		}),
 		FixtureWithRootAndroidBp(bp),
diff --git a/android/provider.go b/android/provider.go
index 81d17a1..b48fd91 100644
--- a/android/provider.go
+++ b/android/provider.go
@@ -4,8 +4,8 @@
 	"github.com/google/blueprint"
 )
 
-// OtherModuleProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
-// TopDownMutatorContext for use in OtherModuleProvider.
+// OtherModuleProviderContext is a helper interface that is a subset of ModuleContext or BottomUpMutatorContext
+// for use in OtherModuleProvider.
 type OtherModuleProviderContext interface {
 	otherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
 }
@@ -13,7 +13,6 @@
 var _ OtherModuleProviderContext = BaseModuleContext(nil)
 var _ OtherModuleProviderContext = ModuleContext(nil)
 var _ OtherModuleProviderContext = BottomUpMutatorContext(nil)
-var _ OtherModuleProviderContext = TopDownMutatorContext(nil)
 var _ OtherModuleProviderContext = SingletonContext(nil)
 var _ OtherModuleProviderContext = (*TestContext)(nil)
 
@@ -21,8 +20,7 @@
 // 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.
+// OtherModuleProviderContext is a helper interface that accepts ModuleContext or BottomUpMutatorContext.
 func OtherModuleProvider[K any](ctx OtherModuleProviderContext, module blueprint.Module, provider blueprint.ProviderKey[K]) (K, bool) {
 	value, ok := ctx.otherModuleProvider(getWrappedModule(module), provider)
 	if !ok {
@@ -37,8 +35,8 @@
 	return value
 }
 
-// ModuleProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
-// TopDownMutatorContext for use in ModuleProvider.
+// ModuleProviderContext is a helper interface that is a subset of ModuleContext or BottomUpMutatorContext
+// for use in ModuleProvider.
 type ModuleProviderContext interface {
 	provider(provider blueprint.AnyProviderKey) (any, bool)
 }
@@ -46,14 +44,12 @@
 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.
+// ModuleProviderContext is a helper interface that accepts ModuleContext or BottomUpMutatorContext.
 func ModuleProvider[K any](ctx ModuleProviderContext, provider blueprint.ProviderKey[K]) (K, bool) {
 	value, ok := ctx.provider(provider)
 	if !ok {
@@ -63,8 +59,8 @@
 	return value.(K), ok
 }
 
-// SetProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
-// TopDownMutatorContext for use in SetProvider.
+// SetProviderContext is a helper interface that is a subset of ModuleContext or BottomUpMutatorContext
+// for use in SetProvider.
 type SetProviderContext interface {
 	setProvider(provider blueprint.AnyProviderKey, value any)
 }
@@ -72,15 +68,13 @@
 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.
+// SetProviderContext is a helper interface that accepts ModuleContext or BottomUpMutatorContext.
 func SetProvider[K any](ctx SetProviderContext, provider blueprint.ProviderKey[K], value K) {
 	ctx.setProvider(provider, value)
 }
diff --git a/android/register.go b/android/register.go
index 332ec27..10c9114 100644
--- a/android/register.go
+++ b/android/register.go
@@ -89,7 +89,6 @@
 type mutator struct {
 	name              string
 	bottomUpMutator   blueprint.BottomUpMutator
-	topDownMutator    blueprint.TopDownMutator
 	transitionMutator blueprint.TransitionMutator
 
 	usesRename              bool
diff --git a/android/util.go b/android/util.go
index 30d8ec6..e8d9301 100644
--- a/android/util.go
+++ b/android/util.go
@@ -221,13 +221,13 @@
 	diff2 := []T{}
 	m1 := setFromList(l1)
 	m2 := setFromList(l2)
-	for t := range m1 {
+	for _, t := range l1 {
 		if _, ok := m2[t]; !ok {
 			diff1 = append(diff1, t)
 			listsDiffer = true
 		}
 	}
-	for t := range m2 {
+	for _, t := range l2 {
 		if _, ok := m1[t]; !ok {
 			diff2 = append(diff2, t)
 			listsDiffer = true
diff --git a/android/vintf_fragment.go b/android/vintf_fragment.go
index a3343fd..85beb72 100644
--- a/android/vintf_fragment.go
+++ b/android/vintf_fragment.go
@@ -19,8 +19,9 @@
 	Src string `android:"path"`
 }
 
-type vintfFragmentModule struct {
+type VintfFragmentModule struct {
 	ModuleBase
+	ApexModuleBase
 
 	properties vintfFragmentProperties
 
@@ -40,7 +41,7 @@
 // Vintf fragment files formerly listed in vintf_fragment property would be transformed into
 // this module type.
 func vintfLibraryFactory() Module {
-	m := &vintfFragmentModule{}
+	m := &VintfFragmentModule{}
 	m.AddProperties(
 		&m.properties,
 	)
@@ -49,7 +50,7 @@
 	return m
 }
 
-func (m *vintfFragmentModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+func (m *VintfFragmentModule) GenerateAndroidBuildActions(ctx ModuleContext) {
 	builder := NewRuleBuilder(pctx, ctx)
 	srcVintfFragment := PathForModuleSrc(ctx, m.properties.Src)
 	processedVintfFragment := PathForModuleOut(ctx, srcVintfFragment.Base())
@@ -69,8 +70,12 @@
 	ctx.InstallFile(m.installDirPath, processedVintfFragment.Base(), processedVintfFragment)
 }
 
+func (m *VintfFragmentModule) OutputFile() Path {
+	return m.outputFilePath
+}
+
 // Make this module visible to AndroidMK so it can be referenced from modules defined from Android.mk files
-func (m *vintfFragmentModule) AndroidMkEntries() []AndroidMkEntries {
+func (m *VintfFragmentModule) AndroidMkEntries() []AndroidMkEntries {
 	return []AndroidMkEntries{{
 		Class:      "ETC",
 		OutputFile: OptionalPathForPath(m.outputFilePath),
@@ -82,3 +87,11 @@
 		},
 	}}
 }
+
+var _ ApexModule = (*VintfFragmentModule)(nil)
+
+// Implements android.ApexModule
+func (m *VintfFragmentModule) ShouldSupportSdkVersion(ctx BaseModuleContext, sdkVersion ApiLevel) error {
+	// VintfFragmetModule is independent from the SDK version.
+	return nil
+}
diff --git a/apex/apex.go b/apex/apex.go
index 33538fb..0481658 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1475,6 +1475,12 @@
 	return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, config)
 }
 
+func apexFileForVintfFragment(ctx android.BaseModuleContext, vintfFragment *android.VintfFragmentModule) apexFile {
+	dirInApex := filepath.Join("etc", "vintf")
+
+	return newApexFile(ctx, vintfFragment.OutputFile(), vintfFragment.BaseModuleName(), dirInApex, etc, vintfFragment)
+}
+
 // javaModule is an interface to handle all Java modules (java_library, dex_import, etc) in the same
 // way.
 type javaModule interface {
@@ -2160,7 +2166,13 @@
 		// nothing
 	} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
 		ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
+	} else if android.IsVintfDepTag(depTag) {
+		if vf, ok := child.(*android.VintfFragmentModule); ok {
+			apexFile := apexFileForVintfFragment(ctx, vf)
+			vctx.filesInfo = append(vctx.filesInfo, apexFile)
+		}
 	}
+
 	return false
 }
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index f88c09e..6c1a2d6 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -12184,3 +12184,35 @@
 	fileList := android.ContentFromFileRuleForTests(t, result, partition.Output("fileList"))
 	android.AssertDeepEquals(t, "filesystem with apex", "apex/myapex.apex\n", fileList)
 }
+
+func TestVintfFragmentInApex(t *testing.T) {
+	t.Parallel()
+	ctx := testApex(t, apex_default_bp+`
+		apex {
+			name: "myapex",
+			manifest: ":myapex.manifest",
+			androidManifest: ":myapex.androidmanifest",
+			key: "myapex.key",
+			binaries: [ "mybin" ],
+			updatable: false,
+		}
+
+		cc_binary {
+			name: "mybin",
+			srcs: ["mybin.cpp"],
+			vintf_fragment_modules: ["my_vintf_fragment.xml"],
+			apex_available: [ "myapex" ],
+		}
+
+		vintf_fragment {
+			name: "my_vintf_fragment.xml",
+			src: "my_vintf_fragment.xml",
+		}
+	`)
+
+	generateFsRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("generateFsConfig")
+	cmd := generateFsRule.RuleParams.Command
+
+	// Ensure that vintf fragment file is being installed
+	ensureContains(t, cmd, "/etc/vintf/my_vintf_fragment.xml ")
+}
diff --git a/apex/builder.go b/apex/builder.go
index b34dc84..03a0bb9 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -528,9 +528,10 @@
 	})
 }
 
-func isVintfFragment(fi apexFile) bool {
+func shouldApplyAssembleVintf(fi apexFile) bool {
 	isVintfFragment, _ := path.Match("etc/vintf/*", fi.path())
-	return isVintfFragment
+	_, fromVintfFragmentModule := fi.module.(*android.VintfFragmentModule)
+	return isVintfFragment && !fromVintfFragmentModule
 }
 
 func runAssembleVintf(ctx android.ModuleContext, vintfFragment android.Path) android.Path {
@@ -639,7 +640,7 @@
 			copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath)
 		} else {
 			// Copy the file into APEX
-			if !a.testApex && isVintfFragment(fi) {
+			if !a.testApex && shouldApplyAssembleVintf(fi) {
 				// copy the output of assemble_vintf instead of the original
 				vintfFragment := runAssembleVintf(ctx, fi.builtFile)
 				copyCommands = append(copyCommands, "cp -f "+vintfFragment.String()+" "+destPath)
diff --git a/cc/binary.go b/cc/binary.go
index 4b77bea..c4791c5 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -426,7 +426,7 @@
 	validations = append(validations, objs.tidyDepFiles...)
 	linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
 
-	if generatedLib := generateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil {
+	if generatedLib := GenerateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil {
 		deps.StaticLibs = append(deps.StaticLibs, generatedLib)
 	}
 
diff --git a/cc/builder.go b/cc/builder.go
index 16f006d..f4f8596 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -817,7 +817,7 @@
 }
 
 // Generate a Rust staticlib from a list of rlibDeps. Returns nil if TransformRlibstoStaticlib is nil or rlibDeps is empty.
-func generateRustStaticlib(ctx android.ModuleContext, rlibDeps []RustRlibDep) android.Path {
+func GenerateRustStaticlib(ctx android.ModuleContext, rlibDeps []RustRlibDep) android.Path {
 	if TransformRlibstoStaticlib == nil && len(rlibDeps) > 0 {
 		// This should only be reachable if a module defines Rust deps in static_libs and
 		// soong-rust hasn't been loaded alongside soong-cc (e.g. in soong-cc tests).
diff --git a/cc/library.go b/cc/library.go
index dce3b92..532b7e9 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -1237,7 +1237,7 @@
 	linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
 	linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
 
-	if generatedLib := generateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil && !library.BuildStubs() {
+	if generatedLib := GenerateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil && !library.BuildStubs() {
 		if ctx.Module().(*Module).WholeRustStaticlib {
 			deps.WholeStaticLibs = append(deps.WholeStaticLibs, generatedLib)
 		} else {
diff --git a/filesystem/android_device.go b/filesystem/android_device.go
index b783d0f..960c96a 100644
--- a/filesystem/android_device.go
+++ b/filesystem/android_device.go
@@ -153,7 +153,7 @@
 		}
 	}
 
-	//a.buildTargetFilesZip(ctx) TODO(b/393203512): re-enable target_files.zip
+	a.buildTargetFilesZip(ctx)
 	var deps []android.Path
 	if proptools.String(a.partitionProps.Super_partition_name) != "" {
 		superImage := ctx.GetDirectDepProxyWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag)
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 9f2b239..3f77433 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -537,14 +537,27 @@
 	f.buildEventLogtagsFile(ctx, builder, rebasedDir, &fullInstallPaths)
 	f.buildAconfigFlagsFiles(ctx, builder, specs, rebasedDir, &fullInstallPaths)
 	f.filesystemBuilder.BuildLinkerConfigFile(ctx, builder, rebasedDir, &fullInstallPaths)
+	// Assemeble the staging dir and output a timestamp
+	builder.Command().Text("touch").Output(f.fileystemStagingDirTimestamp(ctx))
+	builder.Build("assemble_filesystem_staging_dir", fmt.Sprintf("Assemble filesystem staging dir %s", f.BaseModuleName()))
 
+	// Create a new rule builder for build_image
+	builder = android.NewRuleBuilder(pctx, ctx)
 	var mapFile android.Path
-	var outputHermetic android.Path
+	var outputHermetic android.WritablePath
 	var buildImagePropFile android.Path
 	var buildImagePropFileDeps android.Paths
 	switch f.fsType(ctx) {
 	case ext4Type, erofsType, f2fsType:
-		f.output, outputHermetic, buildImagePropFile, buildImagePropFileDeps = f.buildImageUsingBuildImage(ctx, builder, rootDir, rebasedDir)
+		buildImagePropFile, buildImagePropFileDeps = f.buildPropFile(ctx)
+		output := android.PathForModuleOut(ctx, f.installFileName())
+		f.buildImageUsingBuildImage(ctx, builder, buildImageParams{rootDir, buildImagePropFile, buildImagePropFileDeps, output})
+		f.output = output
+		// Create the hermetic img file using a separate rule builder so that it can be built independently
+		hermeticBuilder := android.NewRuleBuilder(pctx, ctx)
+		outputHermetic = android.PathForModuleOut(ctx, "for_target_files", f.installFileName())
+		propFileHermetic := f.propFileForHermeticImg(ctx, hermeticBuilder, buildImagePropFile)
+		f.buildImageUsingBuildImage(ctx, hermeticBuilder, buildImageParams{rootDir, propFileHermetic, buildImagePropFileDeps, outputHermetic})
 		mapFile = f.getMapFile(ctx)
 	case compressedCpioType:
 		f.output = f.buildCpioImage(ctx, builder, rootDir, true)
@@ -590,6 +603,10 @@
 	f.setVbmetaPartitionProvider(ctx)
 }
 
+func (f *filesystem) fileystemStagingDirTimestamp(ctx android.ModuleContext) android.WritablePath {
+	return android.PathForModuleOut(ctx, "staging_dir.timestamp")
+}
+
 func (f *filesystem) setVbmetaPartitionProvider(ctx android.ModuleContext) {
 	var extractedPublicKey android.ModuleOutPath
 	if f.properties.Avb_private_key != nil {
@@ -783,21 +800,26 @@
 	return f.partitionName()
 }
 
+type buildImageParams struct {
+	// inputs
+	rootDir  android.OutputPath
+	propFile android.Path
+	toolDeps android.Paths
+	// outputs
+	output android.WritablePath
+}
+
 func (f *filesystem) buildImageUsingBuildImage(
 	ctx android.ModuleContext,
 	builder *android.RuleBuilder,
-	rootDir android.OutputPath,
-	rebasedDir android.OutputPath,
-) (android.Path, android.Path, android.Path, android.Paths) {
+	params buildImageParams) {
 	// run host_init_verifier
 	// Ideally we should have a concept of pluggable linters that verify the generated image.
 	// While such concept is not implement this will do.
 	// TODO(b/263574231): substitute with pluggable linter.
 	builder.Command().
 		BuiltTool("host_init_verifier").
-		FlagWithArg("--out_system=", rootDir.String()+"/system")
-
-	propFile, toolDeps := f.buildPropFile(ctx)
+		FlagWithArg("--out_system=", params.rootDir.String()+"/system")
 
 	// Most of the time, if build_image were to call a host tool, it accepts the path to the
 	// host tool in a field in the prop file. However, it doesn't have that option for fec, which
@@ -805,44 +827,32 @@
 	fec := ctx.Config().HostToolPath(ctx, "fec")
 	pathToolDirs := []string{filepath.Dir(fec.String())}
 
-	output := android.PathForModuleOut(ctx, f.installFileName())
-	builder.Command().Text("touch").Output(f.getMapFile(ctx))
 	builder.Command().
 		Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
 		BuiltTool("build_image").
-		Text(rootDir.String()). // input directory
-		Input(propFile).
-		Implicits(toolDeps).
+		Text(params.rootDir.String()). // input directory
+		Input(params.propFile).
+		Implicits(params.toolDeps).
 		Implicit(fec).
-		Output(output).
-		Text(rootDir.String()) // directory where to find fs_config_files|dirs
-
-	// TODO (b/393203512): Re-enable hermetic img file creation for target_files.zip
-	// Add an additional cmd to create a hermetic img file. This will contain pinned timestamps e.g.
-	//propFilePinnedTimestamp := android.PathForModuleOut(ctx, "for_target_files", "prop")
-	//builder.Command().Textf("cat").Input(propFile).Flag(">").Output(propFilePinnedTimestamp).
-	//	Textf(" && echo use_fixed_timestamp=true >> %s", propFilePinnedTimestamp).
-	//	Textf(" && echo block_list=%s >> %s", f.getMapFile(ctx).String(), propFilePinnedTimestamp) // mapfile will be an implicit output
-
-	//outputHermetic := android.PathForModuleOut(ctx, "for_target_files", f.installFileName())
-	//builder.Command().
-	//	Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
-	//	BuiltTool("build_image").
-	//	Text(rootDir.String()). // input directory
-	//	Flag(propFilePinnedTimestamp.String()).
-	//	Implicits(toolDeps).
-	//	Implicit(fec).
-	//	Output(outputHermetic).
-	//	Text(rootDir.String()) // directory where to find fs_config_files|dirs
+		Implicit(f.fileystemStagingDirTimestamp(ctx)). // assemble the staging directory
+		Output(params.output).
+		Text(params.rootDir.String()) // directory where to find fs_config_files|dirs
 
 	if f.properties.Partition_size != nil {
-		assertMaxImageSize(builder, output, *f.properties.Partition_size, false)
+		assertMaxImageSize(builder, params.output, *f.properties.Partition_size, false)
 	}
 
 	// rootDir is not deleted. Might be useful for quick inspection.
-	builder.Build("build_filesystem_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
+	builder.Build("build_"+params.output.String(), fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
+}
 
-	return output, nil, propFile, toolDeps
+func (f *filesystem) propFileForHermeticImg(ctx android.ModuleContext, builder *android.RuleBuilder, inputPropFile android.Path) android.Path {
+	propFilePinnedTimestamp := android.PathForModuleOut(ctx, "for_target_files", "prop")
+	builder.Command().Textf("cat").Input(inputPropFile).Flag(">").Output(propFilePinnedTimestamp).
+		Textf(" && echo use_fixed_timestamp=true >> %s", propFilePinnedTimestamp).
+		Textf(" && echo block_list=%s >> %s", f.getMapFile(ctx).String(), propFilePinnedTimestamp) // mapfile will be an implicit output
+	builder.Command().Text("touch").Output(f.getMapFile(ctx))
+	return propFilePinnedTimestamp
 }
 
 func (f *filesystem) buildFileContexts(ctx android.ModuleContext) android.Path {
@@ -1053,6 +1063,7 @@
 	output := android.PathForModuleOut(ctx, f.installFileName())
 	cmd := builder.Command().
 		BuiltTool("mkbootfs").
+		Implicit(f.fileystemStagingDirTimestamp(ctx)).
 		Text(rootDir.String()) // input directory
 
 	for i := range len(rootDirs) {
@@ -1373,3 +1384,9 @@
 		return true
 	})
 }
+
+func (f *filesystem) MakeVars(ctx android.MakeVarsModuleContext) {
+	if f.Name() == ctx.Config().SoongDefinedSystemImage() {
+		ctx.StrictRaw("SOONG_DEFINED_SYSTEM_IMAGE_PATH", f.output.String())
+	}
+}
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index d9bf242..37260c1 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -332,7 +332,7 @@
 	`)
 
 	filesystem := result.ModuleForTests("myfilesystem", "android_common_cov")
-	inputs := filesystem.Output("myfilesystem.img").Implicits
+	inputs := filesystem.Output("staging_dir.timestamp").Implicits
 	android.AssertStringListContains(t, "filesystem should have libfoo(cov)",
 		inputs.Strings(),
 		"out/soong/.intermediates/libfoo/android_arm64_armv8-a_shared_cov/libfoo.so")
diff --git a/java/base.go b/java/base.go
index d89c324..3a435a1 100644
--- a/java/base.go
+++ b/java/base.go
@@ -2948,14 +2948,18 @@
 // Get the jarjar rule text for a given provider for the fully resolved rules. Classes that map
 // to "" won't be in this list because they shouldn't be renamed yet.
 func getJarJarRuleText(provider *JarJarProviderData) string {
-	result := ""
+	result := strings.Builder{}
 	for _, orig := range android.SortedKeys(provider.Rename) {
 		renamed := provider.Rename[orig]
 		if renamed != "" {
-			result += "rule " + orig + " " + renamed + "\n"
+			result.WriteString("rule ")
+			result.WriteString(orig)
+			result.WriteString(" ")
+			result.WriteString(renamed)
+			result.WriteString("\n")
 		}
 	}
-	return result
+	return result.String()
 }
 
 // Repackage the flags if the jarjar rule txt for the flags is generated
diff --git a/phony/phony.go b/phony/phony.go
index 807b95b..4f61c45 100644
--- a/phony/phony.go
+++ b/phony/phony.go
@@ -38,9 +38,11 @@
 
 type phony struct {
 	android.ModuleBase
+
 	requiredModuleNames       []string
 	hostRequiredModuleNames   []string
 	targetRequiredModuleNames []string
+	outputDeps                android.Paths
 }
 
 func PhonyFactory() android.Module {
@@ -54,6 +56,14 @@
 	p.requiredModuleNames = ctx.RequiredModuleNames(ctx)
 	p.hostRequiredModuleNames = ctx.HostRequiredModuleNames()
 	p.targetRequiredModuleNames = ctx.TargetRequiredModuleNames()
+
+	ctx.VisitDirectDepsWithTag(android.RequiredDepTag, func(dep android.Module) {
+		if o, ok := android.OtherModuleProvider(ctx, dep, android.OutputFilesProvider); ok {
+			p.outputDeps = append(p.outputDeps, o.DefaultOutputFiles...)
+		}
+	})
+
+	ctx.Phony(p.Name(), p.outputDeps...)
 }
 
 func (p *phony) AndroidMk() android.AndroidMkData {
@@ -77,6 +87,10 @@
 				fmt.Fprintln(w, "LOCAL_TARGET_REQUIRED_MODULES :=",
 					strings.Join(p.targetRequiredModuleNames, " "))
 			}
+			if len(p.outputDeps) > 0 {
+				fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES :=",
+					strings.Join(p.outputDeps.Strings(), " "))
+			}
 			// AconfigUpdateAndroidMkData may have added elements to Extra.  Process them here.
 			for _, extra := range data.Extra {
 				extra(w, nil)
diff --git a/rust/builder.go b/rust/builder.go
index 8a869aa..1b6a6c1 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -171,7 +171,7 @@
 	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "rlib"))
 }
 
-// TransformRlibstoStaticlib is assumed to be called from the cc module, and
+// TransformRlibstoStaticlib is assumed to be callable from the cc module, and
 // thus needs to reconstruct the common set of flags which need to be passed
 // to the rustc compiler.
 func TransformRlibstoStaticlib(ctx android.ModuleContext, mainSrc android.Path, deps []cc.RustRlibDep,
@@ -185,7 +185,7 @@
 		rustPathDeps.linkDirs = append(rustPathDeps.linkDirs, rlibDep.LinkDirs...)
 	}
 
-	ccModule := ctx.(cc.ModuleContext).Module().(*cc.Module)
+	mod := ctx.Module().(cc.LinkableInterface)
 	toolchain := config.FindToolchain(ctx.Os(), ctx.Arch())
 	t := transformProperties{
 		// Crate name can be a predefined value as this is a staticlib and
@@ -195,10 +195,10 @@
 		crateName:       "generated_rust_staticlib",
 		is64Bit:         toolchain.Is64Bit(),
 		targetTriple:    toolchain.RustTriple(),
-		bootstrap:       ccModule.Bootstrap(),
-		inRecovery:      ccModule.InRecovery(),
-		inRamdisk:       ccModule.InRamdisk(),
-		inVendorRamdisk: ccModule.InVendorRamdisk(),
+		bootstrap:       mod.Bootstrap(),
+		inRecovery:      mod.InRecovery(),
+		inRamdisk:       mod.InRamdisk(),
+		inVendorRamdisk: mod.InVendorRamdisk(),
 
 		// crateType indicates what type of crate to build
 		crateType: "staticlib",
@@ -402,6 +402,11 @@
 		linkFlags = append(linkFlags, dynamicLinker)
 	}
 
+	if generatedLib := cc.GenerateRustStaticlib(ctx, deps.ccRlibDeps); generatedLib != nil {
+		deps.StaticLibs = append(deps.StaticLibs, generatedLib)
+		linkFlags = append(linkFlags, generatedLib.String())
+	}
+
 	libFlags := makeLibFlags(deps)
 
 	// Collect dependencies
diff --git a/rust/library.go b/rust/library.go
index 24ae8b0..7f5861f 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -737,12 +737,15 @@
 	if library.rlib() {
 		library.flagExporter.exportStaticLibs(deps.staticLibObjects...)
 	}
-
 	// Since we have FFI rlibs, we need to collect their includes as well
 	if library.static() || library.shared() || library.rlib() || library.stubs() {
-		android.SetProvider(ctx, cc.FlagExporterInfoProvider, cc.FlagExporterInfo{
+		ccExporter := cc.FlagExporterInfo{
 			IncludeDirs: android.FirstUniquePaths(library.includeDirs),
-		})
+		}
+		if library.rlib() {
+			ccExporter.RustRlibDeps = append(ccExporter.RustRlibDeps, deps.reexportedCcRlibDeps...)
+		}
+		android.SetProvider(ctx, cc.FlagExporterInfoProvider, ccExporter)
 	}
 
 	if library.shared() || library.stubs() {
diff --git a/rust/rust.go b/rust/rust.go
index ad68d60..4eec5d2 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -495,6 +495,10 @@
 	depFlags     []string
 	depLinkFlags []string
 
+	// track cc static-libs that have Rlib dependencies
+	reexportedCcRlibDeps []cc.RustRlibDep
+	ccRlibDeps           []cc.RustRlibDep
+
 	// linkDirs are link paths passed via -L to rustc. linkObjects are objects passed directly to the linker
 	// Both of these are exported and propagate to dependencies.
 	linkDirs              []string
@@ -1531,6 +1535,13 @@
 					}
 				}
 
+				if !mod.Rlib() {
+					depPaths.ccRlibDeps = append(depPaths.ccRlibDeps, exportedInfo.RustRlibDeps...)
+				} else {
+					// rlibs need to reexport these
+					depPaths.reexportedCcRlibDeps = append(depPaths.reexportedCcRlibDeps, exportedInfo.RustRlibDeps...)
+				}
+
 			case depTag == procMacroDepTag:
 				directProcMacroDeps = append(directProcMacroDeps, linkableInfo)
 				mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName)
@@ -1651,8 +1662,8 @@
 				} else {
 					// Otherwise add to staticLibObjects, which only propagate through rlibs to their dependents.
 					depPaths.staticLibObjects = append(depPaths.staticLibObjects, ccLibPath.String())
-
 				}
+
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
 
 				exportedInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider)
@@ -1660,6 +1671,14 @@
 				depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
 				depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
 				depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
+
+				if !mod.Rlib() {
+					// rlibs don't need to build the generated static library, so they don't need to track these.
+					depPaths.ccRlibDeps = append(depPaths.ccRlibDeps, exportedInfo.RustRlibDeps...)
+				} else {
+					depPaths.reexportedCcRlibDeps = append(depPaths.reexportedCcRlibDeps, exportedInfo.RustRlibDeps...)
+				}
+
 				directStaticLibDeps = append(directStaticLibDeps, linkableInfo)
 
 				// Record baseLibName for snapshots.
@@ -1815,6 +1834,8 @@
 	depPaths.depIncludePaths = android.FirstUniquePaths(depPaths.depIncludePaths)
 	depPaths.depSystemIncludePaths = android.FirstUniquePaths(depPaths.depSystemIncludePaths)
 	depPaths.depLinkFlags = android.FirstUniqueStrings(depPaths.depLinkFlags)
+	depPaths.reexportedCcRlibDeps = android.FirstUniqueFunc(depPaths.reexportedCcRlibDeps, cc.EqRustRlibDeps)
+	depPaths.ccRlibDeps = android.FirstUniqueFunc(depPaths.ccRlibDeps, cc.EqRustRlibDeps)
 
 	return depPaths
 }
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 4390f55..2e016f0 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -449,12 +449,26 @@
 		}
 
 		rust_ffi_static {
+			name: "libfoo_from_rlib",
+			crate_name: "foo_from_rlib",
+			srcs: ["src/lib.rs"],
+			export_include_dirs: ["foo_includes"]
+		}
+
+		rust_ffi_static {
 			name: "libbuzz",
 			crate_name: "buzz",
 			srcs: ["src/lib.rs"],
 			export_include_dirs: ["buzz_includes"]
 		}
 
+		rust_ffi_static {
+			name: "libbuzz_from_rlib",
+			crate_name: "buzz_from_rlib",
+			srcs: ["src/lib.rs"],
+			export_include_dirs: ["buzz_includes"]
+		}
+
 		cc_library_shared {
 			name: "libcc_shared",
 			srcs:["foo.c"],
@@ -468,12 +482,34 @@
 			whole_static_libs: ["libfoo"],
 		}
 
+		cc_library_static {
+			name: "libcc_static_from_rlib",
+			srcs:["foo.c"],
+			static_libs: ["libbuzz_from_rlib"],
+			whole_static_libs: ["libfoo_from_rlib"],
+		}
+
 		cc_binary {
 			name: "ccBin",
 			srcs:["foo.c"],
 			static_libs: ["libcc_static", "libbar"],
 		}
-		`)
+
+		rust_library {
+			name: "librs",
+			srcs:["src/foo.rs"],
+			crate_name: "rs",
+			static_libs: ["libcc_static_from_rlib"],
+		}
+
+		rust_binary {
+			name: "rsBin",
+			srcs:["src/foo.rs"],
+			crate_name: "rsBin",
+			rlibs: ["librs", "libbar"],
+			static_libs: ["libcc_static"],
+		}
+	`)
 
 	libbar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_rlib_rlib-std").Rule("rustc")
 	libcc_shared_rustc := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("rustc")
@@ -482,6 +518,9 @@
 	ccbin_rustc := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("rustc")
 	ccbin_ld := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("ld")
 	ccbin_cc := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("cc")
+	rustbin_genlib := ctx.ModuleForTests("rsBin", "android_arm64_armv8-a").Output("generated_rust_staticlib/librustlibs.a")
+	rustbin := ctx.ModuleForTests("rsBin", "android_arm64_armv8-a").Output("unstripped/rsBin")
+	librs_rlib := ctx.ModuleForTests("librs", "android_arm64_armv8-a_rlib_dylib-std").MaybeOutput("generated_rust_staticlib/librustlibs.a")
 
 	if !strings.Contains(libbar.Args["rustcFlags"], "crate-type=rlib") {
 		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "rlib", libbar.Args["rustcFlags"])
@@ -513,7 +552,7 @@
 	// Make sure the static lib is included in the cc command
 	if !strings.Contains(ccbin_ld.Args["libFlags"], "generated_rust_staticlib/librustlibs.a") {
 		t.Errorf("missing generated static library in linker step libFlags, expecting %#v, libFlags: %#v",
-			"ccBin.generated_rust_staticlib.a", ccbin_ld.Args["libFlags"])
+			"generated_rust_staticlib/librustlibs.a", ccbin_ld.Args["libFlags"])
 	}
 
 	// Make sure the static lib includes are in the ld command
@@ -534,11 +573,43 @@
 		t.Errorf("Missing direct dependency libbar when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"])
 	}
 
+	// Make sure the static lib is included in the rustc command
+	if !strings.Contains(rustbin.Args["linkFlags"], "generated_rust_staticlib/librustlibs.a") {
+		t.Errorf("missing generated static library in linker step libFlags in Rust module, expecting %#v, libFlags: %#v",
+			"generated_rust_staticlib/librustlibs.a", rustbin.Args["libFlags"])
+	}
+
+	// Make sure that direct dependencies and indirect whole static dependencies are
+	// propagating correctly for the rlib -> cc_library_static -> rust_* generated library example.
+	if !strings.Contains(rustbin_genlib.Args["libFlags"], "--extern foo=") {
+		t.Errorf("Missing indirect whole_static_lib dependency libfoo from cc static_lib when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
+	}
+	if strings.Contains(rustbin_genlib.Args["libFlags"], "--extern buzz=") {
+		t.Errorf("Indirect rlib dependency libbuzz from cc static_lib found when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
+	}
+	if strings.Contains(rustbin_genlib.Args["libFlags"], "--extern bar=") {
+		t.Errorf("Direct rlib dependency libbar getting included in the generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
+	}
+	if !strings.Contains(rustbin_genlib.Args["libFlags"], "--extern foo_from_rlib=") {
+		t.Errorf("Missing indirect whole_static_lib dependency libfoo_from_rlib from cc static_lib when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
+	}
+	if strings.Contains(rustbin_genlib.Args["libFlags"], "--extern buzz_from_rlib=") {
+		// While static-libs propagate for rust modules, this is not the
+		// expected behavior for cc modules. Thus, libbuzz_from_rlib would
+		// be expected to have to be re-declared as a direct rlib dependency.
+		t.Errorf("Indirect rlib dependency libbuzz_from_rlib from cc static_lib found when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
+	}
+
 	// Test indirect includes propagation
 	if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ifoo_includes") {
 		t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
 			"-Ifoo_includes", ccbin_cc.Args)
 	}
+
+	// Make sure we're not generating superfluous mto staticlibs.
+	if librs_rlib.Rule != nil {
+		t.Error("rlibs should not be generating mto staticlibs", "rlib", libbar.Args["rustcFlags"])
+	}
 }
 
 func assertString(t *testing.T, got, expected string) {
diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh
index ef0f44a..b600443 100755
--- a/scripts/build-ndk-prebuilts.sh
+++ b/scripts/build-ndk-prebuilts.sh
@@ -23,9 +23,18 @@
 # TODO: remove ALLOW_MISSING_DEPENDENCIES=true when all the riscv64
 # dependencies exist (currently blocked by http://b/273792258).
 # TODO: remove BUILD_BROKEN_DISABLE_BAZEL=1 when bazel supports riscv64 (http://b/262192655).
+#
+# LTO is disabled because the NDK compiler is not necessarily in-sync with the
+# compiler used to build the platform sysroot, and the sysroot includes static
+# libraries which would be incompatible with mismatched compilers when built
+# with LTO. Disabling LTO globally for the NDK sysroot is okay because the only
+# compiled code in the sysroot that will end up in apps is those static
+# libraries.
+# https://github.com/android/ndk/issues/1591
 TARGET_RELEASE=trunk_staging \
 ALLOW_MISSING_DEPENDENCIES=true \
 BUILD_BROKEN_DISABLE_BAZEL=1 \
+DISABLE_LTO=true \
     TARGET_PRODUCT=ndk build/soong/soong_ui.bash --make-mode --soong-only ${OUT_DIR}/soong/ndk.timestamp
 
 if [ -n "${DIST_DIR}" ]; then