Merge "Shipping_api_level in build." into main
diff --git a/aconfig/build_flags/Android.bp b/aconfig/build_flags/Android.bp
index 69e4316..b3c7339 100644
--- a/aconfig/build_flags/Android.bp
+++ b/aconfig/build_flags/Android.bp
@@ -14,6 +14,7 @@
     ],
     srcs: [
         "all_build_flag_declarations.go",
+        "build_flags.go",
         "declarations.go",
         "init.go",
     ],
diff --git a/aconfig/build_flags/build_flags.go b/aconfig/build_flags/build_flags.go
new file mode 100644
index 0000000..e878b5a
--- /dev/null
+++ b/aconfig/build_flags/build_flags.go
@@ -0,0 +1,71 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build_flags
+
+import (
+	"fmt"
+
+	"android/soong/android"
+)
+
+const (
+	outJsonFileName = "build_flags.json"
+)
+
+func init() {
+	registerBuildFlagsModuleType(android.InitRegistrationContext)
+}
+
+func registerBuildFlagsModuleType(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("build_flags_json", buildFlagsFactory)
+}
+
+type buildFlags struct {
+	android.ModuleBase
+
+	outputPath android.OutputPath
+}
+
+func buildFlagsFactory() android.Module {
+	module := &buildFlags{}
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	return module
+}
+
+func (m *buildFlags) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Read the build_flags_<partition>.json file generated by soong
+	// 'release-config' command.
+	srcPath := android.PathForOutput(ctx, "release-config", fmt.Sprintf("build_flags_%s.json", m.PartitionTag(ctx.DeviceConfig())))
+	m.outputPath = android.PathForModuleOut(ctx, outJsonFileName).OutputPath
+
+	// The 'release-config' command is called for every build, and generates the
+	// build_flags_<partition>.json file.
+	// Update the output file only if the source file is changed.
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.CpIfChanged,
+		Input:  srcPath,
+		Output: m.outputPath,
+	})
+
+	installPath := android.PathForModuleInstall(ctx, "etc")
+	ctx.InstallFile(installPath, outJsonFileName, m.outputPath)
+}
+
+func (m *buildFlags) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(m.outputPath),
+	}}
+}
diff --git a/aconfig/build_flags/init.go b/aconfig/build_flags/init.go
index 5907f4e..dc1369c 100644
--- a/aconfig/build_flags/init.go
+++ b/aconfig/build_flags/init.go
@@ -69,6 +69,7 @@
 
 func init() {
 	RegisterBuildComponents(android.InitRegistrationContext)
+	pctx.Import("android/soong/android")
 	pctx.HostBinToolVariable("buildFlagDeclarations", "build-flag-declarations")
 }
 
diff --git a/android/apex_contributions.go b/android/apex_contributions.go
index 91549e5..8b72f8e 100644
--- a/android/apex_contributions.go
+++ b/android/apex_contributions.go
@@ -106,7 +106,13 @@
 
 // Creates a dep to each selected apex_contributions
 func (a *allApexContributions) DepsMutator(ctx BottomUpMutatorContext) {
-	ctx.AddDependency(ctx.Module(), AcDepTag, ctx.Config().AllApexContributions()...)
+	// Skip apex_contributions if BuildApexContributionContents is true
+	// This product config var allows some products in the same family to use mainline modules from source
+	// (e.g. shiba and shiba_fullmte)
+	// Eventually these product variants will have their own release config maps.
+	if !proptools.Bool(ctx.Config().BuildIgnoreApexContributionContents()) {
+		ctx.AddDependency(ctx.Module(), AcDepTag, ctx.Config().AllApexContributions()...)
+	}
 }
 
 // Set PrebuiltSelectionInfoProvider in post deps phase
@@ -126,19 +132,13 @@
 	}
 
 	p := PrebuiltSelectionInfoMap{}
-	// Skip apex_contributions if BuildApexContributionContents is true
-	// This product config var allows some products in the same family to use mainline modules from source
-	// (e.g. shiba and shiba_fullmte)
-	// Eventually these product variants will have their own release config maps.
-	if !proptools.Bool(ctx.Config().BuildIgnoreApexContributionContents()) {
-		ctx.VisitDirectDepsWithTag(AcDepTag, func(child Module) {
-			if m, ok := child.(*apexContributions); ok {
-				addContentsToProvider(&p, m)
-			} else {
-				ctx.ModuleErrorf("%s is not an apex_contributions module\n", child.Name())
-			}
-		})
-	}
+	ctx.VisitDirectDepsWithTag(AcDepTag, func(child Module) {
+		if m, ok := child.(*apexContributions); ok {
+			addContentsToProvider(&p, m)
+		} else {
+			ctx.ModuleErrorf("%s is not an apex_contributions module\n", child.Name())
+		}
+	})
 	SetProvider(ctx, PrebuiltSelectionInfoProvider, p)
 }
 
diff --git a/android/config.go b/android/config.go
index bb49c9f..f60991a 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1466,10 +1466,6 @@
 	return c.config.productVariables.ExtraVndkVersions
 }
 
-func (c *deviceConfig) VndkUseCoreVariant() bool {
-	return Bool(c.config.productVariables.VndkUseCoreVariant) && Bool(c.config.productVariables.KeepVndk)
-}
-
 func (c *deviceConfig) SystemSdkVersions() []string {
 	return c.config.productVariables.DeviceSystemSdkVersions
 }
@@ -2120,6 +2116,7 @@
 		"RELEASE_APEX_CONTRIBUTIONS_SDKEXTENSIONS",
 		"RELEASE_APEX_CONTRIBUTIONS_SWCODEC",
 		"RELEASE_APEX_CONTRIBUTIONS_STATSD",
+		"RELEASE_APEX_CONTRIBUTIONS_TELEMETRY_TVP",
 		"RELEASE_APEX_CONTRIBUTIONS_TZDATA",
 		"RELEASE_APEX_CONTRIBUTIONS_UWB",
 		"RELEASE_APEX_CONTRIBUTIONS_WIFI",
diff --git a/android/filegroup.go b/android/filegroup.go
index a8326d4..ff0f74e 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -19,6 +19,7 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -36,9 +37,9 @@
 
 type fileGroupProperties struct {
 	// srcs lists files that will be included in this filegroup
-	Srcs []string `android:"path"`
+	Srcs proptools.Configurable[[]string] `android:"path"`
 
-	Exclude_srcs []string `android:"path"`
+	Exclude_srcs proptools.Configurable[[]string] `android:"path"`
 
 	// The base path to the files.  May be used by other modules to determine which portion
 	// of the path to use.  For example, when a filegroup is used as data in a cc_test rule,
@@ -89,7 +90,7 @@
 }
 
 func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) {
-	fg.srcs = PathsForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs)
+	fg.srcs = PathsForModuleSrcExcludes(ctx, fg.properties.Srcs.GetOrDefault(ctx, nil), fg.properties.Exclude_srcs.GetOrDefault(ctx, nil))
 	if fg.properties.Path != nil {
 		fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
 	}
diff --git a/android/module.go b/android/module.go
index c4cc5e6..dc585d2 100644
--- a/android/module.go
+++ b/android/module.go
@@ -915,6 +915,10 @@
 	// moduleInfoJSON can be filled out by GenerateAndroidBuildActions to write a JSON file that will
 	// be included in the final module-info.json produced by Make.
 	moduleInfoJSON *ModuleInfoJSON
+
+	// outputFiles stores the output of a module by tag and is used to set
+	// the OutputFilesProvider in GenerateBuildActions
+	outputFiles OutputFilesInfo
 }
 
 func (m *ModuleBase) AddJSONData(d *map[string]interface{}) {
@@ -1076,6 +1080,12 @@
 			return
 		}
 
+		// ... also don't make a dependency between native bridge arch and non-native bridge
+		// arches. b/342945184
+		if ctx.Target().NativeBridge != target.NativeBridge {
+			return
+		}
+
 		variation := target.Variations()
 		if ctx.OtherModuleFarDependencyVariantExists(variation, depName) {
 			ctx.AddFarVariationDependencies(variation, RequiredDepTag, depName)
@@ -1990,6 +2000,10 @@
 	m.buildParams = ctx.buildParams
 	m.ruleParams = ctx.ruleParams
 	m.variables = ctx.variables
+
+	if m.outputFiles.DefaultOutputFiles != nil || m.outputFiles.TaggedOutputFiles != nil {
+		SetProvider(ctx, OutputFilesProvider, m.outputFiles)
+	}
 }
 
 func SetJarJarPrefixHandler(handler func(ModuleContext)) {
@@ -2439,11 +2453,15 @@
 }
 
 func outputFilesForModule(ctx PathContext, module blueprint.Module, tag string) (Paths, error) {
+	outputFilesFromProvider, err := outputFilesForModuleFromProvider(ctx, module, tag)
+	if outputFilesFromProvider != nil || err != nil {
+		return outputFilesFromProvider, err
+	}
 	if outputFileProducer, ok := module.(OutputFileProducer); ok {
 		paths, err := outputFileProducer.OutputFiles(tag)
 		if err != nil {
-			return nil, fmt.Errorf("failed to get output file from module %q: %s",
-				pathContextName(ctx, module), err.Error())
+			return nil, fmt.Errorf("failed to get output file from module %q at tag %q: %s",
+				pathContextName(ctx, module), tag, err.Error())
 		}
 		return paths, nil
 	} else if sourceFileProducer, ok := module.(SourceFileProducer); ok {
@@ -2453,10 +2471,54 @@
 		paths := sourceFileProducer.Srcs()
 		return paths, nil
 	} else {
-		return nil, fmt.Errorf("module %q is not an OutputFileProducer", pathContextName(ctx, module))
+		return nil, fmt.Errorf("module %q is not an OutputFileProducer or SourceFileProducer", pathContextName(ctx, module))
 	}
 }
 
+// This method uses OutputFilesProvider for output files
+// *inter-module-communication*.
+// If mctx module is the same as the param module the output files are obtained
+// from outputFiles property of module base, to avoid both setting and
+// reading OutputFilesProvider before  GenerateBuildActions is finished. Also
+// only empty-string-tag is supported in this case.
+// If a module doesn't have the OutputFilesProvider, nil is returned.
+func outputFilesForModuleFromProvider(ctx PathContext, module blueprint.Module, tag string) (Paths, error) {
+	// TODO: support OutputFilesProvider for singletons
+	mctx, ok := ctx.(ModuleContext)
+	if !ok {
+		return nil, nil
+	}
+	if mctx.Module() != module {
+		if outputFilesProvider, ok := OtherModuleProvider(mctx, module, OutputFilesProvider); ok {
+			if tag == "" {
+				return outputFilesProvider.DefaultOutputFiles, nil
+			} else if taggedOutputFiles, hasTag := outputFilesProvider.TaggedOutputFiles[tag]; hasTag {
+				return taggedOutputFiles, nil
+			} else {
+				return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+			}
+		}
+	} else {
+		if tag == "" {
+			return mctx.Module().base().outputFiles.DefaultOutputFiles, nil
+		} else {
+			return nil, fmt.Errorf("unsupported tag %q for module getting its own output files", tag)
+		}
+	}
+	// TODO: Add a check for param module not having OutputFilesProvider set
+	return nil, nil
+}
+
+type OutputFilesInfo struct {
+	// default output files when tag is an empty string ""
+	DefaultOutputFiles Paths
+
+	// the corresponding output files for given tags
+	TaggedOutputFiles map[string]Paths
+}
+
+var OutputFilesProvider = blueprint.NewProvider[OutputFilesInfo]()
+
 // Modules can implement HostToolProvider and return a valid OptionalPath from HostToolPath() to
 // specify that they can be used as a tool by a genrule module.
 type HostToolProvider interface {
diff --git a/android/module_context.go b/android/module_context.go
index 18adb30..bc08911 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -212,6 +212,10 @@
 	// GenerateAndroidBuildActions.  If it is called then the struct will be written out and included in
 	// the module-info.json generated by Make, and Make will not generate its own data for this module.
 	ModuleInfoJSON() *ModuleInfoJSON
+
+	// SetOutputFiles stores the outputFiles to outputFiles property, which is used
+	// to set the OutputFilesProvider later.
+	SetOutputFiles(outputFiles Paths, tag string)
 }
 
 type moduleContext struct {
@@ -707,6 +711,21 @@
 	return moduleInfoJSON
 }
 
+func (m *moduleContext) SetOutputFiles(outputFiles Paths, tag string) {
+	if tag == "" {
+		if len(m.module.base().outputFiles.DefaultOutputFiles) > 0 {
+			m.ModuleErrorf("Module %s default OutputFiles cannot be overwritten", m.ModuleName())
+		}
+		m.module.base().outputFiles.DefaultOutputFiles = outputFiles
+	} else {
+		if _, exists := m.module.base().outputFiles.TaggedOutputFiles[tag]; exists {
+			m.ModuleErrorf("Module %s OutputFiles at tag %s cannot be overwritten", m.ModuleName(), tag)
+		} else {
+			m.module.base().outputFiles.TaggedOutputFiles[tag] = outputFiles
+		}
+	}
+}
+
 // Returns a list of paths expanded from globs and modules referenced using ":module" syntax.  The property must
 // be tagged with `android:"path" to support automatic source module dependency resolution.
 //
diff --git a/android/override_module.go b/android/override_module.go
index 55f384f..f69f963 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -253,6 +253,15 @@
 
 var overrideBaseDepTag overrideBaseDependencyTag
 
+// Override module should always override the source module.
+// Overrides are implemented as a variant of the overridden module, and the build actions are created in the
+// module context of the overridden module.
+// If we replace override module with the prebuilt of the overridden module, `GenerateAndroidBuildActions` for
+// the override module will have a very different meaning.
+func (tag overrideBaseDependencyTag) ReplaceSourceWithPrebuilt() bool {
+	return false
+}
+
 // Adds dependency on the base module to the overriding module so that they can be visited in the
 // next phase.
 func overrideModuleDepsMutator(ctx BottomUpMutatorContext) {
diff --git a/android/paths.go b/android/paths.go
index 8d92aa4..edc0700 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -565,21 +565,15 @@
 	if aModule, ok := module.(Module); ok && !aModule.Enabled(ctx) {
 		return nil, missingDependencyError{[]string{moduleName}}
 	}
-	if outProducer, ok := module.(OutputFileProducer); ok {
-		outputFiles, err := outProducer.OutputFiles(tag)
-		if err != nil {
-			return nil, fmt.Errorf("path dependency %q: %s", path, err)
-		}
-		return outputFiles, nil
-	} else if tag != "" {
-		return nil, fmt.Errorf("path dependency %q is not an output file producing module", path)
-	} else if goBinary, ok := module.(bootstrap.GoBinaryTool); ok {
+	if goBinary, ok := module.(bootstrap.GoBinaryTool); ok && tag == "" {
 		goBinaryPath := PathForGoBinary(ctx, goBinary)
 		return Paths{goBinaryPath}, nil
-	} else if srcProducer, ok := module.(SourceFileProducer); ok {
-		return srcProducer.Srcs(), nil
+	}
+	outputFiles, err := outputFilesForModule(ctx, module, tag)
+	if outputFiles != nil && err == nil {
+		return outputFiles, nil
 	} else {
-		return nil, fmt.Errorf("path dependency %q is not a source file producing module", path)
+		return nil, err
 	}
 }
 
diff --git a/android/variable.go b/android/variable.go
index 3538e2f..b939acc 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -187,6 +187,7 @@
 		Release_aidl_use_unfrozen struct {
 			Cflags          []string
 			Cmd             *string
+			Required        []string
 			Vintf_fragments []string
 		}
 	} `android:"arch_variant"`
@@ -367,7 +368,6 @@
 
 	PgoAdditionalProfileDirs []string `json:",omitempty"`
 
-	VndkUseCoreVariant         *bool `json:",omitempty"`
 	VndkSnapshotBuildArtifacts *bool `json:",omitempty"`
 
 	DirectedVendorSnapshot bool            `json:",omitempty"`
diff --git a/apex/apex.go b/apex/apex.go
index 1dec61b..e79afad 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -2112,7 +2112,7 @@
 			}
 		case bpfTag:
 			if bpfProgram, ok := child.(bpf.BpfModule); ok {
-				filesToCopy, _ := bpfProgram.OutputFiles("")
+				filesToCopy := android.OutputFilesForModule(ctx, bpfProgram, "")
 				apex_sub_dir := bpfProgram.SubDir()
 				for _, bpfFile := range filesToCopy {
 					vctx.filesInfo = append(vctx.filesInfo, apexFileForBpfProgram(ctx, bpfFile, apex_sub_dir, bpfProgram))
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 4cac0cc..c60ee73 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -11671,3 +11671,38 @@
 	checkMinSdkVersion(t, overridingModuleDifferentMinSdkVersion, "31")
 	checkHasDep(t, ctx, overridingModuleDifferentMinSdkVersion.Module(), javalibApex31Variant.Module())
 }
+
+func TestOverrideApexWithPrebuiltApexPreferred(t *testing.T) {
+	context := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		PrepareForTestWithApexBuildComponents,
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/foo-file_contexts": nil,
+		}),
+	)
+	res := context.RunTestWithBp(t, `
+		apex {
+			name: "foo",
+			key: "myapex.key",
+			apex_available_name: "com.android.foo",
+			variant_version: "0",
+			updatable: false,
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+		prebuilt_apex {
+			name: "foo",
+			src: "foo.apex",
+			prefer: true,
+		}
+		override_apex {
+			name: "myoverrideapex",
+			base: "foo",
+		}
+	`)
+
+	java.CheckModuleHasDependency(t, res.TestContext, "myoverrideapex", "android_common_myoverrideapex_myoverrideapex", "foo")
+}
diff --git a/bin/allmod b/bin/allmod
new file mode 100755
index 0000000..f7d25e5
--- /dev/null
+++ b/bin/allmod
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# List all modules for the current device, as cached in all_modules.txt. If any build change is
+# made and it should be reflected in the output, you should run 'refreshmod' first.
+
+cat $ANDROID_PRODUCT_OUT/all_modules.txt 2>/dev/null
+
diff --git a/bin/core b/bin/core
new file mode 100755
index 0000000..01dbd13
--- /dev/null
+++ b/bin/core
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# core - send SIGV and pull the core for process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must be called once per boot for core dumps to be
+#       enabled globally.
+
+set -e
+
+PID=$1;
+
+if [ -z "$PID" ]; then
+    printf "Expecting a PID!\n";
+    exit 1
+fi;
+
+CORENAME=core.$PID;
+COREPATH=/cores/$CORENAME;
+SIG=SEGV;
+
+coredump_enable $1;
+
+done=0;
+while [ $(adb shell "[ -d /proc/$PID ] && echo -n yes") ]; do
+    printf "\tSending SIG%s to %d...\n" $SIG $PID;
+    adb shell kill -$SIG $PID;
+    sleep 1;
+done;
+
+adb shell "while [ ! -f $COREPATH ] ; do echo waiting for $COREPATH to be generated; sleep 1; done"
+echo "Done: core is under $COREPATH on device.";
+
+
diff --git a/bin/coredump_enable b/bin/coredump_enable
new file mode 100755
index 0000000..da63a0c
--- /dev/null
+++ b/bin/coredump_enable
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+PID=$1;
+if [ -z "$PID" ]; then
+    printf "Expecting a PID!\n";
+    exit 1
+fi;
+echo "Setting core limit for $PID to infinite...";
+adb shell /system/bin/ulimit -P $PID -c unlimited
+
diff --git a/bin/coredump_setup b/bin/coredump_setup
new file mode 100755
index 0000000..7647659
--- /dev/null
+++ b/bin/coredump_setup
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_setup - enable core dumps globally for any process
+#                  that has the core-file-size limit set correctly
+#
+# NOTE: You must call also coredump_enable for a specific process
+#       if its core-file-size limit is not set already.
+# NOTE: Core dumps are written to ramdisk; they will not survive a reboot!
+
+set -e
+
+echo "Getting root...";
+adb root;
+adb wait-for-device;
+
+echo "Remounting root partition read-write...";
+adb shell mount -w -o remount -t rootfs rootfs;
+sleep 1;
+adb wait-for-device;
+adb shell mkdir -p /cores;
+adb shell mount -t tmpfs tmpfs /cores;
+adb shell chmod 0777 /cores;
+
+echo "Granting SELinux permission to dump in /cores...";
+adb shell restorecon -R /cores;
+
+echo "Set core pattern.";
+adb shell 'echo /cores/core.%p > /proc/sys/kernel/core_pattern';
+
+echo "Done."
+
diff --git a/bin/dirmods b/bin/dirmods
new file mode 100755
index 0000000..52d935a
--- /dev/null
+++ b/bin/dirmods
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+'''
+Lists all modules in the given directory or its decendant directories, as cached
+in module-info.json. If any build change is made, and it should be reflected in
+the output, you should run 'refreshmod' first.
+'''
+
+import sys
+sys.dont_write_bytecode = True
+
+import argparse
+import os
+
+import modinfo
+
+
+def main():
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('path')
+    args = parser.parse_args()
+
+    d = os.path.normpath(args.path)
+    prefix = d + '/'
+
+    module_info = modinfo.ReadModuleInfo()
+
+    results = set()
+    for m in module_info.values():
+        for path in m.get(u'path', []):
+            if path == d or path.startswith(prefix):
+                name = m.get(u'module_name')
+                if name:
+                    results.add(name)
+
+    for name in sorted(results):
+        print(name)
+
+if __name__ == "__main__":
+    main()
diff --git a/bin/get_abs_build_var b/bin/get_abs_build_var
new file mode 100755
index 0000000..8072cf1
--- /dev/null
+++ b/bin/get_abs_build_var
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Get the value of a build variable as an absolute path.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+$TOP/build/soong/soong_ui.bash --dumpvar-mode --abs $1
+
+exit $?
diff --git a/bin/get_build_var b/bin/get_build_var
new file mode 100755
index 0000000..9fdf55f
--- /dev/null
+++ b/bin/get_build_var
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Get the exact value of a build variable.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+$TOP/build/soong/soong_ui.bash --dumpvar-mode $1
+
+exit $?
diff --git a/bin/getlastscreenshot b/bin/getlastscreenshot
new file mode 100755
index 0000000..dfe9a6b
--- /dev/null
+++ b/bin/getlastscreenshot
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+screenshot_path=$(getscreenshotpath)
+screenshot=`adb ${adbOptions} ls ${screenshot_path} | grep Screenshot_[0-9-]*.*\.png | sort -rk 3 | cut -d " " -f 4 | head -n 1`
+if [ "$screenshot" = "" ]; then
+    echo "No screenshots found."
+    exit 1
+fi
+echo "${screenshot}"
+adb ${adbOptions} pull ${screenshot_path}/${screenshot}
+
diff --git a/bin/getprebuilt b/bin/getprebuilt
new file mode 100755
index 0000000..68e65b4
--- /dev/null
+++ b/bin/getprebuilt
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+get_abs_build_var ANDROID_PREBUILTS
+
diff --git a/bin/getscreenshotpath b/bin/getscreenshotpath
new file mode 100755
index 0000000..ff8e327
--- /dev/null
+++ b/bin/getscreenshotpath
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+echo "$(getsdcardpath)/Pictures/Screenshots"
+
diff --git a/bin/getsdcardpath b/bin/getsdcardpath
new file mode 100755
index 0000000..655659a
--- /dev/null
+++ b/bin/getsdcardpath
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+adb ${adbOptions} shell echo -n \$\{EXTERNAL_STORAGE\}
+
diff --git a/bin/gettargetarch b/bin/gettargetarch
new file mode 100755
index 0000000..e53ce3f
--- /dev/null
+++ b/bin/gettargetarch
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+get_build_var TARGET_ARCH
+
diff --git a/bin/hmm b/bin/hmm
index c3d60fa..161bad6 100755
--- a/bin/hmm
+++ b/bin/hmm
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-# Copyright (C) 2022 The Android Open Source Project
+# Copyright (C) 2024 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -14,6 +14,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
 cat <<EOF
 
 Run "m help" for help with the build system itself.
@@ -56,7 +59,6 @@
 - godir:      Go to the directory containing a file.
 - allmod:     List all modules.
 - gomod:      Go to the directory containing a module.
-- bmod:       Get the Bazel label of a Soong module if it is converted with bp2build.
 - pathmod:    Get the directory containing a module.
 - outmod:     Gets the location of a module's installed outputs with a certain extension.
 - dirmods:    Gets the modules defined in a given directory.
@@ -68,12 +70,12 @@
 - SANITIZE_HOST: Set to 'address' to use ASAN for all host modules.
 - ANDROID_QUIET_BUILD: set to 'true' to display only the essential messages.
 
-Look at build/make/envsetup for more functions:
+Look at the source to view more functions. The complete list is:
 EOF
-    local T=$(gettop)
-    local A=""
-    local i
-    for i in `(cat $T/build/envsetup.sh | sed -n "/^[[:blank:]]*function /s/function \([a-z]*\).*/\1/p" | sort | uniq`; do
+    T=$(gettop)
+    A=""
+    for i in `cat $T/build/envsetup.sh | sed -n "/^[[:blank:]]*function /s/function \([a-z_]*\).*/\1/p" | sort | uniq`; do
       A="$A $i"
     done
     echo $A
+
diff --git a/bin/installmod b/bin/installmod
new file mode 100755
index 0000000..1d0d836
--- /dev/null
+++ b/bin/installmod
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# adb install a module's apk, as cached in module-info.json. If any build change
+# is made, and it should be reflected in the output, you should run 'refreshmod' first.
+# Usage: installmod [adb install arguments] <module>
+# For example: installmod -r Dialer -> adb install -r /path/to/Dialer.apk
+
+if [[ $# -eq 0 ]]; then
+    echo "usage: installmod [adb install arguments] <module>" >&2
+    echo "" >&2
+    echo "Only flags to be passed after the \"install\" in adb install are supported," >&2
+    echo "with the exception of -s. If -s is passed it will be placed before the \"install\"." >&2
+    echo "-s must be the first flag passed if it exists." >&2
+    return 1
+fi
+
+local _path
+_path=$(outmod ${@:$#:1})
+if [ $? -ne 0 ]; then
+    return 1
+fi
+
+_path=$(echo "$_path" | grep -E \\.apk$ | head -n 1)
+if [ -z "$_path" ]; then
+    echo "Module '$1' does not produce a file ending with .apk (try 'refreshmod' if there have been build changes?)" >&2
+    return 1
+fi
+local serial_device=""
+if [[ "$1" == "-s" ]]; then
+    if [[ $# -le 2 ]]; then
+        echo "-s requires an argument" >&2
+        return 1
+    fi
+    serial_device="-s $2"
+    shift 2
+fi
+local length=$(( $# - 1 ))
+echo adb $serial_device install ${@:1:$length} $_path
+adb $serial_device install ${@:1:$length} $_path
+
diff --git a/bin/is64bit b/bin/is64bit
new file mode 100755
index 0000000..35bbcc3
--- /dev/null
+++ b/bin/is64bit
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Read the ELF header from /proc/$PID/exe to determine if the process is
+# 64-bit.
+
+set -e
+
+local PID="$1"
+if [ "$PID" ] ; then
+    if [[ "$(adb shell cat /proc/$PID/exe | xxd -l 1 -s 4 -p)" -eq "02" ]] ; then
+        echo "64"
+    else
+        echo ""
+    fi
+else
+    echo ""
+fi
+
diff --git a/bin/isviewserverstarted b/bin/isviewserverstarted
new file mode 100755
index 0000000..c7c82af
--- /dev/null
+++ b/bin/isviewserverstarted
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+adb shell service call window 3
+
diff --git a/bin/key_back b/bin/key_back
new file mode 100755
index 0000000..2de8d07
--- /dev/null
+++ b/bin/key_back
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+adb shell input keyevent 4
+
diff --git a/bin/key_home b/bin/key_home
new file mode 100755
index 0000000..653a5f9
--- /dev/null
+++ b/bin/key_home
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+adb shell input keyevent 3
+
diff --git a/bin/key_menu b/bin/key_menu
new file mode 100755
index 0000000..29b2bc6
--- /dev/null
+++ b/bin/key_menu
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+adb shell input keyevent 82
+
diff --git a/bin/list_products b/bin/list_products
new file mode 100755
index 0000000..cd8dd5c
--- /dev/null
+++ b/bin/list_products
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+# In almost call cases including get_build_var, TARGET_RELEASE is required,
+# but the list of available products is not dependent on the release config
+# (but note that the list of available release configs is dependent on the
+# product). So for list_products, we'll just set it to trunk_staging, which
+# exists everwhere, so we don't trigger the unspecified TARGET_RELEASE error.
+
+# We also unset TARGET_BUILD_APPS, so it doesn't interfere.
+
+TARGET_RELEASE=trunk_staging TARGET_BUILD_APPS= $TOP/build/soong/soong_ui.bash --dumpvar-mode all_named_products | sed 's/ /\n/g'
+
+exit $?
diff --git a/bin/list_releases b/bin/list_releases
new file mode 100755
index 0000000..ca18110
--- /dev/null
+++ b/bin/list_releases
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if [[ $# -eq 1 ]]; then
+    # Override anything that's already set
+    export TARGET_PRODUCT=$1
+elif [[ -z $TARGET_PRODUCT ]]; then
+    echo "Usage: list_releases [PRODUCT]" 1>&2
+    echo "" 1>&2
+    echo "If the optional PRODUCT parameter is bit provided, then TARGET_PRODUCT" 1>&2
+    echo "must have been set, for example by lunch or banchan." 1>&2
+    exit 1
+fi
+
+
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+# In almost call cases including get_build_var, TARGET_RELEASE is required,
+# but the list of available products is not dependent on the release config
+# (but note that the list of available release configs is dependent on the
+# product). So for list_products, we'll just set it to trunk_staging, which
+# exists everwhere, so we don't trigger the unspecified TARGET_RELEASE error.
+
+# We also unset TARGET_BUILD_APPS, so it doesn't interfere.
+
+TARGET_RELEASE=trunk_staging TARGET_BUILD_APPS= $TOP/build/soong/soong_ui.bash --dumpvar-mode ALL_RELEASE_CONFIGS_FOR_PRODUCT | sed 's/ /\n/g'
+
+exit $?
diff --git a/bin/list_variants b/bin/list_variants
new file mode 100755
index 0000000..ac89e6a
--- /dev/null
+++ b/bin/list_variants
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+echo user
+echo userdebug
+echo eng
+
diff --git a/bin/modinfo.py b/bin/modinfo.py
new file mode 100644
index 0000000..015129f
--- /dev/null
+++ b/bin/modinfo.py
@@ -0,0 +1,44 @@
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import os
+import pathlib
+import sys
+
+
+def OpenModuleInfoFile():
+    product_out = os.getenv("ANDROID_PRODUCT_OUT")
+    if not product_out:
+        if os.getenv("QUIET_VERIFYMODINFO") != "true":
+            sys.stderr.write("No ANDROID_PRODUCT_OUT. Try running 'lunch' first.\n")
+        sys.exit(1)
+    try:
+        return open(pathlib.Path(product_out) / "module-info.json")
+    except (FileNotFoundError, PermissionError):
+        if os.getenv("QUIET_VERIFYMODINFO") != "true":
+            sys.stderr.write("Could not find module-info.json. Please run 'refreshmod' first.\n")
+        sys.exit(1)
+
+
+def ReadModuleInfo():
+    with OpenModuleInfoFile() as f:
+        return json.load(f)
+
+def GetModule(modules, module_name):
+    if module_name not in modules:
+        sys.stderr.write(f"Could not find module '{module_name}' (try 'refreshmod' if there have been build changes?)\n")
+        sys.exit(1)
+    return modules[module_name]
+
diff --git a/bin/outmod b/bin/outmod
new file mode 100755
index 0000000..022ff36
--- /dev/null
+++ b/bin/outmod
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+'''
+Lists the output files of a specific module in the android tree, as cached in
+module-info.json. If any build change is made, and it should be reflected in the
+output, you should run 'refreshmod' first.
+'''
+
+import sys
+sys.dont_write_bytecode = True
+
+import argparse
+import os
+
+import modinfo
+
+
+def main():
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('module')
+    args = parser.parse_args()
+
+    for output in modinfo.GetModule(modinfo.ReadModuleInfo(), args.module)['installed']:
+        print(os.path.join(os.getenv("ANDROID_BUILD_TOP", ""), output))
+
+if __name__ == "__main__":
+    main()
+
diff --git a/bin/pathmod b/bin/pathmod
new file mode 100755
index 0000000..70cf958
--- /dev/null
+++ b/bin/pathmod
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+'''
+Get the path of a specific module in the android tree, as cached in module-info.json.
+If any build change is made, and it should be reflected in the output, you should run
+'refreshmod' first.  Note: This is the inverse of dirmods.
+'''
+
+import sys
+sys.dont_write_bytecode = True
+
+import argparse
+import os
+
+import modinfo
+
+
+def main():
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('module')
+    args = parser.parse_args()
+
+    path = modinfo.GetModule(modinfo.ReadModuleInfo(), args.module)['path'][0]
+    print(os.path.join(os.getenv("ANDROID_BUILD_TOP", ""), path))
+
+if __name__ == "__main__":
+    main()
diff --git a/bin/qpid b/bin/qpid
new file mode 100755
index 0000000..b47cb6b
--- /dev/null
+++ b/bin/qpid
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# adb install a module's apk, as cached in module-info.json. If any build change
+# is made, and it should be reflected in the output, you should run 'refreshmod' first.
+# Usage: installmod [adb install arguments] <module>
+# For example: installmod -r Dialer -> adb install -r /path/to/Dialer.apk
+
+function _impl() {
+    local prepend=''
+    local append=''
+    if [ "$1" = "--exact" ]; then
+        prepend=' '
+        append='$'
+        shift
+    elif [ "$1" = "--help" -o "$1" = "-h" ]; then
+        echo "usage: qpid [[--exact] <process name|pid>"
+        return 255
+    fi
+
+    local EXE="$1"
+    if [ "$EXE" ] ; then
+        _impl | \grep "$prepend$EXE$append"
+    else
+        adb shell ps \
+            | tr -d '\r' \
+            | sed -e 1d -e 's/^[^ ]* *\([0-9]*\).* \([^ ]*\)$/\1 \2/'
+    fi
+}
+
+_impl "$@"
diff --git a/bin/startviewserver b/bin/startviewserver
new file mode 100755
index 0000000..4d612b8
--- /dev/null
+++ b/bin/startviewserver
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+port=4939
+if [ $# -gt 0 ]; then
+    port=$1
+fi
+adb shell service call window 1 i32 $port
+
diff --git a/bin/stopviewserver b/bin/stopviewserver
new file mode 100755
index 0000000..a734e4b
--- /dev/null
+++ b/bin/stopviewserver
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+adb shell service call window 2
+
diff --git a/bin/systemstack b/bin/systemstack
new file mode 100755
index 0000000..b259133
--- /dev/null
+++ b/bin/systemstack
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# systemstack - dump the current stack trace of all threads in the system process
+# to the usual ANR traces file
+
+stacks system_server
+
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 38fbd88..2eb869e 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -65,8 +65,6 @@
 type BpfModule interface {
 	android.Module
 
-	OutputFiles(tag string) (android.Paths, error)
-
 	// Returns the sub install directory if the bpf module is included by apex.
 	SubDir() string
 }
@@ -213,6 +211,8 @@
 	}
 
 	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()})
+
+	ctx.SetOutputFiles(bpf.objs, "")
 }
 
 func (bpf *bpf) AndroidMk() android.AndroidMkData {
@@ -255,23 +255,10 @@
 	}
 }
 
-// Implements OutputFileFileProducer interface so that the obj output can be used in the data property
-// of other modules.
-func (bpf *bpf) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return bpf.objs, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
 func (bpf *bpf) SubDir() string {
 	return bpf.properties.Sub_dir
 }
 
-var _ android.OutputFileProducer = (*bpf)(nil)
-
 func BpfFactory() android.Module {
 	module := &bpf{}
 
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 0c6f97c..62ba4de 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -261,15 +261,6 @@
 		if library.coverageOutputFile.Valid() {
 			entries.SetString("LOCAL_PREBUILT_COVERAGE_ARCHIVE", library.coverageOutputFile.String())
 		}
-
-		if library.useCoreVariant {
-			entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-			entries.SetBool("LOCAL_NO_NOTICE_FILE", true)
-			entries.SetBool("LOCAL_VNDK_DEPEND_ON_CORE_VARIANT", true)
-		}
-		if library.checkSameCoreVariant {
-			entries.SetBool("LOCAL_CHECK_SAME_VNDK_VARIANTS", true)
-		}
 	})
 
 	if library.shared() && !library.buildStubs() {
@@ -495,14 +486,14 @@
 	ctx.subAndroidMk(entries, p.libraryDecorator)
 	if p.shared() {
 		ctx.subAndroidMk(entries, &p.prebuiltLinker)
-		androidMkWriteAllowUndefinedSymbols(p.baseLinker, entries)
+		androidMkWritePrebuiltOptions(p.baseLinker, entries)
 	}
 }
 
 func (p *prebuiltBinaryLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
 	ctx.subAndroidMk(entries, p.binaryDecorator)
 	ctx.subAndroidMk(entries, &p.prebuiltLinker)
-	androidMkWriteAllowUndefinedSymbols(p.baseLinker, entries)
+	androidMkWritePrebuiltOptions(p.baseLinker, entries)
 }
 
 func (a *apiLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
@@ -533,11 +524,17 @@
 	})
 }
 
-func androidMkWriteAllowUndefinedSymbols(linker *baseLinker, entries *android.AndroidMkEntries) {
+func androidMkWritePrebuiltOptions(linker *baseLinker, entries *android.AndroidMkEntries) {
 	allow := linker.Properties.Allow_undefined_symbols
 	if allow != nil {
 		entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 			entries.SetBool("LOCAL_ALLOW_UNDEFINED_SYMBOLS", *allow)
 		})
 	}
+	ignore := linker.Properties.Ignore_max_page_size
+	if ignore != nil {
+		entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+			entries.SetBool("LOCAL_IGNORE_MAX_PAGE_SIZE", *ignore)
+		})
+	}
 }
diff --git a/cc/cc.go b/cc/cc.go
index a64775d..cb82f86 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -374,10 +374,6 @@
 	// build system and source tree.
 	Cmake_snapshot_supported *bool
 
-	// Even if DeviceConfig().VndkUseCoreVariant() is set, this module must use vendor variant.
-	// see soong/cc/config/vndk.go
-	MustUseVendorVariant bool `blueprint:"mutated"`
-
 	Installable *bool `android:"arch_variant"`
 
 	// Set by factories of module types that can only be referenced from variants compiled against
@@ -479,10 +475,6 @@
 	// IsLLNDK is set to true for the vendor variant of a cc_library module that has LLNDK stubs.
 	IsLLNDK bool `blueprint:"mutated"`
 
-	// IsVNDKUsingCoreVariant is true for VNDK modules if the global VndkUseCoreVariant option is
-	// set and the module is not listed in VndkMustUseVendorVariantList.
-	IsVNDKUsingCoreVariant bool `blueprint:"mutated"`
-
 	// IsVNDKCore is set if a VNDK module does not set the vndk.support_system_process property.
 	IsVNDKCore bool `blueprint:"mutated"`
 
@@ -548,7 +540,6 @@
 	apexVariationName() string
 	apexSdkVersion() android.ApiLevel
 	bootstrap() bool
-	mustUseVendorVariant() bool
 	nativeCoverage() bool
 	directlyInAnyApex() bool
 	isPreventInstall() bool
@@ -1472,10 +1463,6 @@
 	return c.Properties.SubName
 }
 
-func (c *Module) MustUseVendorVariant() bool {
-	return c.IsVndkSp() || c.Properties.MustUseVendorVariant
-}
-
 func (c *Module) getVndkExtendsModuleName() string {
 	if vndkdep := c.vndkdep; vndkdep != nil {
 		return vndkdep.getVndkExtendsModuleName()
@@ -1768,10 +1755,6 @@
 	return ctx.mod.IsVendorPublicLibrary()
 }
 
-func (ctx *moduleContextImpl) mustUseVendorVariant() bool {
-	return ctx.mod.MustUseVendorVariant()
-}
-
 func (ctx *moduleContextImpl) selectedStl() string {
 	if stl := ctx.mod.stl; stl != nil {
 		return stl.Properties.SelectedStl
@@ -3648,12 +3631,7 @@
 		}
 	}
 
-	if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.IsVndk() && !ccDep.MustUseVendorVariant() &&
-		!c.InRamdisk() && !c.InVendorRamdisk() && !c.InRecovery() {
-		// The vendor module is a no-vendor-variant VNDK library.  Depend on the
-		// core module instead.
-		return libName
-	} else if ccDep.InVendorOrProduct() && nonSystemVariantsExist {
+	if ccDep.InVendorOrProduct() && nonSystemVariantsExist {
 		// The vendor and product modules in Make will have been renamed to not conflict with the
 		// core module, so update the dependency name here accordingly.
 		return libName + ccDep.SubName()
@@ -3866,8 +3844,6 @@
 		// TODO(b/114741097): use the correct ndk stl once build errors have been fixed
 		//family, link := getNdkStlFamilyAndLinkType(c)
 		//return fmt.Sprintf("native:ndk:%s:%s", family, link)
-	} else if actx.DeviceConfig().VndkUseCoreVariant() && !c.MustUseVendorVariant() {
-		return "native:platform_vndk"
 	} else {
 		return "native:platform"
 	}
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index fdc94ad..289409f 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -15,7 +15,6 @@
         "global.go",
         "tidy.go",
         "toolchain.go",
-        "vndk.go",
 
         "bionic.go",
 
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
deleted file mode 100644
index dd612ce..0000000
--- a/cc/config/vndk.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2019 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 config
-
-// List of VNDK libraries that have different core variant and vendor variant.
-// For these libraries, the vendor variants must be installed even if the device
-// has VndkUseCoreVariant set.
-// Note that AIDL-generated modules must use vendor variants by default.
-var VndkMustUseVendorVariantList = []string{
-	"android.hardware.nfc@1.2",
-	"libbinder",
-	"libcrypto",
-	"libexpat",
-	"libgatekeeper",
-	"libgui",
-	"libhidlcache",
-	"libkeymaster_messages",
-	"libkeymaster_portable",
-	"libmedia_omx",
-	"libpuresoftkeymasterdevice",
-	"libselinux",
-	"libsoftkeymasterdevice",
-	"libsqlite",
-	"libssl",
-	"libstagefright_bufferpool@2.0",
-	"libstagefright_bufferqueue_helper",
-	"libstagefright_foundation",
-	"libstagefright_omx",
-	"libstagefright_omx_utils",
-	"libstagefright_xmlparser",
-	"libui",
-	"libxml2",
-}
diff --git a/cc/library.go b/cc/library.go
index b9018a7..b9c1466 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -419,11 +419,6 @@
 
 	postInstallCmds []string
 
-	// If useCoreVariant is true, the vendor variant of a VNDK library is
-	// not installed.
-	useCoreVariant       bool
-	checkSameCoreVariant bool
-
 	skipAPIDefine bool
 
 	// Decorated interfaces
@@ -1767,27 +1762,6 @@
 				}
 			}
 
-			// In some cases we want to use core variant for VNDK-Core libs.
-			// Skip product variant since VNDKs use only the vendor variant.
-			if ctx.isVndk() && !ctx.isVndkSp() && !ctx.IsVndkExt() && !ctx.inProduct() {
-				mayUseCoreVariant := true
-
-				if ctx.mustUseVendorVariant() {
-					mayUseCoreVariant = false
-				}
-
-				if ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) {
-					mayUseCoreVariant = false
-				}
-
-				if mayUseCoreVariant {
-					library.checkSameCoreVariant = true
-					if ctx.DeviceConfig().VndkUseCoreVariant() {
-						library.useCoreVariant = true
-					}
-				}
-			}
-
 			// do not install vndk libs
 			// vndk libs are packaged into VNDK APEX
 			if ctx.isVndk() && !ctx.IsVndkExt() && !ctx.Config().IsVndkDeprecated() && !ctx.inProduct() {
diff --git a/cc/linkable.go b/cc/linkable.go
index 5579aae..fecc6a2 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -165,7 +165,6 @@
 	// IsVndkSp returns true if this is a VNDK-SP module.
 	IsVndkSp() bool
 
-	MustUseVendorVariant() bool
 	IsVndk() bool
 	IsVndkExt() bool
 	IsVndkPrivate() bool
diff --git a/cc/linker.go b/cc/linker.go
index 1b5a33a..1675df6 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -65,6 +65,10 @@
 	// This flag should only be necessary for compiling low-level libraries like libc.
 	Allow_undefined_symbols *bool `android:"arch_variant"`
 
+	// ignore max page size. By default, max page size must be the
+	// max page size set for the target.
+	Ignore_max_page_size *bool `android:"arch_variant"`
+
 	// don't link in libclang_rt.builtins-*.a
 	No_libcrt *bool `android:"arch_variant"`
 
diff --git a/cc/vndk.go b/cc/vndk.go
index 548992d..ea55835 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -20,7 +20,6 @@
 	"strings"
 
 	"android/soong/android"
-	"android/soong/cc/config"
 	"android/soong/etc"
 
 	"github.com/google/blueprint"
@@ -219,11 +218,10 @@
 type moduleListerFunc func(ctx android.SingletonContext) (moduleNames, fileNames []string)
 
 var (
-	vndkSPLibraries               = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKSP })
-	vndkCoreLibraries             = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKCore })
-	vndkPrivateLibraries          = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKPrivate })
-	vndkProductLibraries          = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKProduct })
-	vndkUsingCoreVariantLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKUsingCoreVariant })
+	vndkSPLibraries      = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKSP })
+	vndkCoreLibraries    = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKCore })
+	vndkPrivateLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKPrivate })
+	vndkProductLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKProduct })
 )
 
 // vndkModuleLister takes a predicate that operates on a Module and returns a moduleListerFunc
@@ -266,22 +264,6 @@
 	}
 }
 
-var vndkMustUseVendorVariantListKey = android.NewOnceKey("vndkMustUseVendorVariantListKey")
-
-func vndkMustUseVendorVariantList(cfg android.Config) []string {
-	return cfg.Once(vndkMustUseVendorVariantListKey, func() interface{} {
-		return config.VndkMustUseVendorVariantList
-	}).([]string)
-}
-
-// test may call this to override global configuration(config.VndkMustUseVendorVariantList)
-// when it is called, it must be before the first call to vndkMustUseVendorVariantList()
-func setVndkMustUseVendorVariantListForTest(config android.Config, mustUseVendorVariantList []string) {
-	config.Once(vndkMustUseVendorVariantListKey, func() interface{} {
-		return mustUseVendorVariantList
-	})
-}
-
 func processVndkLibrary(mctx android.BottomUpMutatorContext, m *Module) {
 	if m.InProduct() {
 		// We may skip the steps for the product variants because they
@@ -301,13 +283,6 @@
 		mctx.PropertyErrorf("vndk.enabled", "This library provides stubs. Shouldn't be VNDK. Consider making it as LLNDK")
 	}
 
-	if inList(name, vndkMustUseVendorVariantList(mctx.Config())) {
-		m.Properties.MustUseVendorVariant = true
-	}
-	if mctx.DeviceConfig().VndkUseCoreVariant() && !m.Properties.MustUseVendorVariant {
-		m.VendorProperties.IsVNDKUsingCoreVariant = true
-	}
-
 	if m.vndkdep.isVndkSp() {
 		m.VendorProperties.IsVNDKSP = true
 	} else {
@@ -356,8 +331,7 @@
 		if lib.buildStubs() {
 			return false
 		}
-		useCoreVariant := mctx.DeviceConfig().VndkUseCoreVariant() && !m.MustUseVendorVariant()
-		return lib.shared() && m.InVendor() && m.IsVndk() && !m.IsVndkExt() && !useCoreVariant
+		return lib.shared() && m.InVendor() && m.IsVndk() && !m.IsVndkExt()
 	}
 	return false
 }
@@ -400,7 +374,6 @@
 	ctx.RegisterParallelSingletonModuleType("vndkcore_libraries_txt", vndkCoreLibrariesTxtFactory)
 	ctx.RegisterParallelSingletonModuleType("vndkprivate_libraries_txt", vndkPrivateLibrariesTxtFactory)
 	ctx.RegisterParallelSingletonModuleType("vndkproduct_libraries_txt", vndkProductLibrariesTxtFactory)
-	ctx.RegisterParallelSingletonModuleType("vndkcorevariant_libraries_txt", vndkUsingCoreVariantLibrariesTxtFactory)
 }
 
 type vndkLibrariesTxt struct {
@@ -453,13 +426,6 @@
 	return newVndkLibrariesTxt(vndkProductLibraries, "VNDK_PRODUCT_LIBRARIES")
 }
 
-// vndkcorevariant_libraries_txt is a singleton module whose content is a list of VNDK libraries
-// that are using the core variant, generated by Soong but can be referenced by other modules.
-// For example, apex_vndk can depend on these files as prebuilt.
-func vndkUsingCoreVariantLibrariesTxtFactory() android.SingletonModule {
-	return newVndkLibrariesTxt(vndkUsingCoreVariantLibraries, "VNDK_USING_CORE_VARIANT_LIBRARIES")
-}
-
 func newVndkLibrariesWithMakeVarFilter(lister moduleListerFunc, makeVarName string, filter string) android.SingletonModule {
 	m := &vndkLibrariesTxt{
 		lister:               lister,
diff --git a/cmd/release_config/build_flag/main.go b/cmd/release_config/build_flag/main.go
index cc2b57a..f74784b 100644
--- a/cmd/release_config/build_flag/main.go
+++ b/cmd/release_config/build_flag/main.go
@@ -48,6 +48,11 @@
 
 	// Panic on errors.
 	debug bool
+
+	// Allow missing release config.
+	// If true, and we cannot find the named release config, values for
+	// `trunk_staging` will be used.
+	allowMissing bool
 }
 
 type CommandFunc func(*rc_lib.ReleaseConfigs, Flags, string, []string) error
@@ -284,7 +289,7 @@
 	}
 
 	// Reload the release configs.
-	configs, err = rc_lib.ReadReleaseConfigMaps(commonFlags.maps, commonFlags.targetReleases[0], commonFlags.useGetBuildVar)
+	configs, err = rc_lib.ReadReleaseConfigMaps(commonFlags.maps, commonFlags.targetReleases[0], commonFlags.useGetBuildVar, commonFlags.allowMissing)
 	if err != nil {
 		return err
 	}
@@ -307,6 +312,7 @@
 	flag.Var(&commonFlags.maps, "map", "path to a release_config_map.textproto. may be repeated")
 	flag.StringVar(&commonFlags.outDir, "out-dir", rc_lib.GetDefaultOutDir(), "basepath for the output. Multiple formats are created")
 	flag.Var(&commonFlags.targetReleases, "release", "TARGET_RELEASE for this build")
+	flag.BoolVar(&commonFlags.allowMissing, "allow-missing", false, "Use trunk_staging values if release not found")
 	flag.BoolVar(&commonFlags.allReleases, "all-releases", false, "operate on all releases. (Ignored for set command)")
 	flag.BoolVar(&commonFlags.useGetBuildVar, "use-get-build-var", true, "use get_build_var PRODUCT_RELEASE_CONFIG_MAPS to get needed maps")
 	flag.BoolVar(&commonFlags.debug, "debug", false, "turn on debugging output for errors")
@@ -342,7 +348,7 @@
 	if relName == "--all" || relName == "-all" {
 		commonFlags.allReleases = true
 	}
-	configs, err = rc_lib.ReadReleaseConfigMaps(commonFlags.maps, relName, commonFlags.useGetBuildVar)
+	configs, err = rc_lib.ReadReleaseConfigMaps(commonFlags.maps, relName, commonFlags.useGetBuildVar, commonFlags.allowMissing)
 	if err != nil {
 		errorExit(err)
 	}
diff --git a/cmd/release_config/build_flag_declarations/main.go b/cmd/release_config/build_flag_declarations/main.go
index 7fdd084..cc286b6 100644
--- a/cmd/release_config/build_flag_declarations/main.go
+++ b/cmd/release_config/build_flag_declarations/main.go
@@ -6,6 +6,7 @@
 	"os"
 
 	rc_lib "android/soong/cmd/release_config/release_config_lib"
+	rc_proto "android/soong/cmd/release_config/release_config_proto"
 )
 
 type Flags struct {
@@ -62,18 +63,17 @@
 	}
 
 	flagArtifacts := rc_lib.FlagArtifactsFactory("")
+	intermediates := []*rc_proto.FlagDeclarationArtifacts{}
 	for _, intermediate := range flags.intermediates {
-		fas := rc_lib.FlagArtifactsFactory(intermediate)
-		for _, fa := range *fas {
-			(*flagArtifacts)[*fa.FlagDeclaration.Name] = fa
-		}
+		fda := rc_lib.FlagDeclarationArtifactsFactory(intermediate)
+		intermediates = append(intermediates, fda)
 	}
 	for _, decl := range flags.decls {
 		fa := rc_lib.FlagArtifactFactory(decl)
 		(*flagArtifacts)[*fa.FlagDeclaration.Name] = fa
 	}
 
-	message := flagArtifacts.GenerateFlagDeclarationArtifacts()
+	message := flagArtifacts.GenerateFlagDeclarationArtifacts(intermediates)
 	err = rc_lib.WriteFormattedMessage(flags.output, flags.format, message)
 	if err != nil {
 		errorExit(err)
diff --git a/cmd/release_config/release_config/main.go b/cmd/release_config/release_config/main.go
index 0617838..bd4ab49 100644
--- a/cmd/release_config/release_config/main.go
+++ b/cmd/release_config/release_config/main.go
@@ -31,10 +31,10 @@
 	var outputDir string
 	var err error
 	var configs *rc_lib.ReleaseConfigs
-	var json, pb, textproto bool
+	var json, pb, textproto, inheritance bool
 	var product string
 	var allMake bool
-	var useBuildVar bool
+	var useBuildVar, allowMissing bool
 	var guard bool
 
 	defaultRelease := os.Getenv("TARGET_RELEASE")
@@ -47,11 +47,13 @@
 	flag.BoolVar(&quiet, "quiet", false, "disable warning messages")
 	flag.Var(&releaseConfigMapPaths, "map", "path to a release_config_map.textproto. may be repeated")
 	flag.StringVar(&targetRelease, "release", defaultRelease, "TARGET_RELEASE for this build")
+	flag.BoolVar(&allowMissing, "allow-missing", false, "Use trunk_staging values if release not found")
 	flag.StringVar(&outputDir, "out_dir", rc_lib.GetDefaultOutDir(), "basepath for the output. Multiple formats are created")
 	flag.BoolVar(&textproto, "textproto", true, "write artifacts as text protobuf")
 	flag.BoolVar(&json, "json", true, "write artifacts as json")
 	flag.BoolVar(&pb, "pb", true, "write artifacts as binary protobuf")
 	flag.BoolVar(&allMake, "all_make", false, "write makefiles for all release configs")
+	flag.BoolVar(&inheritance, "inheritance", true, "write inheritance graph")
 	flag.BoolVar(&useBuildVar, "use_get_build_var", false, "use get_build_var PRODUCT_RELEASE_CONFIG_MAPS")
 	flag.BoolVar(&guard, "guard", true, "whether to guard with RELEASE_BUILD_FLAGS_IN_PROTOBUF")
 
@@ -64,7 +66,7 @@
 	if err = os.Chdir(top); err != nil {
 		panic(err)
 	}
-	configs, err = rc_lib.ReadReleaseConfigMaps(releaseConfigMapPaths, targetRelease, useBuildVar)
+	configs, err = rc_lib.ReadReleaseConfigMaps(releaseConfigMapPaths, targetRelease, useBuildVar, allowMissing)
 	if err != nil {
 		panic(err)
 	}
@@ -102,6 +104,13 @@
 			}
 		}
 	}
+	if inheritance {
+		inheritPath := filepath.Join(outputDir, fmt.Sprintf("inheritance_graph-%s.dot", product))
+		err = configs.WriteInheritanceGraph(inheritPath)
+		if err != nil {
+			panic(err)
+		}
+	}
 	if json {
 		err = configs.WriteArtifact(outputDir, product, "json")
 		if err != nil {
@@ -120,7 +129,7 @@
 			panic(err)
 		}
 	}
-	if err = config.WritePartitionBuildFlags(outputDir, product, targetRelease); err != nil {
+	if err = config.WritePartitionBuildFlags(outputDir); err != nil {
 		panic(err)
 	}
 
diff --git a/cmd/release_config/release_config_lib/flag_artifact.go b/cmd/release_config/release_config_lib/flag_artifact.go
index 6919ff5..6d36595 100644
--- a/cmd/release_config/release_config_lib/flag_artifact.go
+++ b/cmd/release_config/release_config_lib/flag_artifact.go
@@ -102,11 +102,24 @@
 	return ret
 }
 
-func (fas *FlagArtifacts) GenerateFlagDeclarationArtifacts() *rc_proto.FlagDeclarationArtifacts {
+func FlagDeclarationArtifactsFactory(path string) *rc_proto.FlagDeclarationArtifacts {
+	ret := &rc_proto.FlagDeclarationArtifacts{}
+	if path != "" {
+		LoadMessage(path, ret)
+	} else {
+		ret.FlagDeclarationArtifacts = []*rc_proto.FlagDeclarationArtifact{}
+	}
+	return ret
+}
+
+func (fas *FlagArtifacts) GenerateFlagDeclarationArtifacts(intermediates []*rc_proto.FlagDeclarationArtifacts) *rc_proto.FlagDeclarationArtifacts {
 	ret := &rc_proto.FlagDeclarationArtifacts{FlagDeclarationArtifacts: []*rc_proto.FlagDeclarationArtifact{}}
 	for _, fa := range *fas {
 		ret.FlagDeclarationArtifacts = append(ret.FlagDeclarationArtifacts, fa.GenerateFlagDeclarationArtifact())
 	}
+	for _, fda := range intermediates {
+		ret.FlagDeclarationArtifacts = append(ret.FlagDeclarationArtifacts, fda.FlagDeclarationArtifacts...)
+	}
 	slices.SortFunc(ret.FlagDeclarationArtifacts, func(a, b *rc_proto.FlagDeclarationArtifact) int {
 		return cmp.Compare(*a.Name, *b.Name)
 	})
diff --git a/cmd/release_config/release_config_lib/release_config.go b/cmd/release_config/release_config_lib/release_config.go
index 547f0dc..02b693c 100644
--- a/cmd/release_config/release_config_lib/release_config.go
+++ b/cmd/release_config/release_config_lib/release_config.go
@@ -18,6 +18,7 @@
 	"cmp"
 	"fmt"
 	"path/filepath"
+	"regexp"
 	"slices"
 	"sort"
 	"strings"
@@ -80,6 +81,10 @@
 
 	// Partitioned artifacts for {partition}/etc/build_flags.json
 	PartitionBuildFlags map[string]*rc_proto.FlagArtifacts
+
+	// Prior stage(s) for flag advancement (during development).
+	// Once a flag has met criteria in a prior stage, it can advance to this one.
+	PriorStagesMap map[string]bool
 }
 
 func ReleaseConfigFactory(name string, index int) (c *ReleaseConfig) {
@@ -87,6 +92,7 @@
 		Name:             name,
 		DeclarationIndex: index,
 		FilesUsedMap:     make(map[string]bool),
+		PriorStagesMap:   make(map[string]bool),
 	}
 }
 
@@ -117,14 +123,7 @@
 }
 
 func (config *ReleaseConfig) GetSortedFileList() []string {
-	ret := []string{}
-	for k := range config.FilesUsedMap {
-		ret = append(ret, k)
-	}
-	slices.SortFunc(ret, func(a, b string) int {
-		return cmp.Compare(a, b)
-	})
-	return ret
+	return SortedMapKeys(config.FilesUsedMap)
 }
 
 func (config *ReleaseConfig) GenerateReleaseConfig(configs *ReleaseConfigs) error {
@@ -137,9 +136,15 @@
 	config.compileInProgress = true
 	isRoot := config.Name == "root"
 
+	// Is this a build-prefix release config, such as 'ap3a'?
+	isBuildPrefix, err := regexp.MatchString("^[a-z][a-z][0-9][0-9a-z]$", config.Name)
+	if err != nil {
+		return err
+	}
 	// Start with only the flag declarations.
 	config.FlagArtifacts = configs.FlagArtifacts.Clone()
 	releaseAconfigValueSets := config.FlagArtifacts["RELEASE_ACONFIG_VALUE_SETS"]
+	releasePlatformVersion := config.FlagArtifacts["RELEASE_PLATFORM_VERSION"]
 
 	// Generate any configs we need to inherit.  This will detect loops in
 	// the config.
@@ -147,7 +152,7 @@
 	myInherits := []string{}
 	myInheritsSet := make(map[string]bool)
 	// If there is a "root" release config, it is the start of every inheritance chain.
-	_, err := configs.GetReleaseConfig("root")
+	_, err = configs.GetReleaseConfig("root")
 	if err == nil && !isRoot {
 		config.InheritNames = append([]string{"root"}, config.InheritNames...)
 	}
@@ -155,6 +160,9 @@
 		if _, ok := myInheritsSet[inherit]; ok {
 			continue
 		}
+		if isBuildPrefix && configs.Aliases[inherit] != nil {
+			return fmt.Errorf("%s cannot inherit from alias %s", config.Name, inherit)
+		}
 		myInherits = append(myInherits, inherit)
 		myInheritsSet[inherit] = true
 		iConfig, err := configs.GetReleaseConfig(inherit)
@@ -179,6 +187,20 @@
 
 	workflowManual := rc_proto.Workflow(rc_proto.Workflow_MANUAL)
 	myDirsMap := make(map[int]bool)
+	if isBuildPrefix && releasePlatformVersion != nil {
+		if MarshalValue(releasePlatformVersion.Value) != strings.ToUpper(config.Name) {
+			value := FlagValue{
+				path: config.Contributions[0].path,
+				proto: rc_proto.FlagValue{
+					Name:  releasePlatformVersion.FlagDeclaration.Name,
+					Value: UnmarshalValue(strings.ToUpper(config.Name)),
+				},
+			}
+			if err := releasePlatformVersion.UpdateValue(value); err != nil {
+				return err
+			}
+		}
+	}
 	for _, contrib := range contributionsToApply {
 		contribAconfigValueSets := []string{}
 		// Gather the aconfig_value_sets from this contribution, allowing duplicates for simplicity.
@@ -195,6 +217,9 @@
 				Value:  &rc_proto.Value{Val: &rc_proto.Value_StringValue{contribAconfigValueSetsString}},
 			})
 
+		for _, priorStage := range contrib.proto.PriorStages {
+			config.PriorStagesMap[priorStage] = true
+		}
 		myDirsMap[contrib.DeclarationIndex] = true
 		if config.AconfigFlagsOnly && len(contrib.FlagValues) > 0 {
 			return fmt.Errorf("%s does not allow build flag overrides", config.Name)
@@ -278,19 +303,22 @@
 		AconfigValueSets: myAconfigValueSets,
 		Inherits:         myInherits,
 		Directories:      directories,
+		PriorStages:      SortedMapKeys(config.PriorStagesMap),
 	}
 
 	config.compileInProgress = false
 	return nil
 }
 
-func (config *ReleaseConfig) WritePartitionBuildFlags(outDir, product, targetRelease string) error {
+func (config *ReleaseConfig) WritePartitionBuildFlags(outDir string) error {
 	var err error
 	for partition, flags := range config.PartitionBuildFlags {
 		slices.SortFunc(flags.FlagArtifacts, func(a, b *rc_proto.FlagArtifact) int {
 			return cmp.Compare(*a.FlagDeclaration.Name, *b.FlagDeclaration.Name)
 		})
-		if err = WriteMessage(filepath.Join(outDir, fmt.Sprintf("build_flags_%s-%s-%s.json", partition, config.Name, product)), flags); err != nil {
+		// The json file name must not be modified as this is read from
+		// build_flags_json module
+		if err = WriteMessage(filepath.Join(outDir, fmt.Sprintf("build_flags_%s.json", partition)), flags); err != nil {
 			return err
 		}
 	}
diff --git a/cmd/release_config/release_config_lib/release_configs.go b/cmd/release_config/release_config_lib/release_configs.go
index c62a78e..02eedc8 100644
--- a/cmd/release_config/release_config_lib/release_configs.go
+++ b/cmd/release_config/release_config_lib/release_configs.go
@@ -76,6 +76,77 @@
 	// A map from the config directory to its order in the list of config
 	// directories.
 	configDirIndexes ReleaseConfigDirMap
+
+	// True if we should allow a missing primary release config.  In this
+	// case, we will substitute `trunk_staging` values, but the release
+	// config will not be in ALL_RELEASE_CONFIGS_FOR_PRODUCT.
+	allowMissing bool
+}
+
+func (configs *ReleaseConfigs) WriteInheritanceGraph(outFile string) error {
+	data := []string{}
+	usedAliases := make(map[string]bool)
+	priorStages := make(map[string][]string)
+	rankedStageNames := make(map[string]bool)
+	for _, config := range configs.ReleaseConfigs {
+		var fillColor string
+		inherits := []string{}
+		for _, inherit := range config.InheritNames {
+			if inherit == "root" {
+				// Only show "root" if we have no other inheritance.
+				if len(config.InheritNames) > 1 {
+					continue
+				}
+			}
+			data = append(data, fmt.Sprintf(`"%s" -> "%s"`, config.Name, inherit))
+			inherits = append(inherits, inherit)
+			// If inheriting an alias, add a link from the alias to that release config.
+			if name, found := configs.Aliases[inherit]; found {
+				if !usedAliases[inherit] {
+					usedAliases[inherit] = true
+					data = append(data, fmt.Sprintf(`"%s" -> "%s"`, inherit, *name))
+					data = append(data,
+						fmt.Sprintf(`"%s" [ label="%s\ncurrently: %s" shape=oval ]`,
+							inherit, inherit, *name))
+				}
+			}
+		}
+		// Add links for all of the advancement progressions.
+		for priorStage := range config.PriorStagesMap {
+			stageName := config.Name
+			if len(config.OtherNames) > 0 {
+				stageName = config.OtherNames[0]
+			}
+			data = append(data, fmt.Sprintf(`"%s" -> "%s" [ style=dashed color="#81c995" ]`,
+				priorStage, stageName))
+			priorStages[stageName] = append(priorStages[stageName], priorStage)
+			rankedStageNames[stageName] = true
+		}
+		label := config.Name
+		if len(inherits) > 0 {
+			label += "\\ninherits: " + strings.Join(inherits, " ")
+		}
+		if len(config.OtherNames) > 0 {
+			label += "\\nother names: " + strings.Join(config.OtherNames, " ")
+		}
+		// The active release config has a light blue fill.
+		if config.Name == *configs.Artifact.ReleaseConfig.Name {
+			fillColor = `fillcolor="#d2e3fc" `
+		}
+		data = append(data,
+			fmt.Sprintf(`"%s" [ label="%s" %s]`, config.Name, label, fillColor))
+	}
+	if len(rankedStageNames) > 0 {
+		data = append(data, fmt.Sprintf("subgraph {rank=same %s}", strings.Join(SortedMapKeys(rankedStageNames), " ")))
+	}
+	slices.Sort(data)
+	data = append([]string{
+		"digraph {",
+		"graph [ ratio=.5 ]",
+		"node [ shape=box style=filled fillcolor=white colorscheme=svg fontcolor=black ]",
+	}, data...)
+	data = append(data, "}")
+	return os.WriteFile(outFile, []byte(strings.Join(data, "\n")), 0644)
 }
 
 // Write the "all_release_configs" artifact.
@@ -256,7 +327,17 @@
 		}
 		config := configs.ReleaseConfigs[name]
 		config.FilesUsedMap[path] = true
-		config.InheritNames = append(config.InheritNames, releaseConfigContribution.proto.Inherits...)
+		inheritNames := make(map[string]bool)
+		for _, inh := range config.InheritNames {
+			inheritNames[inh] = true
+		}
+		// If this contribution says to inherit something we already inherited, we do not want the duplicate.
+		for _, cInh := range releaseConfigContribution.proto.Inherits {
+			if !inheritNames[cInh] {
+				config.InheritNames = append(config.InheritNames, cInh)
+				inheritNames[cInh] = true
+			}
+		}
 
 		// Only walk flag_values/{RELEASE} for defined releases.
 		err2 := WalkTextprotoFiles(dir, filepath.Join("flag_values", name), func(path string, d fs.DirEntry, err error) error {
@@ -298,6 +379,11 @@
 	if config, ok := configs.ReleaseConfigs[name]; ok {
 		return config, nil
 	}
+	if configs.allowMissing {
+		if config, ok := configs.ReleaseConfigs["trunk_staging"]; ok {
+			return config, nil
+		}
+	}
 	return nil, fmt.Errorf("Missing config %s.  Trace=%v", name, trace)
 }
 
@@ -378,8 +464,8 @@
 	if targetRelease != config.Name {
 		data += fmt.Sprintf("# User specified TARGET_RELEASE=%s\n", targetRelease)
 	}
-	// The variable _all_release_configs will get deleted during processing, so do not mark it read-only.
-	data += fmt.Sprintf("_all_release_configs := %s\n", strings.Join(configs.GetAllReleaseNames(), " "))
+	// As it stands this list is not per-product, but conceptually it is, and will be.
+	data += fmt.Sprintf("ALL_RELEASE_CONFIGS_FOR_PRODUCT :=$= %s\n", strings.Join(configs.GetAllReleaseNames(), " "))
 	data += fmt.Sprintf("_used_files := %s\n", strings.Join(config.GetSortedFileList(), " "))
 	data += fmt.Sprintf("_ALL_RELEASE_FLAGS :=$= %s\n", strings.Join(names, " "))
 	for _, pName := range pNames {
@@ -445,7 +531,7 @@
 	return nil
 }
 
-func ReadReleaseConfigMaps(releaseConfigMapPaths StringList, targetRelease string, useBuildVar bool) (*ReleaseConfigs, error) {
+func ReadReleaseConfigMaps(releaseConfigMapPaths StringList, targetRelease string, useBuildVar, allowMissing bool) (*ReleaseConfigs, error) {
 	var err error
 
 	if len(releaseConfigMapPaths) == 0 {
@@ -462,6 +548,7 @@
 	}
 
 	configs := ReleaseConfigsFactory()
+	configs.allowMissing = allowMissing
 	mapsRead := make(map[string]bool)
 	var idx int
 	for _, releaseConfigMapPath := range releaseConfigMapPaths {
diff --git a/cmd/release_config/release_config_lib/util.go b/cmd/release_config/release_config_lib/util.go
index 0af99a6..b8824d1 100644
--- a/cmd/release_config/release_config_lib/util.go
+++ b/cmd/release_config/release_config_lib/util.go
@@ -22,6 +22,7 @@
 	"os/exec"
 	"path/filepath"
 	"regexp"
+	"slices"
 	"strings"
 
 	"google.golang.org/protobuf/encoding/prototext"
@@ -159,6 +160,15 @@
 	return 0, nil
 }
 
+func SortedMapKeys(inputMap map[string]bool) []string {
+	ret := []string{}
+	for k := range inputMap {
+		ret = append(ret, k)
+	}
+	slices.Sort(ret)
+	return ret
+}
+
 func validContainer(container string) bool {
 	return containerRegexp.MatchString(container)
 }
diff --git a/cmd/release_config/release_config_proto/build_flags_out.pb.go b/cmd/release_config/release_config_proto/build_flags_out.pb.go
index 42ae6f9..309ec34 100644
--- a/cmd/release_config/release_config_proto/build_flags_out.pb.go
+++ b/cmd/release_config/release_config_proto/build_flags_out.pb.go
@@ -226,6 +226,9 @@
 	// The release config directories used for this config.
 	// For example, "build/release".
 	Directories []string `protobuf:"bytes,6,rep,name=directories" json:"directories,omitempty"`
+	// Prior stage(s) for flag advancement (during development).
+	// Once a flag has met criteria in a prior stage, it can advance to this one.
+	PriorStages []string `protobuf:"bytes,7,rep,name=prior_stages,json=priorStages" json:"prior_stages,omitempty"`
 }
 
 func (x *ReleaseConfigArtifact) Reset() {
@@ -302,6 +305,13 @@
 	return nil
 }
 
+func (x *ReleaseConfigArtifact) GetPriorStages() []string {
+	if x != nil {
+		return x.PriorStages
+	}
+	return nil
+}
+
 type ReleaseConfigsArtifact struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -403,7 +413,7 @@
 	0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f,
 	0x74, 0x6f, 0x2e, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74,
 	0x52, 0x0d, 0x66, 0x6c, 0x61, 0x67, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x22,
-	0x8e, 0x02, 0x0a, 0x17, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
+	0xb1, 0x02, 0x0a, 0x17, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
 	0x69, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e,
 	0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
 	0x1f, 0x0a, 0x0b, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02,
@@ -420,40 +430,43 @@
 	0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x12, 0x20,
 	0x0a, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20,
 	0x03, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73,
-	0x22, 0xe8, 0x03, 0x0a, 0x18, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x73, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x5c, 0x0a,
-	0x0e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
-	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x0d, 0x72, 0x65,
-	0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x69, 0x0a, 0x15, 0x6f,
-	0x74, 0x68, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x61, 0x6e, 0x64,
-	0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
-	0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63,
-	0x74, 0x52, 0x13, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43,
-	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x17, 0x72, 0x65, 0x6c, 0x65, 0x61,
-	0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x5f, 0x6d,
-	0x61, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x50, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x67, 0x65, 0x73,
+	0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x53, 0x74, 0x61,
+	0x67, 0x65, 0x73, 0x22, 0xe8, 0x03, 0x0a, 0x18, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f,
+	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74,
+	0x12, 0x5c, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
+	0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
 	0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
 	0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f,
-	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74,
-	0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61,
-	0x70, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x72, 0x65, 0x6c, 0x65,
-	0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x73, 0x4d, 0x61, 0x70,
-	0x1a, 0x79, 0x0a, 0x19, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x4d, 0x61, 0x70, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
-	0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
-	0x46, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30,
-	0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65,
-	0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65,
+	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52,
+	0x0d, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x69,
+	0x0a, 0x15, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f,
+	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e,
+	0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f,
+	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c,
+	0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69,
+	0x66, 0x61, 0x63, 0x74, 0x52, 0x13, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x6c, 0x65, 0x61,
+	0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x17, 0x72, 0x65,
 	0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6d, 0x61, 0x70,
-	0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x33, 0x5a, 0x31, 0x61,
-	0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6c,
-	0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65,
+	0x73, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x50, 0x2e, 0x61, 0x6e,
+	0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61,
+	0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66,
+	0x61, 0x63, 0x74, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+	0x67, 0x4d, 0x61, 0x70, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x72,
+	0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x73,
+	0x4d, 0x61, 0x70, 0x1a, 0x79, 0x0a, 0x19, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79,
+	0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
+	0x65, 0x79, 0x12, 0x46, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x30, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65,
 	0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f,
+	0x6d, 0x61, 0x70, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x33,
+	0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f,
+	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72,
+	0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72,
+	0x6f, 0x74, 0x6f,
 }
 
 var (
diff --git a/cmd/release_config/release_config_proto/build_flags_out.proto b/cmd/release_config/release_config_proto/build_flags_out.proto
index 8c3be5f..0cbc157 100644
--- a/cmd/release_config/release_config_proto/build_flags_out.proto
+++ b/cmd/release_config/release_config_proto/build_flags_out.proto
@@ -83,6 +83,10 @@
   // The release config directories used for this config.
   // For example, "build/release".
   repeated string directories = 6;
+
+  // Prior stage(s) for flag advancement (during development).
+  // Once a flag has met criteria in a prior stage, it can advance to this one.
+  repeated string prior_stages = 7;
 }
 
 message release_configs_artifact {
diff --git a/cmd/release_config/release_config_proto/build_flags_src.pb.go b/cmd/release_config/release_config_proto/build_flags_src.pb.go
index 1b63961..8de340e 100644
--- a/cmd/release_config/release_config_proto/build_flags_src.pb.go
+++ b/cmd/release_config/release_config_proto/build_flags_src.pb.go
@@ -326,6 +326,9 @@
 	AconfigValueSets []string `protobuf:"bytes,3,rep,name=aconfig_value_sets,json=aconfigValueSets" json:"aconfig_value_sets,omitempty"`
 	// Only aconfig flags are allowed in this release config.
 	AconfigFlagsOnly *bool `protobuf:"varint,4,opt,name=aconfig_flags_only,json=aconfigFlagsOnly" json:"aconfig_flags_only,omitempty"`
+	// Prior stage(s) for flag advancement (during development).
+	// Once a flag has met criteria in a prior stage, it can advance to this one.
+	PriorStages []string `protobuf:"bytes,5,rep,name=prior_stages,json=priorStages" json:"prior_stages,omitempty"`
 }
 
 func (x *ReleaseConfig) Reset() {
@@ -388,6 +391,13 @@
 	return false
 }
 
+func (x *ReleaseConfig) GetPriorStages() []string {
+	if x != nil {
+		return x.PriorStages
+	}
+	return nil
+}
+
 // Any aliases.  These are used for continuous integration builder config.
 type ReleaseAlias struct {
 	state         protoimpl.MessageState
@@ -556,7 +566,7 @@
 	0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74,
 	0x6f, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1b,
 	0x0a, 0x08, 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x65, 0x64, 0x18, 0xca, 0x01, 0x20, 0x01, 0x28,
-	0x08, 0x52, 0x08, 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x65, 0x64, 0x22, 0x9c, 0x01, 0x0a, 0x0e,
+	0x08, 0x52, 0x08, 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x65, 0x64, 0x22, 0xbf, 0x01, 0x0a, 0x0e,
 	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12,
 	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
 	0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x18, 0x02,
@@ -566,25 +576,27 @@
 	0x66, 0x69, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x65, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12,
 	0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5f, 0x6f, 0x6e,
 	0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x3b, 0x0a, 0x0d, 0x72, 0x65,
-	0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e,
-	0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
-	0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xac, 0x01, 0x0a, 0x12, 0x72, 0x65, 0x6c, 0x65,
-	0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6d, 0x61, 0x70, 0x12, 0x45,
-	0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
-	0x2b, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
-	0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72,
-	0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x07, 0x61, 0x6c,
-	0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
-	0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63,
-	0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x64, 0x65, 0x66, 0x61, 0x75,
-	0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20,
-	0x03, 0x28, 0x09, 0x52, 0x11, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74,
-	0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x42, 0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
-	0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f,
-	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63,
-	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x67, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72,
+	0x69, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x67, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09,
+	0x52, 0x0b, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x67, 0x65, 0x73, 0x22, 0x3b, 0x0a,
+	0x0d, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x12,
+	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xac, 0x01, 0x0a, 0x12, 0x72,
+	0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6d, 0x61,
+	0x70, 0x12, 0x45, 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
+	0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c,
+	0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x52,
+	0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63,
+	0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64,
+	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x64, 0x65,
+	0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73,
+	0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43,
+	0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x42, 0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64,
+	0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61,
+	0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
+	0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 }
 
 var (
diff --git a/cmd/release_config/release_config_proto/build_flags_src.proto b/cmd/release_config/release_config_proto/build_flags_src.proto
index 6623294..4fad478 100644
--- a/cmd/release_config/release_config_proto/build_flags_src.proto
+++ b/cmd/release_config/release_config_proto/build_flags_src.proto
@@ -113,6 +113,10 @@
 
   // Only aconfig flags are allowed in this release config.
   optional bool aconfig_flags_only = 4;
+
+  // Prior stage(s) for flag advancement (during development).
+  // Once a flag has met criteria in a prior stage, it can advance to this one.
+  repeated string prior_stages = 5;
 }
 
 // Any aliases.  These are used for continuous integration builder config.
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index fe3f8f7..2d3156a 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -155,7 +155,6 @@
 
 	// Create a new trace file writer, making it log events to the log instance.
 	trace := tracer.New(log)
-	defer trace.Close()
 
 	// Create a new Status instance, which manages action counts and event output channels.
 	stat := &status.Status{}
@@ -194,14 +193,29 @@
 	soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
 	rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
 	soongBuildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_build_metrics.pb")
+	buildTraceFile := filepath.Join(logsDir, c.logsPrefix+"build.trace.gz")
 
 	metricsFiles := []string{
 		buildErrorFile,        // build error strings
 		rbeMetricsFile,        // high level metrics related to remote build execution.
 		soongMetricsFile,      // high level metrics related to this build system.
 		soongBuildMetricsFile, // high level metrics related to soong build
+		buildTraceFile,
 	}
 
+	defer func() {
+		stat.Finish()
+		criticalPath.WriteToMetrics(met)
+		met.Dump(soongMetricsFile)
+		if !config.SkipMetricsUpload() {
+			build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, metricsFiles...)
+		}
+	}()
+
+	// This has to come after the metrics uploading function, so that
+	// build.trace.gz is closed and ready for upload.
+	defer trace.Close()
+
 	os.MkdirAll(logsDir, 0777)
 
 	log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
@@ -222,16 +236,7 @@
 		config = freshConfig()
 	}
 
-	defer func() {
-		stat.Finish()
-		criticalPath.WriteToMetrics(met)
-		met.Dump(soongMetricsFile)
-		if !config.SkipMetricsUpload() {
-			build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, metricsFiles...)
-		}
-	}()
 	c.run(buildCtx, config, args)
-
 }
 
 // This function must not modify config, since product config may cause us to recreate the config,
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index e168edc..fd3b27f 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -74,11 +74,11 @@
 type prebuiltEtcProperties struct {
 	// Source file of this prebuilt. Can reference a genrule type module with the ":module" syntax.
 	// Mutually exclusive with srcs.
-	Src *string `android:"path,arch_variant"`
+	Src proptools.Configurable[string] `android:"path,arch_variant,replace_instead_of_append"`
 
 	// Source files of this prebuilt. Can reference a genrule type module with the ":module" syntax.
 	// Mutually exclusive with src. When used, filename_from_src is set to true.
-	Srcs []string `android:"path,arch_variant"`
+	Srcs proptools.Configurable[[]string] `android:"path,arch_variant"`
 
 	// Optional name for the installed file. If unspecified, name of the module is used as the file
 	// name. Only available when using a single source (src).
@@ -158,6 +158,8 @@
 	installDirPath         android.InstallPath
 	additionalDependencies *android.Paths
 
+	usedSrcsProperty bool
+
 	makeClass string
 }
 
@@ -247,10 +249,10 @@
 }
 
 func (p *PrebuiltEtc) SourceFilePath(ctx android.ModuleContext) android.Path {
-	if len(p.properties.Srcs) > 0 {
+	if len(p.properties.Srcs.GetOrDefault(ctx, nil)) > 0 {
 		panic(fmt.Errorf("SourceFilePath not available on multi-source prebuilt %q", p.Name()))
 	}
-	return android.PathForModuleSrc(ctx, proptools.String(p.properties.Src))
+	return android.PathForModuleSrc(ctx, p.properties.Src.GetOrDefault(ctx, ""))
 }
 
 func (p *PrebuiltEtc) InstallDirPath() android.InstallPath {
@@ -264,7 +266,7 @@
 }
 
 func (p *PrebuiltEtc) OutputFile() android.OutputPath {
-	if len(p.properties.Srcs) > 0 {
+	if p.usedSrcsProperty {
 		panic(fmt.Errorf("OutputFile not available on multi-source prebuilt %q", p.Name()))
 	}
 	return p.outputFilePaths[0]
@@ -319,7 +321,9 @@
 func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	var installs []installProperties
 
-	if p.properties.Src != nil && len(p.properties.Srcs) > 0 {
+	srcProperty := p.properties.Src.Get(ctx)
+	srcsProperty := p.properties.Srcs.GetOrDefault(ctx, nil)
+	if srcProperty.IsPresent() && len(srcsProperty) > 0 {
 		ctx.PropertyErrorf("src", "src is set. Cannot set srcs")
 	}
 
@@ -331,8 +335,8 @@
 
 	filename := proptools.String(p.properties.Filename)
 	filenameFromSrc := proptools.Bool(p.properties.Filename_from_src)
-	if p.properties.Src != nil {
-		p.sourceFilePaths = android.PathsForModuleSrc(ctx, []string{proptools.String(p.properties.Src)})
+	if srcProperty.IsPresent() {
+		p.sourceFilePaths = android.PathsForModuleSrc(ctx, []string{srcProperty.Get()})
 		// If the source was not found, set a fake source path to
 		// support AllowMissingDependencies executions.
 		if len(p.sourceFilePaths) == 0 {
@@ -367,7 +371,8 @@
 			symlinks:       p.properties.Symlinks,
 		}
 		installs = append(installs, ip)
-	} else if len(p.properties.Srcs) > 0 {
+	} else if len(srcsProperty) > 0 {
+		p.usedSrcsProperty = true
 		if filename != "" {
 			ctx.PropertyErrorf("filename", "filename cannot be set when using srcs")
 		}
@@ -377,7 +382,7 @@
 		if p.properties.Filename_from_src != nil {
 			ctx.PropertyErrorf("filename_from_src", "filename_from_src is implicitly set to true when using srcs")
 		}
-		p.sourceFilePaths = android.PathsForModuleSrc(ctx, p.properties.Srcs)
+		p.sourceFilePaths = android.PathsForModuleSrc(ctx, srcsProperty)
 		for _, src := range p.sourceFilePaths {
 			filename := src.Base()
 			output := android.PathForModuleOut(ctx, filename).OutputPath
diff --git a/genrule/genrule.go b/genrule/genrule.go
index dd980cb..26dad01 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -126,7 +126,7 @@
 	//  $(out): a single output file.
 	//  $(genDir): the sandbox directory for this tool; contains $(out).
 	//  $$: a literal $
-	Cmd *string
+	Cmd proptools.Configurable[string] `android:"replace_instead_of_append"`
 
 	// name of the modules (if any) that produces the host executable.   Leave empty for
 	// prebuilts or scripts that do not need a module to build them.
@@ -403,7 +403,7 @@
 	var outputFiles android.WritablePaths
 	var zipArgs strings.Builder
 
-	cmd := String(g.properties.Cmd)
+	cmd := g.properties.Cmd.GetOrDefault(ctx, "")
 	if g.CmdModifier != nil {
 		cmd = g.CmdModifier(ctx, cmd)
 	}
diff --git a/java/app.go b/java/app.go
index d2f2d0b..a24099c 100644
--- a/java/app.go
+++ b/java/app.go
@@ -1380,6 +1380,8 @@
 		HostRequiredModuleNames: a.HostRequiredModuleNames(),
 		TestSuites:              a.testProperties.Test_suites,
 		IsHost:                  false,
+		LocalCertificate:        a.certificate.AndroidMkString(),
+		IsUnitTest:              Bool(a.testProperties.Test_options.Unit_test),
 	})
 	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
 		TestOnly:       true,
diff --git a/java/base.go b/java/base.go
index b9e236c..b4f800b 100644
--- a/java/base.go
+++ b/java/base.go
@@ -1650,11 +1650,28 @@
 				classesJar:    implementationAndResourcesJar,
 				jarName:       jarName,
 			}
-			dexOutputFile = j.dexer.compileDex(ctx, params)
+			if j.GetProfileGuided() && j.optimizeOrObfuscateEnabled() && !j.EnableProfileRewriting() {
+				ctx.PropertyErrorf("enable_profile_rewriting",
+					"Enable_profile_rewriting must be true when profile_guided dexpreopt and R8 optimization/obfuscation is turned on. The attached profile should be sourced from an unoptimized/unobfuscated APK.",
+				)
+			}
+			if j.EnableProfileRewriting() {
+				profile := j.GetProfile()
+				if profile == "" || !j.GetProfileGuided() {
+					ctx.PropertyErrorf("enable_profile_rewriting", "Profile and Profile_guided must be set when enable_profile_rewriting is true")
+				}
+				params.artProfileInput = &profile
+			}
+			dexOutputFile, dexArtProfileOutput := j.dexer.compileDex(ctx, params)
 			if ctx.Failed() {
 				return
 			}
 
+			// If r8/d8 provides a profile that matches the optimized dex, use that for dexpreopt.
+			if dexArtProfileOutput != nil {
+				j.dexpreopter.SetRewrittenProfile(*dexArtProfileOutput)
+			}
+
 			// merge dex jar with resources if necessary
 			if j.resourceJar != nil {
 				jars := android.Paths{dexOutputFile, j.resourceJar}
diff --git a/java/dex.go b/java/dex.go
index 8cfffaf..32546d9 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -119,6 +119,10 @@
 	return d.resourceShrinkingEnabled(ctx) && Bool(d.Optimize.Optimized_shrink_resources)
 }
 
+func (d *dexer) optimizeOrObfuscateEnabled() bool {
+	return d.effectiveOptimizeEnabled() && (proptools.Bool(d.dexProperties.Optimize.Optimize) || proptools.Bool(d.dexProperties.Optimize.Obfuscate))
+}
+
 var d8, d8RE = pctx.MultiCommandRemoteStaticRules("d8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
@@ -253,17 +257,25 @@
 	return flags, deps
 }
 
-func d8Flags(flags javaBuilderFlags) (d8Flags []string, d8Deps android.Paths) {
+func (d *dexer) d8Flags(ctx android.ModuleContext, dexParams *compileDexParams) (d8Flags []string, d8Deps android.Paths, artProfileOutput *android.OutputPath) {
+	flags := dexParams.flags
 	d8Flags = append(d8Flags, flags.bootClasspath.FormRepeatedClassPath("--lib ")...)
 	d8Flags = append(d8Flags, flags.dexClasspath.FormRepeatedClassPath("--lib ")...)
 
 	d8Deps = append(d8Deps, flags.bootClasspath...)
 	d8Deps = append(d8Deps, flags.dexClasspath...)
 
-	return d8Flags, d8Deps
+	if flags, deps, profileOutput := d.addArtProfile(ctx, dexParams); profileOutput != nil {
+		d8Flags = append(d8Flags, flags...)
+		d8Deps = append(d8Deps, deps...)
+		artProfileOutput = profileOutput
+	}
+
+	return d8Flags, d8Deps, artProfileOutput
 }
 
-func (d *dexer) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Flags []string, r8Deps android.Paths) {
+func (d *dexer) r8Flags(ctx android.ModuleContext, dexParams *compileDexParams) (r8Flags []string, r8Deps android.Paths, artProfileOutput *android.OutputPath) {
+	flags := dexParams.flags
 	opt := d.dexProperties.Optimize
 
 	// When an app contains references to APIs that are not in the SDK specified by
@@ -375,18 +387,44 @@
 		}
 	}
 
-	return r8Flags, r8Deps
+	if flags, deps, profileOutput := d.addArtProfile(ctx, dexParams); profileOutput != nil {
+		r8Flags = append(r8Flags, flags...)
+		r8Deps = append(r8Deps, deps...)
+		artProfileOutput = profileOutput
+	}
+
+	return r8Flags, r8Deps, artProfileOutput
 }
 
 type compileDexParams struct {
-	flags         javaBuilderFlags
-	sdkVersion    android.SdkSpec
-	minSdkVersion android.ApiLevel
-	classesJar    android.Path
-	jarName       string
+	flags           javaBuilderFlags
+	sdkVersion      android.SdkSpec
+	minSdkVersion   android.ApiLevel
+	classesJar      android.Path
+	jarName         string
+	artProfileInput *string
 }
 
-func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParams) android.OutputPath {
+// Adds --art-profile to r8/d8 command.
+// r8/d8 will output a generated profile file to match the optimized dex code.
+func (d *dexer) addArtProfile(ctx android.ModuleContext, dexParams *compileDexParams) (flags []string, deps android.Paths, artProfileOutputPath *android.OutputPath) {
+	if dexParams.artProfileInput != nil {
+		artProfileInputPath := android.PathForModuleSrc(ctx, *dexParams.artProfileInput)
+		artProfileOutputPathValue := android.PathForModuleOut(ctx, "profile.prof.txt").OutputPath
+		artProfileOutputPath = &artProfileOutputPathValue
+		flags = []string{
+			"--art-profile",
+			artProfileInputPath.String(),
+			artProfileOutputPath.String(),
+		}
+		deps = append(deps, artProfileInputPath)
+	}
+	return flags, deps, artProfileOutputPath
+
+}
+
+// Return the compiled dex jar and (optional) profile _after_ r8 optimization
+func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParams) (android.OutputPath, *android.OutputPath) {
 
 	// Compile classes.jar into classes.dex and then javalib.jar
 	javalibJar := android.PathForModuleOut(ctx, "dex", dexParams.jarName).OutputPath
@@ -406,6 +444,7 @@
 	}
 
 	useR8 := d.effectiveOptimizeEnabled()
+	var artProfileOutputPath *android.OutputPath
 	if useR8 {
 		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
 		d.proguardDictionary = android.OptionalPathForPath(proguardDictionary)
@@ -418,8 +457,19 @@
 		d.proguardUsageZip = android.OptionalPathForPath(proguardUsageZip)
 		resourcesOutput := android.PathForModuleOut(ctx, "package-res-shrunken.apk")
 		d.resourcesOutput = android.OptionalPathForPath(resourcesOutput)
-		r8Flags, r8Deps := d.r8Flags(ctx, dexParams.flags)
-		r8Deps = append(r8Deps, commonDeps...)
+		implicitOutputs := android.WritablePaths{
+			proguardDictionary,
+			proguardUsageZip,
+			proguardConfiguration,
+		}
+		r8Flags, r8Deps, r8ArtProfileOutputPath := d.r8Flags(ctx, dexParams)
+		if r8ArtProfileOutputPath != nil {
+			artProfileOutputPath = r8ArtProfileOutputPath
+			implicitOutputs = append(
+				implicitOutputs,
+				artProfileOutputPath,
+			)
+		}
 		rule := r8
 		args := map[string]string{
 			"r8Flags":        strings.Join(append(commonFlags, r8Flags...), " "),
@@ -436,10 +486,6 @@
 			rule = r8RE
 			args["implicits"] = strings.Join(r8Deps.Strings(), ",")
 		}
-		implicitOutputs := android.WritablePaths{
-			proguardDictionary,
-			proguardUsageZip,
-			proguardConfiguration}
 		if d.resourcesInput.Valid() {
 			implicitOutputs = append(implicitOutputs, resourcesOutput)
 			args["resourcesOutput"] = resourcesOutput.String()
@@ -454,18 +500,27 @@
 			Args:            args,
 		})
 	} else {
-		d8Flags, d8Deps := d8Flags(dexParams.flags)
+		implicitOutputs := android.WritablePaths{}
+		d8Flags, d8Deps, d8ArtProfileOutputPath := d.d8Flags(ctx, dexParams)
+		if d8ArtProfileOutputPath != nil {
+			artProfileOutputPath = d8ArtProfileOutputPath
+			implicitOutputs = append(
+				implicitOutputs,
+				artProfileOutputPath,
+			)
+		}
 		d8Deps = append(d8Deps, commonDeps...)
 		rule := d8
 		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8") {
 			rule = d8RE
 		}
 		ctx.Build(pctx, android.BuildParams{
-			Rule:        rule,
-			Description: "d8",
-			Output:      javalibJar,
-			Input:       dexParams.classesJar,
-			Implicits:   d8Deps,
+			Rule:            rule,
+			Description:     "d8",
+			Output:          javalibJar,
+			Input:           dexParams.classesJar,
+			ImplicitOutputs: implicitOutputs,
+			Implicits:       d8Deps,
 			Args: map[string]string{
 				"d8Flags":        strings.Join(append(commonFlags, d8Flags...), " "),
 				"zipFlags":       zipFlags,
@@ -480,5 +535,5 @@
 		javalibJar = alignedJavalibJar
 	}
 
-	return javalibJar
+	return javalibJar, artProfileOutputPath
 }
diff --git a/java/dex_test.go b/java/dex_test.go
index 1ecdae0..4862d06 100644
--- a/java/dex_test.go
+++ b/java/dex_test.go
@@ -662,3 +662,54 @@
 	android.AssertStringDoesContain(t, "expected aarimports's proguard flags",
 		appR8.Args["r8Flags"], "proguard.txt")
 }
+
+func TestR8FlagsArtProfile(t *testing.T) {
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
+		android_app {
+			name: "app",
+			srcs: ["foo.java"],
+			platform_apis: true,
+			dex_preopt: {
+				profile_guided: true,
+				profile: "profile.txt.prof",
+				enable_profile_rewriting: true,
+			},
+		}
+	`)
+
+	app := result.ModuleForTests("app", "android_common")
+	appR8 := app.Rule("r8")
+	android.AssertStringDoesContain(t, "expected --art-profile in app r8 flags",
+		appR8.Args["r8Flags"], "--art-profile")
+
+	appDexpreopt := app.Rule("dexpreopt")
+	android.AssertStringDoesContain(t,
+		"expected --art-profile output to be used to create .prof binary",
+		appDexpreopt.RuleParams.Command,
+		"--create-profile-from=out/soong/.intermediates/app/android_common/profile.prof.txt --output-profile-type=app",
+	)
+}
+
+// This test checks that users explicitly set `enable_profile_rewriting` to true when the following are true
+// 1. optimize or obfuscate is enabled AND
+// 2. dex_preopt.profile_guided is enabled
+//
+// The rewritten profile should be used since the dex signatures in the checked-in profile will not match the optimized binary.
+func TestEnableProfileRewritingIsRequiredForOptimizedApps(t *testing.T) {
+	testJavaError(t,
+		"Enable_profile_rewriting must be true when profile_guided dexpreopt and R8 optimization/obfuscation is turned on",
+		`
+android_app {
+	name: "app",
+	srcs: ["foo.java"],
+	platform_apis: true,
+	dex_preopt: {
+		profile_guided: true,
+		profile: "profile.txt.prof",
+		// enable_profile_rewriting is not set, this is an error
+	},
+	optimize: {
+		optimize: true,
+	}
+}`)
+}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 4d6dbff..832b850 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -19,6 +19,8 @@
 	"sort"
 	"strings"
 
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/dexpreopt"
 )
@@ -139,6 +141,10 @@
 	// The path to the profile that dexpreopter accepts. It must be in the binary format. If this is
 	// set, it overrides the profile settings in `dexpreoptProperties`.
 	inputProfilePathOnHost android.Path
+
+	// The path to the profile that matches the dex optimized by r8/d8. It is in text format. If this is
+	// set, it will be converted to a binary profile which will be subsequently used for dexpreopt.
+	rewrittenProfile android.Path
 }
 
 type DexpreoptProperties struct {
@@ -158,6 +164,11 @@
 		// defaults to searching for a file that matches the name of this module in the default
 		// profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found.
 		Profile *string `android:"path"`
+
+		// If set to true, r8/d8 will use `profile` as input to generate a new profile that matches
+		// the optimized dex.
+		// The new profile will be subsequently used as the profile to dexpreopt the dex file.
+		Enable_profile_rewriting *bool
 	}
 
 	Dex_preopt_result struct {
@@ -421,13 +432,17 @@
 	if d.inputProfilePathOnHost != nil {
 		profileClassListing = android.OptionalPathForPath(d.inputProfilePathOnHost)
 	} else if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) && !forPrebuiltApex(ctx) {
-		// If dex_preopt.profile_guided is not set, default it based on the existence of the
-		// dexprepot.profile option or the profile class listing.
-		if String(d.dexpreoptProperties.Dex_preopt.Profile) != "" {
+		// If enable_profile_rewriting is set, use the rewritten profile instead of the checked-in profile
+		if d.EnableProfileRewriting() {
+			profileClassListing = android.OptionalPathForPath(d.GetRewrittenProfile())
+			profileIsTextListing = true
+		} else if profile := d.GetProfile(); profile != "" {
+			// If dex_preopt.profile_guided is not set, default it based on the existence of the
+			// dexprepot.profile option or the profile class listing.
 			profileClassListing = android.OptionalPathForPath(
-				android.PathForModuleSrc(ctx, String(d.dexpreoptProperties.Dex_preopt.Profile)))
+				android.PathForModuleSrc(ctx, profile))
 			profileBootListing = android.ExistentPathForSource(ctx,
-				ctx.ModuleDir(), String(d.dexpreoptProperties.Dex_preopt.Profile)+"-boot")
+				ctx.ModuleDir(), profile+"-boot")
 			profileIsTextListing = true
 		} else if global.ProfileDir != "" {
 			profileClassListing = android.ExistentPathForSource(ctx,
@@ -588,3 +603,23 @@
 func (d *dexpreopter) disableDexpreopt() {
 	d.shouldDisableDexpreopt = true
 }
+
+func (d *dexpreopter) EnableProfileRewriting() bool {
+	return proptools.Bool(d.dexpreoptProperties.Dex_preopt.Enable_profile_rewriting)
+}
+
+func (d *dexpreopter) GetProfile() string {
+	return proptools.String(d.dexpreoptProperties.Dex_preopt.Profile)
+}
+
+func (d *dexpreopter) GetProfileGuided() bool {
+	return proptools.Bool(d.dexpreoptProperties.Dex_preopt.Profile_guided)
+}
+
+func (d *dexpreopter) GetRewrittenProfile() android.Path {
+	return d.rewrittenProfile
+}
+
+func (d *dexpreopter) SetRewrittenProfile(p android.Path) {
+	d.rewrittenProfile = p
+}
diff --git a/java/droidstubs.go b/java/droidstubs.go
index ca81343..5ca6c25 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -993,6 +993,7 @@
 func (d *Droidstubs) everythingOptionalCmd(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, doApiLint bool, doCheckReleased bool) {
 
 	// Add API lint options.
+	treatDocumentationIssuesAsErrors := false
 	if doApiLint {
 		var newSince android.Paths
 		if d.properties.Check_api.Api_lint.New_since != nil {
@@ -1006,7 +1007,7 @@
 		// TODO(b/154317059): Clean up this allowlist by baselining and/or checking in last-released.
 		if d.Name() != "android.car-system-stubs-docs" &&
 			d.Name() != "android.car-stubs-docs" {
-			cmd.Flag("--lints-as-errors")
+			treatDocumentationIssuesAsErrors = true
 			cmd.Flag("--warnings-as-errors") // Most lints are actually warnings.
 		}
 
@@ -1052,6 +1053,11 @@
 		cmd.FlagWithArg("--error-message:api-lint ", msg)
 	}
 
+	if !treatDocumentationIssuesAsErrors {
+		// Treat documentation issues as warnings, but error when new.
+		cmd.Flag("--error-when-new-category").Flag("Documentation")
+	}
+
 	// Add "check released" options. (Detect incompatible API changes from the last public release)
 	if doCheckReleased {
 		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file)
@@ -1147,6 +1153,9 @@
 		}
 	}
 
+	// Treat documentation issues as warnings, but error when new.
+	cmd.Flag("--error-when-new-category").Flag("Documentation")
+
 	if params.stubConfig.generateStubs {
 		rule.Command().
 			BuiltTool("soong_zip").
diff --git a/java/java.go b/java/java.go
index 9fe7a2f..ccccbac 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1504,6 +1504,8 @@
 		RequiredModuleNames: j.RequiredModuleNames(),
 		TestSuites:          j.testProperties.Test_suites,
 		IsHost:              true,
+		LocalSdkVersion:     j.sdkVersion.String(),
+		IsUnitTest:          Bool(j.testProperties.Test_options.Unit_test),
 	})
 }
 
@@ -2339,7 +2341,7 @@
 		classesJar:    al.stubsJar,
 		jarName:       ctx.ModuleName() + ".jar",
 	}
-	dexOutputFile := al.dexer.compileDex(ctx, dexParams)
+	dexOutputFile, _ := al.dexer.compileDex(ctx, dexParams)
 	uncompressed := true
 	al.initHiddenAPI(ctx, makeDexJarPathFromPath(dexOutputFile), al.stubsJar, &uncompressed)
 	dexOutputFile = al.hiddenAPIEncodeDex(ctx, dexOutputFile)
@@ -2723,7 +2725,7 @@
 				jarName:       jarName,
 			}
 
-			dexOutputFile = j.dexer.compileDex(ctx, dexParams)
+			dexOutputFile, _ = j.dexer.compileDex(ctx, dexParams)
 			if ctx.Failed() {
 				return
 			}
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index d240e70..39f8c76 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -492,7 +492,7 @@
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
 	).
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": path dependency ":foo{.public.annotations.zip}": annotations.zip not available for api scope public`)).
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": failed to get output file from module "foo" at tag ".public.annotations.zip": annotations.zip not available for api scope public`)).
 		RunTestWithBp(t, `
 		java_sdk_library {
 			name: "foo",
diff --git a/rust/compiler.go b/rust/compiler.go
index efc3dee..a2546a1 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -197,7 +197,7 @@
 	Features []string `android:"arch_variant"`
 
 	// list of configuration options to enable for this crate. To enable features, use the "features" property.
-	Cfgs []string `android:"arch_variant"`
+	Cfgs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// specific rust edition that should be used if the default version is not desired
 	Edition *string `android:"arch_variant"`
@@ -338,7 +338,7 @@
 }
 
 func cfgsToFlags(cfgs []string) []string {
-	flags := []string{}
+	flags := make([]string, 0, len(cfgs))
 	for _, cfg := range cfgs {
 		flags = append(flags, "--cfg '"+cfg+"'")
 	}
@@ -385,8 +385,9 @@
 func (compiler *baseCompiler) cfgFlags(ctx ModuleContext, flags Flags) Flags {
 	flags = CommonDefaultCfgFlags(flags, ctx.RustModule().InVendor(), ctx.RustModule().InProduct())
 
-	flags.RustFlags = append(flags.RustFlags, cfgsToFlags(compiler.Properties.Cfgs)...)
-	flags.RustdocFlags = append(flags.RustdocFlags, cfgsToFlags(compiler.Properties.Cfgs)...)
+	cfgFlags := cfgsToFlags(compiler.Properties.Cfgs.GetOrDefault(ctx, nil))
+	flags.RustFlags = append(flags.RustFlags, cfgFlags...)
+	flags.RustdocFlags = append(flags.RustdocFlags, cfgFlags...)
 
 	return flags
 }
diff --git a/rust/library.go b/rust/library.go
index 1eb0c5e..2a21263 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -504,15 +504,19 @@
 	flags = library.baseCompiler.cfgFlags(ctx, flags)
 	flags = CommonLibraryCfgFlags(ctx, flags)
 
+	cfgs := library.baseCompiler.Properties.Cfgs.GetOrDefault(ctx, nil)
+
 	if library.dylib() {
 		// We need to add a dependency on std in order to link crates as dylibs.
 		// The hack to add this dependency is guarded by the following cfg so
 		// that we don't force a dependency when it isn't needed.
-		library.baseCompiler.Properties.Cfgs = append(library.baseCompiler.Properties.Cfgs, "android_dylib")
+		cfgs = append(cfgs, "android_dylib")
 	}
 
-	flags.RustFlags = append(flags.RustFlags, cfgsToFlags(library.baseCompiler.Properties.Cfgs)...)
-	flags.RustdocFlags = append(flags.RustdocFlags, cfgsToFlags(library.baseCompiler.Properties.Cfgs)...)
+	cfgFlags := cfgsToFlags(cfgs)
+
+	flags.RustFlags = append(flags.RustFlags, cfgFlags...)
+	flags.RustdocFlags = append(flags.RustdocFlags, cfgFlags...)
 
 	return flags
 }
diff --git a/rust/rust.go b/rust/rust.go
index 93853e5..5790dd6 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -341,10 +341,6 @@
 	return Bool(mod.Properties.Bootstrap)
 }
 
-func (mod *Module) MustUseVendorVariant() bool {
-	return true
-}
-
 func (mod *Module) SubName() string {
 	return mod.Properties.SubName
 }
diff --git a/tradefed/providers.go b/tradefed/providers.go
index 66cb625..0abac12 100644
--- a/tradefed/providers.go
+++ b/tradefed/providers.go
@@ -6,7 +6,8 @@
 	"github.com/google/blueprint"
 )
 
-// Output files we need from a base test that we derive from.
+// Data that test_module_config[_host] modules types will need from
+// their dependencies to write out build rules and AndroidMkEntries.
 type BaseTestProviderData struct {
 	// data files and apps for android_test
 	InstalledFiles android.Paths
@@ -19,8 +20,14 @@
 	RequiredModuleNames     []string
 	// List of test suites base uses.
 	TestSuites []string
-	// Used for bases that are Host
+	// True indicates the base modules is built for Host.
 	IsHost bool
+	// Base's sdk version for AndroidMkEntries, generally only used for Host modules.
+	LocalSdkVersion string
+	// Base's certificate for AndroidMkEntries, generally only used for device modules.
+	LocalCertificate string
+	// Indicates if the base module was a unit test.
+	IsUnitTest bool
 }
 
 var BaseTestProviderKey = blueprint.NewProvider[BaseTestProviderData]()
diff --git a/tradefed_modules/test_module_config.go b/tradefed_modules/test_module_config.go
index b2d5631..f9622d3 100644
--- a/tradefed_modules/test_module_config.go
+++ b/tradefed_modules/test_module_config.go
@@ -5,6 +5,8 @@
 	"android/soong/tradefed"
 	"encoding/json"
 	"fmt"
+	"io"
+	"strings"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -23,14 +25,17 @@
 type testModuleConfigModule struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
-	base android.Module
 
 	tradefedProperties
 
 	// Our updated testConfig.
 	testConfig android.OutputPath
-	manifest   android.InstallPath
+	manifest   android.OutputPath
 	provider   tradefed.BaseTestProviderData
+
+	supportFiles android.InstallPaths
+
+	isHost bool
 }
 
 // Host is mostly the same as non-host, just some diffs for AddDependency and
@@ -94,7 +99,6 @@
 
 // Takes base's Tradefed Config xml file and generates a new one with the test properties
 // appeneded from this module.
-// Rewrite the name of the apk in "test-file-name" to be our module's name, rather than the original one.
 func (m *testModuleConfigModule) fixTestConfig(ctx android.ModuleContext, baseTestConfig android.Path) android.OutputPath {
 	// Test safe to do when no test_runner_options, but check for that earlier?
 	fixedConfig := android.PathForModuleOut(ctx, "test_config_fixer", ctx.ModuleName()+".config")
@@ -106,9 +110,8 @@
 	}
 	xmlTestModuleConfigSnippet, _ := json.Marshal(options)
 	escaped := proptools.NinjaAndShellEscape(string(xmlTestModuleConfigSnippet))
-	command.FlagWithArg("--test-file-name=", ctx.ModuleName()+".apk").
-		FlagWithArg("--orig-test-file-name=", *m.tradefedProperties.Base+".apk").
-		FlagWithArg("--test-runner-options=", escaped)
+	command.FlagWithArg("--test-runner-options=", escaped)
+
 	rule.Build("fix_test_config", "fix test config")
 	return fixedConfig.OutputPath
 }
@@ -190,6 +193,7 @@
 	module.AddProperties(&module.tradefedProperties)
 	android.InitAndroidMultiTargetsArchModule(module, android.HostSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
+	module.isHost = true
 
 	return module
 }
@@ -198,39 +202,66 @@
 var _ android.AndroidMkEntriesProvider = (*testModuleConfigModule)(nil)
 
 func (m *testModuleConfigModule) AndroidMkEntries() []android.AndroidMkEntries {
-	// We rely on base writing LOCAL_COMPATIBILITY_SUPPORT_FILES for its data files
-	entriesList := m.base.(android.AndroidMkEntriesProvider).AndroidMkEntries()
-	entries := &entriesList[0]
-	entries.OutputFile = android.OptionalPathForPath(m.provider.OutputFile)
-	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		entries.SetString("LOCAL_MODULE", m.Name()) //  out module name, not base's
+	appClass := "APPS"
+	include := "$(BUILD_SYSTEM)/soong_app_prebuilt.mk"
+	if m.isHost {
+		appClass = "JAVA_LIBRARIES"
+		include = "$(BUILD_SYSTEM)/soong_java_prebuilt.mk"
+	}
+	return []android.AndroidMkEntries{{
+		Class:      appClass,
+		OutputFile: android.OptionalPathForPath(m.manifest),
+		Include:    include,
+		Required:   []string{*m.Base},
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetPath("LOCAL_FULL_TEST_CONFIG", m.testConfig)
+				entries.SetString("LOCAL_MODULE_TAGS", "tests")
+				entries.SetString("LOCAL_TEST_MODULE_CONFIG_BASE", *m.Base)
+				if m.provider.LocalSdkVersion != "" {
+					entries.SetString("LOCAL_SDK_VERSION", m.provider.LocalSdkVersion)
+				}
+				if m.provider.LocalCertificate != "" {
+					entries.SetString("LOCAL_CERTIFICATE", m.provider.LocalCertificate)
+				}
 
-		// Out update config file with extra options.
-		entries.SetPath("LOCAL_FULL_TEST_CONFIG", m.testConfig)
-		entries.SetString("LOCAL_MODULE_TAGS", "tests")
+				entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", m.provider.IsUnitTest)
+				entries.AddCompatibilityTestSuites(m.tradefedProperties.Test_suites...)
 
-		// Don't append to base's test-suites, only use the ones we define, so clear it before
-		// appending to it.
-		entries.SetString("LOCAL_COMPATIBILITY_SUITE", "")
-		entries.AddCompatibilityTestSuites(m.tradefedProperties.Test_suites...)
+				// The app_prebuilt_internal.mk files try create a copy of the OutputFile as an .apk.
+				// Normally, this copies the "package.apk" from the intermediate directory here.
+				// To prevent the copy of the large apk and to prevent confusion with the real .apk we
+				// link to, we set the STEM here to a bogus name and we set OutputFile to a small file (our manifest).
+				// We do this so we don't have to add more conditionals to base_rules.mk
+				// soong_java_prebult has the same issue for .jars so use this in both module types.
+				entries.SetString("LOCAL_MODULE_STEM", fmt.Sprintf("UNUSED-%s", *m.Base))
 
-		if len(m.provider.HostRequiredModuleNames) > 0 {
-			entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", m.provider.HostRequiredModuleNames...)
-		}
-		if len(m.provider.RequiredModuleNames) > 0 {
-			entries.AddStrings("LOCAL_REQUIRED_MODULES", m.provider.RequiredModuleNames...)
-		}
-
-		if m.provider.IsHost == false {
-			// Not needed for jar_host_test
-			//
-			// Clear the JNI symbols because they belong to base not us. Either transform the names in the string
-			// or clear the variable because we don't need it, we are copying bases libraries not generating
-			// new ones.
-			entries.SetString("LOCAL_SOONG_JNI_LIBS_SYMBOLS", "")
-		}
-	})
-	return entriesList
+				// In normal java/app modules, the module writes LOCAL_COMPATIBILITY_SUPPORT_FILES
+				// and then base_rules.mk ends up copying each of those dependencies from .intermediates to the install directory.
+				// tasks/general-tests.mk, tasks/devices-tests.mk also use these to figure out
+				// which testcase files to put in a zip for running tests on another machine.
+				//
+				// We need our files to end up in the zip, but we don't want \.mk files to
+				// `install` files for us.
+				// So we create a new make variable to indicate these should be in the zip
+				// but not installed.
+				entries.AddStrings("LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES", m.supportFiles.Strings()...)
+			},
+		},
+		// Ensure each of our supportFiles depends on the installed file in base so that our symlinks will always
+		// resolve.  The provider gives us the .intermediate path for the support file in base, we change it to
+		// the installed path with a string substitution.
+		ExtraFooters: []android.AndroidMkExtraFootersFunc{
+			func(w io.Writer, name, prefix, moduleDir string) {
+				for _, f := range m.supportFiles.Strings() {
+					// convert out/.../testcases/FrameworksServicesTests_contentprotection/file1.apk
+					// to      out/.../testcases/FrameworksServicesTests/file1.apk
+					basePath := strings.Replace(f, "/"+m.Name()+"/", "/"+*m.Base+"/", 1)
+					fmt.Fprintf(w, "%s: %s\n", f, basePath)
+				}
+			},
+		},
+	}}
 }
 
 func (m *testModuleConfigHostModule) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -248,7 +279,7 @@
 //   - written via soong_java_prebuilt.mk
 //
 // 4) out/host/linux-x86/testcases/derived-module/* # data dependencies from base.
-//   - written via soong_java_prebuilt.mk
+//   - written via our InstallSymlink
 func (m *testModuleConfigHostModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	m.validateBase(ctx, &testModuleConfigHostTag, "java_test_host", true)
 	m.generateManifestAndConfig(ctx)
@@ -260,7 +291,6 @@
 	ctx.VisitDirectDepsWithTag(*depTag, func(dep android.Module) {
 		if provider, ok := android.OtherModuleProvider(ctx, dep, tradefed.BaseTestProviderKey); ok {
 			if baseShouldBeHost == provider.IsHost {
-				m.base = dep
 				m.provider = provider
 			} else {
 				if baseShouldBeHost {
@@ -277,7 +307,9 @@
 
 // Actions to write:
 //  1. manifest file to testcases dir
-//  2. New Module.config / AndroidTest.xml file with our options.
+//  2. Symlink to base.apk under base's arch dir
+//  3. Symlink to all data dependencies
+//  4. New Module.config / AndroidTest.xml file with our options.
 func (m *testModuleConfigModule) generateManifestAndConfig(ctx android.ModuleContext) {
 	// Keep before early returns.
 	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
@@ -292,7 +324,6 @@
 	if m.provider.TestConfig == nil {
 		return
 	}
-
 	// 1) A manifest file listing the base, write text to a tiny file.
 	installDir := android.PathForModuleInstall(ctx, ctx.ModuleName())
 	manifest := android.PathForModuleOut(ctx, "test_module_config.manifest")
@@ -301,9 +332,53 @@
 	//    Assume the primary install file is last
 	// so we need to Install our file last.
 	ctx.InstallFile(installDir, manifest.Base(), manifest)
+	m.manifest = manifest.OutputPath
 
-	// 2) Module.config / AndroidTest.xml
+	// 2) Symlink to base.apk
+	baseApk := m.provider.OutputFile
+
+	// Typically looks like this for baseApk
+	// FrameworksServicesTests
+	// └── x86_64
+	//    └── FrameworksServicesTests.apk
+	symlinkName := fmt.Sprintf("%s/%s", ctx.DeviceConfig().DeviceArch(), baseApk.Base())
+	// Only android_test, not java_host_test puts the output in the DeviceArch dir.
+	if m.provider.IsHost || ctx.DeviceConfig().DeviceArch() == "" {
+		// testcases/CtsDevicePolicyManagerTestCases
+		// ├── CtsDevicePolicyManagerTestCases.jar
+		symlinkName = baseApk.Base()
+	}
+	target := installedBaseRelativeToHere(symlinkName, *m.tradefedProperties.Base)
+	installedApk := ctx.InstallAbsoluteSymlink(installDir, symlinkName, target)
+	m.supportFiles = append(m.supportFiles, installedApk)
+
+	// 3) Symlink for all data deps
+	// And like this for data files and required modules
+	// FrameworksServicesTests
+	// ├── data
+	// │   └── broken_shortcut.xml
+	// ├── JobTestApp.apk
+	for _, f := range m.provider.InstalledFiles {
+		symlinkName := f.Rel()
+		target := installedBaseRelativeToHere(symlinkName, *m.tradefedProperties.Base)
+		installedPath := ctx.InstallAbsoluteSymlink(installDir, symlinkName, target)
+		m.supportFiles = append(m.supportFiles, installedPath)
+	}
+
+	// 4) Module.config / AndroidTest.xml
 	m.testConfig = m.fixTestConfig(ctx, m.provider.TestConfig)
 }
 
 var _ android.AndroidMkEntriesProvider = (*testModuleConfigHostModule)(nil)
+
+// Given a relative path to a file in the current directory or a subdirectory,
+// return a relative path under our sibling directory named `base`.
+// There should be one "../" for each subdir we descend plus one to backup to "base".
+//
+//	 ThisDir/file1
+//	 ThisDir/subdir/file2
+//	would return "../base/file1" or "../../subdir/file2"
+func installedBaseRelativeToHere(targetFileName string, base string) string {
+	backup := strings.Repeat("../", strings.Count(targetFileName, "/")+1)
+	return fmt.Sprintf("%s%s/%s", backup, base, targetFileName)
+}
diff --git a/tradefed_modules/test_module_config_test.go b/tradefed_modules/test_module_config_test.go
index b2049b1..97179f5 100644
--- a/tradefed_modules/test_module_config_test.go
+++ b/tradefed_modules/test_module_config_test.go
@@ -16,6 +16,7 @@
 import (
 	"android/soong/android"
 	"android/soong/java"
+	"fmt"
 	"strconv"
 	"strings"
 	"testing"
@@ -69,15 +70,36 @@
 	entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0]
 
 	// Ensure some entries from base are there, specifically support files for data and helper apps.
-	assertEntryPairValues(t, entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{"HelperApp.apk", "data/testfile"})
+	// Do not use LOCAL_COMPATIBILITY_SUPPORT_FILES, but instead use LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES
+	android.AssertStringPathsRelativeToTopEquals(t, "support-files", ctx.Config,
+		[]string{"out/soong/target/product/test_device/testcases/derived_test/arm64/base.apk",
+			"out/soong/target/product/test_device/testcases/derived_test/HelperApp.apk",
+			"out/soong/target/product/test_device/testcases/derived_test/data/testfile"},
+		entries.EntryMap["LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES"])
+	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{})
+
+	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_REQUIRED_MODULES"], []string{"base"})
+	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_CERTIFICATE"], []string{"build/make/target/product/security/testkey.x509.pem"})
+	android.AssertStringEquals(t, "", entries.Class, "APPS")
 
 	// And some new derived entries are there.
 	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_MODULE_TAGS"], []string{"tests"})
 
-	// And ones we override
-	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_SOONG_JNI_LIBS_SYMBOLS"], []string{""})
-
 	android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], "derived_test/android_common/test_config_fixer/derived_test.config")
+
+	// Check the footer lines.  Our support files should depend on base's support files.
+	convertedActual := make([]string, 5)
+	for i, e := range entries.FooterLinesForTests() {
+		// AssertStringPathsRelativeToTop doesn't replace both instances
+		convertedActual[i] = strings.Replace(e, ctx.Config.SoongOutDir(), "", 2)
+	}
+	android.AssertArrayString(t, fmt.Sprintf("%s", ctx.Config.SoongOutDir()), convertedActual, []string{
+		"include $(BUILD_SYSTEM)/soong_app_prebuilt.mk",
+		"/target/product/test_device/testcases/derived_test/arm64/base.apk: /target/product/test_device/testcases/base/arm64/base.apk",
+		"/target/product/test_device/testcases/derived_test/HelperApp.apk: /target/product/test_device/testcases/base/HelperApp.apk",
+		"/target/product/test_device/testcases/derived_test/data/testfile: /target/product/test_device/testcases/base/data/testfile",
+		"",
+	})
 }
 
 // Make sure we call test-config-fixer with the right args.
@@ -92,7 +114,7 @@
 	derived := ctx.ModuleForTests("derived_test", "android_common")
 	rule_cmd := derived.Rule("fix_test_config").RuleParams.Command
 	android.AssertStringDoesContain(t, "Bad FixConfig rule inputs", rule_cmd,
-		`--test-file-name=derived_test.apk --orig-test-file-name=base.apk --test-runner-options='[{"Name":"exclude-filter","Key":"","Value":"android.test.example.devcodelab.DevCodelabTest#testHelloFail"},{"Name":"include-annotation","Key":"","Value":"android.platform.test.annotations.LargeTest"}]'`)
+		`--test-runner-options='[{"Name":"exclude-filter","Key":"","Value":"android.test.example.devcodelab.DevCodelabTest#testHelloFail"},{"Name":"include-annotation","Key":"","Value":"android.platform.test.annotations.LargeTest"}]'`)
 }
 
 // Ensure we error for a base we don't support.
@@ -195,8 +217,14 @@
 			name: "base",
 			sdk_version: "current",
                         srcs: ["a.java"],
+                        data: [":HelperApp", "data/testfile"],
 		}
 
+                android_test_helper_app {
+                        name: "HelperApp",
+                        srcs: ["helper.java"],
+                }
+
                 test_module_config {
                         name: "derived_test",
                         base: "base",
@@ -220,8 +248,12 @@
 		derived := ctx.ModuleForTests("derived_test", "android_common")
 		entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0]
 		// All these should be the same in both derived tests
-		assertEntryPairValues(t, entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{"HelperApp.apk", "data/testfile"})
-		android.AssertArrayString(t, "", entries.EntryMap["LOCAL_SOONG_JNI_LIBS_SYMBOLS"], []string{""})
+		android.AssertStringPathsRelativeToTopEquals(t, "support-files", ctx.Config,
+			[]string{"out/soong/target/product/test_device/testcases/derived_test/arm64/base.apk",
+				"out/soong/target/product/test_device/testcases/derived_test/HelperApp.apk",
+				"out/soong/target/product/test_device/testcases/derived_test/data/testfile"},
+			entries.EntryMap["LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES"])
+
 		// Except this one, which points to the updated tradefed xml file.
 		android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], "derived_test/android_common/test_config_fixer/derived_test.config")
 		// And this one, the module name.
@@ -232,8 +264,11 @@
 		derived := ctx.ModuleForTests("another_derived_test", "android_common")
 		entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0]
 		// All these should be the same in both derived tests
-		assertEntryPairValues(t, entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{"HelperApp.apk", "data/testfile"})
-		android.AssertArrayString(t, "", entries.EntryMap["LOCAL_SOONG_JNI_LIBS_SYMBOLS"], []string{""})
+		android.AssertStringPathsRelativeToTopEquals(t, "support-files", ctx.Config,
+			[]string{"out/soong/target/product/test_device/testcases/another_derived_test/arm64/base.apk",
+				"out/soong/target/product/test_device/testcases/another_derived_test/HelperApp.apk",
+				"out/soong/target/product/test_device/testcases/another_derived_test/data/testfile"},
+			entries.EntryMap["LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES"])
 		// Except this one, which points to the updated tradefed xml file.
 		android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], "another_derived_test/android_common/test_config_fixer/another_derived_test.config")
 		// And this one, the module name.
@@ -269,6 +304,8 @@
 	allEntries := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)
 	entries := allEntries[0]
 	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_MODULE"], []string{"derived_test"})
+	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_SDK_VERSION"], []string{"private_current"})
+	android.AssertStringEquals(t, "", entries.Class, "JAVA_LIBRARIES")
 
 	if !mod.Host() {
 		t.Errorf("host bit is not set for a java_test_host module.")
@@ -385,16 +422,3 @@
 		t.Errorf("test-only: Expected but not found: %v, Found but not expected: %v", left, right)
 	}
 }
-
-// Use for situations where the entries map contains pairs:  [srcPath:installedPath1, srcPath2:installedPath2]
-// and we want to compare the RHS of the pairs, i.e. installedPath1, installedPath2
-func assertEntryPairValues(t *testing.T, actual []string, expected []string) {
-	for i, e := range actual {
-		parts := strings.Split(e, ":")
-		if len(parts) != 2 {
-			t.Errorf("Expected entry to have a value delimited by :, received: %s", e)
-			return
-		}
-		android.AssertStringEquals(t, "", parts[1], expected[i])
-	}
-}