Merge "Revert "Handle symlinks in sbox"" into main
diff --git a/OWNERS b/OWNERS
index e5374ed..58edc89 100644
--- a/OWNERS
+++ b/OWNERS
@@ -3,29 +3,13 @@
 # approving build related projects.
 
 # AMER
-agespino@google.com  #{LAST_RESORT_SUGGESTION}
 ccross@android.com
 colefaust@google.com
-cparsons@google.com  #{LAST_RESORT_SUGGESTION}
-dacek@google.com  #{LAST_RESORT_SUGGESTION}
-delmerico@google.com  #{LAST_RESORT_SUGGESTION}
 dwillemsen@google.com
-eakammer@google.com  #{LAST_RESORT_SUGGESTION}
 jihoonkang@google.com
-jobredeaux@google.com  #{LAST_RESORT_SUGGESTION}
 joeo@google.com
-juu@google.com  #{LAST_RESORT_SUGGESTION}
 lamontjones@google.com
 mrziwang@google.com
 spandandas@google.com
-tradical@google.com  #{LAST_RESORT_SUGGESTION}
-usta@google.com  #{LAST_RESORT_SUGGESTION}
-vinhdaitran@google.com  #{LAST_RESORT_SUGGESTION}
 weiwli@google.com
-yudiliu@google.com
-
-# APAC
-jingwen@google.com  #{LAST_RESORT_SUGGESTION}
-
-# EMEA
-lberki@google.com  #{LAST_RESORT_SUGGESTION}
+yudiliu@google.com
\ No newline at end of file
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index faa4ddb..04de919 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -12,28 +12,19 @@
         "soong",
         "soong-android",
         "soong-bazel",
-        "soong-android",
-        "soong-java",
-        "soong-rust",
     ],
     srcs: [
         "aconfig_declarations.go",
         "aconfig_values.go",
         "aconfig_value_set.go",
         "all_aconfig_declarations.go",
-        "cc_aconfig_library.go",
         "init.go",
-        "java_aconfig_library.go",
         "testing.go",
-        "rust_aconfig_library.go",
     ],
     testSrcs: [
         "aconfig_declarations_test.go",
         "aconfig_values_test.go",
         "aconfig_value_set_test.go",
-        "java_aconfig_library_test.go",
-        "cc_aconfig_library_test.go",
-        "rust_aconfig_library_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go
index 897f892..05a6371 100644
--- a/aconfig/aconfig_declarations.go
+++ b/aconfig/aconfig_declarations.go
@@ -38,6 +38,9 @@
 
 		// Values from TARGET_RELEASE / RELEASE_ACONFIG_VALUE_SETS
 		Values []string `blueprint:"mutated"`
+
+		// Container(system/vendor/apex) that this module belongs to
+		Container string
 	}
 
 	intermediatePath android.WritablePath
@@ -69,6 +72,8 @@
 	if len(module.properties.Package) == 0 {
 		ctx.PropertyErrorf("package", "missing package property")
 	}
+	// TODO(b/311155208): Add mandatory check for container after all pre-existing
+	// ones are changed.
 
 	// Add a dependency on the aconfig_value_sets defined in
 	// RELEASE_ACONFIG_VALUE_SETS, and add any aconfig_values that
@@ -110,12 +115,21 @@
 }
 
 // Provider published by aconfig_value_set
-type declarationsProviderData struct {
+type DeclarationsProviderData struct {
 	Package          string
+	Container        string
 	IntermediatePath android.WritablePath
 }
 
-var declarationsProviderKey = blueprint.NewProvider(declarationsProviderData{})
+var DeclarationsProviderKey = blueprint.NewProvider(DeclarationsProviderData{})
+
+// This is used to collect the aconfig declarations info on the transitive closure,
+// the data is keyed on the container.
+type TransitiveDeclarationsInfo struct {
+	AconfigFiles map[string]android.Paths
+}
+
+var TransitiveDeclarationsInfoProvider = blueprint.NewProvider(TransitiveDeclarationsInfo{})
 
 func (module *DeclarationsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// Get the values that came from the global RELEASE_ACONFIG_VALUE_SETS flag
@@ -156,12 +170,57 @@
 		},
 	})
 
-	ctx.SetProvider(declarationsProviderKey, declarationsProviderData{
+	ctx.SetProvider(DeclarationsProviderKey, DeclarationsProviderData{
 		Package:          module.properties.Package,
+		Container:        module.properties.Container,
 		IntermediatePath: intermediatePath,
 	})
 
 }
+func CollectDependencyAconfigFiles(ctx android.ModuleContext, mergedAconfigFiles *map[string]android.Paths) {
+	if *mergedAconfigFiles == nil {
+		*mergedAconfigFiles = make(map[string]android.Paths)
+	}
+	ctx.VisitDirectDeps(func(module android.Module) {
+		if dep := ctx.OtherModuleProvider(module, DeclarationsProviderKey).(DeclarationsProviderData); dep.IntermediatePath != nil {
+			(*mergedAconfigFiles)[dep.Container] = append((*mergedAconfigFiles)[dep.Container], dep.IntermediatePath)
+			return
+		}
+		if dep := ctx.OtherModuleProvider(module, TransitiveDeclarationsInfoProvider).(TransitiveDeclarationsInfo); len(dep.AconfigFiles) > 0 {
+			for container, v := range dep.AconfigFiles {
+				(*mergedAconfigFiles)[container] = append((*mergedAconfigFiles)[container], v...)
+			}
+		}
+	})
+
+	for container, aconfigFiles := range *mergedAconfigFiles {
+		(*mergedAconfigFiles)[container] = mergeAconfigFiles(ctx, aconfigFiles)
+	}
+
+	ctx.SetProvider(TransitiveDeclarationsInfoProvider, TransitiveDeclarationsInfo{
+		AconfigFiles: *mergedAconfigFiles,
+	})
+}
+
+func mergeAconfigFiles(ctx android.ModuleContext, inputs android.Paths) android.Paths {
+	if len(inputs) == 1 {
+		return android.Paths{inputs[0]}
+	}
+
+	output := android.PathForModuleOut(ctx, "aconfig_merged.pb")
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        mergeAconfigFilesRule,
+		Description: "merge aconfig files",
+		Inputs:      inputs,
+		Output:      output,
+		Args: map[string]string{
+			"flags": android.JoinWithPrefix(inputs.Strings(), "--cache "),
+		},
+	})
+
+	return android.Paths{output}
+}
 
 type bazelAconfigDeclarationsAttributes struct {
 	Srcs    bazel.LabelListAttribute
diff --git a/aconfig/aconfig_declarations_test.go b/aconfig/aconfig_declarations_test.go
index e0d8f7d..e6bd87c 100644
--- a/aconfig/aconfig_declarations_test.go
+++ b/aconfig/aconfig_declarations_test.go
@@ -26,6 +26,7 @@
 		aconfig_declarations {
 			name: "module_name",
 			package: "com.example.package",
+			container: "com.android.foo",
 			srcs: [
 				"foo.aconfig",
 				"bar.aconfig",
@@ -37,8 +38,9 @@
 	module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule)
 
 	// Check that the provider has the right contents
-	depData := result.ModuleProvider(module, declarationsProviderKey).(declarationsProviderData)
+	depData := result.ModuleProvider(module, DeclarationsProviderKey).(DeclarationsProviderData)
 	android.AssertStringEquals(t, "package", depData.Package, "com.example.package")
+	android.AssertStringEquals(t, "container", depData.Container, "com.android.foo")
 	if !strings.HasSuffix(depData.IntermediatePath.String(), "/intermediate.pb") {
 		t.Errorf("Missing intermediates path in provider: %s", depData.IntermediatePath.String())
 	}
diff --git a/aconfig/all_aconfig_declarations.go b/aconfig/all_aconfig_declarations.go
index 6096c6c..7ccb81a 100644
--- a/aconfig/all_aconfig_declarations.go
+++ b/aconfig/all_aconfig_declarations.go
@@ -37,17 +37,17 @@
 	// Find all of the aconfig_declarations modules
 	var cacheFiles android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
-		if !ctx.ModuleHasProvider(module, declarationsProviderKey) {
+		if !ctx.ModuleHasProvider(module, DeclarationsProviderKey) {
 			return
 		}
-		decl := ctx.ModuleProvider(module, declarationsProviderKey).(declarationsProviderData)
+		decl := ctx.ModuleProvider(module, DeclarationsProviderKey).(DeclarationsProviderData)
 		cacheFiles = append(cacheFiles, decl.IntermediatePath)
 	})
 
 	// Generate build action for aconfig
 	this.intermediatePath = android.PathForIntermediates(ctx, "all_aconfig_declarations.pb")
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        allDeclarationsRule,
+		Rule:        AllDeclarationsRule,
 		Inputs:      cacheFiles,
 		Output:      this.intermediatePath,
 		Description: "all_aconfig_declarations",
diff --git a/aconfig/codegen/Android.bp b/aconfig/codegen/Android.bp
new file mode 100644
index 0000000..494f7e6
--- /dev/null
+++ b/aconfig/codegen/Android.bp
@@ -0,0 +1,32 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-aconfig-codegen",
+    pkgPath: "android/soong/aconfig/codegen",
+    deps: [
+        "blueprint",
+        "blueprint-pathtools",
+        "sbox_proto",
+        "soong",
+        "soong-aconfig",
+        "soong-android",
+        "soong-bazel",
+        "soong-java",
+        "soong-rust",
+    ],
+    srcs: [
+        "cc_aconfig_library.go",
+        "init.go",
+        "java_aconfig_library.go",
+        "rust_aconfig_library.go",
+        "testing.go",
+    ],
+    testSrcs: [
+        "java_aconfig_library_test.go",
+        "cc_aconfig_library_test.go",
+        "rust_aconfig_library_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/aconfig/cc_aconfig_library.go b/aconfig/codegen/cc_aconfig_library.go
similarity index 96%
rename from aconfig/cc_aconfig_library.go
rename to aconfig/codegen/cc_aconfig_library.go
index 210a581..7b68844 100644
--- a/aconfig/cc_aconfig_library.go
+++ b/aconfig/codegen/cc_aconfig_library.go
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package aconfig
+package codegen
 
 import (
+	"android/soong/aconfig"
 	"android/soong/android"
 	"android/soong/bazel"
 	"android/soong/cc"
@@ -92,7 +93,7 @@
 	if len(declarationsModules) != 1 {
 		panic(fmt.Errorf("Exactly one aconfig_declarations property required"))
 	}
-	declarations := ctx.OtherModuleProvider(declarationsModules[0], declarationsProviderKey).(declarationsProviderData)
+	declarations := ctx.OtherModuleProvider(declarationsModules[0], aconfig.DeclarationsProviderKey).(aconfig.DeclarationsProviderData)
 
 	// Figure out the generated file paths.  This has to match aconfig's codegen_cpp.rs.
 	this.generatedDir = android.PathForModuleGen(ctx)
@@ -122,7 +123,7 @@
 	if len(declarationsModules) != 1 {
 		panic(fmt.Errorf("Exactly one aconfig_declarations property required"))
 	}
-	declarations := ctx.OtherModuleProvider(declarationsModules[0], declarationsProviderKey).(declarationsProviderData)
+	declarations := ctx.OtherModuleProvider(declarationsModules[0], aconfig.DeclarationsProviderKey).(aconfig.DeclarationsProviderData)
 
 	mode := proptools.StringDefault(this.properties.Mode, "production")
 	if !isModeSupported(mode) {
diff --git a/aconfig/cc_aconfig_library_test.go b/aconfig/codegen/cc_aconfig_library_test.go
similarity index 99%
rename from aconfig/cc_aconfig_library_test.go
rename to aconfig/codegen/cc_aconfig_library_test.go
index ba27250..0c8a969 100644
--- a/aconfig/cc_aconfig_library_test.go
+++ b/aconfig/codegen/cc_aconfig_library_test.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package aconfig
+package codegen
 
 import (
 	"fmt"
diff --git a/aconfig/codegen/init.go b/aconfig/codegen/init.go
new file mode 100644
index 0000000..0bff9d2
--- /dev/null
+++ b/aconfig/codegen/init.go
@@ -0,0 +1,83 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package codegen
+
+import (
+	"android/soong/android"
+
+	"github.com/google/blueprint"
+)
+
+var (
+	pctx = android.NewPackageContext("android/soong/aconfig/codegen")
+
+	// For java_aconfig_library: Generate java library
+	javaRule = pctx.AndroidStaticRule("java_aconfig_library",
+		blueprint.RuleParams{
+			Command: `rm -rf ${out}.tmp` +
+				` && mkdir -p ${out}.tmp` +
+				` && ${aconfig} create-java-lib` +
+				`    --mode ${mode}` +
+				`    --cache ${in}` +
+				`    --out ${out}.tmp` +
+				` && $soong_zip -write_if_changed -jar -o ${out} -C ${out}.tmp -D ${out}.tmp` +
+				` && rm -rf ${out}.tmp`,
+			CommandDeps: []string{
+				"$aconfig",
+				"$soong_zip",
+			},
+			Restat: true,
+		}, "mode")
+
+	// For cc_aconfig_library: Generate C++ library
+	cppRule = pctx.AndroidStaticRule("cc_aconfig_library",
+		blueprint.RuleParams{
+			Command: `rm -rf ${gendir}` +
+				` && mkdir -p ${gendir}` +
+				` && ${aconfig} create-cpp-lib` +
+				`    --mode ${mode}` +
+				`    --cache ${in}` +
+				`    --out ${gendir}`,
+			CommandDeps: []string{
+				"$aconfig",
+			},
+		}, "gendir", "mode")
+
+	// For rust_aconfig_library: Generate Rust library
+	rustRule = pctx.AndroidStaticRule("rust_aconfig_library",
+		blueprint.RuleParams{
+			Command: `rm -rf ${gendir}` +
+				` && mkdir -p ${gendir}` +
+				` && ${aconfig} create-rust-lib` +
+				`    --mode ${mode}` +
+				`    --cache ${in}` +
+				`    --out ${gendir}`,
+			CommandDeps: []string{
+				"$aconfig",
+			},
+		}, "gendir", "mode")
+)
+
+func init() {
+	RegisterBuildComponents(android.InitRegistrationContext)
+	pctx.HostBinToolVariable("aconfig", "aconfig")
+	pctx.HostBinToolVariable("soong_zip", "soong_zip")
+}
+
+func RegisterBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("cc_aconfig_library", CcAconfigLibraryFactory)
+	ctx.RegisterModuleType("java_aconfig_library", JavaDeclarationsLibraryFactory)
+	ctx.RegisterModuleType("rust_aconfig_library", RustAconfigLibraryFactory)
+}
diff --git a/aconfig/java_aconfig_library.go b/aconfig/codegen/java_aconfig_library.go
similarity index 94%
rename from aconfig/java_aconfig_library.go
rename to aconfig/codegen/java_aconfig_library.go
index eedb3c3..e2fb15b 100644
--- a/aconfig/java_aconfig_library.go
+++ b/aconfig/codegen/java_aconfig_library.go
@@ -12,11 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package aconfig
+package codegen
 
 import (
 	"fmt"
 
+	"android/soong/aconfig"
 	"android/soong/android"
 	"android/soong/bazel"
 	"android/soong/java"
@@ -73,7 +74,7 @@
 	if len(declarationsModules) != 1 {
 		panic(fmt.Errorf("Exactly one aconfig_declarations property required"))
 	}
-	declarations := ctx.OtherModuleProvider(declarationsModules[0], declarationsProviderKey).(declarationsProviderData)
+	declarations := ctx.OtherModuleProvider(declarationsModules[0], aconfig.DeclarationsProviderKey).(aconfig.DeclarationsProviderData)
 
 	// Generate the action to build the srcjar
 	srcJarPath := android.PathForModuleGen(ctx, ctx.ModuleName()+".srcjar")
@@ -93,10 +94,6 @@
 		},
 	})
 
-	// Tell the java module about the .aconfig files, so they can be propagated up the dependency chain.
-	// TODO: It would be nice to have that propagation code here instead of on java.Module and java.JavaInfo.
-	module.AddAconfigIntermediate(declarations.IntermediatePath)
-
 	return srcJarPath
 }
 
diff --git a/aconfig/java_aconfig_library_test.go b/aconfig/codegen/java_aconfig_library_test.go
similarity index 84%
rename from aconfig/java_aconfig_library_test.go
rename to aconfig/codegen/java_aconfig_library_test.go
index a803672..2523abc 100644
--- a/aconfig/java_aconfig_library_test.go
+++ b/aconfig/codegen/java_aconfig_library_test.go
@@ -12,11 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package aconfig
+package codegen
 
 import (
 	"fmt"
-	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -34,14 +33,25 @@
 		ExtendWithErrorHandler(android.FixtureExpectsNoErrors).
 		RunTestWithBp(t, bp+`
 			aconfig_declarations {
-				name: "my_aconfig_declarations",
+				name: "my_aconfig_declarations_foo",
 				package: "com.example.package",
 				srcs: ["foo.aconfig"],
 			}
 
 			java_aconfig_library {
-				name: "my_java_aconfig_library",
-				aconfig_declarations: "my_aconfig_declarations",
+				name: "my_java_aconfig_library_foo",
+				aconfig_declarations: "my_aconfig_declarations_foo",
+			}
+
+			aconfig_declarations {
+				name: "my_aconfig_declarations_bar",
+				package: "com.example.package",
+				srcs: ["bar.aconfig"],
+			}
+
+			java_aconfig_library {
+				name: "my_java_aconfig_library_bar",
+				aconfig_declarations: "my_aconfig_declarations_bar",
 			}
 		`)
 
@@ -51,9 +61,7 @@
 
 	makeVar := entry.EntryMap["LOCAL_ACONFIG_FILES"]
 	android.AssertIntEquals(t, "len(LOCAL_ACONFIG_FILES)", 1, len(makeVar))
-	if !strings.HasSuffix(makeVar[0], "intermediate.pb") {
-		t.Errorf("LOCAL_ACONFIG_FILES should end with /intermediates.pb, instead it is: %s", makeVar[0])
-	}
+	android.EnsureListContainsSuffix(t, makeVar, "android_common/aconfig_merged.pb")
 }
 
 func TestAndroidMkJavaLibrary(t *testing.T) {
@@ -64,7 +72,8 @@
 				"src/foo.java",
 			],
 			static_libs: [
-				"my_java_aconfig_library",
+				"my_java_aconfig_library_foo",
+				"my_java_aconfig_library_bar",
 			],
 			platform_apis: true,
 		}
@@ -81,7 +90,8 @@
 				"src/foo.java",
 			],
 			static_libs: [
-				"my_java_aconfig_library",
+				"my_java_aconfig_library_foo",
+				"my_java_aconfig_library_bar",
 			],
 			platform_apis: true,
 		}
@@ -98,7 +108,8 @@
 				"src/foo.java",
 			],
 			static_libs: [
-				"my_java_aconfig_library",
+				"my_java_aconfig_library_foo",
+				"my_java_aconfig_library_bar",
 			],
 			platform_apis: true,
 			main_class: "foo",
@@ -116,7 +127,8 @@
 				"src/foo.java",
 			],
 			static_libs: [
-				"my_java_aconfig_library",
+				"my_java_aconfig_library_foo",
+				"my_java_aconfig_library_bar",
 			],
 			platform_apis: true,
 		}
@@ -134,7 +146,8 @@
 				"src/foo.java",
 			],
 			static_libs: [
-				"my_java_aconfig_library",
+				"my_java_aconfig_library_foo",
+				"my_java_aconfig_library_bar",
 			],
 			platform_apis: true,
 		}
diff --git a/aconfig/rust_aconfig_library.go b/aconfig/codegen/rust_aconfig_library.go
similarity index 96%
rename from aconfig/rust_aconfig_library.go
rename to aconfig/codegen/rust_aconfig_library.go
index 265685e..3525de1 100644
--- a/aconfig/rust_aconfig_library.go
+++ b/aconfig/codegen/rust_aconfig_library.go
@@ -1,8 +1,9 @@
-package aconfig
+package codegen
 
 import (
 	"fmt"
 
+	"android/soong/aconfig"
 	"android/soong/android"
 	"android/soong/rust"
 
@@ -64,7 +65,7 @@
 	if len(declarationsModules) != 1 {
 		panic(fmt.Errorf("Exactly one aconfig_declarations property required"))
 	}
-	declarations := ctx.OtherModuleProvider(declarationsModules[0], declarationsProviderKey).(declarationsProviderData)
+	declarations := ctx.OtherModuleProvider(declarationsModules[0], aconfig.DeclarationsProviderKey).(aconfig.DeclarationsProviderData)
 
 	mode := proptools.StringDefault(a.Properties.Mode, "production")
 	if !isModeSupported(mode) {
diff --git a/aconfig/rust_aconfig_library_test.go b/aconfig/codegen/rust_aconfig_library_test.go
similarity index 99%
rename from aconfig/rust_aconfig_library_test.go
rename to aconfig/codegen/rust_aconfig_library_test.go
index 3aeab76..c09f701 100644
--- a/aconfig/rust_aconfig_library_test.go
+++ b/aconfig/codegen/rust_aconfig_library_test.go
@@ -1,4 +1,4 @@
-package aconfig
+package codegen
 
 import (
 	"fmt"
diff --git a/aconfig/codegen/testing.go b/aconfig/codegen/testing.go
new file mode 100644
index 0000000..3e1c22e
--- /dev/null
+++ b/aconfig/codegen/testing.go
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 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.
+
+package codegen
+
+import (
+	"android/soong/aconfig"
+	"android/soong/android"
+)
+
+var PrepareForTestWithAconfigBuildComponents = android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("aconfig_declarations", aconfig.DeclarationsFactory)
+	RegisterBuildComponents(ctx)
+})
diff --git a/aconfig/init.go b/aconfig/init.go
index 626e66d..2e24db9 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -40,60 +40,20 @@
 			Restat: true,
 		}, "release_version", "package", "declarations", "values", "default-permission")
 
-	// For java_aconfig_library: Generate java library
-	javaRule = pctx.AndroidStaticRule("java_aconfig_library",
-		blueprint.RuleParams{
-			Command: `rm -rf ${out}.tmp` +
-				` && mkdir -p ${out}.tmp` +
-				` && ${aconfig} create-java-lib` +
-				`    --mode ${mode}` +
-				`    --cache ${in}` +
-				`    --out ${out}.tmp` +
-				` && $soong_zip -write_if_changed -jar -o ${out} -C ${out}.tmp -D ${out}.tmp` +
-				` && rm -rf ${out}.tmp`,
-			CommandDeps: []string{
-				"$aconfig",
-				"$soong_zip",
-			},
-			Restat: true,
-		}, "mode")
-
-	// For cc_aconfig_library: Generate C++ library
-	cppRule = pctx.AndroidStaticRule("cc_aconfig_library",
-		blueprint.RuleParams{
-			Command: `rm -rf ${gendir}` +
-				` && mkdir -p ${gendir}` +
-				` && ${aconfig} create-cpp-lib` +
-				`    --mode ${mode}` +
-				`    --cache ${in}` +
-				`    --out ${gendir}`,
-			CommandDeps: []string{
-				"$aconfig",
-			},
-		}, "gendir", "mode")
-
-	// For rust_aconfig_library: Generate Rust library
-	rustRule = pctx.AndroidStaticRule("rust_aconfig_library",
-		blueprint.RuleParams{
-			Command: `rm -rf ${gendir}` +
-				` && mkdir -p ${gendir}` +
-				` && ${aconfig} create-rust-lib` +
-				`    --mode ${mode}` +
-				`    --cache ${in}` +
-				`    --out ${gendir}`,
-			CommandDeps: []string{
-				"$aconfig",
-			},
-		}, "gendir", "mode")
-
 	// For all_aconfig_declarations: Combine all parsed_flags proto files
-	allDeclarationsRule = pctx.AndroidStaticRule("all_aconfig_declarations_dump",
+	AllDeclarationsRule = pctx.AndroidStaticRule("All_aconfig_declarations_dump",
 		blueprint.RuleParams{
 			Command: `${aconfig} dump --format protobuf --out ${out} ${cache_files}`,
 			CommandDeps: []string{
 				"${aconfig}",
 			},
 		}, "cache_files")
+
+	mergeAconfigFilesRule = pctx.AndroidStaticRule("mergeAconfigFilesRule",
+		blueprint.RuleParams{
+			Command:     `${aconfig} dump --dedup --format protobuf --out $out $flags`,
+			CommandDeps: []string{"${aconfig}"},
+		}, "flags")
 )
 
 func init() {
@@ -106,8 +66,5 @@
 	ctx.RegisterModuleType("aconfig_declarations", DeclarationsFactory)
 	ctx.RegisterModuleType("aconfig_values", ValuesFactory)
 	ctx.RegisterModuleType("aconfig_value_set", ValueSetFactory)
-	ctx.RegisterModuleType("cc_aconfig_library", CcAconfigLibraryFactory)
-	ctx.RegisterModuleType("java_aconfig_library", JavaDeclarationsLibraryFactory)
-	ctx.RegisterModuleType("rust_aconfig_library", RustAconfigLibraryFactory)
 	ctx.RegisterParallelSingletonType("all_aconfig_declarations", AllAconfigDeclarationsFactory)
 }
diff --git a/android/androidmk.go b/android/androidmk.go
index f6e8799..c4b93c7 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -540,6 +540,10 @@
 		a.SetPaths("LOCAL_SOONG_INSTALL_SYMLINKS", base.katiSymlinks.InstallPaths().Paths())
 	}
 
+	if len(base.testData) > 0 {
+		a.AddStrings("LOCAL_TEST_DATA", androidMkDataPaths(base.testData)...)
+	}
+
 	if am, ok := mod.(ApexModule); ok {
 		a.SetBoolIfTrue("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", am.NotAvailableForPlatform())
 	}
@@ -936,10 +940,13 @@
 
 // A utility func to format LOCAL_TEST_DATA outputs. See the comments on DataPath to understand how
 // to use this func.
-func AndroidMkDataPaths(data []DataPath) []string {
+func androidMkDataPaths(data []DataPath) []string {
 	var testFiles []string
 	for _, d := range data {
 		rel := d.SrcPath.Rel()
+		if d.WithoutRel {
+			rel = d.SrcPath.Base()
+		}
 		path := d.SrcPath.String()
 		// LOCAL_TEST_DATA requires the rel portion of the path to be removed from the path.
 		if !strings.HasSuffix(path, rel) {
diff --git a/android/config.go b/android/config.go
index a69adc3..9293505 100644
--- a/android/config.go
+++ b/android/config.go
@@ -195,10 +195,12 @@
 	return String(c.config.productVariables.DeviceMaxPageSizeSupported)
 }
 
-// PageSizeAgnostic returns true when AOSP is page size agnostic,
-// othersise it returns false.
-func (c Config) PageSizeAgnostic() bool {
-	return Bool(c.config.productVariables.DevicePageSizeAgnostic)
+// NoBionicPageSizeMacro returns true when AOSP is page size agnostic.
+// This means that the bionic's macro PAGE_SIZE won't be defined.
+// Returns false when AOSP is NOT page size agnostic.
+// This means that bionic's macro PAGE_SIZE is defined.
+func (c Config) NoBionicPageSizeMacro() bool {
+	return Bool(c.config.productVariables.DeviceNoBionicPageSizeMacro)
 }
 
 // The release version passed to aconfig, derived from RELEASE_VERSION
@@ -239,6 +241,11 @@
 		Bool(c.config.productVariables.ReleaseDefaultModuleBuildFromSource)
 }
 
+// Enables ABI monitoring of NDK libraries
+func (c Config) ReleaseNdkAbiMonitored() bool {
+	return c.config.productVariables.GetBuildFlagBool("RELEASE_NDK_ABI_MONITORED")
+}
+
 // A DeviceConfig object represents the configuration for a particular device
 // being built. For now there will only be one of these, but in the future there
 // may be multiple devices being built.
@@ -691,6 +698,7 @@
 		"framework-location":                {},
 		"framework-media":                   {},
 		"framework-mediaprovider":           {},
+		"framework-nfc":                     {},
 		"framework-ondevicepersonalization": {},
 		"framework-pdf":                     {},
 		"framework-permission":              {},
@@ -1425,6 +1433,10 @@
 	return !Bool(c.productVariables.KeepVndk)
 }
 
+func (c *config) VendorApiLevel() string {
+	return String(c.productVariables.VendorApiLevel)
+}
+
 func (c *deviceConfig) Arches() []Arch {
 	var arches []Arch
 	for _, target := range c.config.Targets[Android] {
diff --git a/android/defs.go b/android/defs.go
index b28d2fa..03968c1 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -232,7 +232,7 @@
 
 // ContentFromFileRuleForTests returns the content that was passed to a WriteFileRule for use
 // in tests.
-func ContentFromFileRuleForTests(t *testing.T, params TestingBuildParams) string {
+func ContentFromFileRuleForTests(t *testing.T, ctx *TestContext, params TestingBuildParams) string {
 	t.Helper()
 	if g, w := params.Rule, writeFile; g != w {
 		t.Errorf("expected params.Rule to be %q, was %q", w, g)
diff --git a/android/filegroup.go b/android/filegroup.go
index 856c50e..c34a615 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -290,6 +290,7 @@
 	if fg.properties.Path != nil {
 		fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
 	}
+	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: fg.srcs.Strings()})
 }
 
 func (fg *fileGroup) Srcs() Paths {
diff --git a/android/license_metadata.go b/android/license_metadata.go
index 8933bd5..609ca79 100644
--- a/android/license_metadata.go
+++ b/android/license_metadata.go
@@ -50,7 +50,14 @@
 		outputFiles = PathsIfNonNil(outputFiles...)
 	}
 
-	isContainer := isContainerFromFileExtensions(base.installFiles, outputFiles)
+	// Only pass the last installed file to isContainerFromFileExtensions so a *.zip file in test data
+	// doesn't mark the whole module as a container.
+	var installFiles InstallPaths
+	if len(base.installFiles) > 0 {
+		installFiles = InstallPaths{base.installFiles[len(base.installFiles)-1]}
+	}
+
+	isContainer := isContainerFromFileExtensions(installFiles, outputFiles)
 
 	var allDepMetadataFiles Paths
 	var allDepMetadataArgs []string
diff --git a/android/module.go b/android/module.go
index af69a1b..9d74642 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1115,6 +1115,7 @@
 	// to Make to convert to ninja rules so that Make can add additional dependencies.
 	katiInstalls katiInstalls
 	katiSymlinks katiInstalls
+	testData     []DataPath
 
 	// The files to copy to the dist as explicitly specified in the .bp file.
 	distFiles TaggedDistFiles
@@ -2065,6 +2066,7 @@
 		m.packagingSpecs = append(m.packagingSpecs, ctx.packagingSpecs...)
 		m.katiInstalls = append(m.katiInstalls, ctx.katiInstalls...)
 		m.katiSymlinks = append(m.katiSymlinks, ctx.katiSymlinks...)
+		m.testData = append(m.testData, ctx.testData...)
 	} else if ctx.Config().AllowMissingDependencies() {
 		// If the module is not enabled it will not create any build rules, nothing will call
 		// ctx.GetMissingDependencies(), and blueprint will consider the missing dependencies to be unhandled
diff --git a/android/module_context.go b/android/module_context.go
index a0a4104..39986df 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -153,6 +153,15 @@
 	// for which IsInstallDepNeeded returns true.
 	InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath
 
+	// InstallTestData creates rules to install test data (e.g. data files used during a test) into
+	// the installPath directory.
+	//
+	// The installed files will be returned by FilesToInstall(), and the PackagingSpec for the
+	// installed files will be returned by PackagingSpecs() on this module or by
+	// TransitivePackagingSpecs() on modules that depend on this module through dependency tags
+	// for which IsInstallDepNeeded returns true.
+	InstallTestData(installPath InstallPath, data []DataPath) InstallPaths
+
 	// PackageFile creates a PackagingSpec as if InstallFile was called, but without creating
 	// the rule to copy the file.  This is useful to define how a module would be packaged
 	// without installing it into the global installation directories.
@@ -213,6 +222,8 @@
 	katiInstalls []katiInstall
 	katiSymlinks []katiInstall
 
+	testData []DataPath
+
 	// For tests
 	buildParams []BuildParams
 	ruleParams  map[blueprint.Rule]blueprint.RuleParams
@@ -452,17 +463,17 @@
 
 func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path,
 	deps ...InstallPath) InstallPath {
-	return m.installFile(installPath, name, srcPath, deps, false, nil)
+	return m.installFile(installPath, name, srcPath, deps, false, true, nil)
 }
 
 func (m *moduleContext) InstallExecutable(installPath InstallPath, name string, srcPath Path,
 	deps ...InstallPath) InstallPath {
-	return m.installFile(installPath, name, srcPath, deps, true, nil)
+	return m.installFile(installPath, name, srcPath, deps, true, true, nil)
 }
 
 func (m *moduleContext) InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path,
 	extraZip Path, deps ...InstallPath) InstallPath {
-	return m.installFile(installPath, name, srcPath, deps, false, &extraFilesZip{
+	return m.installFile(installPath, name, srcPath, deps, false, true, &extraFilesZip{
 		zip: extraZip,
 		dir: installPath,
 	})
@@ -488,10 +499,12 @@
 }
 
 func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path, deps []InstallPath,
-	executable bool, extraZip *extraFilesZip) InstallPath {
+	executable bool, hooks bool, extraZip *extraFilesZip) InstallPath {
 
 	fullInstallPath := installPath.Join(m, name)
-	m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, false)
+	if hooks {
+		m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, false)
+	}
 
 	if !m.skipInstall() {
 		deps = append(deps, InstallPaths(m.module.base().installFilesDepSet.ToList())...)
@@ -647,6 +660,19 @@
 	return fullInstallPath
 }
 
+func (m *moduleContext) InstallTestData(installPath InstallPath, data []DataPath) InstallPaths {
+	m.testData = append(m.testData, data...)
+
+	ret := make(InstallPaths, 0, len(data))
+	for _, d := range data {
+		relPath := d.ToRelativeInstallPath()
+		installed := m.installFile(installPath, relPath, d.SrcPath, nil, false, false, nil)
+		ret = append(ret, installed)
+	}
+
+	return ret
+}
+
 func (m *moduleContext) CheckbuildFile(srcPath Path) {
 	m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
 }
diff --git a/android/paths.go b/android/paths.go
index 37504b6..3f35449 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -2208,10 +2208,15 @@
 	SrcPath Path
 	// The install path of the data file, relative to the install root.
 	RelativeInstallPath string
+	// If WithoutRel is true, use SrcPath.Base() instead of SrcPath.Rel() as the filename.
+	WithoutRel bool
 }
 
 func (d *DataPath) ToRelativeInstallPath() string {
 	relPath := d.SrcPath.Rel()
+	if d.WithoutRel {
+		relPath = d.SrcPath.Base()
+	}
 	if d.RelativeInstallPath != "" {
 		relPath = filepath.Join(d.RelativeInstallPath, relPath)
 	}
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 5c6dffe..1a491f7 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -1341,9 +1341,9 @@
 
 // RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox()
 // and returns sbox testproto generated by the RuleBuilder.
-func RuleBuilderSboxProtoForTests(t *testing.T, params TestingBuildParams) *sbox_proto.Manifest {
+func RuleBuilderSboxProtoForTests(t *testing.T, ctx *TestContext, params TestingBuildParams) *sbox_proto.Manifest {
 	t.Helper()
-	content := ContentFromFileRuleForTests(t, params)
+	content := ContentFromFileRuleForTests(t, ctx, params)
 	manifest := sbox_proto.Manifest{}
 	err := prototext.Unmarshal([]byte(content), &manifest)
 	if err != nil {
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index a6b3a27..63c3527 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -683,7 +683,7 @@
 			t.Errorf("want Deps = %q, got %q", blueprint.DepsGCC, params.Deps)
 		}
 
-		rspFile2Content := ContentFromFileRuleForTests(t, rspFile2Params)
+		rspFile2Content := ContentFromFileRuleForTests(t, result.TestContext, rspFile2Params)
 		AssertStringEquals(t, "rspFile2 content", "rsp_in2\n", rspFile2Content)
 	}
 
@@ -797,7 +797,7 @@
 		t.Run(test.name, func(t *testing.T) {
 			t.Run("sbox", func(t *testing.T) {
 				gen := result.ModuleForTests(test.name+"_sbox", "")
-				manifest := RuleBuilderSboxProtoForTests(t, gen.Output("sbox.textproto"))
+				manifest := RuleBuilderSboxProtoForTests(t, result.TestContext, gen.Output("sbox.textproto"))
 				hash := manifest.Commands[0].GetInputHash()
 
 				AssertStringEquals(t, "hash", test.expectedHash, hash)
diff --git a/android/testing.go b/android/testing.go
index da3b75a..c596468 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -1293,3 +1293,10 @@
 func StringsRelativeToTop(config Config, command []string) []string {
 	return normalizeStringArrayRelativeToTop(config, command)
 }
+
+func EnsureListContainsSuffix(t *testing.T, result []string, expected string) {
+	t.Helper()
+	if !SuffixInList(result, expected) {
+		t.Errorf("%q is not found in %v", expected, result)
+	}
+}
diff --git a/android/variable.go b/android/variable.go
index 307deaf..aecad3b 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -224,7 +224,9 @@
 	DeviceCurrentApiLevelForVendorModules *string  `json:",omitempty"`
 	DeviceSystemSdkVersions               []string `json:",omitempty"`
 	DeviceMaxPageSizeSupported            *string  `json:",omitempty"`
-	DevicePageSizeAgnostic                *bool    `json:",omitempty"`
+	DeviceNoBionicPageSizeMacro           *bool    `json:",omitempty"`
+
+	VendorApiLevel *string `json:",omitempty"`
 
 	RecoverySnapshotVersion *string `json:",omitempty"`
 
@@ -580,20 +582,20 @@
 		Platform_version_all_preview_codenames: []string{"S"},
 		Platform_vndk_version:                  stringPtr("S"),
 
-		HostArch:                   stringPtr("x86_64"),
-		HostSecondaryArch:          stringPtr("x86"),
-		DeviceName:                 stringPtr("generic_arm64"),
-		DeviceProduct:              stringPtr("aosp_arm-eng"),
-		DeviceArch:                 stringPtr("arm64"),
-		DeviceArchVariant:          stringPtr("armv8-a"),
-		DeviceCpuVariant:           stringPtr("generic"),
-		DeviceAbi:                  []string{"arm64-v8a"},
-		DeviceSecondaryArch:        stringPtr("arm"),
-		DeviceSecondaryArchVariant: stringPtr("armv8-a"),
-		DeviceSecondaryCpuVariant:  stringPtr("generic"),
-		DeviceSecondaryAbi:         []string{"armeabi-v7a", "armeabi"},
-		DeviceMaxPageSizeSupported: stringPtr("4096"),
-		DevicePageSizeAgnostic:     boolPtr(false),
+		HostArch:                    stringPtr("x86_64"),
+		HostSecondaryArch:           stringPtr("x86"),
+		DeviceName:                  stringPtr("generic_arm64"),
+		DeviceProduct:               stringPtr("aosp_arm-eng"),
+		DeviceArch:                  stringPtr("arm64"),
+		DeviceArchVariant:           stringPtr("armv8-a"),
+		DeviceCpuVariant:            stringPtr("generic"),
+		DeviceAbi:                   []string{"arm64-v8a"},
+		DeviceSecondaryArch:         stringPtr("arm"),
+		DeviceSecondaryArchVariant:  stringPtr("armv8-a"),
+		DeviceSecondaryCpuVariant:   stringPtr("generic"),
+		DeviceSecondaryAbi:          []string{"armeabi-v7a", "armeabi"},
+		DeviceMaxPageSizeSupported:  stringPtr("4096"),
+		DeviceNoBionicPageSizeMacro: boolPtr(false),
 
 		AAPTConfig:          []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
 		AAPTPreferredConfig: stringPtr("xhdpi"),
@@ -618,6 +620,14 @@
 	}
 }
 
+func (this *ProductVariables) GetBuildFlagBool(flag string) bool {
+	val, ok := this.BuildFlags[flag]
+	if !ok {
+		return false
+	}
+	return val == "true"
+}
+
 // ProductConfigContext requires the access to the Module to get product config properties.
 type ProductConfigContext interface {
 	Module() Module
diff --git a/android_sdk/sdk_repo_host.go b/android_sdk/sdk_repo_host.go
index 7212a07..373e883 100644
--- a/android_sdk/sdk_repo_host.go
+++ b/android_sdk/sdk_repo_host.go
@@ -165,10 +165,11 @@
 				Flag(dir.Join(ctx, strip).String())
 		}
 	} else {
+		llvmObjCopy := config.ClangPath(ctx, "bin/llvm-objcopy")
 		llvmStrip := config.ClangPath(ctx, "bin/llvm-strip")
 		llvmLib := config.ClangPath(ctx, "lib/x86_64-unknown-linux-gnu/libc++.so")
 		for _, strip := range s.properties.Strip_files {
-			cmd := builder.Command().Tool(llvmStrip).ImplicitTool(llvmLib)
+			cmd := builder.Command().Tool(llvmStrip).ImplicitTool(llvmLib).ImplicitTool(llvmObjCopy)
 			if !ctx.Windows() {
 				cmd.Flag("-x")
 			}
diff --git a/apex/Android.bp b/apex/Android.bp
index 0791497..0a5063f 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -8,6 +8,8 @@
     deps: [
         "blueprint",
         "soong",
+        "soong-aconfig",
+        "soong-aconfig-codegen",
         "soong-android",
         "soong-bazel",
         "soong-bpf",
diff --git a/apex/apex.go b/apex/apex.go
index ecc794b..7ddb3e6 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -24,6 +24,7 @@
 	"sort"
 	"strings"
 
+	"android/soong/aconfig"
 	"android/soong/bazel/cquery"
 
 	"github.com/google/blueprint"
@@ -482,6 +483,8 @@
 	nativeApisUsedByModuleFile   android.ModuleOutPath
 	nativeApisBackedByModuleFile android.ModuleOutPath
 	javaApisUsedByModuleFile     android.ModuleOutPath
+
+	aconfigFiles []android.Path
 }
 
 // apexFileClass represents a type of file that can be included in APEX.
@@ -2008,6 +2011,8 @@
 
 	// visitor skips these from this list of module names
 	unwantedTransitiveDeps []string
+
+	aconfigFiles []android.Path
 }
 
 func (vctx *visitorContext) normalizeFileInfo(mctx android.ModuleContext) {
@@ -2062,11 +2067,19 @@
 		switch depTag {
 		case sharedLibTag, jniLibTag:
 			isJniLib := depTag == jniLibTag
+			propertyName := "native_shared_libs"
+			if isJniLib {
+				propertyName = "jni_libs"
+			}
 			switch ch := child.(type) {
 			case *cc.Module:
+				if ch.IsStubs() {
+					ctx.PropertyErrorf(propertyName, "%q is a stub. Remove it from the list.", depName)
+				}
 				fi := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs)
 				fi.isJniLib = isJniLib
 				vctx.filesInfo = append(vctx.filesInfo, fi)
+				addAconfigFiles(vctx, ctx, child)
 				// Collect the list of stub-providing libs except:
 				// - VNDK libs are only for vendors
 				// - bootstrap bionic libs are treated as provided by system
@@ -2080,16 +2093,13 @@
 				vctx.filesInfo = append(vctx.filesInfo, fi)
 				return true // track transitive dependencies
 			default:
-				propertyName := "native_shared_libs"
-				if isJniLib {
-					propertyName = "jni_libs"
-				}
 				ctx.PropertyErrorf(propertyName, "%q is not a cc_library or cc_library_shared module", depName)
 			}
 		case executableTag:
 			switch ch := child.(type) {
 			case *cc.Module:
 				vctx.filesInfo = append(vctx.filesInfo, apexFileForExecutable(ctx, ch))
+				addAconfigFiles(vctx, ctx, child)
 				return true // track transitive dependencies
 			case *rust.Module:
 				vctx.filesInfo = append(vctx.filesInfo, apexFileForRustExecutable(ctx, ch))
@@ -2132,6 +2142,7 @@
 					return false
 				}
 				vctx.filesInfo = append(vctx.filesInfo, af)
+				addAconfigFiles(vctx, ctx, child)
 				return true // track transitive dependencies
 			default:
 				ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
@@ -2140,6 +2151,7 @@
 			switch ap := child.(type) {
 			case *java.AndroidApp:
 				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
+				addAconfigFiles(vctx, ctx, child)
 				return true // track transitive dependencies
 			case *java.AndroidAppImport:
 				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
@@ -2298,6 +2310,7 @@
 			}
 
 			vctx.filesInfo = append(vctx.filesInfo, af)
+			addAconfigFiles(vctx, ctx, child)
 			return true // track transitive dependencies
 		} else if rm, ok := child.(*rust.Module); ok {
 			af := apexFileForRustLibrary(ctx, rm)
@@ -2378,6 +2391,13 @@
 	return false
 }
 
+func addAconfigFiles(vctx *visitorContext, ctx android.ModuleContext, module blueprint.Module) {
+	dep := ctx.OtherModuleProvider(module, aconfig.TransitiveDeclarationsInfoProvider).(aconfig.TransitiveDeclarationsInfo)
+	if len(dep.AconfigFiles) > 0 && dep.AconfigFiles[ctx.ModuleName()] != nil {
+		vctx.aconfigFiles = append(vctx.aconfigFiles, dep.AconfigFiles[ctx.ModuleName()]...)
+	}
+}
+
 func (a *apexBundle) shouldCheckDuplicate(ctx android.ModuleContext) bool {
 	// TODO(b/263308293) remove this
 	if a.properties.IsCoverageVariant {
@@ -2459,6 +2479,7 @@
 	// 3) some fields in apexBundle struct are configured
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	a.filesInfo = vctx.filesInfo
+	a.aconfigFiles = android.FirstUniquePaths(vctx.aconfigFiles)
 
 	a.setPayloadFsType(ctx)
 	a.setSystemLibLink(ctx)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index ddb9a40..2ed053e 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -25,6 +25,8 @@
 	"strings"
 	"testing"
 
+	"android/soong/aconfig/codegen"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
@@ -151,6 +153,7 @@
 	prebuilt_etc.PrepareForTestWithPrebuiltEtc,
 	rust.PrepareForTestWithRustDefaultModules,
 	sh.PrepareForTestWithShBuildComponents,
+	codegen.PrepareForTestWithAconfigBuildComponents,
 
 	PrepareForTestWithApexBuildComponents,
 
@@ -595,13 +598,15 @@
 		t.Errorf("Could not find all expected symlinks! foo: %t, foo_link_64: %t. Command was %s", found_foo, found_foo_link_64, copyCmds)
 	}
 
-	fullDepsInfo := strings.Split(ctx.ModuleForTests("myapex", "android_common_myapex").Output("depsinfo/fulllist.txt").Args["content"], "\\n")
+	fullDepsInfo := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
+		ctx.ModuleForTests("myapex", "android_common_myapex").Output("depsinfo/fulllist.txt")), "\n")
 	ensureListContains(t, fullDepsInfo, "  myjar(minSdkVersion:(no version)) <- myapex")
 	ensureListContains(t, fullDepsInfo, "  mylib2(minSdkVersion:(no version)) <- mylib")
 	ensureListContains(t, fullDepsInfo, "  myotherjar(minSdkVersion:(no version)) <- myjar")
 	ensureListContains(t, fullDepsInfo, "  mysharedjar(minSdkVersion:(no version)) (external) <- myjar")
 
-	flatDepsInfo := strings.Split(ctx.ModuleForTests("myapex", "android_common_myapex").Output("depsinfo/flatlist.txt").Args["content"], "\\n")
+	flatDepsInfo := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
+		ctx.ModuleForTests("myapex", "android_common_myapex").Output("depsinfo/flatlist.txt")), "\n")
 	ensureListContains(t, flatDepsInfo, "myjar(minSdkVersion:(no version))")
 	ensureListContains(t, flatDepsInfo, "mylib2(minSdkVersion:(no version))")
 	ensureListContains(t, flatDepsInfo, "myotherjar(minSdkVersion:(no version))")
@@ -956,6 +961,32 @@
 	ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.shared_from_rust.so")
 }
 
+func TestApexShouldNotEmbedStubVariant(t *testing.T) {
+	testApexError(t, `module "myapex" .*: native_shared_libs: "libbar" is a stub`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			vendor: true,
+			updatable: false,
+			native_shared_libs: ["libbar"], // should not add an LLNDK stub in a vendor apex
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libbar",
+			srcs: ["mylib.cpp"],
+			llndk: {
+				symbol_file: "libbar.map.txt",
+			}
+		}
+	`)
+}
+
 func TestApexCanUsePrivateApis(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -1281,10 +1312,12 @@
 	// Ensure that libfoo stubs is not linking to libbar (since it is a stubs)
 	ensureNotContains(t, libFooStubsLdFlags, "libbar.so")
 
-	fullDepsInfo := strings.Split(ctx.ModuleForTests("myapex2", "android_common_myapex2").Output("depsinfo/fulllist.txt").Args["content"], "\\n")
+	fullDepsInfo := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
+		ctx.ModuleForTests("myapex2", "android_common_myapex2").Output("depsinfo/fulllist.txt")), "\n")
 	ensureListContains(t, fullDepsInfo, "  libfoo(minSdkVersion:(no version)) (external) <- mylib")
 
-	flatDepsInfo := strings.Split(ctx.ModuleForTests("myapex2", "android_common_myapex2").Output("depsinfo/flatlist.txt").Args["content"], "\\n")
+	flatDepsInfo := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
+		ctx.ModuleForTests("myapex2", "android_common_myapex2").Output("depsinfo/flatlist.txt")), "\n")
 	ensureListContains(t, flatDepsInfo, "libfoo(minSdkVersion:(no version)) (external)")
 }
 
@@ -2030,7 +2063,8 @@
 		"out/soong/.intermediates/myapex2/android_common_myapex2/depsinfo/flatlist.txt")
 
 	myapex := ctx.ModuleForTests("myapex", "android_common_myapex")
-	flatlist := strings.Split(myapex.Output("depsinfo/flatlist.txt").BuildParams.Args["content"], "\\n")
+	flatlist := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
+		myapex.Output("depsinfo/flatlist.txt")), "\n")
 	android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep",
 		flatlist, "libbar(minSdkVersion:(no version)) (external)")
 	android.AssertStringListDoesNotContain(t, "do not track if not available for platform",
@@ -4703,6 +4737,72 @@
 	ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common_test"), "android_arm64_armv8-a_shared")
 }
 
+func TestLibzVendorIsntStable(t *testing.T) {
+	ctx := testApex(t, `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		updatable: false,
+		binaries: ["mybin"],
+	}
+	apex {
+		name: "myvendorapex",
+		key: "myapex.key",
+		file_contexts: "myvendorapex_file_contexts",
+		vendor: true,
+		updatable: false,
+		binaries: ["mybin"],
+	}
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+	cc_binary {
+		name: "mybin",
+		vendor_available: true,
+		system_shared_libs: [],
+		stl: "none",
+		shared_libs: ["libz"],
+		apex_available: ["//apex_available:anyapex"],
+	}
+	cc_library {
+		name: "libz",
+		vendor_available: true,
+		system_shared_libs: [],
+		stl: "none",
+		stubs: {
+			versions: ["28", "30"],
+		},
+		target: {
+			vendor: {
+				no_stubs: true,
+			},
+		},
+	}
+	`, withFiles(map[string][]byte{
+		"myvendorapex_file_contexts": nil,
+	}))
+
+	// libz provides stubs for core variant.
+	{
+		ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{
+			"bin/mybin",
+		})
+		apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule")
+		android.AssertStringEquals(t, "should require libz", apexManifestRule.Args["requireNativeLibs"], "libz.so")
+	}
+	// libz doesn't provide stubs for vendor variant.
+	{
+		ensureExactContents(t, ctx, "myvendorapex", "android_common_myvendorapex", []string{
+			"bin/mybin",
+			"lib64/libz.so",
+		})
+		apexManifestRule := ctx.ModuleForTests("myvendorapex", "android_common_myvendorapex").Rule("apexManifestRule")
+		android.AssertStringEquals(t, "should not require libz", apexManifestRule.Args["requireNativeLibs"], "")
+	}
+}
+
 func TestApexWithTarget(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -8154,7 +8254,7 @@
 		`, withManifestPackageNameOverrides([]string{"AppFoo:com.android.foo"}))
 
 	bundleConfigRule := ctx.ModuleForTests("myapex", "android_common_myapex").Output("bundle_config.json")
-	content := bundleConfigRule.Args["content"]
+	content := android.ContentFromFileRuleForTests(t, ctx, bundleConfigRule)
 
 	ensureContains(t, content, `"compression":{"uncompressed_glob":["apex_payload.img","apex_manifest.*"]}`)
 	ensureContains(t, content, `"apex_config":{"apex_embedded_apk_config":[{"package_name":"com.android.foo","path":"app/AppFoo@TEST.BUILD_ID/AppFoo.apk"}]}`)
@@ -8181,7 +8281,7 @@
 		}`)
 	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
 	bundleConfigRule := mod.Output("bundle_config.json")
-	content := bundleConfigRule.Args["content"]
+	content := android.ContentFromFileRuleForTests(t, ctx, bundleConfigRule)
 	ensureContains(t, content, `"compression":{"uncompressed_glob":["apex_payload.img","apex_manifest.*"]}`)
 	s := mod.Rule("apexRule").Args["copy_commands"]
 	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
@@ -9126,7 +9226,7 @@
 	`)
 
 	myapex := ctx.ModuleForTests("myapex", "android_common_myapex")
-	content := myapex.Output("apexkeys.txt").BuildParams.Args["content"]
+	content := android.ContentFromFileRuleForTests(t, ctx, myapex.Output("apexkeys.txt"))
 	ensureContains(t, content, `name="myapex.apex" public_key="vendor/foo/devkeys/testkey.avbpubkey" private_key="vendor/foo/devkeys/testkey.pem" container_certificate="vendor/foo/devkeys/test.x509.pem" container_private_key="vendor/foo/devkeys/test.pk8" partition="system" sign_tool="sign_myapex"`)
 }
 
@@ -9166,9 +9266,11 @@
 		}
 	`)
 
-	content := ctx.ModuleForTests("myapex", "android_common_myapex").Output("apexkeys.txt").BuildParams.Args["content"]
+	content := android.ContentFromFileRuleForTests(t, ctx,
+		ctx.ModuleForTests("myapex", "android_common_myapex").Output("apexkeys.txt"))
 	ensureContains(t, content, `name="myapex.apex" public_key="vendor/foo/devkeys/testkey.avbpubkey" private_key="vendor/foo/devkeys/testkey.pem" container_certificate="vendor/foo/devkeys/test.x509.pem" container_private_key="vendor/foo/devkeys/test.pk8" partition="system" sign_tool="sign_myapex"`)
-	content = ctx.ModuleForTests("myapex_set", "android_common_myapex_set").Output("apexkeys.txt").BuildParams.Args["content"]
+	content = android.ContentFromFileRuleForTests(t, ctx,
+		ctx.ModuleForTests("myapex_set", "android_common_myapex_set").Output("apexkeys.txt"))
 	ensureContains(t, content, `name="myapex_set.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED" partition="system"`)
 }
 
@@ -10756,3 +10858,437 @@
 		inputs.Strings(),
 		"out/soong/.intermediates/libbar/android_arm64_armv8-a_shared/libbar.so")
 }
+
+var apex_default_bp = `
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		filegroup {
+			name: "myapex.manifest",
+			srcs: ["apex_manifest.json"],
+		}
+
+		filegroup {
+			name: "myapex.androidmanifest",
+			srcs: ["AndroidManifest.xml"],
+		}
+`
+
+func TestAconfigFilesJavaDeps(t *testing.T) {
+	ctx := testApex(t, apex_default_bp+`
+		apex {
+			name: "myapex",
+			manifest: ":myapex.manifest",
+			androidManifest: ":myapex.androidmanifest",
+			key: "myapex.key",
+			java_libs: [
+				"my_java_library_foo",
+				"my_java_library_bar",
+			],
+			updatable: false,
+		}
+
+		java_library {
+			name: "my_java_library_foo",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			static_libs: ["my_java_aconfig_library_foo"],
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+
+		java_library {
+			name: "my_java_library_bar",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			static_libs: ["my_java_aconfig_library_bar"],
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+
+		aconfig_declarations {
+			name: "my_aconfig_declarations_foo",
+			package: "com.example.package",
+			container: "myapex",
+			srcs: ["foo.aconfig"],
+		}
+
+		java_aconfig_library {
+			name: "my_java_aconfig_library_foo",
+			aconfig_declarations: "my_aconfig_declarations_foo",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+
+		aconfig_declarations {
+			name: "my_aconfig_declarations_bar",
+			package: "com.example.package",
+			container: "myapex",
+			srcs: ["bar.aconfig"],
+		}
+
+		java_aconfig_library {
+			name: "my_java_aconfig_library_bar",
+			aconfig_declarations: "my_aconfig_declarations_bar",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+	`)
+
+	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
+	s := mod.Rule("apexRule").Args["copy_commands"]
+	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
+	if len(copyCmds) != 5 {
+		t.Fatalf("Expected 5 commands, got %d in:\n%s", len(copyCmds), s)
+	}
+
+	ensureMatches(t, copyCmds[4], "^cp -f .*/aconfig_flags.pb .*/image.apex$")
+
+	combineAconfigRule := mod.Rule("All_aconfig_declarations_dump")
+	s = " " + combineAconfigRule.Args["cache_files"]
+	aconfigArgs := regexp.MustCompile(" --cache ").Split(s, -1)[1:]
+	if len(aconfigArgs) != 2 {
+		t.Fatalf("Expected 2 commands, got %d in:\n%s", len(aconfigArgs), s)
+	}
+	android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_foo/intermediate.pb")
+	android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_bar/intermediate.pb")
+
+	buildParams := combineAconfigRule.BuildParams
+	android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_foo/intermediate.pb")
+	android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_bar/intermediate.pb")
+	ensureContains(t, buildParams.Output.String(), "android_common_myapex/aconfig_flags.pb")
+}
+
+func TestAconfigFilesJavaAndCcDeps(t *testing.T) {
+	ctx := testApex(t, apex_default_bp+`
+		apex {
+			name: "myapex",
+			manifest: ":myapex.manifest",
+			androidManifest: ":myapex.androidmanifest",
+			key: "myapex.key",
+			java_libs: [
+				"my_java_library_foo",
+			],
+			native_shared_libs: [
+				"my_cc_library_bar",
+			],
+			binaries: [
+				"my_cc_binary_baz",
+			],
+			updatable: false,
+		}
+
+		java_library {
+			name: "my_java_library_foo",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			static_libs: ["my_java_aconfig_library_foo"],
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+
+		cc_library {
+			name: "my_cc_library_bar",
+			srcs: ["foo/bar/MyClass.cc"],
+			static_libs: ["my_cc_aconfig_library_bar"],
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+
+		cc_binary {
+			name: "my_cc_binary_baz",
+			srcs: ["foo/bar/MyClass.cc"],
+			static_libs: ["my_cc_aconfig_library_baz"],
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+
+		aconfig_declarations {
+			name: "my_aconfig_declarations_foo",
+			package: "com.example.package",
+			container: "myapex",
+			srcs: ["foo.aconfig"],
+		}
+
+		java_aconfig_library {
+			name: "my_java_aconfig_library_foo",
+			aconfig_declarations: "my_aconfig_declarations_foo",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+
+		aconfig_declarations {
+			name: "my_aconfig_declarations_bar",
+			package: "com.example.package",
+			container: "myapex",
+			srcs: ["bar.aconfig"],
+		}
+
+		cc_aconfig_library {
+			name: "my_cc_aconfig_library_bar",
+			aconfig_declarations: "my_aconfig_declarations_bar",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+
+		aconfig_declarations {
+			name: "my_aconfig_declarations_baz",
+			package: "com.example.package",
+			container: "myapex",
+			srcs: ["baz.aconfig"],
+		}
+
+		cc_aconfig_library {
+			name: "my_cc_aconfig_library_baz",
+			aconfig_declarations: "my_aconfig_declarations_baz",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+
+		cc_library {
+			name: "server_configurable_flags",
+			srcs: ["server_configurable_flags.cc"],
+		}
+	`)
+
+	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
+	s := mod.Rule("apexRule").Args["copy_commands"]
+	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
+	if len(copyCmds) != 9 {
+		t.Fatalf("Expected 9 commands, got %d in:\n%s", len(copyCmds), s)
+	}
+
+	ensureMatches(t, copyCmds[8], "^cp -f .*/aconfig_flags.pb .*/image.apex$")
+
+	combineAconfigRule := mod.Rule("All_aconfig_declarations_dump")
+	s = " " + combineAconfigRule.Args["cache_files"]
+	aconfigArgs := regexp.MustCompile(" --cache ").Split(s, -1)[1:]
+	if len(aconfigArgs) != 3 {
+		t.Fatalf("Expected 3 commands, got %d in:\n%s", len(aconfigArgs), s)
+	}
+	android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_foo/intermediate.pb")
+	android.EnsureListContainsSuffix(t, aconfigArgs, "my_cc_library_bar/android_arm64_armv8-a_shared_apex10000/aconfig_merged.pb")
+	android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_baz/intermediate.pb")
+
+	buildParams := combineAconfigRule.BuildParams
+	if len(buildParams.Inputs) != 3 {
+		t.Fatalf("Expected 3 input, got %d", len(buildParams.Inputs))
+	}
+	android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_foo/intermediate.pb")
+	android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_cc_library_bar/android_arm64_armv8-a_shared_apex10000/aconfig_merged.pb")
+	android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_baz/intermediate.pb")
+	ensureContains(t, buildParams.Output.String(), "android_common_myapex/aconfig_flags.pb")
+}
+
+func TestAconfigFilesOnlyMatchCurrentApex(t *testing.T) {
+	ctx := testApex(t, apex_default_bp+`
+		apex {
+			name: "myapex",
+			manifest: ":myapex.manifest",
+			androidManifest: ":myapex.androidmanifest",
+			key: "myapex.key",
+			java_libs: [
+				"my_java_library_foo",
+				"other_java_library_bar",
+			],
+			updatable: false,
+		}
+
+		java_library {
+			name: "my_java_library_foo",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			static_libs: ["my_java_aconfig_library_foo"],
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+
+		java_library {
+			name: "other_java_library_bar",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			static_libs: ["other_java_aconfig_library_bar"],
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+
+		aconfig_declarations {
+			name: "my_aconfig_declarations_foo",
+			package: "com.example.package",
+			container: "myapex",
+			srcs: ["foo.aconfig"],
+		}
+
+		java_aconfig_library {
+			name: "my_java_aconfig_library_foo",
+			aconfig_declarations: "my_aconfig_declarations_foo",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+
+		aconfig_declarations {
+			name: "other_aconfig_declarations_bar",
+			package: "com.example.package",
+			container: "otherapex",
+			srcs: ["bar.aconfig"],
+		}
+
+		java_aconfig_library {
+			name: "other_java_aconfig_library_bar",
+			aconfig_declarations: "other_aconfig_declarations_bar",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+	`)
+
+	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
+	combineAconfigRule := mod.Rule("All_aconfig_declarations_dump")
+	s := " " + combineAconfigRule.Args["cache_files"]
+	aconfigArgs := regexp.MustCompile(" --cache ").Split(s, -1)[1:]
+	if len(aconfigArgs) != 1 {
+		t.Fatalf("Expected 1 commands, got %d in:\n%s", len(aconfigArgs), s)
+	}
+	android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_foo/intermediate.pb")
+
+	buildParams := combineAconfigRule.BuildParams
+	if len(buildParams.Inputs) != 1 {
+		t.Fatalf("Expected 1 input, got %d", len(buildParams.Inputs))
+	}
+	android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_foo/intermediate.pb")
+	ensureContains(t, buildParams.Output.String(), "android_common_myapex/aconfig_flags.pb")
+}
+
+func TestAconfigFilesRemoveDuplicates(t *testing.T) {
+	ctx := testApex(t, apex_default_bp+`
+		apex {
+			name: "myapex",
+			manifest: ":myapex.manifest",
+			androidManifest: ":myapex.androidmanifest",
+			key: "myapex.key",
+			java_libs: [
+				"my_java_library_foo",
+				"my_java_library_bar",
+			],
+			updatable: false,
+		}
+
+		java_library {
+			name: "my_java_library_foo",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			static_libs: ["my_java_aconfig_library_foo"],
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+
+		java_library {
+			name: "my_java_library_bar",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			static_libs: ["my_java_aconfig_library_bar"],
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+
+		aconfig_declarations {
+			name: "my_aconfig_declarations_foo",
+			package: "com.example.package",
+			container: "myapex",
+			srcs: ["foo.aconfig"],
+		}
+
+		java_aconfig_library {
+			name: "my_java_aconfig_library_foo",
+			aconfig_declarations: "my_aconfig_declarations_foo",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+
+		java_aconfig_library {
+			name: "my_java_aconfig_library_bar",
+			aconfig_declarations: "my_aconfig_declarations_foo",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+	`)
+
+	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
+	combineAconfigRule := mod.Rule("All_aconfig_declarations_dump")
+	s := " " + combineAconfigRule.Args["cache_files"]
+	aconfigArgs := regexp.MustCompile(" --cache ").Split(s, -1)[1:]
+	if len(aconfigArgs) != 1 {
+		t.Fatalf("Expected 1 commands, got %d in:\n%s", len(aconfigArgs), s)
+	}
+	android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_foo/intermediate.pb")
+
+	buildParams := combineAconfigRule.BuildParams
+	if len(buildParams.Inputs) != 1 {
+		t.Fatalf("Expected 1 input, got %d", len(buildParams.Inputs))
+	}
+	android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_foo/intermediate.pb")
+	ensureContains(t, buildParams.Output.String(), "android_common_myapex/aconfig_flags.pb")
+}
diff --git a/apex/builder.go b/apex/builder.go
index 3f358ac..3078863 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -24,6 +24,7 @@
 	"strconv"
 	"strings"
 
+	"android/soong/aconfig"
 	"android/soong/android"
 	"android/soong/java"
 
@@ -36,6 +37,7 @@
 )
 
 func init() {
+	pctx.Import("android/soong/aconfig")
 	pctx.Import("android/soong/android")
 	pctx.Import("android/soong/cc/config")
 	pctx.Import("android/soong/java")
@@ -80,6 +82,7 @@
 	pctx.HostBinToolVariable("conv_linker_config", "conv_linker_config")
 	pctx.HostBinToolVariable("assemble_vintf", "assemble_vintf")
 	pctx.HostBinToolVariable("apex_elf_checker", "apex_elf_checker")
+	pctx.HostBinToolVariable("aconfig", "aconfig")
 }
 
 var (
@@ -574,6 +577,7 @@
 
 		installMapSet[installMapPath.String()+":"+fi.installDir+"/"+fi.builtFile.Base()] = true
 	}
+
 	implicitInputs = append(implicitInputs, a.manifestPbOut)
 
 	if len(installMapSet) > 0 {
@@ -628,10 +632,28 @@
 	outHostBinDir := ctx.Config().HostToolPath(ctx, "").String()
 	prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
 
+	defaultReadOnlyFiles := []string{"apex_manifest.json", "apex_manifest.pb"}
+	if len(a.aconfigFiles) > 0 {
+		apexAconfigFile := android.PathForModuleOut(ctx, "aconfig_flags.pb")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        aconfig.AllDeclarationsRule,
+			Inputs:      a.aconfigFiles,
+			Output:      apexAconfigFile,
+			Description: "combine_aconfig_declarations",
+			Args: map[string]string{
+				"cache_files": android.JoinPathsWithPrefix(a.aconfigFiles, "--cache "),
+			},
+		})
+
+		copyCommands = append(copyCommands, "cp -f "+apexAconfigFile.String()+" "+imageDir.String())
+		implicitInputs = append(implicitInputs, apexAconfigFile)
+		defaultReadOnlyFiles = append(defaultReadOnlyFiles, apexAconfigFile.Base())
+	}
+
 	////////////////////////////////////////////////////////////////////////////////////
 	// Step 2: create canned_fs_config which encodes filemode,uid,gid of each files
 	// in this APEX. The file will be used by apexer in later steps.
-	cannedFsConfig := a.buildCannedFsConfig(ctx)
+	cannedFsConfig := a.buildCannedFsConfig(ctx, defaultReadOnlyFiles)
 	implicitInputs = append(implicitInputs, cannedFsConfig)
 
 	////////////////////////////////////////////////////////////////////////////////////
@@ -1082,8 +1104,8 @@
 	a.lintReports = java.BuildModuleLintReportZips(ctx, depSetsBuilder.Build())
 }
 
-func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext) android.OutputPath {
-	var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"}
+func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext, defaultReadOnlyFiles []string) android.OutputPath {
+	var readOnlyPaths = defaultReadOnlyFiles
 	var executablePaths []string // this also includes dirs
 	var appSetDirs []string
 	appSetFiles := make(map[string]android.Path)
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 64ee01f..c998074 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -6,7 +6,6 @@
     name: "soong-bp2build",
     pkgPath: "android/soong/bp2build",
     srcs: [
-        "aconfig_conversion_test.go",
         "androidbp_to_build_templates.go",
         "bp2build.go",
         "bp2build_product_config.go",
@@ -43,6 +42,7 @@
     testSrcs: [
         "go_conversion_test.go",
         "aar_conversion_test.go",
+        "aconfig_conversion_test.go",
         "aidl_library_conversion_test.go",
         "android_app_certificate_conversion_test.go",
         "android_app_conversion_test.go",
diff --git a/bp2build/aconfig_conversion_test.go b/bp2build/aconfig_conversion_test.go
index d6e20df..ac830c6 100644
--- a/bp2build/aconfig_conversion_test.go
+++ b/bp2build/aconfig_conversion_test.go
@@ -15,6 +15,7 @@
 package bp2build
 
 import (
+	"android/soong/aconfig/codegen"
 	"testing"
 
 	"android/soong/aconfig"
@@ -25,6 +26,7 @@
 
 func registerAconfigModuleTypes(ctx android.RegistrationContext) {
 	aconfig.RegisterBuildComponents(ctx)
+	codegen.RegisterBuildComponents(ctx)
 	ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
 	ctx.RegisterModuleType("java_library", java.LibraryFactory)
 }
diff --git a/bp2build/bp2build_product_config.go b/bp2build/bp2build_product_config.go
index 0d1e433..4c09d67 100644
--- a/bp2build/bp2build_product_config.go
+++ b/bp2build/bp2build_product_config.go
@@ -320,7 +320,7 @@
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_abi=%s\n", strings.Join(productVariables.DeviceAbi, ",")))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_max_page_size_supported=%s\n", proptools.String(productVariables.DeviceMaxPageSizeSupported)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_name=%s\n", proptools.String(productVariables.DeviceName)))
-		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_page_size_agnostic=%t\n", proptools.Bool(productVariables.DevicePageSizeAgnostic)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_no_bionic_page_size_macro=%t\n", proptools.Bool(productVariables.DeviceNoBionicPageSizeMacro)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_product=%s\n", proptools.String(productVariables.DeviceProduct)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_platform=%s\n", label.String()))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:enable_cfi=%t\n", proptools.BoolDefault(productVariables.EnableCFI, true)))
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 58213aa..8c0d28b 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -206,6 +206,7 @@
 		}
 
 	}
+	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()})
 }
 
 func (bpf *bpf) AndroidMk() android.AndroidMkData {
diff --git a/cc/Android.bp b/cc/Android.bp
index 77e96db..351f3f6 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -9,6 +9,7 @@
         "blueprint",
         "blueprint-pathtools",
         "soong",
+        "soong-aconfig",
         "soong-aidl-library",
         "soong-android",
         "soong-bazel",
@@ -40,7 +41,6 @@
         "lto.go",
         "makevars.go",
         "orderfile.go",
-        "pgo.go",
         "prebuilt.go",
         "proto.go",
         "rs.go",
diff --git a/cc/afdo.go b/cc/afdo.go
index 91cf0b8..e7dea0e 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -64,6 +64,13 @@
 	return []interface{}{&afdo.Properties}
 }
 
+func (afdo *afdo) begin(ctx BaseModuleContext) {
+	// Disable on eng builds for faster build.
+	if ctx.Config().Eng() {
+		afdo.Properties.Afdo = false
+	}
+}
+
 // afdoEnabled returns true for binaries and shared libraries
 // that set afdo prop to True and there is a profile available
 func (afdo *afdo) afdoEnabled() bool {
diff --git a/cc/androidmk.go b/cc/androidmk.go
index e0e543f..8cae634 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -133,6 +133,8 @@
 					entries.SetString("SOONG_SDK_VARIANT_MODULES",
 						"$(SOONG_SDK_VARIANT_MODULES) $(patsubst %.sdk,%,$(LOCAL_MODULE))")
 				}
+				// TODO(b/311155208): The container here should be system.
+				entries.SetPaths("LOCAL_ACONFIG_FILES", c.mergedAconfigFiles[""])
 			},
 		},
 		ExtraFooters: []android.AndroidMkExtraFootersFunc{
@@ -175,15 +177,6 @@
 	}
 }
 
-func AndroidMkWriteTestData(data []android.DataPath, entries *android.AndroidMkEntries) {
-	testFiles := android.AndroidMkDataPaths(data)
-	if len(testFiles) > 0 {
-		entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-			entries.AddStrings("LOCAL_TEST_DATA", testFiles...)
-		})
-	}
-}
-
 func makeOverrideModuleNames(ctx AndroidMkContext, overrides []string) []string {
 	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
 		var result []string
@@ -379,11 +372,6 @@
 			entries.SetBool("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", true)
 		}
 	})
-	dataPaths := []android.DataPath{}
-	for _, srcPath := range benchmark.data {
-		dataPaths = append(dataPaths, android.DataPath{SrcPath: srcPath})
-	}
-	AndroidMkWriteTestData(dataPaths, entries)
 }
 
 func (test *testBinary) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
@@ -411,40 +399,16 @@
 		test.Properties.Test_options.CommonTestOptions.SetAndroidMkEntries(entries)
 	})
 
-	AndroidMkWriteTestData(test.data, entries)
 	androidMkWriteExtraTestConfigs(test.extraTestConfigs, entries)
 }
 
 func (fuzz *fuzzBinary) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
 	ctx.subAndroidMk(entries, fuzz.binaryDecorator)
 
-	var fuzzFiles []string
-	for _, d := range fuzz.fuzzPackagedModule.Corpus {
-		fuzzFiles = append(fuzzFiles,
-			filepath.Dir(fuzz.fuzzPackagedModule.CorpusIntermediateDir.String())+":corpus/"+d.Base())
-	}
-
-	for _, d := range fuzz.fuzzPackagedModule.Data {
-		fuzzFiles = append(fuzzFiles,
-			filepath.Dir(fuzz.fuzzPackagedModule.DataIntermediateDir.String())+":data/"+d.Rel())
-	}
-
-	if fuzz.fuzzPackagedModule.Dictionary != nil {
-		fuzzFiles = append(fuzzFiles,
-			filepath.Dir(fuzz.fuzzPackagedModule.Dictionary.String())+":"+fuzz.fuzzPackagedModule.Dictionary.Base())
-	}
-
-	if fuzz.fuzzPackagedModule.Config != nil {
-		fuzzFiles = append(fuzzFiles,
-			filepath.Dir(fuzz.fuzzPackagedModule.Config.String())+":config.json")
-	}
-
 	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		entries.SetBool("LOCAL_IS_FUZZ_TARGET", true)
-		if len(fuzzFiles) > 0 {
-			entries.AddStrings("LOCAL_TEST_DATA", fuzzFiles...)
-		}
 		if fuzz.installedSharedDeps != nil {
+			// TOOD: move to install dep
 			entries.AddStrings("LOCAL_FUZZ_INSTALLED_SHARED_DEPS", fuzz.installedSharedDeps...)
 		}
 	})
diff --git a/cc/cc.go b/cc/cc.go
index e215438..f800dc0 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -30,6 +30,7 @@
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
+	"android/soong/aconfig"
 	"android/soong/aidl_library"
 	"android/soong/android"
 	"android/soong/bazel/cquery"
@@ -141,6 +142,8 @@
 
 	// List of libs that need to be excluded for APEX variant
 	ExcludeLibsForApex []string
+	// List of libs that need to be excluded for non-APEX variant
+	ExcludeLibsForNonApex []string
 }
 
 // PathDeps is a struct containing file paths to dependencies of a module.
@@ -529,7 +532,6 @@
 	baseModuleName() string
 	getVndkExtendsModuleName() string
 	isAfdoCompile() bool
-	isPgoCompile() bool
 	isOrderfileCompile() bool
 	isCfi() bool
 	isFuzzer() bool
@@ -728,6 +730,8 @@
 
 	// Whether or not this dependency has to be followed for the apex variants
 	excludeInApex bool
+	// Whether or not this dependency has to be followed for the non-apex variants
+	excludeInNonApex bool
 
 	// If true, don't automatically export symbols from the static library into a shared library.
 	unexportedSymbols bool
@@ -891,7 +895,6 @@
 	vndkdep   *vndkdep
 	lto       *lto
 	afdo      *afdo
-	pgo       *pgo
 	orderfile *orderfile
 
 	library libraryInterface
@@ -923,6 +926,9 @@
 	apexSdkVersion android.ApiLevel
 
 	hideApexVariantFromMake bool
+
+	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
+	mergedAconfigFiles map[string]android.Paths
 }
 
 func (c *Module) AddJSONData(d *map[string]interface{}) {
@@ -1274,9 +1280,6 @@
 	if c.afdo != nil {
 		c.AddProperties(c.afdo.props()...)
 	}
-	if c.pgo != nil {
-		c.AddProperties(c.pgo.props()...)
-	}
 	if c.orderfile != nil {
 		c.AddProperties(c.orderfile.props()...)
 	}
@@ -1406,13 +1409,6 @@
 	return false
 }
 
-func (c *Module) isPgoCompile() bool {
-	if pgo := c.pgo; pgo != nil {
-		return pgo.Properties.PgoCompile
-	}
-	return false
-}
-
 func (c *Module) isOrderfileCompile() bool {
 	if orderfile := c.orderfile; orderfile != nil {
 		return orderfile.Properties.OrderfileLoad
@@ -1721,10 +1717,6 @@
 	return ctx.mod.isAfdoCompile()
 }
 
-func (ctx *moduleContextImpl) isPgoCompile() bool {
-	return ctx.mod.isPgoCompile()
-}
-
 func (ctx *moduleContextImpl) isOrderfileCompile() bool {
 	return ctx.mod.isOrderfileCompile()
 }
@@ -1837,7 +1829,6 @@
 	module.vndkdep = &vndkdep{}
 	module.lto = &lto{}
 	module.afdo = &afdo{}
-	module.pgo = &pgo{}
 	module.orderfile = &orderfile{}
 	return module
 }
@@ -2263,9 +2254,6 @@
 	if c.afdo != nil {
 		flags = c.afdo.flags(ctx, flags)
 	}
-	if c.pgo != nil {
-		flags = c.pgo.flags(ctx, flags)
-	}
 	if c.orderfile != nil {
 		flags = c.orderfile.flags(ctx, flags)
 	}
@@ -2334,6 +2322,9 @@
 	if c.testModule {
 		ctx.SetProvider(testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 	}
+	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: deps.GeneratedSources.Strings()})
+
+	aconfig.CollectDependencyAconfigFiles(ctx, &c.mergedAconfigFiles)
 
 	c.maybeInstall(ctx, apexInfo)
 }
@@ -2411,15 +2402,15 @@
 	if c.coverage != nil {
 		c.coverage.begin(ctx)
 	}
+	if c.afdo != nil {
+		c.afdo.begin(ctx)
+	}
 	if c.lto != nil {
 		c.lto.begin(ctx)
 	}
 	if c.orderfile != nil {
 		c.orderfile.begin(ctx)
 	}
-	if c.pgo != nil {
-		c.pgo.begin(ctx)
-	}
 	if ctx.useSdk() && c.IsSdkVariant() {
 		version, err := nativeApiLevelFromUser(ctx, ctx.sdkVersion())
 		if err != nil {
@@ -2819,6 +2810,9 @@
 		if inList(lib, deps.ExcludeLibsForApex) {
 			depTag.excludeInApex = true
 		}
+		if inList(lib, deps.ExcludeLibsForNonApex) {
+			depTag.excludeInNonApex = true
+		}
 
 		name, version := StubsLibNameAndVersion(lib)
 		if apiLibraryName, ok := apiImportInfo.SharedLibs[name]; ok && !ctx.OtherModuleExists(name) {
@@ -3335,6 +3329,9 @@
 			if !apexInfo.IsForPlatform() && libDepTag.excludeInApex {
 				return
 			}
+			if apexInfo.IsForPlatform() && libDepTag.excludeInNonApex {
+				return
+			}
 
 			depExporterInfo := ctx.OtherModuleProvider(dep, FlagExporterInfoProvider).(FlagExporterInfo)
 
@@ -4323,7 +4320,6 @@
 		&VndkProperties{},
 		&LTOProperties{},
 		&AfdoProperties{},
-		&PgoProperties{},
 		&OrderfileProperties{},
 		&android.ProtoProperties{},
 		// RustBindgenProperties is included here so that cc_defaults can be used for rust_bindgen modules.
diff --git a/cc/cc_test.go b/cc/cc_test.go
index e2dba90..710b72b 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -40,6 +40,7 @@
 var prepareForCcTest = android.GroupFixturePreparers(
 	PrepareForTestWithCcIncludeVndk,
 	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+		variables.VendorApiLevel = StringPtr("202404")
 		variables.DeviceVndkVersion = StringPtr("current")
 		variables.Platform_vndk_version = StringPtr("29")
 	}),
@@ -341,9 +342,9 @@
 	}
 }
 
-func checkWriteFileOutput(t *testing.T, params android.TestingBuildParams, expected []string) {
+func checkWriteFileOutput(t *testing.T, ctx *android.TestContext, params android.TestingBuildParams, expected []string) {
 	t.Helper()
-	content := android.ContentFromFileRuleForTests(t, params)
+	content := android.ContentFromFileRuleForTests(t, ctx, params)
 	actual := strings.FieldsFunc(content, func(r rune) bool { return r == '\n' })
 	assertArrayString(t, actual, expected)
 }
@@ -351,7 +352,7 @@
 func checkVndkOutput(t *testing.T, ctx *android.TestContext, output string, expected []string) {
 	t.Helper()
 	vndkSnapshot := ctx.SingletonForTests("vndk-snapshot")
-	checkWriteFileOutput(t, vndkSnapshot.Output(output), expected)
+	checkWriteFileOutput(t, ctx, vndkSnapshot.Output(output), expected)
 }
 
 func checkVndkLibrariesOutput(t *testing.T, ctx *android.TestContext, module string, expected []string) {
@@ -2131,11 +2132,13 @@
 	ensureStringContains(t, vendor_cflags, "-D__ANDROID_VNDK__")
 	ensureStringContains(t, vendor_cflags, "-D__ANDROID_VENDOR__")
 	ensureStringNotContains(t, vendor_cflags, "-D__ANDROID_PRODUCT__")
+	ensureStringContains(t, vendor_cflags, "-D__ANDROID_VENDOR_API__=202404")
 
 	product_cflags := product_static.Rule("cc").Args["cFlags"]
 	ensureStringContains(t, product_cflags, "-D__ANDROID_VNDK__")
 	ensureStringContains(t, product_cflags, "-D__ANDROID_PRODUCT__")
 	ensureStringNotContains(t, product_cflags, "-D__ANDROID_VENDOR__")
+	ensureStringNotContains(t, product_cflags, "-D__ANDROID_VENDOR_API__=202404")
 }
 
 func TestEnforceProductVndkVersionErrors(t *testing.T) {
@@ -4302,7 +4305,7 @@
 		libfoo.Rule("aidl_library").Implicits,
 	)
 
-	manifest := android.RuleBuilderSboxProtoForTests(t, libfoo.Output("aidl_library.sbox.textproto"))
+	manifest := android.RuleBuilderSboxProtoForTests(t, ctx, libfoo.Output("aidl_library.sbox.textproto"))
 	aidlCommand := manifest.Commands[0].GetCommand()
 
 	expectedAidlFlags := "-Ipackage_foo/a -Ipackage_bar/x"
@@ -4337,7 +4340,7 @@
 	`)
 
 	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static")
-	manifest := android.RuleBuilderSboxProtoForTests(t, libfoo.Output("aidl.sbox.textproto"))
+	manifest := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, libfoo.Output("aidl.sbox.textproto"))
 	aidlCommand := manifest.Commands[0].GetCommand()
 	expectedAidlFlag := "-Werror"
 	if !strings.Contains(aidlCommand, expectedAidlFlag) {
@@ -4388,7 +4391,7 @@
 				}
 			`)
 			libfoo := ctx.ModuleForTests("libfoo", tc.variant)
-			manifest := android.RuleBuilderSboxProtoForTests(t, libfoo.Output("aidl.sbox.textproto"))
+			manifest := android.RuleBuilderSboxProtoForTests(t, ctx, libfoo.Output("aidl.sbox.textproto"))
 			aidlCommand := manifest.Commands[0].GetCommand()
 			expectedAidlFlag := "--min_sdk_version=" + tc.expected
 			if !strings.Contains(aidlCommand, expectedAidlFlag) {
@@ -4818,7 +4821,7 @@
 
 	cflags := []string{"-Werror", "-std=candcpp"}
 	cstd := []string{"-std=gnu17", "-std=conly"}
-	cppstd := []string{"-std=gnu++17", "-std=cpp", "-fno-rtti"}
+	cppstd := []string{"-std=gnu++20", "-std=cpp", "-fno-rtti"}
 
 	lastIncludes := []string{
 		"out/soong/ndk/sysroot/usr/include",
diff --git a/cc/compiler.go b/cc/compiler.go
index 490d3cc..bb7885b 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -406,6 +406,15 @@
 		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VNDK__")
 		if ctx.inVendor() {
 			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VENDOR__")
+
+			vendorApiLevel := ctx.Config().VendorApiLevel()
+			if vendorApiLevel == "" {
+				// TODO(b/314036847): This is a fallback for UDC targets.
+				// This must be a build failure when UDC is no longer built
+				// from this source tree.
+				vendorApiLevel = ctx.Config().PlatformSdkVersion().String()
+			}
+			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VENDOR_API__="+vendorApiLevel)
 		} else if ctx.inProduct() {
 			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_PRODUCT__")
 		}
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 12722a7..82beb29 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -103,7 +103,7 @@
 	exportedVars.ExportStringList("Arm64Cflags", arm64Cflags)
 	pctx.VariableFunc("Arm64Cflags", func(ctx android.PackageVarContext) string {
 		flags := arm64Cflags
-		if ctx.Config().PageSizeAgnostic() {
+		if ctx.Config().NoBionicPageSizeMacro() {
 			flags = append(flags, "-D__BIONIC_NO_PAGE_SIZE_MACRO")
 		}
 		return strings.Join(flags, " ")
diff --git a/cc/config/global.go b/cc/config/global.go
index 2ca9df9..8b6df30 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -357,9 +357,9 @@
 	}
 
 	CStdVersion               = "gnu17"
-	CppStdVersion             = "gnu++17"
+	CppStdVersion             = "gnu++20"
 	ExperimentalCStdVersion   = "gnu2x"
-	ExperimentalCppStdVersion = "gnu++2a"
+	ExperimentalCppStdVersion = "gnu++2b"
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
diff --git a/cc/config/riscv64_device.go b/cc/config/riscv64_device.go
index d5bc760..0964015 100644
--- a/cc/config/riscv64_device.go
+++ b/cc/config/riscv64_device.go
@@ -25,7 +25,6 @@
 	riscv64Cflags = []string{
 		// Help catch common 32/64-bit errors.
 		"-Werror=implicit-function-declaration",
-		"-fno-emulated-tls",
 		"-march=rv64gcv_zba_zbb_zbs",
 		// Equivalent to "-munaligned-access", but our clang doesn't have that yet.
 		"-Xclang -target-feature -Xclang +unaligned-scalar-mem",
@@ -40,11 +39,13 @@
 		// Equivalent to "-munaligned-access", but our clang doesn't have that yet.
 		"-Xclang -target-feature -Xclang +unaligned-scalar-mem",
 		"-Xclang -target-feature -Xclang +unaligned-vector-mem",
+		// We should change the default for this in clang, but for now...
+		// (https://github.com/google/android-riscv64/issues/124)
+		"-mllvm -jump-is-expensive=false",
 	}
 
 	riscv64Lldflags = append(riscv64Ldflags,
 		"-Wl,-z,max-page-size=4096",
-		"-Wl,-plugin-opt,-emulated-tls=0",
 	)
 
 	riscv64Cppflags = []string{}
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index 00a395f..e43848c 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -107,7 +107,7 @@
 	exportedVars.ExportStringList("X86_64Cflags", x86_64Cflags)
 	pctx.VariableFunc("X86_64Cflags", func(ctx android.PackageVarContext) string {
 		flags := x86_64Cflags
-		if ctx.Config().PageSizeAgnostic() {
+		if ctx.Config().NoBionicPageSizeMacro() {
 			flags = append(flags, "-D__BIONIC_NO_PAGE_SIZE_MACRO")
 		}
 		return strings.Join(flags, " ")
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 8fc4898..ef2e68c 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -106,6 +106,7 @@
 	fuzzPackagedModule  fuzz.FuzzPackagedModule
 	installedSharedDeps []string
 	sharedLibraries     android.RuleBuilderInstalls
+	data                []android.DataPath
 }
 
 func (fuzz *fuzzBinary) fuzzBinary() bool {
@@ -231,16 +232,10 @@
 }
 
 func (fuzzBin *fuzzBinary) install(ctx ModuleContext, file android.Path) {
-	installBase := "fuzz"
-
-	fuzzBin.binaryDecorator.baseInstaller.dir = filepath.Join(
-		installBase, ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
-	fuzzBin.binaryDecorator.baseInstaller.dir64 = filepath.Join(
-		installBase, ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
-	fuzzBin.binaryDecorator.baseInstaller.install(ctx, file)
-
 	fuzzBin.fuzzPackagedModule = PackageFuzzModule(ctx, fuzzBin.fuzzPackagedModule, pctx)
 
+	installBase := "fuzz"
+
 	// Grab the list of required shared libraries.
 	fuzzBin.sharedLibraries, _ = CollectAllSharedDependencies(ctx)
 
@@ -256,34 +251,35 @@
 				SharedLibrarySymbolsInstallLocation(install, installBase, ctx.Arch().ArchType.String()))
 		}
 	}
+
+	for _, d := range fuzzBin.fuzzPackagedModule.Corpus {
+		fuzzBin.data = append(fuzzBin.data, android.DataPath{SrcPath: d, RelativeInstallPath: "corpus", WithoutRel: true})
+	}
+
+	for _, d := range fuzzBin.fuzzPackagedModule.Data {
+		fuzzBin.data = append(fuzzBin.data, android.DataPath{SrcPath: d, RelativeInstallPath: "data"})
+	}
+
+	if d := fuzzBin.fuzzPackagedModule.Dictionary; d != nil {
+		fuzzBin.data = append(fuzzBin.data, android.DataPath{SrcPath: d, WithoutRel: true})
+	}
+
+	if d := fuzzBin.fuzzPackagedModule.Config; d != nil {
+		fuzzBin.data = append(fuzzBin.data, android.DataPath{SrcPath: d, WithoutRel: true})
+	}
+
+	fuzzBin.binaryDecorator.baseInstaller.dir = filepath.Join(
+		installBase, ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
+	fuzzBin.binaryDecorator.baseInstaller.dir64 = filepath.Join(
+		installBase, ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
+	fuzzBin.binaryDecorator.baseInstaller.installTestData(ctx, fuzzBin.data)
+	fuzzBin.binaryDecorator.baseInstaller.install(ctx, file)
 }
 
 func PackageFuzzModule(ctx android.ModuleContext, fuzzPackagedModule fuzz.FuzzPackagedModule, pctx android.PackageContext) fuzz.FuzzPackagedModule {
 	fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Corpus)
-	intermediateDir := android.PathForModuleOut(ctx, "corpus")
-
-	// Create one rule per file to avoid MAX_ARG_STRLEN hardlimit.
-	for _, entry := range fuzzPackagedModule.Corpus {
-		ctx.Build(pctx, android.BuildParams{
-			Rule:   android.Cp,
-			Output: intermediateDir.Join(ctx, entry.Base()),
-			Input:  entry,
-		})
-	}
-	fuzzPackagedModule.CorpusIntermediateDir = intermediateDir
 
 	fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Data)
-	intermediateDir = android.PathForModuleOut(ctx, "data")
-
-	// Create one rule per file to avoid MAX_ARG_STRLEN hardlimit.
-	for _, entry := range fuzzPackagedModule.Data {
-		ctx.Build(pctx, android.BuildParams{
-			Rule:   android.Cp,
-			Output: intermediateDir.Join(ctx, entry.Rel()),
-			Input:  entry,
-		})
-	}
-	fuzzPackagedModule.DataIntermediateDir = intermediateDir
 
 	if fuzzPackagedModule.FuzzProperties.Dictionary != nil {
 		fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *fuzzPackagedModule.FuzzProperties.Dictionary)
diff --git a/cc/gen_test.go b/cc/gen_test.go
index 85df333..439f0a9 100644
--- a/cc/gen_test.go
+++ b/cc/gen_test.go
@@ -67,7 +67,7 @@
 			t.Errorf("missing aidl includes in global flags")
 		}
 
-		aidlCommand := android.RuleBuilderSboxProtoForTests(t, aidlManifest).Commands[0].GetCommand()
+		aidlCommand := android.RuleBuilderSboxProtoForTests(t, ctx, aidlManifest).Commands[0].GetCommand()
 		if !strings.Contains(aidlCommand, "-Isub") {
 			t.Errorf("aidl command for c.aidl should contain \"-Isub\", but was %q", aidlCommand)
 		}
diff --git a/cc/genrule_test.go b/cc/genrule_test.go
index 0d16e62..9295244 100644
--- a/cc/genrule_test.go
+++ b/cc/genrule_test.go
@@ -178,7 +178,7 @@
 				android.OptionalFixturePreparer(tt.preparer),
 			).RunTestWithBp(t, bp)
 			gen := result.ModuleForTests("gen", tt.variant)
-			sboxProto := android.RuleBuilderSboxProtoForTests(t, gen.Output("genrule.sbox.textproto"))
+			sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, gen.Output("genrule.sbox.textproto"))
 			cmd := *sboxProto.Commands[0].Command
 			android.AssertStringDoesContain(t, "incorrect CC_ARCH", cmd, "CC_ARCH="+tt.arch+" ")
 			android.AssertStringDoesContain(t, "incorrect CC_NATIVE_BRIDGE", cmd, "CC_NATIVE_BRIDGE="+tt.nativeBridge+" ")
diff --git a/cc/image.go b/cc/image.go
index 239f1db..4f36111 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -23,6 +23,8 @@
 
 	"android/soong/android"
 	"android/soong/snapshot"
+
+	"github.com/google/blueprint/proptools"
 )
 
 var _ android.ImageInterface = (*Module)(nil)
@@ -622,6 +624,10 @@
 
 		lib.baseCompiler.Properties.Exclude_generated_sources = append(lib.baseCompiler.Properties.Exclude_generated_sources,
 			lib.baseCompiler.Properties.Target.Vendor.Exclude_generated_sources...)
+
+		if lib.Properties.Target.Vendor.No_stubs {
+			proptools.Clear(&lib.Properties.Stubs)
+		}
 	}
 }
 
@@ -635,6 +641,10 @@
 
 		lib.baseCompiler.Properties.Exclude_generated_sources = append(lib.baseCompiler.Properties.Exclude_generated_sources,
 			lib.baseCompiler.Properties.Target.Product.Exclude_generated_sources...)
+
+		if lib.Properties.Target.Product.No_stubs {
+			proptools.Clear(&lib.Properties.Stubs)
+		}
 	}
 }
 
diff --git a/cc/installer.go b/cc/installer.go
index e2c0e7b..a0b6295 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -59,6 +59,8 @@
 	relative string
 	location installLocation
 
+	installDeps android.InstallPaths
+
 	path android.InstallPath
 }
 
@@ -97,7 +99,12 @@
 }
 
 func (installer *baseInstaller) install(ctx ModuleContext, file android.Path) {
-	installer.path = ctx.InstallFile(installer.installDir(ctx), file.Base(), file)
+	installer.path = ctx.InstallFile(installer.installDir(ctx), file.Base(), file, installer.installDeps...)
+}
+
+func (installer *baseInstaller) installTestData(ctx ModuleContext, data []android.DataPath) {
+	installedData := ctx.InstallTestData(installer.installDir(ctx), data)
+	installer.installDeps = append(installer.installDeps, installedData...)
 }
 
 func (installer *baseInstaller) everInstallable() bool {
diff --git a/cc/library.go b/cc/library.go
index d22bcec..719455c 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -107,6 +107,13 @@
 			Suffix *string `android:"arch_variant"`
 
 			Header_abi_checker headerAbiCheckerProperties
+
+			// Disable stubs for vendor/product variants
+			// This is a workaround to keep `stubs` only for "core" variant (not product/vendor).
+			// It would be nice if we could put `stubs` into a `target: { core: {} }`
+			// block but it's not supported in soong yet. This could be removed/simplified once we have
+			// a better syntax.
+			No_stubs bool
 		}
 
 		Platform struct {
diff --git a/cc/linker.go b/cc/linker.go
index 257fe86..357d1ce 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -214,6 +214,11 @@
 			// variant of the C/C++ module.
 			Exclude_static_libs []string
 		}
+		Non_apex struct {
+			// list of shared libs that should not be used to build the non-apex
+			// variant of the C/C++ module.
+			Exclude_shared_libs []string
+		}
 	} `android:"arch_variant"`
 
 	// make android::build:GetBuildNumber() available containing the build ID.
@@ -300,6 +305,10 @@
 	// variants.
 	deps.ExcludeLibsForApex = append(deps.ExcludeLibsForApex, linker.Properties.Target.Apex.Exclude_shared_libs...)
 	deps.ExcludeLibsForApex = append(deps.ExcludeLibsForApex, linker.Properties.Target.Apex.Exclude_static_libs...)
+	// Record the libraries that need to be excluded when building for non-APEX variants
+	// for the same reason above. This is used for marking deps and marked deps are
+	// ignored for non-apex variants.
+	deps.ExcludeLibsForNonApex = append(deps.ExcludeLibsForNonApex, linker.Properties.Target.Non_apex.Exclude_shared_libs...)
 
 	if Bool(linker.Properties.Use_version_lib) {
 		deps.WholeStaticLibs = append(deps.WholeStaticLibs, "libbuildversion")
diff --git a/cc/lto.go b/cc/lto.go
index 30d7b757..8b0880c 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -116,7 +116,8 @@
 		// better dead code elimination and CFG simplification, but do
 		// not perform costly optimizations for a balance between compile
 		// time, binary size and performance.
-		if !lto.ThinLTO() {
+		// Apply the same for Eng builds as well.
+		if !lto.ThinLTO() || ctx.Config().Eng() {
 			ltoLdFlags = append(ltoLdFlags, "-Wl,--lto-O0")
 		}
 
@@ -140,7 +141,7 @@
 		// Reduce the inlining threshold for a better balance of binary size and
 		// performance.
 		if !ctx.Darwin() {
-			if ctx.isPgoCompile() || ctx.isAfdoCompile() {
+			if ctx.isAfdoCompile() {
 				ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=40")
 			} else {
 				ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=5")
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index df775de..348c3bc 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -334,18 +334,12 @@
 		return false
 	}
 	// http://b/156513478
-	// http://b/277624006
-	// This step is expensive. We're not able to do anything with the outputs of
-	// this step yet (canDiffAbi is flagged off because libabigail isn't able to
-	// handle all our libraries), disable it. There's no sense in protecting
-	// against checking in code that breaks abidw since by the time any of this
-	// can be turned on we'll need to migrate to STG anyway.
-	return false
+	return config.ReleaseNdkAbiMonitored()
 }
 
 // Feature flag to disable diffing against prebuilts.
-func canDiffAbi() bool {
-	return false
+func canDiffAbi(config android.Config) bool {
+	return config.ReleaseNdkAbiMonitored()
 }
 
 func (this *stubDecorator) dumpAbi(ctx ModuleContext, symbolList android.Path) {
@@ -476,7 +470,7 @@
 	c.versionScriptPath = nativeAbiResult.versionScript
 	if canDumpAbi(ctx.Config()) {
 		c.dumpAbi(ctx, nativeAbiResult.symbolList)
-		if canDiffAbi() {
+		if canDiffAbi(ctx.Config()) {
 			c.diffAbi(ctx)
 		}
 	}
diff --git a/cc/pgo.go b/cc/pgo.go
deleted file mode 100644
index 463e2e6..0000000
--- a/cc/pgo.go
+++ /dev/null
@@ -1,294 +0,0 @@
-// Copyright 2017 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 cc
-
-import (
-	"fmt"
-	"path/filepath"
-	"strings"
-
-	"github.com/google/blueprint/proptools"
-
-	"android/soong/android"
-)
-
-var (
-	// Add flags to ignore warnings that profiles are old or missing for
-	// some functions.
-	profileUseOtherFlags = []string{
-		"-Wno-backend-plugin",
-	}
-
-	globalPgoProfileProjects = []string{
-		"toolchain/pgo-profiles/pgo",
-		"vendor/google_data/pgo_profile/pgo",
-	}
-)
-
-var pgoProfileProjectsConfigKey = android.NewOnceKey("PgoProfileProjects")
-
-const profileInstrumentFlag = "-fprofile-generate=/data/local/tmp"
-const profileUseInstrumentFormat = "-fprofile-use=%s"
-
-func getPgoProfileProjects(config android.DeviceConfig) []string {
-	return config.OnceStringSlice(pgoProfileProjectsConfigKey, func() []string {
-		return append(globalPgoProfileProjects, config.PgoAdditionalProfileDirs()...)
-	})
-}
-
-func recordMissingProfileFile(ctx BaseModuleContext, missing string) {
-	getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true)
-}
-
-type PgoProperties struct {
-	Pgo struct {
-		Instrumentation    *bool
-		Profile_file       *string `android:"arch_variant"`
-		Benchmarks         []string
-		Enable_profile_use *bool `android:"arch_variant"`
-		// Additional compiler flags to use when building this module
-		// for profiling.
-		Cflags []string `android:"arch_variant"`
-	} `android:"arch_variant"`
-
-	PgoPresent          bool `blueprint:"mutated"`
-	ShouldProfileModule bool `blueprint:"mutated"`
-	PgoCompile          bool `blueprint:"mutated"`
-	PgoInstrLink        bool `blueprint:"mutated"`
-}
-
-type pgo struct {
-	Properties PgoProperties
-}
-
-func (props *PgoProperties) isInstrumentation() bool {
-	return props.Pgo.Instrumentation != nil && *props.Pgo.Instrumentation == true
-}
-
-func (pgo *pgo) props() []interface{} {
-	return []interface{}{&pgo.Properties}
-}
-
-func (props *PgoProperties) addInstrumentationProfileGatherFlags(ctx ModuleContext, flags Flags) Flags {
-	// Add to C flags iff PGO is explicitly enabled for this module.
-	if props.ShouldProfileModule {
-		flags.Local.CFlags = append(flags.Local.CFlags, props.Pgo.Cflags...)
-		flags.Local.CFlags = append(flags.Local.CFlags, profileInstrumentFlag)
-	}
-	flags.Local.LdFlags = append(flags.Local.LdFlags, profileInstrumentFlag)
-	return flags
-}
-
-func (props *PgoProperties) getPgoProfileFile(ctx BaseModuleContext) android.OptionalPath {
-	profileFile := *props.Pgo.Profile_file
-
-	// Test if the profile_file is present in any of the PGO profile projects
-	for _, profileProject := range getPgoProfileProjects(ctx.DeviceConfig()) {
-		// Bug: http://b/74395273 If the profile_file is unavailable,
-		// use a versioned file named
-		// <profile_file>.<arbitrary-version> when available.  This
-		// works around an issue where ccache serves stale cache
-		// entries when the profile file has changed.
-		globPattern := filepath.Join(profileProject, profileFile+".*")
-		versionedProfiles, err := ctx.GlobWithDeps(globPattern, nil)
-		if err != nil {
-			ctx.ModuleErrorf("glob: %s", err.Error())
-		}
-
-		path := android.ExistentPathForSource(ctx, profileProject, profileFile)
-		if path.Valid() {
-			if len(versionedProfiles) != 0 {
-				ctx.PropertyErrorf("pgo.profile_file", "Profile_file has multiple versions: "+filepath.Join(profileProject, profileFile)+", "+strings.Join(versionedProfiles, ", "))
-			}
-			return path
-		}
-
-		if len(versionedProfiles) > 1 {
-			ctx.PropertyErrorf("pgo.profile_file", "Profile_file has multiple versions: "+strings.Join(versionedProfiles, ", "))
-		} else if len(versionedProfiles) == 1 {
-			return android.OptionalPathForPath(android.PathForSource(ctx, versionedProfiles[0]))
-		}
-	}
-
-	// Record that this module's profile file is absent
-	missing := *props.Pgo.Profile_file + ":" + ctx.ModuleDir() + "/Android.bp:" + ctx.ModuleName()
-	recordMissingProfileFile(ctx, missing)
-
-	return android.OptionalPathForPath(nil)
-}
-
-func (props *PgoProperties) profileUseFlags(ctx ModuleContext, file string) []string {
-	flags := []string{fmt.Sprintf(profileUseInstrumentFormat, file)}
-	flags = append(flags, profileUseOtherFlags...)
-	return flags
-}
-
-func (props *PgoProperties) addProfileUseFlags(ctx ModuleContext, flags Flags) Flags {
-	// Return if 'pgo' property is not present in this module.
-	if !props.PgoPresent {
-		return flags
-	}
-
-	if props.PgoCompile {
-		profileFile := props.getPgoProfileFile(ctx)
-		profileFilePath := profileFile.Path()
-		profileUseFlags := props.profileUseFlags(ctx, profileFilePath.String())
-
-		flags.Local.CFlags = append(flags.Local.CFlags, profileUseFlags...)
-		flags.Local.LdFlags = append(flags.Local.LdFlags, profileUseFlags...)
-
-		// Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
-		// if profileFile gets updated
-		flags.CFlagsDeps = append(flags.CFlagsDeps, profileFilePath)
-		flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFilePath)
-	}
-	return flags
-}
-
-func (props *PgoProperties) isPGO(ctx BaseModuleContext) bool {
-	isInstrumentation := props.isInstrumentation()
-
-	profileKindPresent := isInstrumentation
-	filePresent := props.Pgo.Profile_file != nil
-	benchmarksPresent := len(props.Pgo.Benchmarks) > 0
-
-	// If all three properties are absent, PGO is OFF for this module
-	if !profileKindPresent && !filePresent && !benchmarksPresent {
-		return false
-	}
-
-	// profileKindPresent and filePresent are mandatory properties.
-	if !profileKindPresent || !filePresent {
-		var missing []string
-		if !profileKindPresent {
-			missing = append(missing, "profile kind")
-		}
-		if !filePresent {
-			missing = append(missing, "profile_file property")
-		}
-		missingProps := strings.Join(missing, ", ")
-		ctx.ModuleErrorf("PGO specification is missing properties: " + missingProps)
-	}
-
-	// Benchmark property is mandatory for instrumentation PGO.
-	if isInstrumentation && !benchmarksPresent {
-		ctx.ModuleErrorf("Instrumentation PGO specification is missing benchmark property")
-	}
-
-	return true
-}
-
-func (pgo *pgo) begin(ctx BaseModuleContext) {
-	// TODO Evaluate if we need to support PGO for host modules
-	if ctx.Host() {
-		return
-	}
-
-	// Check if PGO is needed for this module
-	pgo.Properties.PgoPresent = pgo.Properties.isPGO(ctx)
-
-	if !pgo.Properties.PgoPresent {
-		return
-	}
-
-	// This module should be instrumented if ANDROID_PGO_INSTRUMENT is set
-	// and includes 'all', 'ALL' or a benchmark listed for this module.
-	//
-	// TODO Validate that each benchmark instruments at least one module
-	pgo.Properties.ShouldProfileModule = false
-	pgoBenchmarks := ctx.Config().Getenv("ANDROID_PGO_INSTRUMENT")
-	pgoBenchmarksMap := make(map[string]bool)
-	for _, b := range strings.Split(pgoBenchmarks, ",") {
-		pgoBenchmarksMap[b] = true
-	}
-
-	if pgoBenchmarksMap["all"] == true || pgoBenchmarksMap["ALL"] == true {
-		pgo.Properties.ShouldProfileModule = true
-		pgo.Properties.PgoInstrLink = pgo.Properties.isInstrumentation()
-	} else {
-		for _, b := range pgo.Properties.Pgo.Benchmarks {
-			if pgoBenchmarksMap[b] == true {
-				pgo.Properties.ShouldProfileModule = true
-				pgo.Properties.PgoInstrLink = pgo.Properties.isInstrumentation()
-				break
-			}
-		}
-	}
-
-	// PGO profile use is not feasible for a Clang coverage build because
-	// -fprofile-use and -fprofile-instr-generate are incompatible.
-	if ctx.DeviceConfig().ClangCoverageEnabled() {
-		return
-	}
-
-	if !ctx.Config().IsEnvTrue("ANDROID_PGO_NO_PROFILE_USE") &&
-		proptools.BoolDefault(pgo.Properties.Pgo.Enable_profile_use, true) {
-		if profileFile := pgo.Properties.getPgoProfileFile(ctx); profileFile.Valid() {
-			pgo.Properties.PgoCompile = true
-		}
-	}
-}
-
-func (pgo *pgo) flags(ctx ModuleContext, flags Flags) Flags {
-	if ctx.Host() {
-		return flags
-	}
-
-	// Deduce PgoInstrLink property i.e. whether this module needs to be
-	// linked with profile-generation flags.  Here, we're setting it if any
-	// dependency needs PGO instrumentation.  It is initially set in
-	// begin() if PGO is directly enabled for this module.
-	if ctx.static() && !ctx.staticBinary() {
-		// For static libraries, check if any whole_static_libs are
-		// linked with profile generation
-		ctx.VisitDirectDeps(func(m android.Module) {
-			if depTag, ok := ctx.OtherModuleDependencyTag(m).(libraryDependencyTag); ok {
-				if depTag.static() && depTag.wholeStatic {
-					if cc, ok := m.(*Module); ok {
-						if cc.pgo.Properties.PgoInstrLink {
-							pgo.Properties.PgoInstrLink = true
-						}
-					}
-				}
-			}
-		})
-	} else {
-		// For executables and shared libraries, check all static dependencies.
-		ctx.VisitDirectDeps(func(m android.Module) {
-			if depTag, ok := ctx.OtherModuleDependencyTag(m).(libraryDependencyTag); ok {
-				if depTag.static() {
-					if cc, ok := m.(*Module); ok {
-						if cc.pgo.Properties.PgoInstrLink {
-							pgo.Properties.PgoInstrLink = true
-						}
-					}
-				}
-			}
-		})
-	}
-
-	props := pgo.Properties
-	// Add flags to profile this module based on its profile_kind
-	if (props.ShouldProfileModule && props.isInstrumentation()) || props.PgoInstrLink {
-		// Instrumentation PGO use and gather flags cannot coexist.
-		return props.addInstrumentationProfileGatherFlags(ctx, flags)
-	}
-
-	if !ctx.Config().IsEnvTrue("ANDROID_PGO_NO_PROFILE_USE") {
-		flags = props.addProfileUseFlags(ctx, flags)
-	}
-
-	return flags
-}
diff --git a/cc/stub_library.go b/cc/stub_library.go
index 3a6d0ae..47c6cb9 100644
--- a/cc/stub_library.go
+++ b/cc/stub_library.go
@@ -27,7 +27,8 @@
 }
 
 type stubLibraries struct {
-	stubLibraryMap map[string]bool
+	stubLibraryMap       map[string]bool
+	stubVendorLibraryMap map[string]bool
 
 	apiListCoverageXmlPaths []string
 }
@@ -54,6 +55,9 @@
 			if IsStubTarget(m) {
 				if name := getInstalledFileName(m); name != "" {
 					s.stubLibraryMap[name] = true
+					if m.InVendor() {
+						s.stubVendorLibraryMap[name] = true
+					}
 				}
 			}
 			if m.library != nil {
@@ -67,13 +71,15 @@
 
 func stubLibrariesSingleton() android.Singleton {
 	return &stubLibraries{
-		stubLibraryMap: make(map[string]bool),
+		stubLibraryMap:       make(map[string]bool),
+		stubVendorLibraryMap: make(map[string]bool),
 	}
 }
 
 func (s *stubLibraries) MakeVars(ctx android.MakeVarsContext) {
 	// Convert stub library file names into Makefile variable.
 	ctx.Strict("STUB_LIBRARIES", strings.Join(android.SortedKeys(s.stubLibraryMap), " "))
+	ctx.Strict("SOONG_STUB_VENDOR_LIBRARIES", strings.Join(android.SortedKeys(s.stubVendorLibraryMap), " "))
 
 	// Export the list of API XML files to Make.
 	sort.Strings(s.apiListCoverageXmlPaths)
diff --git a/cc/test.go b/cc/test.go
index a4224c3..78e1646 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -424,6 +424,8 @@
 	if ctx.Host() && test.gtest() && test.Properties.Test_options.Unit_test == nil {
 		test.Properties.Test_options.Unit_test = proptools.BoolPtr(true)
 	}
+
+	test.binaryDecorator.baseInstaller.installTestData(ctx, test.data)
 	test.binaryDecorator.baseInstaller.install(ctx, file)
 }
 
@@ -584,7 +586,7 @@
 type benchmarkDecorator struct {
 	*binaryDecorator
 	Properties BenchmarkProperties
-	data       android.Paths
+	data       []android.DataPath
 	testConfig android.Path
 }
 
@@ -605,7 +607,9 @@
 }
 
 func (benchmark *benchmarkDecorator) install(ctx ModuleContext, file android.Path) {
-	benchmark.data = android.PathsForModuleSrc(ctx, benchmark.Properties.Data)
+	for _, d := range android.PathsForModuleSrc(ctx, benchmark.Properties.Data) {
+		benchmark.data = append(benchmark.data, android.DataPath{SrcPath: d})
+	}
 
 	var configs []tradefed.Config
 	if Bool(benchmark.Properties.Require_root) {
@@ -623,6 +627,7 @@
 
 	benchmark.binaryDecorator.baseInstaller.dir = filepath.Join("benchmarktest", ctx.ModuleName())
 	benchmark.binaryDecorator.baseInstaller.dir64 = filepath.Join("benchmarktest64", ctx.ModuleName())
+	benchmark.binaryDecorator.baseInstaller.installTestData(ctx, benchmark.data)
 	benchmark.binaryDecorator.baseInstaller.install(ctx, file)
 }
 
diff --git a/cc/testing.go b/cc/testing.go
index 71d986b..b1583f1 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -797,7 +797,7 @@
 func checkOverrides(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, jsonPath string, expected []string) {
 	t.Helper()
 	out := singleton.MaybeOutput(jsonPath)
-	content := android.ContentFromFileRuleForTests(t, out)
+	content := android.ContentFromFileRuleForTests(t, ctx, out)
 
 	var flags snapshotJsonFlags
 	if err := json.Unmarshal([]byte(content), &flags); err != nil {
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index c5431b3..890a533 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -23,13 +23,14 @@
 	"testing"
 )
 
-func checkJsonContents(t *testing.T, ctx android.TestingSingleton, jsonPath string, key string, value string) {
-	jsonOut := ctx.MaybeOutput(jsonPath)
+func checkJsonContents(t *testing.T, ctx *android.TestContext, snapshotSingleton android.TestingSingleton, jsonPath string, key string, value string) {
+	jsonOut := snapshotSingleton.MaybeOutput(jsonPath)
 	if jsonOut.Rule == nil {
 		t.Errorf("%q expected but not found", jsonPath)
 		return
 	}
-	if !strings.Contains(jsonOut.Args["content"], fmt.Sprintf("%q:%q", key, value)) {
+	content := android.ContentFromFileRuleForTests(t, ctx, jsonOut)
+	if !strings.Contains(content, fmt.Sprintf("%q:%q", key, value)) {
 		t.Errorf("%q must include %q:%q but it only has %v", jsonPath, key, value, jsonOut.Args["content"])
 	}
 }
@@ -167,8 +168,8 @@
 			filepath.Join(staticDir, "libvendor_available.a.json"),
 			filepath.Join(staticDir, "libvendor_available.cfi.a.json"))
 
-		checkJsonContents(t, snapshotSingleton, filepath.Join(staticDir, "libb.a.json"), "MinSdkVersion", "apex_inherit")
-		checkJsonContents(t, snapshotSingleton, filepath.Join(staticDir, "libvendor_available.a.json"), "MinSdkVersion", "29")
+		checkJsonContents(t, ctx, snapshotSingleton, filepath.Join(staticDir, "libb.a.json"), "MinSdkVersion", "apex_inherit")
+		checkJsonContents(t, ctx, snapshotSingleton, filepath.Join(staticDir, "libvendor_available.a.json"), "MinSdkVersion", "29")
 
 		// For binary executables, all vendor:true and vendor_available modules are captured.
 		if archType == "arm64" {
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index c871e85..6163952 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -184,8 +184,6 @@
 	PreoptBootClassPathDexFiles     android.Paths // file paths of boot class path files
 	PreoptBootClassPathDexLocations []string      // virtual locations of boot class path files
 
-	PreoptExtractedApk bool // Overrides OnlyPreoptModules
-
 	NoCreateAppImage    bool
 	ForceCreateAppImage bool
 
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index c13e14a..94707ba 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -124,7 +124,7 @@
 		return true
 	}
 
-	if global.OnlyPreoptArtBootImage && !module.PreoptExtractedApk {
+	if global.OnlyPreoptArtBootImage {
 		return true
 	}
 
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 2b19c9d..230fbb4 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -87,7 +87,6 @@
 		DexPreoptImageLocationsOnHost:   []string{},
 		PreoptBootClassPathDexFiles:     nil,
 		PreoptBootClassPathDexLocations: nil,
-		PreoptExtractedApk:              false,
 		NoCreateAppImage:                false,
 		ForceCreateAppImage:             false,
 		PresignedPrebuilt:               false,
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index 94b795f..5d7bc2b 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -399,13 +399,11 @@
 }
 
 type FuzzPackagedModule struct {
-	FuzzProperties        FuzzProperties
-	Dictionary            android.Path
-	Corpus                android.Paths
-	CorpusIntermediateDir android.Path
-	Config                android.Path
-	Data                  android.Paths
-	DataIntermediateDir   android.Path
+	FuzzProperties FuzzProperties
+	Dictionary     android.Path
+	Corpus         android.Paths
+	Config         android.Path
+	Data           android.Paths
 }
 
 func GetFramework(ctx android.LoadHookContext, lang Lang) Framework {
diff --git a/genrule/allowlists.go b/genrule/allowlists.go
index e6af14f..926d6c0 100644
--- a/genrule/allowlists.go
+++ b/genrule/allowlists.go
@@ -24,15 +24,9 @@
 	SandboxingDenyModuleList = []string{
 		// go/keep-sorted start
 		"CtsApkVerityTestDebugFiles",
-		"aidl-golden-test-build-hook-gen",
 		"aidl_camera_build_version",
 		"camera-its",
 		"chre_atoms_log.h",
-		"deqp_spvtools_update_build_version",
-		"libcore-non-cts-tests-txt",
-		"seller-frontend-service-stub-lite",
-		"swiftshader_spvtools_update_build_version",
-		"vm-tests-tf-lib",
 		// go/keep-sorted end
 	}
 
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 8f2c047..6b11019 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -424,6 +424,7 @@
 		return srcFiles
 	}
 	srcFiles := addLabelsForInputs("srcs", g.properties.Srcs, g.properties.Exclude_srcs)
+	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcFiles.Strings()})
 
 	var copyFrom android.Paths
 	var outputFiles android.WritablePaths
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 7c14531..4cff4b8 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -543,7 +543,7 @@
 	for _, test := range testcases {
 		t.Run(test.name, func(t *testing.T) {
 			gen := result.ModuleForTests(test.name, "")
-			manifest := android.RuleBuilderSboxProtoForTests(t, gen.Output("genrule.sbox.textproto"))
+			manifest := android.RuleBuilderSboxProtoForTests(t, result.TestContext, gen.Output("genrule.sbox.textproto"))
 			hash := manifest.Commands[0].GetInputHash()
 
 			android.AssertStringEquals(t, "hash", test.expectedHash, hash)
diff --git a/go.mod b/go.mod
index 0a11bd2..1174958 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module android/soong
 
-go 1.20
+go 1.21
 
 require (
 	github.com/google/blueprint v0.0.0
diff --git a/go.work b/go.work
index 67f6549..7c6022b 100644
--- a/go.work
+++ b/go.work
@@ -1,4 +1,4 @@
-go 1.19
+go 1.21
 
 use (
 	.
diff --git a/java/Android.bp b/java/Android.bp
index cf96871..d39a195 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -9,6 +9,7 @@
         "blueprint",
         "blueprint-pathtools",
         "soong",
+        "soong-aconfig",
         "soong-android",
         "soong-bazel",
         "soong-cc",
@@ -67,7 +68,6 @@
         "plugin.go",
         "prebuilt_apis.go",
         "proto.go",
-        "resourceshrinker.go",
         "robolectric.go",
         "rro.go",
         "sdk.go",
@@ -86,6 +86,7 @@
         "app_import_test.go",
         "app_set_test.go",
         "app_test.go",
+        "code_metadata_test.go",
         "bootclasspath_fragment_test.go",
         "device_host_converter_test.go",
         "dex_test.go",
@@ -107,7 +108,6 @@
         "plugin_test.go",
         "prebuilt_apis_test.go",
         "proto_test.go",
-        "resourceshrinker_test.go",
         "rro_test.go",
         "sdk_test.go",
         "sdk_library_test.go",
diff --git a/java/aar.go b/java/aar.go
index e579008..57a05d4 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"path/filepath"
+	"slices"
 	"strconv"
 	"strings"
 
@@ -108,26 +109,26 @@
 }
 
 type aapt struct {
-	aaptSrcJar                     android.Path
-	transitiveAaptRJars            android.Paths
-	transitiveAaptResourcePackages android.Paths
-	exportPackage                  android.Path
-	manifestPath                   android.Path
-	proguardOptionsFile            android.Path
-	rTxt                           android.Path
-	rJar                           android.Path
-	extraAaptPackagesFile          android.Path
-	mergedManifestFile             android.Path
-	noticeFile                     android.OptionalPath
-	assetPackage                   android.OptionalPath
-	isLibrary                      bool
-	defaultManifestVersion         string
-	useEmbeddedNativeLibs          bool
-	useEmbeddedDex                 bool
-	usesNonSdkApis                 bool
-	hasNoCode                      bool
-	LoggingParent                  string
-	resourceFiles                  android.Paths
+	aaptSrcJar                         android.Path
+	transitiveAaptRJars                android.Paths
+	transitiveAaptResourcePackagesFile android.Path
+	exportPackage                      android.Path
+	manifestPath                       android.Path
+	proguardOptionsFile                android.Path
+	rTxt                               android.Path
+	rJar                               android.Path
+	extraAaptPackagesFile              android.Path
+	mergedManifestFile                 android.Path
+	noticeFile                         android.OptionalPath
+	assetPackage                       android.OptionalPath
+	isLibrary                          bool
+	defaultManifestVersion             string
+	useEmbeddedNativeLibs              bool
+	useEmbeddedDex                     bool
+	usesNonSdkApis                     bool
+	hasNoCode                          bool
+	LoggingParent                      string
+	resourceFiles                      android.Paths
 
 	splitNames []string
 	splits     []split
@@ -552,9 +553,16 @@
 		aapt2ExtractExtraPackages(ctx, extraPackages, srcJar)
 	}
 
+	transitiveAaptResourcePackages := staticDeps.resPackages().Strings()
+	transitiveAaptResourcePackages = slices.DeleteFunc(transitiveAaptResourcePackages, func(p string) bool {
+		return p == packageRes.String()
+	})
+	transitiveAaptResourcePackagesFile := android.PathForModuleOut(ctx, "transitive-res-packages")
+	android.WriteFileRule(ctx, transitiveAaptResourcePackagesFile, strings.Join(transitiveAaptResourcePackages, "\n"))
+
 	a.aaptSrcJar = srcJar
 	a.transitiveAaptRJars = transitiveRJars
-	a.transitiveAaptResourcePackages = staticDeps.resPackages()
+	a.transitiveAaptResourcePackagesFile = transitiveAaptResourcePackagesFile
 	a.exportPackage = packageRes
 	a.manifestPath = manifestPath
 	a.proguardOptionsFile = proguardOptionsFile
@@ -820,9 +828,13 @@
 
 	proguardSpecInfo := a.collectProguardSpecInfo(ctx)
 	ctx.SetProvider(ProguardSpecInfoProvider, proguardSpecInfo)
-	a.exportedProguardFlagFiles = proguardSpecInfo.ProguardFlagsFiles.ToList()
-	a.extraProguardFlagFiles = append(a.extraProguardFlagFiles, a.exportedProguardFlagFiles...)
-	a.extraProguardFlagFiles = append(a.extraProguardFlagFiles, a.proguardOptionsFile)
+	exportedProguardFlagsFiles := proguardSpecInfo.ProguardFlagsFiles.ToList()
+	a.extraProguardFlagsFiles = append(a.extraProguardFlagsFiles, exportedProguardFlagsFiles...)
+	a.extraProguardFlagsFiles = append(a.extraProguardFlagsFiles, a.proguardOptionsFile)
+
+	combinedExportedProguardFlagFile := android.PathForModuleOut(ctx, "export_proguard_flags")
+	writeCombinedProguardFlagsFile(ctx, combinedExportedProguardFlagFile, exportedProguardFlagsFiles)
+	a.combinedExportedProguardFlagsFile = combinedExportedProguardFlagFile
 
 	var extraSrcJars android.Paths
 	var extraCombinedJars android.Paths
@@ -940,15 +952,15 @@
 
 	properties AARImportProperties
 
-	classpathFile                  android.WritablePath
-	proguardFlags                  android.WritablePath
-	exportPackage                  android.WritablePath
-	transitiveAaptResourcePackages android.Paths
-	extraAaptPackagesFile          android.WritablePath
-	manifest                       android.WritablePath
-	assetsPackage                  android.WritablePath
-	rTxt                           android.WritablePath
-	rJar                           android.WritablePath
+	classpathFile                      android.WritablePath
+	proguardFlags                      android.WritablePath
+	exportPackage                      android.WritablePath
+	transitiveAaptResourcePackagesFile android.Path
+	extraAaptPackagesFile              android.WritablePath
+	manifest                           android.WritablePath
+	assetsPackage                      android.WritablePath
+	rTxt                               android.WritablePath
+	rJar                               android.WritablePath
 
 	resourcesNodesDepSet *android.DepSet[*resourcesNode]
 	manifestsDepSet      *android.DepSet[android.Path]
@@ -1211,7 +1223,13 @@
 	_ = staticManifestsDepSet
 	a.manifestsDepSet = manifestDepSetBuilder.Build()
 
-	a.transitiveAaptResourcePackages = staticDeps.resPackages()
+	transitiveAaptResourcePackages := staticDeps.resPackages().Strings()
+	transitiveAaptResourcePackages = slices.DeleteFunc(transitiveAaptResourcePackages, func(p string) bool {
+		return p == a.exportPackage.String()
+	})
+	transitiveAaptResourcePackagesFile := android.PathForModuleOut(ctx, "transitive-res-packages")
+	android.WriteFileRule(ctx, transitiveAaptResourcePackagesFile, strings.Join(transitiveAaptResourcePackages, "\n"))
+	a.transitiveAaptResourcePackagesFile = transitiveAaptResourcePackagesFile
 
 	a.collectTransitiveHeaderJars(ctx)
 	ctx.SetProvider(JavaInfoProvider, JavaInfo{
diff --git a/java/androidmk.go b/java/androidmk.go
index 84f78c8..a3f94cd 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -128,8 +128,9 @@
 					if library.dexpreopter.configPath != nil {
 						entries.SetPath("LOCAL_SOONG_DEXPREOPT_CONFIG", library.dexpreopter.configPath)
 					}
+					// TODO(b/311155208): The container here should be system.
 
-					entries.SetOptionalPaths("LOCAL_ACONFIG_FILES", library.getTransitiveAconfigFiles().ToList())
+					entries.SetPaths("LOCAL_ACONFIG_FILES", library.mergedAconfigFiles[""])
 				},
 			},
 		})
@@ -274,7 +275,7 @@
 				entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.classpathFile)
 				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.classpathFile)
 				entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", prebuilt.exportPackage)
-				entries.SetPaths("LOCAL_SOONG_TRANSITIVE_RES_PACKAGES", prebuilt.transitiveAaptResourcePackages)
+				entries.SetPath("LOCAL_SOONG_TRANSITIVE_RES_PACKAGES", prebuilt.transitiveAaptResourcePackagesFile)
 				entries.SetPath("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", prebuilt.proguardFlags)
 				entries.SetPath("LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES", prebuilt.extraAaptPackagesFile)
 				entries.SetPath("LOCAL_FULL_MANIFEST_FILE", prebuilt.manifest)
@@ -306,7 +307,8 @@
 					if len(binary.dexpreopter.builtInstalled) > 0 {
 						entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", binary.dexpreopter.builtInstalled)
 					}
-					entries.SetOptionalPaths("LOCAL_ACONFIG_FILES", binary.getTransitiveAconfigFiles().ToList())
+					// TODO(b/311155208): The container here should be system.
+					entries.SetPaths("LOCAL_ACONFIG_FILES", binary.mergedAconfigFiles[""])
 				},
 			},
 			ExtraFooters: []android.AndroidMkExtraFootersFunc{
@@ -459,7 +461,8 @@
 				entries.SetOptionalPaths("LOCAL_SOONG_LINT_REPORTS", app.linter.reports)
 
 				if app.Name() != "framework-res" {
-					entries.SetOptionalPaths("LOCAL_ACONFIG_FILES", app.getTransitiveAconfigFiles().ToList())
+					// TODO(b/311155208): The container here should be system.
+					entries.SetPaths("LOCAL_ACONFIG_FILES", app.mergedAconfigFiles[""])
 				}
 			},
 		},
@@ -532,12 +535,13 @@
 		}
 
 		entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", a.exportPackage)
-		entries.SetPaths("LOCAL_SOONG_TRANSITIVE_RES_PACKAGES", a.transitiveAaptResourcePackages)
+		entries.SetPath("LOCAL_SOONG_TRANSITIVE_RES_PACKAGES", a.transitiveAaptResourcePackagesFile)
 		entries.SetPath("LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES", a.extraAaptPackagesFile)
 		entries.SetPath("LOCAL_FULL_MANIFEST_FILE", a.mergedManifestFile)
-		entries.AddStrings("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", a.exportedProguardFlagFiles.Strings()...)
+		entries.SetPath("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", a.combinedExportedProguardFlagsFile)
 		entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", true)
-		entries.SetOptionalPaths("LOCAL_ACONFIG_FILES", a.getTransitiveAconfigFiles().ToList())
+		// TODO(b/311155208): The container here should be system.
+		entries.SetPaths("LOCAL_ACONFIG_FILES", a.mergedAconfigFiles[""])
 	})
 
 	return entriesList
diff --git a/java/app.go b/java/app.go
index 6d7411d..0f46b4e 100755
--- a/java/app.go
+++ b/java/app.go
@@ -526,8 +526,8 @@
 
 	staticLibProguardFlagFiles = android.FirstUniquePaths(staticLibProguardFlagFiles)
 
-	a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, staticLibProguardFlagFiles...)
-	a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, a.proguardOptionsFile)
+	a.Module.extraProguardFlagsFiles = append(a.Module.extraProguardFlagsFiles, staticLibProguardFlagFiles...)
+	a.Module.extraProguardFlagsFiles = append(a.Module.extraProguardFlagsFiles, a.proguardOptionsFile)
 }
 
 func (a *AndroidApp) installPath(ctx android.ModuleContext) android.InstallPath {
@@ -544,7 +544,7 @@
 	return android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
 }
 
-func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path {
+func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) (android.Path, android.Path) {
 	a.dexpreopter.installPath = a.installPath(ctx)
 	a.dexpreopter.isApp = true
 	if a.dexProperties.Uncompress_dex == nil {
@@ -557,7 +557,15 @@
 	a.dexpreopter.manifestFile = a.mergedManifestFile
 	a.dexpreopter.preventInstall = a.appProperties.PreventInstall
 
+	var packageResources = a.exportPackage
+
 	if ctx.ModuleName() != "framework-res" {
+		if Bool(a.dexProperties.Optimize.Shrink_resources) {
+			protoFile := android.PathForModuleOut(ctx, packageResources.Base()+".proto.apk")
+			aapt2Convert(ctx, protoFile, packageResources, "proto")
+			a.dexer.resourcesInput = android.OptionalPathForPath(protoFile)
+		}
+
 		var extraSrcJars android.Paths
 		var extraClasspathJars android.Paths
 		var extraCombinedJars android.Paths
@@ -575,9 +583,14 @@
 		}
 
 		a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars)
+		if Bool(a.dexProperties.Optimize.Shrink_resources) {
+			binaryResources := android.PathForModuleOut(ctx, packageResources.Base()+".binary.out.apk")
+			aapt2Convert(ctx, binaryResources, a.dexer.resourcesOutput.Path(), "binary")
+			packageResources = binaryResources
+		}
 	}
 
-	return a.dexJarFile.PathOrNil()
+	return a.dexJarFile.PathOrNil(), packageResources
 }
 
 func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, prebuiltJniPackages android.Paths, ctx android.ModuleContext) android.WritablePath {
@@ -762,7 +775,6 @@
 
 	// Process all building blocks, from AAPT to certificates.
 	a.aaptBuildActions(ctx)
-
 	// The decision to enforce <uses-library> checks is made before adding implicit SDK libraries.
 	a.usesLibrary.freezeEnforceUsesLibraries()
 
@@ -788,7 +800,7 @@
 	a.linter.resources = a.aapt.resourceFiles
 	a.linter.buildModuleReportZip = ctx.Config().UnbundledBuildApps()
 
-	dexJarFile := a.dexBuildActions(ctx)
+	dexJarFile, packageResources := a.dexBuildActions(ctx)
 
 	jniLibs, prebuiltJniPackages, certificates := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis))
 	jniJarFile := a.jniBuildActions(jniLibs, prebuiltJniPackages, ctx)
@@ -812,7 +824,7 @@
 	}
 	rotationMinSdkVersion := String(a.overridableAppProperties.RotationMinSdkVersion)
 
-	CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion, Bool(a.dexProperties.Optimize.Shrink_resources))
+	CreateAndSignAppPackage(ctx, packageFile, packageResources, jniJarFile, dexJarFile, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion)
 	a.outputFile = packageFile
 	if v4SigningRequested {
 		a.extraOutputFiles = append(a.extraOutputFiles, v4SignatureFile)
@@ -841,7 +853,7 @@
 		if v4SigningRequested {
 			v4SignatureFile = android.PathForModuleOut(ctx, a.installApkName+"_"+split.suffix+".apk.idsig")
 		}
-		CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion, false)
+		CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion)
 		a.extraOutputFiles = append(a.extraOutputFiles, packageFile)
 		if v4SigningRequested {
 			a.extraOutputFiles = append(a.extraOutputFiles, v4SignatureFile)
diff --git a/java/app_builder.go b/java/app_builder.go
index d397ff7..943ce31 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -52,7 +52,7 @@
 	})
 
 func CreateAndSignAppPackage(ctx android.ModuleContext, outputFile android.WritablePath,
-	packageFile, jniJarFile, dexJarFile android.Path, certificates []Certificate, deps android.Paths, v4SignatureFile android.WritablePath, lineageFile android.Path, rotationMinSdkVersion string, shrinkResources bool) {
+	packageFile, jniJarFile, dexJarFile android.Path, certificates []Certificate, deps android.Paths, v4SignatureFile android.WritablePath, lineageFile android.Path, rotationMinSdkVersion string) {
 
 	unsignedApkName := strings.TrimSuffix(outputFile.Base(), ".apk") + "-unsigned.apk"
 	unsignedApk := android.PathForModuleOut(ctx, unsignedApkName)
@@ -71,12 +71,6 @@
 		Output:    unsignedApk,
 		Implicits: deps,
 	})
-
-	if shrinkResources {
-		shrunkenApk := android.PathForModuleOut(ctx, "resource-shrunken", unsignedApk.Base())
-		ShrinkResources(ctx, unsignedApk, shrunkenApk)
-		unsignedApk = shrunkenApk
-	}
 	SignAppPackage(ctx, outputFile, unsignedApk, certificates, v4SignatureFile, lineageFile, rotationMinSdkVersion)
 }
 
diff --git a/java/base.go b/java/base.go
index fdc164e..c4b4026 100644
--- a/java/base.go
+++ b/java/base.go
@@ -22,9 +22,11 @@
 
 	"android/soong/ui/metrics/bp2build_metrics_proto"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 
+	"android/soong/aconfig"
 	"android/soong/android"
 	"android/soong/dexpreopt"
 	"android/soong/java/config"
@@ -512,13 +514,8 @@
 	// or the module should override Stem().
 	stem string
 
-	// Aconfig "cache files" that went directly into this module.  Transitive ones are
-	// tracked via JavaInfo.TransitiveAconfigFiles
-	// TODO: Extract to something standalone to propagate tags via GeneratedJavaLibraryModule
-	aconfigIntermediates android.Paths
-
-	// Aconfig files for all transitive deps.  Also exposed via JavaInfo
-	transitiveAconfigFiles *android.DepSet[android.Path]
+	// Single aconfig "cache file" merged from this module and all dependencies.
+	mergedAconfigFiles map[string]android.Paths
 }
 
 func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error {
@@ -1174,6 +1171,7 @@
 	uniqueSrcFiles = append(uniqueSrcFiles, uniqueJavaFiles...)
 	uniqueSrcFiles = append(uniqueSrcFiles, uniqueKtFiles...)
 	j.uniqueSrcFiles = uniqueSrcFiles
+	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: uniqueSrcFiles.Strings()})
 
 	// We don't currently run annotation processors in turbine, which means we can't use turbine
 	// generated header jars when an annotation processor that generates API is enabled.  One
@@ -1615,7 +1613,7 @@
 	if ctx.Device() && (Bool(j.properties.Installable) || Bool(compileDex)) {
 		if j.hasCode(ctx) {
 			if j.shouldInstrumentStatic(ctx) {
-				j.dexer.extraProguardFlagFiles = append(j.dexer.extraProguardFlagFiles,
+				j.dexer.extraProguardFlagsFiles = append(j.dexer.extraProguardFlagsFiles,
 					android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags"))
 			}
 			// Dex compilation
@@ -1723,7 +1721,7 @@
 
 	ctx.CheckbuildFile(outputFile)
 
-	j.collectTransitiveAconfigFiles(ctx)
+	aconfig.CollectDependencyAconfigFiles(ctx, &j.mergedAconfigFiles)
 
 	ctx.SetProvider(JavaInfoProvider, JavaInfo{
 		HeaderJars:                     android.PathsIfNonNil(j.headerJarFile),
@@ -1740,7 +1738,6 @@
 		ExportedPluginClasses:          j.exportedPluginClasses,
 		ExportedPluginDisableTurbine:   j.exportedDisableTurbine,
 		JacocoReportClassesFile:        j.jacocoReportClassesFile,
-		TransitiveAconfigFiles:         j.transitiveAconfigFiles,
 	})
 
 	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
@@ -2081,34 +2078,6 @@
 	return Bool(j.properties.Installable)
 }
 
-func (j *Module) collectTransitiveAconfigFiles(ctx android.ModuleContext) {
-	// Aconfig files from this module
-	mine := j.aconfigIntermediates
-
-	// Aconfig files from transitive dependencies
-	fromDeps := []*android.DepSet[android.Path]{}
-	ctx.VisitDirectDeps(func(module android.Module) {
-		dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
-		if dep.TransitiveAconfigFiles != nil {
-			fromDeps = append(fromDeps, dep.TransitiveAconfigFiles)
-		}
-	})
-
-	// DepSet containing aconfig files myself and from dependencies
-	j.transitiveAconfigFiles = android.NewDepSet(android.POSTORDER, mine, fromDeps)
-}
-
-func (j *Module) AddAconfigIntermediate(path android.Path) {
-	j.aconfigIntermediates = append(j.aconfigIntermediates, path)
-}
-
-func (j *Module) getTransitiveAconfigFiles() *android.DepSet[android.Path] {
-	if j.transitiveAconfigFiles == nil {
-		panic(fmt.Errorf("java.Moduile: getTransitiveAconfigFiles called before collectTransitiveAconfigFiles module=%s", j.Name()))
-	}
-	return j.transitiveAconfigFiles
-}
-
 type sdkLinkType int
 
 const (
diff --git a/java/builder.go b/java/builder.go
index ee7e225..d03c8e5 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -264,6 +264,16 @@
 			Command:     `${config.Zip2ZipCmd} -i ${in} -o ${out} -x 'META-INF/services/**/*'`,
 			CommandDeps: []string{"${config.Zip2ZipCmd}"},
 		})
+
+	writeCombinedProguardFlagsFileRule = pctx.AndroidStaticRule("writeCombinedProguardFlagsFileRule",
+		blueprint.RuleParams{
+			Command: `rm -f $out && ` +
+				`for f in $in; do ` +
+				` echo  && ` +
+				` echo "# including $$f" && ` +
+				` cat $$f; ` +
+				`done > $out`,
+		})
 )
 
 func init() {
@@ -686,6 +696,15 @@
 	})
 }
 
+func writeCombinedProguardFlagsFile(ctx android.ModuleContext, outputFile android.WritablePath, files android.Paths) {
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        writeCombinedProguardFlagsFileRule,
+		Description: "write combined proguard flags file",
+		Inputs:      files,
+		Output:      outputFile,
+	})
+}
+
 type classpath android.Paths
 
 func (x *classpath) formJoinedClassPath(optName string, sep string) string {
diff --git a/java/code_metadata_test.go b/java/code_metadata_test.go
new file mode 100644
index 0000000..4b05d9e
--- /dev/null
+++ b/java/code_metadata_test.go
@@ -0,0 +1,125 @@
+package java
+
+import (
+	"strings"
+	"testing"
+
+	"android/soong/android"
+	soongTesting "android/soong/testing"
+	"android/soong/testing/code_metadata_internal_proto"
+	"google.golang.org/protobuf/proto"
+)
+
+func TestCodeMetadata(t *testing.T) {
+	bp := `code_metadata {
+		name: "module-name",
+		teamId: "12345",
+		code: [
+			"foo",
+		]
+	}
+
+	java_sdk_library {
+		name: "foo",
+		srcs: ["a.java"],
+	}`
+	result := runCodeMetadataTest(t, android.FixtureExpectsNoErrors, bp)
+
+	module := result.ModuleForTests(
+		"module-name", "",
+	).Module().(*soongTesting.CodeMetadataModule)
+
+	// Check that the provider has the right contents
+	data := result.ModuleProvider(
+		module, soongTesting.CodeMetadataProviderKey,
+	).(soongTesting.CodeMetadataProviderData)
+	if !strings.HasSuffix(
+		data.IntermediatePath.String(), "/intermediateCodeMetadata.pb",
+	) {
+		t.Errorf(
+			"Missing intermediates path in provider: %s",
+			data.IntermediatePath.String(),
+		)
+	}
+
+	buildParamsSlice := module.BuildParamsForTests()
+	var metadata = ""
+	for _, params := range buildParamsSlice {
+		if params.Rule.String() == "android/soong/android.writeFile" {
+			metadata = params.Args["content"]
+		}
+	}
+
+	metadataList := make([]*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership, 0, 2)
+	teamId := "12345"
+	bpFilePath := "Android.bp"
+	targetName := "foo"
+	srcFile := []string{"a.java"}
+	expectedMetadataProto := code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership{
+		TrendyTeamId: &teamId,
+		TargetName:   &targetName,
+		Path:         &bpFilePath,
+		SourceFiles:  srcFile,
+	}
+	metadataList = append(metadataList, &expectedMetadataProto)
+
+	CodeMetadataMetadata := code_metadata_internal_proto.CodeMetadataInternal{TargetOwnershipList: metadataList}
+	protoData, _ := proto.Marshal(&CodeMetadataMetadata)
+	rawData := string(protoData)
+	formattedData := strings.ReplaceAll(rawData, "\n", "\\n")
+	expectedMetadata := "'" + formattedData + "\\n'"
+
+	if metadata != expectedMetadata {
+		t.Errorf(
+			"Retrieved metadata: %s is not equal to expectedMetadata: %s", metadata,
+			expectedMetadata,
+		)
+	}
+
+	// Tests for all_test_spec singleton.
+	singleton := result.SingletonForTests("all_code_metadata")
+	rule := singleton.Rule("all_code_metadata_rule")
+	prebuiltOs := result.Config.PrebuiltOS()
+	expectedCmd := "out/soong/host/" + prebuiltOs + "/bin/metadata -rule code_metadata -inputFile out/soong/all_code_metadata_paths.rsp -outputFile out/soong/ownership/all_code_metadata.pb"
+	expectedOutputFile := "out/soong/ownership/all_code_metadata.pb"
+	expectedInputFile := "out/soong/.intermediates/module-name/intermediateCodeMetadata.pb"
+	if !strings.Contains(
+		strings.TrimSpace(rule.Output.String()),
+		expectedOutputFile,
+	) {
+		t.Errorf(
+			"Retrieved singletonOutputFile: %s is not equal to expectedSingletonOutputFile: %s",
+			rule.Output.String(), expectedOutputFile,
+		)
+	}
+
+	if !strings.Contains(
+		strings.TrimSpace(rule.Inputs[0].String()),
+		expectedInputFile,
+	) {
+		t.Errorf(
+			"Retrieved singletonInputFile: %s is not equal to expectedSingletonInputFile: %s",
+			rule.Inputs[0].String(), expectedInputFile,
+		)
+	}
+
+	if !strings.Contains(
+		strings.TrimSpace(rule.RuleParams.Command),
+		expectedCmd,
+	) {
+		t.Errorf(
+			"Retrieved cmd: %s doesn't contain expectedCmd: %s",
+			rule.RuleParams.Command, expectedCmd,
+		)
+	}
+}
+func runCodeMetadataTest(
+		t *testing.T, errorHandler android.FixtureErrorHandler, bp string,
+) *android.TestResult {
+	return android.GroupFixturePreparers(
+		soongTesting.PrepareForTestWithTestingBuildComponents, prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("foo"),
+	).
+		ExtendWithErrorHandler(errorHandler).
+		RunTestWithBp(t, bp)
+}
diff --git a/java/config/config.go b/java/config/config.go
index 83c27d3..0098130 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -133,7 +133,12 @@
 		if override := ctx.Config().Getenv("OVERRIDE_JLINK_VERSION_NUMBER"); override != "" {
 			return override
 		}
-		return "17"
+		switch ctx.Config().Getenv("EXPERIMENTAL_USE_OPENJDK21_TOOLCHAIN") {
+		case "true":
+			return "21"
+		default:
+			return "17"
+		}
 	})
 
 	pctx.SourcePathVariable("JavaToolchain", "${JavaHome}/bin")
diff --git a/java/dex.go b/java/dex.go
index dab0836..6f1c09d 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -91,10 +91,12 @@
 	dexProperties DexProperties
 
 	// list of extra proguard flag files
-	extraProguardFlagFiles android.Paths
-	proguardDictionary     android.OptionalPath
-	proguardConfiguration  android.OptionalPath
-	proguardUsageZip       android.OptionalPath
+	extraProguardFlagsFiles android.Paths
+	proguardDictionary      android.OptionalPath
+	proguardConfiguration   android.OptionalPath
+	proguardUsageZip        android.OptionalPath
+	resourcesInput          android.OptionalPath
+	resourcesOutput         android.OptionalPath
 
 	providesTransitiveHeaderJars
 }
@@ -160,7 +162,7 @@
 		"$r8Template": &remoteexec.REParams{
 			Labels:          map[string]string{"type": "compile", "compiler": "r8"},
 			Inputs:          []string{"$implicits", "${config.R8Jar}"},
-			OutputFiles:     []string{"${outUsage}", "${outConfig}", "${outDict}"},
+			OutputFiles:     []string{"${outUsage}", "${outConfig}", "${outDict}", "${resourcesOutput}"},
 			ExecStrategy:    "${config.RER8ExecStrategy}",
 			ToolchainInputs: []string{"${config.JavaCmd}"},
 			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
@@ -180,7 +182,7 @@
 			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
 		},
 	}, []string{"outDir", "outDict", "outConfig", "outUsage", "outUsageZip", "outUsageDir",
-		"r8Flags", "zipFlags", "mergeZipsFlags"}, []string{"implicits"})
+		"r8Flags", "zipFlags", "mergeZipsFlags", "resourcesOutput"}, []string{"implicits"})
 
 func (d *dexer) dexCommonFlags(ctx android.ModuleContext,
 	dexParams *compileDexParams) (flags []string, deps android.Paths) {
@@ -294,7 +296,7 @@
 		android.PathForSource(ctx, "build/make/core/proguard.flags"),
 	}
 
-	flagFiles = append(flagFiles, d.extraProguardFlagFiles...)
+	flagFiles = append(flagFiles, d.extraProguardFlagsFiles...)
 	// TODO(ccross): static android library proguard files
 
 	flagFiles = append(flagFiles, android.PathsForModuleSrc(ctx, opt.Proguard_flags_files)...)
@@ -349,6 +351,12 @@
 		r8Flags = append(r8Flags, "-ignorewarnings")
 	}
 
+	if d.resourcesInput.Valid() {
+		r8Flags = append(r8Flags, "--resource-input", d.resourcesInput.Path().String())
+		r8Deps = append(r8Deps, d.resourcesInput.Path())
+		r8Flags = append(r8Flags, "--resource-output", d.resourcesOutput.Path().String())
+	}
+
 	return r8Flags, r8Deps
 }
 
@@ -390,6 +398,8 @@
 			android.ModuleNameWithPossibleOverride(ctx), "unused.txt")
 		proguardUsageZip := android.PathForModuleOut(ctx, "proguard_usage.zip")
 		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...)
 		rule := r8
@@ -408,17 +418,22 @@
 			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()
+		}
 		ctx.Build(pctx, android.BuildParams{
-			Rule:        rule,
-			Description: "r8",
-			Output:      javalibJar,
-			ImplicitOutputs: android.WritablePaths{
-				proguardDictionary,
-				proguardUsageZip,
-				proguardConfiguration},
-			Input:     dexParams.classesJar,
-			Implicits: r8Deps,
-			Args:      args,
+			Rule:            rule,
+			Description:     "r8",
+			Output:          javalibJar,
+			ImplicitOutputs: implicitOutputs,
+			Input:           dexParams.classesJar,
+			Implicits:       r8Deps,
+			Args:            args,
 		})
 	} else {
 		d8Flags, d8Deps := d8Flags(dexParams.flags)
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 998730e..fe8c5fb 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -374,8 +374,6 @@
 		PreoptBootClassPathDexFiles:     dexFiles.Paths(),
 		PreoptBootClassPathDexLocations: dexLocations,
 
-		PreoptExtractedApk: false,
-
 		NoCreateAppImage:    !BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, true),
 		ForceCreateAppImage: BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, false),
 
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 180ba92..6b8d21f 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -19,7 +19,6 @@
 	"path/filepath"
 	"regexp"
 	"sort"
-	"strconv"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
@@ -500,18 +499,20 @@
 	if metalavaUseRbe(ctx) {
 		rule.Remoteable(android.RemoteRuleSupports{RBE: true})
 		execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
-		compare, _ := strconv.ParseBool(ctx.Config().GetenvWithDefault("RBE_METALAVA_COMPARE", "false"))
+		compare := ctx.Config().IsEnvTrue("RBE_METALAVA_COMPARE")
+		remoteUpdateCache := !ctx.Config().IsEnvFalse("RBE_METALAVA_REMOTE_UPDATE_CACHE")
 		labels := map[string]string{"type": "tool", "name": "metalava"}
 		// TODO: metalava pool rejects these jobs
 		pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "java16")
 		rule.Rewrapper(&remoteexec.REParams{
-			Labels:          labels,
-			ExecStrategy:    execStrategy,
-			ToolchainInputs: []string{config.JavaCmd(ctx).String()},
-			Platform:        map[string]string{remoteexec.PoolKey: pool},
-			Compare:         compare,
-			NumLocalRuns:    1,
-			NumRemoteRuns:   1,
+			Labels:              labels,
+			ExecStrategy:        execStrategy,
+			ToolchainInputs:     []string{config.JavaCmd(ctx).String()},
+			Platform:            map[string]string{remoteexec.PoolKey: pool},
+			Compare:             compare,
+			NumLocalRuns:        1,
+			NumRemoteRuns:       1,
+			NoRemoteUpdateCache: !remoteUpdateCache,
 		})
 	}
 
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index f86e1ac..7b89dde 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -84,7 +84,7 @@
 	for _, c := range testcases {
 		m := ctx.ModuleForTests(c.moduleName, "android_common")
 		manifest := m.Output("metalava.sbox.textproto")
-		sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest)
+		sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, manifest)
 		cmdline := String(sboxProto.Commands[0].Command)
 		android.AssertStringContainsEquals(t, "api-versions generation flag", cmdline, "--generate-api-levels", c.generate_xml)
 		if c.expectedJarFilename != "" {
@@ -133,7 +133,7 @@
 
 	m := ctx.ModuleForTests("foo-stubs", "android_common")
 	manifest := m.Output("metalava.sbox.textproto")
-	cmd := String(android.RuleBuilderSboxProtoForTests(t, manifest).Commands[0].Command)
+	cmd := String(android.RuleBuilderSboxProtoForTests(t, ctx, manifest).Commands[0].Command)
 	r := regexp.MustCompile(`--android-jar-pattern [^ ]+/android.jar`)
 	return r.FindAllString(cmd, -1)
 }
@@ -212,7 +212,7 @@
 		t.Errorf("Expected inputs %q, got %q", w, g)
 	}
 
-	manifest := android.RuleBuilderSboxProtoForTests(t, m.Output("metalava.sbox.textproto"))
+	manifest := android.RuleBuilderSboxProtoForTests(t, ctx, m.Output("metalava.sbox.textproto"))
 	if g, w := manifest.Commands[0].GetCommand(), "reference __SBOX_SANDBOX_DIR__/out/.intermediates/foo/gen/foo.txt"; !strings.Contains(g, w) {
 		t.Errorf("Expected command to contain %q, got %q", w, g)
 	}
@@ -302,7 +302,7 @@
 		})
 	m := ctx.ModuleForTests("baz-stubs", "android_common")
 	manifest := m.Output("metalava.sbox.textproto")
-	cmdline := String(android.RuleBuilderSboxProtoForTests(t, manifest).Commands[0].Command)
+	cmdline := String(android.RuleBuilderSboxProtoForTests(t, ctx, manifest).Commands[0].Command)
 	android.AssertStringDoesContain(t, "sdk-extensions-root present", cmdline, "--sdk-extensions-root sdk/extensions")
 	android.AssertStringDoesContain(t, "sdk-extensions-info present", cmdline, "--sdk-extensions-info sdk/extensions/info.txt")
 }
@@ -434,6 +434,6 @@
 
 	m := result.ModuleForTests("foo", "android_common")
 	manifest := m.Output("metalava.sbox.textproto")
-	cmdline := String(android.RuleBuilderSboxProtoForTests(t, manifest).Commands[0].Command)
+	cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command)
 	android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "--hide-annotation android.annotation.FlaggedApi")
 }
diff --git a/java/generated_java_library_test.go b/java/generated_java_library_test.go
index 7fbbfee..ac9524e 100644
--- a/java/generated_java_library_test.go
+++ b/java/generated_java_library_test.go
@@ -37,7 +37,6 @@
 }
 
 func (callbacks *JavaGenLibTestCallbacks) GenerateSourceJarBuildActions(module *GeneratedJavaLibraryModule, ctx android.ModuleContext) android.Path {
-	module.AddAconfigIntermediate(android.PathForOutput(ctx, "aconfig_cache_file"))
 	return android.PathForOutput(ctx, "blah.srcjar")
 }
 
diff --git a/java/java.go b/java/java.go
index bb9357c..2236d05 100644
--- a/java/java.go
+++ b/java/java.go
@@ -297,15 +297,6 @@
 	// JacocoReportClassesFile is the path to a jar containing uninstrumented classes that will be
 	// instrumented by jacoco.
 	JacocoReportClassesFile android.Path
-
-	// set of aconfig flags for all transitive libs deps
-	// TODO(joeo): It would be nice if this were over in the aconfig package instead of here.
-	// In order to do that, generated_java_library would need a way doing
-	// collectTransitiveAconfigFiles with one of the callbacks, and having that automatically
-	// propagated. If we were to clean up more of the stuff on JavaInfo that's not part of
-	// core java rules (e.g. AidlIncludeDirs), then maybe adding more framework to do that would be
-	// worth it.
-	TransitiveAconfigFiles *android.DepSet[android.Path]
 }
 
 var JavaInfoProvider = blueprint.NewProvider(JavaInfo{})
@@ -639,7 +630,7 @@
 type Library struct {
 	Module
 
-	exportedProguardFlagFiles android.Paths
+	combinedExportedProguardFlagsFile android.Path
 
 	InstallMixin func(ctx android.ModuleContext, installPath android.Path) (extraInstallDeps android.InstallPaths)
 }
@@ -700,8 +691,12 @@
 
 	proguardSpecInfo := j.collectProguardSpecInfo(ctx)
 	ctx.SetProvider(ProguardSpecInfoProvider, proguardSpecInfo)
-	j.exportedProguardFlagFiles = proguardSpecInfo.ProguardFlagsFiles.ToList()
-	j.extraProguardFlagFiles = append(j.extraProguardFlagFiles, j.exportedProguardFlagFiles...)
+	exportedProguardFlagsFiles := proguardSpecInfo.ProguardFlagsFiles.ToList()
+	j.extraProguardFlagsFiles = append(j.extraProguardFlagsFiles, exportedProguardFlagsFiles...)
+
+	combinedExportedProguardFlagFile := android.PathForModuleOut(ctx, "export_proguard_flags")
+	writeCombinedProguardFlagsFile(ctx, combinedExportedProguardFlagFile, exportedProguardFlagsFiles)
+	j.combinedExportedProguardFlagsFile = combinedExportedProguardFlagFile
 
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 	if !apexInfo.IsForPlatform() {
diff --git a/java/java_test.go b/java/java_test.go
index 81119a7..0c750eb 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1921,7 +1921,7 @@
 	for _, c := range testcases {
 		m := ctx.ModuleForTests(c.moduleName, "android_common")
 		manifest := m.Output("metalava.sbox.textproto")
-		sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest)
+		sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, manifest)
 		manifestCommand := sboxProto.Commands[0].GetCommand()
 		sourceFilesFlag := "--source-files " + strings.Join(c.sourceTextFileDirs, " ")
 		android.AssertStringDoesContain(t, "source text files not present", manifestCommand, sourceFilesFlag)
@@ -2026,7 +2026,7 @@
 	for _, c := range testcases {
 		m := ctx.ModuleForTests(c.moduleName, "android_common")
 		manifest := m.Output("metalava.sbox.textproto")
-		sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest)
+		sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, manifest)
 		manifestCommand := sboxProto.Commands[0].GetCommand()
 		sourceFilesFlag := "--source-files " + strings.Join(c.sourceTextFileDirs, " ")
 		android.AssertStringDoesContain(t, "source text files not present", manifestCommand, sourceFilesFlag)
@@ -2316,7 +2316,7 @@
 
 	m := ctx.ModuleForTests("bar1", "android_common")
 	manifest := m.Output("metalava.sbox.textproto")
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest)
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, manifest)
 	manifestCommand := sboxProto.Commands[0].GetCommand()
 	android.AssertStringDoesContain(t, "Command expected to contain full_api_surface_stub output jar", manifestCommand, "lib1.jar")
 }
@@ -2480,7 +2480,7 @@
 	`)
 	m := ctx.ModuleForTests("foo", "android_common")
 	manifest := m.Output("metalava.sbox.textproto")
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest)
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, manifest)
 	manifestCommand := sboxProto.Commands[0].GetCommand()
 	sourceFilesFlag := "--source-files current.txt"
 	android.AssertStringDoesContain(t, "source text files not present", manifestCommand, sourceFilesFlag)
@@ -2501,7 +2501,7 @@
 	`)
 	m := ctx.ModuleForTests("foo", "android_common")
 	manifest := m.Output("metalava.sbox.textproto")
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest)
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, manifest)
 	manifestCommand := sboxProto.Commands[0].GetCommand()
 
 	// Api files are sorted from the narrowest api scope to the widest api scope.
@@ -2543,7 +2543,7 @@
 	`)
 	m := result.ModuleForTests(apiScopePublic.apiLibraryModuleName("foo"), "android_common")
 	manifest := m.Output("metalava.sbox.textproto")
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest)
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest)
 	manifestCommand := sboxProto.Commands[0].GetCommand()
 	classPathFlag := "--classpath __SBOX_SANDBOX_DIR__/out/.intermediates/bar/android_common/turbine-combined/bar.jar"
 	android.AssertStringDoesContain(t, "command expected to contain classpath flag", manifestCommand, classPathFlag)
diff --git a/java/lint_test.go b/java/lint_test.go
index ec901aa..5e6b8bc 100644
--- a/java/lint_test.go
+++ b/java/lint_test.go
@@ -39,7 +39,7 @@
 
 	foo := ctx.ModuleForTests("foo", "android_common")
 
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, foo.Output("lint.sbox.textproto"))
 	if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml") {
 		t.Error("did not pass --baseline flag")
 	}
@@ -61,7 +61,7 @@
 
 	foo := ctx.ModuleForTests("foo", "android_common")
 
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, foo.Output("lint.sbox.textproto"))
 	if strings.Contains(*sboxProto.Commands[0].Command, "--baseline") {
 		t.Error("passed --baseline flag for non existent file")
 	}
@@ -108,7 +108,7 @@
 
 	foo := ctx.ModuleForTests("foo", "android_common")
 
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, foo.Output("lint.sbox.textproto"))
 	if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline mybaseline.xml") {
 		t.Error("did not use the correct file for baseline")
 	}
@@ -276,7 +276,7 @@
 			RunTestWithBp(t, thisBp)
 
 		foo := result.ModuleForTests("foo", "android_common")
-		sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
+		sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, foo.Output("lint.sbox.textproto"))
 		if !strings.Contains(*sboxProto.Commands[0].Command, "/"+testCase.expected_file) {
 			t.Error("did not use full api database for case", testCase)
 		}
diff --git a/java/resourceshrinker.go b/java/resourceshrinker.go
deleted file mode 100644
index af13aa3..0000000
--- a/java/resourceshrinker.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2022 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package java
-
-import (
-	"android/soong/android"
-
-	"github.com/google/blueprint"
-)
-
-var shrinkResources = pctx.AndroidStaticRule("shrinkResources",
-	blueprint.RuleParams{
-		// Note that we suppress stdout to avoid successful log confirmations.
-		Command: `RESOURCESHRINKER_OPTS=-Dcom.android.tools.r8.dexContainerExperiment ` +
-			`${config.ResourceShrinkerCmd} --output $out --input $in --raw_resources $raw_resources >/dev/null`,
-		CommandDeps: []string{"${config.ResourceShrinkerCmd}"},
-	}, "raw_resources")
-
-func ShrinkResources(ctx android.ModuleContext, apk android.Path, outputFile android.WritablePath) {
-	protoFile := android.PathForModuleOut(ctx, apk.Base()+".proto.apk")
-	aapt2Convert(ctx, protoFile, apk, "proto")
-	strictModeFile := android.PathForSource(ctx, "prebuilts/cmdline-tools/shrinker.xml")
-	protoOut := android.PathForModuleOut(ctx, apk.Base()+".proto.out.apk")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:   shrinkResources,
-		Input:  protoFile,
-		Output: protoOut,
-		Args: map[string]string{
-			"raw_resources": strictModeFile.String(),
-		},
-	})
-	aapt2Convert(ctx, outputFile, protoOut, "binary")
-}
diff --git a/java/resourceshrinker_test.go b/java/resourceshrinker_test.go
deleted file mode 100644
index 3bbf116..0000000
--- a/java/resourceshrinker_test.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2022 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package java
-
-import (
-	"testing"
-
-	"android/soong/android"
-)
-
-func TestShrinkResourcesArgs(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		PrepareForTestWithJavaDefaultModules,
-	).RunTestWithBp(t, `
-		android_app {
-			name: "app_shrink",
-			platform_apis: true,
-			optimize: {
-				shrink_resources: true,
-			}
-		}
-
-		android_app {
-			name: "app_no_shrink",
-			platform_apis: true,
-			optimize: {
-				shrink_resources: false,
-			}
-		}
-	`)
-
-	appShrink := result.ModuleForTests("app_shrink", "android_common")
-	appShrinkResources := appShrink.Rule("shrinkResources")
-	android.AssertStringDoesContain(t, "expected shrinker.xml in app_shrink resource shrinker flags",
-		appShrinkResources.Args["raw_resources"], "shrinker.xml")
-
-	appNoShrink := result.ModuleForTests("app_no_shrink", "android_common")
-	if appNoShrink.MaybeRule("shrinkResources").Rule != nil {
-		t.Errorf("unexpected shrinkResources rule for app_no_shrink")
-	}
-}
diff --git a/java/test_spec_test.go b/java/test_spec_test.go
index 39aff4c..7f06785 100644
--- a/java/test_spec_test.go
+++ b/java/test_spec_test.go
@@ -27,7 +27,7 @@
 	java_test {
 		name: "java-test-module-name-two",
 	}`
-	result := runTest(t, android.FixtureExpectsNoErrors, bp)
+	result := runTestSpecTest(t, android.FixtureExpectsNoErrors, bp)
 
 	module := result.ModuleForTests(
 		"module-name", "",
@@ -78,7 +78,7 @@
 
 	if metadata != expectedMetadata {
 		t.Errorf(
-			"Retrieved metadata: %s is not equal to expectedMetadata: %s", metadata,
+			"Retrieved metadata: %s doesn't contain expectedMetadata: %s", metadata,
 			expectedMetadata,
 		)
 	}
@@ -121,11 +121,11 @@
 	}
 }
 
-func runTest(
-	t *testing.T, errorHandler android.FixtureErrorHandler, bp string,
+func runTestSpecTest(
+		t *testing.T, errorHandler android.FixtureErrorHandler, bp string,
 ) *android.TestResult {
 	return android.GroupFixturePreparers(
-		soongTesting.PrepareForTestWithTestSpecBuildComponents,
+		soongTesting.PrepareForTestWithTestingBuildComponents,
 		PrepareForIntegrationTestWithJava,
 	).
 		ExtendWithErrorHandler(errorHandler).
diff --git a/python/python.go b/python/python.go
index 7d77ca7..b4617c8 100644
--- a/python/python.go
+++ b/python/python.go
@@ -430,6 +430,7 @@
 // GenerateAndroidBuildActions performs build actions common to all Python modules
 func (p *PythonLibraryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	expandedSrcs := android.PathsForModuleSrcExcludes(ctx, p.properties.Srcs, p.properties.Exclude_srcs)
+	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: expandedSrcs.Strings()})
 
 	// expand data files from "data" property.
 	expandedData := android.PathsForModuleSrc(ctx, p.properties.Data)
diff --git a/python/test.go b/python/test.go
index cd7c73b..41c5538 100644
--- a/python/test.go
+++ b/python/test.go
@@ -188,8 +188,6 @@
 		panic(fmt.Errorf("unknown python test runner '%s', should be 'tradefed' or 'mobly'", runner))
 	}
 
-	p.installedDest = ctx.InstallFile(installDir(ctx, "nativetest", "nativetest64", ctx.ModuleName()), p.installSource.Base(), p.installSource)
-
 	for _, dataSrcPath := range android.PathsForModuleSrc(ctx, p.testProperties.Data) {
 		p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath})
 	}
@@ -206,6 +204,11 @@
 			p.data = append(p.data, android.DataPath{SrcPath: javaDataSrcPath})
 		}
 	}
+
+	installDir := installDir(ctx, "nativetest", "nativetest64", ctx.ModuleName())
+	installedData := ctx.InstallTestData(installDir, p.data)
+	p.installedDest = ctx.InstallFile(installDir, p.installSource.Base(), p.installSource, installedData...)
+
 	ctx.SetProvider(testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 }
 
@@ -227,8 +230,6 @@
 
 			entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
 
-			entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...)
-
 			p.testProperties.Test_options.SetAndroidMkEntries(entries)
 		})
 
diff --git a/remoteexec/remoteexec.go b/remoteexec/remoteexec.go
index 1e181fb..8294c3f 100644
--- a/remoteexec/remoteexec.go
+++ b/remoteexec/remoteexec.go
@@ -91,6 +91,8 @@
 	NumLocalRuns int
 	// Number of times the action should be rerun remotely.
 	NumRemoteRuns int
+	// Boolean indicating whether to update remote cache entry. Rewrapper defaults to true, so the name is negated here.
+	NoRemoteUpdateCache bool
 }
 
 func init() {
@@ -146,6 +148,10 @@
 		args += fmt.Sprintf(" --compare=true --num_local_reruns=%d --num_remote_reruns=%d", r.NumLocalRuns, r.NumRemoteRuns)
 	}
 
+	if r.NoRemoteUpdateCache {
+		args += " --remote_update_cache=false"
+	}
+
 	if len(r.Inputs) > 0 {
 		args += " --inputs=" + strings.Join(r.Inputs, ",")
 	}
diff --git a/rust/androidmk.go b/rust/androidmk.go
index c684e81..e02c3f6 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -18,7 +18,6 @@
 	"path/filepath"
 
 	"android/soong/android"
-	"android/soong/cc"
 )
 
 type AndroidMkContext interface {
@@ -114,8 +113,6 @@
 
 			test.Properties.Test_options.SetAndroidMkEntries(entries)
 		})
-
-	cc.AndroidMkWriteTestData(test.data, ret)
 }
 
 func (benchmark *benchmarkDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
@@ -216,33 +213,9 @@
 func (fuzz *fuzzDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
 	ctx.SubAndroidMk(ret, fuzz.binaryDecorator)
 
-	var fuzzFiles []string
-	for _, d := range fuzz.fuzzPackagedModule.Corpus {
-		fuzzFiles = append(fuzzFiles,
-			filepath.Dir(fuzz.fuzzPackagedModule.CorpusIntermediateDir.String())+":corpus/"+d.Base())
-	}
-
-	for _, d := range fuzz.fuzzPackagedModule.Data {
-		fuzzFiles = append(fuzzFiles,
-			filepath.Dir(fuzz.fuzzPackagedModule.DataIntermediateDir.String())+":data/"+d.Rel())
-	}
-
-	if fuzz.fuzzPackagedModule.Dictionary != nil {
-		fuzzFiles = append(fuzzFiles,
-			filepath.Dir(fuzz.fuzzPackagedModule.Dictionary.String())+":"+fuzz.fuzzPackagedModule.Dictionary.Base())
-	}
-
-	if fuzz.fuzzPackagedModule.Config != nil {
-		fuzzFiles = append(fuzzFiles,
-			filepath.Dir(fuzz.fuzzPackagedModule.Config.String())+":config.json")
-	}
-
 	ret.ExtraEntries = append(ret.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext,
 		entries *android.AndroidMkEntries) {
 		entries.SetBool("LOCAL_IS_FUZZ_TARGET", true)
-		if len(fuzzFiles) > 0 {
-			entries.AddStrings("LOCAL_TEST_DATA", fuzzFiles...)
-		}
 		if fuzz.installedSharedDeps != nil {
 			entries.AddStrings("LOCAL_FUZZ_INSTALLED_SHARED_DEPS", fuzz.installedSharedDeps...)
 		}
diff --git a/rust/bindgen.go b/rust/bindgen.go
index ffe532f..1cc1574 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -174,6 +174,15 @@
 		cflags = append(cflags, "-D__ANDROID_VNDK__")
 		if ctx.RustModule().InVendor() {
 			cflags = append(cflags, "-D__ANDROID_VENDOR__")
+
+			vendorApiLevel := ctx.Config().VendorApiLevel()
+			if vendorApiLevel == "" {
+				// TODO(b/314036847): This is a fallback for UDC targets.
+				// This must be a build failure when UDC is no longer built
+				// from this source tree.
+				vendorApiLevel = ctx.Config().PlatformSdkVersion().String()
+			}
+			cflags = append(cflags, "-D__ANDROID_VENDOR_API__="+vendorApiLevel)
 		} else if ctx.RustModule().InProduct() {
 			cflags = append(cflags, "-D__ANDROID_PRODUCT__")
 		}
diff --git a/rust/compiler.go b/rust/compiler.go
index d453a5d..a8c5473 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -239,6 +239,8 @@
 
 	distFile android.OptionalPath
 
+	installDeps android.InstallPaths
+
 	// unstripped output file.
 	unstrippedOutputFile android.Path
 
@@ -538,7 +540,12 @@
 
 func (compiler *baseCompiler) install(ctx ModuleContext) {
 	path := ctx.RustModule().OutputFile()
-	compiler.path = ctx.InstallFile(compiler.installDir(ctx), path.Path().Base(), path.Path())
+	compiler.path = ctx.InstallFile(compiler.installDir(ctx), path.Path().Base(), path.Path(), compiler.installDeps...)
+}
+
+func (compiler *baseCompiler) installTestData(ctx ModuleContext, data []android.DataPath) {
+	installedData := ctx.InstallTestData(compiler.installDir(ctx), data)
+	compiler.installDeps = append(compiler.installDeps, installedData...)
 }
 
 func (compiler *baseCompiler) getStem(ctx ModuleContext) string {
diff --git a/rust/config/global.go b/rust/config/global.go
index 64c9460..3e189b5 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -25,7 +25,7 @@
 	pctx         = android.NewPackageContext("android/soong/rust/config")
 	ExportedVars = android.NewExportedVariables(pctx)
 
-	RustDefaultVersion = "1.72.1"
+	RustDefaultVersion = "1.73.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
@@ -72,6 +72,8 @@
 		"-C panic=abort",
 		// Generate additional debug info for AutoFDO
 		"-Z debug-info-for-profiling",
+		// Android has ELF TLS on platform
+		"-Z tls-model=global-dynamic",
 	}
 
 	deviceGlobalLinkFlags = []string{
diff --git a/rust/fuzz.go b/rust/fuzz.go
index 4c04ce8..bacfa2d 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -15,12 +15,11 @@
 package rust
 
 import (
-	"path/filepath"
-
 	"android/soong/android"
 	"android/soong/cc"
 	"android/soong/fuzz"
 	"android/soong/rust/config"
+	"path/filepath"
 )
 
 func init() {
@@ -131,12 +130,6 @@
 }
 
 func (fuzz *fuzzDecorator) install(ctx ModuleContext) {
-	fuzz.binaryDecorator.baseCompiler.dir = filepath.Join(
-		"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
-	fuzz.binaryDecorator.baseCompiler.dir64 = filepath.Join(
-		"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
-	fuzz.binaryDecorator.baseCompiler.install(ctx)
-
 	fuzz.fuzzPackagedModule = cc.PackageFuzzModule(ctx, fuzz.fuzzPackagedModule, pctx)
 
 	installBase := "fuzz"
@@ -157,4 +150,30 @@
 				cc.SharedLibrarySymbolsInstallLocation(install, installBase, ctx.Arch().ArchType.String()))
 		}
 	}
+
+	var fuzzData []android.DataPath
+	for _, d := range fuzz.fuzzPackagedModule.Corpus {
+		fuzzData = append(fuzzData, android.DataPath{SrcPath: d, RelativeInstallPath: "corpus", WithoutRel: true})
+	}
+
+	for _, d := range fuzz.fuzzPackagedModule.Data {
+		fuzzData = append(fuzzData, android.DataPath{SrcPath: d, RelativeInstallPath: "data"})
+	}
+
+	if d := fuzz.fuzzPackagedModule.Dictionary; d != nil {
+		fuzzData = append(fuzzData, android.DataPath{SrcPath: d, WithoutRel: true})
+	}
+
+	if d := fuzz.fuzzPackagedModule.Config; d != nil {
+		fuzzData = append(fuzzData, android.DataPath{SrcPath: d, WithoutRel: true})
+	}
+
+	fuzz.binaryDecorator.baseCompiler.dir = filepath.Join(
+		"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
+	fuzz.binaryDecorator.baseCompiler.dir64 = filepath.Join(
+		"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
+	fuzz.binaryDecorator.baseCompiler.installTestData(ctx, fuzzData)
+
+	fuzz.binaryDecorator.baseCompiler.install(ctx)
+
 }
diff --git a/rust/rust.go b/rust/rust.go
index d4d33c7..3d51a13 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -951,6 +951,7 @@
 			sourceLib := sourceMod.(*Module).compiler.(*libraryDecorator)
 			mod.sourceProvider.setOutputFiles(sourceLib.sourceProvider.Srcs())
 		}
+		ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: mod.sourceProvider.Srcs().Strings()})
 	}
 
 	if mod.compiler != nil && !mod.compiler.Disabled() {
diff --git a/rust/test.go b/rust/test.go
index 7ffc367..6619af6 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -189,6 +189,7 @@
 	if ctx.Host() && test.Properties.Test_options.Unit_test == nil {
 		test.Properties.Test_options.Unit_test = proptools.BoolPtr(true)
 	}
+	test.binaryDecorator.installTestData(ctx, test.data)
 	test.binaryDecorator.install(ctx)
 }
 
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 1bebc60..7962749 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"path/filepath"
-	"sort"
 	"strings"
 
 	"android/soong/testing"
@@ -174,7 +173,7 @@
 
 	installDir android.InstallPath
 
-	data       android.Paths
+	data       []android.DataPath
 	testConfig android.Path
 
 	dataModules map[string]android.Path
@@ -273,6 +272,7 @@
 		Output: s.outputFilePath,
 		Input:  s.sourceFilePath,
 	})
+	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: []string{s.sourceFilePath.String()}})
 }
 
 func (s *ShBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -360,10 +360,21 @@
 		return
 	}
 	s.dataModules[relPath] = path
+	s.data = append(s.data, android.DataPath{SrcPath: path})
 }
 
 func (s *ShTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	s.ShBinary.generateAndroidBuildActions(ctx)
+
+	expandedData := android.PathsForModuleSrc(ctx, s.testProperties.Data)
+	// Emulate the data property for java_data dependencies.
+	for _, javaData := range ctx.GetDirectDepsWithTag(shTestJavaDataTag) {
+		expandedData = append(expandedData, android.OutputFilesForModule(ctx, javaData, "")...)
+	}
+	for _, d := range expandedData {
+		s.data = append(s.data, android.DataPath{SrcPath: d})
+	}
+
 	testDir := "nativetest"
 	if ctx.Target().Arch.ArchType.Multilib == "lib64" {
 		testDir = "nativetest64"
@@ -380,15 +391,6 @@
 	} else {
 		s.installDir = android.PathForModuleInstall(ctx, testDir, s.Name())
 	}
-	s.installedFile = ctx.InstallExecutable(s.installDir, s.outputFilePath.Base(), s.outputFilePath)
-
-	expandedData := android.PathsForModuleSrc(ctx, s.testProperties.Data)
-
-	// Emulate the data property for java_data dependencies.
-	for _, javaData := range ctx.GetDirectDepsWithTag(shTestJavaDataTag) {
-		expandedData = append(expandedData, android.OutputFilesForModule(ctx, javaData, "")...)
-	}
-	s.data = expandedData
 
 	var configs []tradefed.Config
 	if Bool(s.testProperties.Require_root) {
@@ -437,7 +439,7 @@
 				if _, exist := s.dataModules[relPath]; exist {
 					return
 				}
-				relocatedLib := android.PathForModuleOut(ctx, "relocated", relPath)
+				relocatedLib := android.PathForModuleOut(ctx, "relocated").Join(ctx, relPath)
 				ctx.Build(pctx, android.BuildParams{
 					Rule:   android.Cp,
 					Input:  cc.OutputFile().Path(),
@@ -453,6 +455,10 @@
 			ctx.PropertyErrorf(property, "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep))
 		}
 	})
+
+	installedData := ctx.InstallTestData(s.installDir, s.data)
+	s.installedFile = ctx.InstallExecutable(s.installDir, s.outputFilePath.Base(), s.outputFilePath, installedData...)
+
 	ctx.SetProvider(testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 }
 
@@ -473,24 +479,6 @@
 				if s.testConfig != nil {
 					entries.SetPath("LOCAL_FULL_TEST_CONFIG", s.testConfig)
 				}
-				for _, d := range s.data {
-					rel := d.Rel()
-					path := d.String()
-					if !strings.HasSuffix(path, rel) {
-						panic(fmt.Errorf("path %q does not end with %q", path, rel))
-					}
-					path = strings.TrimSuffix(path, rel)
-					entries.AddStrings("LOCAL_TEST_DATA", path+":"+rel)
-				}
-				relPaths := make([]string, 0)
-				for relPath, _ := range s.dataModules {
-					relPaths = append(relPaths, relPath)
-				}
-				sort.Strings(relPaths)
-				for _, relPath := range relPaths {
-					dir := strings.TrimSuffix(s.dataModules[relPath].String(), relPath)
-					entries.AddStrings("LOCAL_TEST_DATA", dir+":"+relPath)
-				}
 				if s.testProperties.Data_bins != nil {
 					entries.AddStrings("LOCAL_TEST_DATA_BINS", s.testProperties.Data_bins...)
 				}
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
index 5fcb58d..37450b0 100644
--- a/sh/sh_binary_test.go
+++ b/sh/sh_binary_test.go
@@ -135,7 +135,7 @@
 		if arch == "darwin_x86_64" {
 			libExt = ".dylib"
 		}
-		relocated := variant.Output("relocated/lib64/libbar" + libExt)
+		relocated := variant.Output(filepath.Join("out/soong/.intermediates/foo", arch, "relocated/lib64/libbar"+libExt))
 		expectedInput := "out/soong/.intermediates/libbar/" + arch + "_shared/libbar" + libExt
 		android.AssertPathRelativeToTopEquals(t, "relocation input", expectedInput, relocated.Input)
 
@@ -204,18 +204,19 @@
 	`)
 
 	buildOS := config.BuildOS.String()
-	variant := ctx.ModuleForTests("foo", buildOS+"_x86_64")
+	variant := buildOS + "_x86_64"
+	foo := ctx.ModuleForTests("foo", variant)
 
-	relocated := variant.Output("relocated/lib64/libbar.so")
+	relocated := foo.Output(filepath.Join("out/soong/.intermediates/foo", variant, "relocated/lib64/libbar.so"))
 	expectedInput := "out/soong/.intermediates/libbar/android_arm64_armv8-a_shared/libbar.so"
 	android.AssertPathRelativeToTopEquals(t, "relocation input", expectedInput, relocated.Input)
 
-	mod := variant.Module().(*ShTest)
+	mod := foo.Module().(*ShTest)
 	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
 	expectedData := []string{
 		"out/soong/.intermediates/bar/android_arm64_armv8-a/:bar",
 		// libbar has been relocated, and so has a variant that matches the host arch.
-		"out/soong/.intermediates/foo/" + buildOS + "_x86_64/relocated/:lib64/libbar.so",
+		"out/soong/.intermediates/foo/" + variant + "/relocated/:lib64/libbar.so",
 	}
 	actualData := entries.EntryMap["LOCAL_TEST_DATA"]
 	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_TEST_DATA", config, expectedData, actualData)
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 8bf5f14..013624f 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -254,12 +254,13 @@
 // generated java_library will depend on these API files.
 func (m *syspropLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	baseModuleName := m.BaseModuleName()
-
-	for _, syspropFile := range android.PathsForModuleSrc(ctx, m.properties.Srcs) {
+	srcs := android.PathsForModuleSrc(ctx, m.properties.Srcs)
+	for _, syspropFile := range srcs {
 		if syspropFile.Ext() != ".sysprop" {
 			ctx.PropertyErrorf("srcs", "srcs contains non-sysprop file %q", syspropFile.String())
 		}
 	}
+	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()})
 
 	if ctx.Failed() {
 		return
@@ -277,7 +278,7 @@
 	rule.Command().
 		BuiltTool("sysprop_api_dump").
 		Output(m.dumpedApiFile).
-		Inputs(android.PathsForModuleSrc(ctx, m.properties.Srcs))
+		Inputs(srcs)
 	rule.Build(baseModuleName+"_api_dump", baseModuleName+" api dump")
 
 	// check API rule
diff --git a/testing/Android.bp b/testing/Android.bp
index 18dccb3..43040b0 100644
--- a/testing/Android.bp
+++ b/testing/Android.bp
@@ -8,11 +8,14 @@
     deps: [
         "blueprint",
         "soong-android",
+        "soong-testing-code_metadata_internal_proto",
         "soong-testing-test_spec_proto",
 
     ],
     srcs: [
+        "all_code_metadata.go",
         "all_test_specs.go",
+        "code_metadata.go",
         "test_spec.go",
         "init.go",
         "test.go",
diff --git a/testing/OWNERS b/testing/OWNERS
new file mode 100644
index 0000000..03bcdf1
--- /dev/null
+++ b/testing/OWNERS
@@ -0,0 +1,4 @@
+dariofreni@google.com
+joeo@google.com
+ronish@google.com
+caditya@google.com
diff --git a/testing/all_code_metadata.go b/testing/all_code_metadata.go
new file mode 100644
index 0000000..16d7aae
--- /dev/null
+++ b/testing/all_code_metadata.go
@@ -0,0 +1,51 @@
+package testing
+
+import (
+	"android/soong/android"
+)
+
+const fileContainingCodeMetadataFilePaths = "all_code_metadata_paths.rsp"
+const allCodeMetadataFile = "all_code_metadata.pb"
+
+func AllCodeMetadataFactory() android.Singleton {
+	return &allCodeMetadataSingleton{}
+}
+
+type allCodeMetadataSingleton struct {
+	// Path where the collected metadata is stored after successful validation.
+	outputPath android.OutputPath
+}
+
+func (this *allCodeMetadataSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	var intermediateMetadataPaths android.Paths
+
+	ctx.VisitAllModules(
+		func(module android.Module) {
+			if !ctx.ModuleHasProvider(module, CodeMetadataProviderKey) {
+				return
+			}
+			intermediateMetadataPaths = append(
+				intermediateMetadataPaths, ctx.ModuleProvider(
+					module, CodeMetadataProviderKey,
+				).(CodeMetadataProviderData).IntermediatePath,
+			)
+		},
+	)
+
+	rspFile := android.PathForOutput(ctx, fileContainingCodeMetadataFilePaths)
+	this.outputPath = android.PathForOutput(ctx, ownershipDirectory, allCodeMetadataFile)
+
+	rule := android.NewRuleBuilder(pctx, ctx)
+	cmd := rule.Command().
+		BuiltTool("metadata").
+		FlagWithArg("-rule ", "code_metadata").
+		FlagWithRspFileInputList("-inputFile ", rspFile, intermediateMetadataPaths)
+	cmd.FlagWithOutput("-outputFile ", this.outputPath)
+	rule.Build("all_code_metadata_rule", "Generate all code metadata")
+
+	ctx.Phony("all_code_metadata", this.outputPath)
+}
+
+func (this *allCodeMetadataSingleton) MakeVars(ctx android.MakeVarsContext) {
+	ctx.DistForGoal("code_metadata", this.outputPath)
+}
diff --git a/testing/code_metadata.go b/testing/code_metadata.go
new file mode 100644
index 0000000..4550283
--- /dev/null
+++ b/testing/code_metadata.go
@@ -0,0 +1,139 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package testing
+
+import (
+	"path/filepath"
+
+	"android/soong/android"
+	"android/soong/testing/code_metadata_internal_proto"
+	"github.com/google/blueprint"
+	"google.golang.org/protobuf/proto"
+)
+
+func CodeMetadataFactory() android.Module {
+	module := &CodeMetadataModule{}
+
+	android.InitAndroidModule(module)
+	android.InitDefaultableModule(module)
+	module.AddProperties(&module.properties)
+
+	return module
+}
+
+type CodeMetadataModule struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+	android.BazelModuleBase
+
+	// Properties for "code_metadata"
+	properties struct {
+		// Specifies the name of the code_config.
+		Name string
+		// Specifies the team ID.
+		TeamId string
+		// Specifies the list of modules that this code_metadata covers.
+		Code []string
+		// An optional field to specify if multiple ownerships for source files is allowed.
+		MultiOwnership bool
+	}
+}
+
+type codeDepTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var codeDepTag = codeDepTagType{}
+
+func (module *CodeMetadataModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// Validate Properties
+	if len(module.properties.TeamId) == 0 {
+		ctx.PropertyErrorf(
+			"TeamId",
+			"Team Id not found in the code_metadata module. Hint: Maybe the teamId property hasn't been properly specified.",
+		)
+	}
+	if !isInt(module.properties.TeamId) {
+		ctx.PropertyErrorf(
+			"TeamId", "Invalid value for Team ID. The Team ID must be an integer.",
+		)
+	}
+	if len(module.properties.Code) == 0 {
+		ctx.PropertyErrorf(
+			"Code",
+			"Targets to be attributed cannot be empty. Hint: Maybe the code property hasn't been properly specified.",
+		)
+	}
+	ctx.AddDependency(ctx.Module(), codeDepTag, module.properties.Code...)
+}
+
+// Provider published by CodeMetadata
+type CodeMetadataProviderData struct {
+	IntermediatePath android.WritablePath
+}
+
+var CodeMetadataProviderKey = blueprint.NewProvider(CodeMetadataProviderData{})
+
+func (module *CodeMetadataModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	metadataList := make(
+		[]*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership, 0,
+		len(module.properties.Code),
+	)
+	bpFilePath := filepath.Join(ctx.ModuleDir(), ctx.BlueprintsFile())
+
+	for _, m := range ctx.GetDirectDepsWithTag(codeDepTag) {
+		targetName := m.Name()
+		var moduleSrcs []string
+		if ctx.OtherModuleHasProvider(m, blueprint.SrcsFileProviderKey) {
+			moduleSrcs = ctx.OtherModuleProvider(
+				m, blueprint.SrcsFileProviderKey,
+			).(blueprint.SrcsFileProviderData).SrcPaths
+		}
+		if module.properties.MultiOwnership {
+			metadata := &code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership{
+				TargetName:     &targetName,
+				TrendyTeamId:   &module.properties.TeamId,
+				Path:           &bpFilePath,
+				MultiOwnership: &module.properties.MultiOwnership,
+				SourceFiles:    moduleSrcs,
+			}
+			metadataList = append(metadataList, metadata)
+		} else {
+			metadata := &code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership{
+				TargetName:   &targetName,
+				TrendyTeamId: &module.properties.TeamId,
+				Path:         &bpFilePath,
+				SourceFiles:  moduleSrcs,
+			}
+			metadataList = append(metadataList, metadata)
+		}
+
+	}
+	codeMetadata := &code_metadata_internal_proto.CodeMetadataInternal{TargetOwnershipList: metadataList}
+	protoData, err := proto.Marshal(codeMetadata)
+	if err != nil {
+		ctx.ModuleErrorf("Error marshaling code metadata: %s", err.Error())
+		return
+	}
+	intermediatePath := android.PathForModuleOut(
+		ctx, "intermediateCodeMetadata.pb",
+	)
+	android.WriteFileRule(ctx, intermediatePath, string(protoData))
+
+	ctx.SetProvider(
+		CodeMetadataProviderKey,
+		CodeMetadataProviderData{IntermediatePath: intermediatePath},
+	)
+}
diff --git a/testing/code_metadata_internal_proto/Android.bp b/testing/code_metadata_internal_proto/Android.bp
new file mode 100644
index 0000000..a534cc2
--- /dev/null
+++ b/testing/code_metadata_internal_proto/Android.bp
@@ -0,0 +1,29 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-testing-code_metadata_internal_proto",
+    pkgPath: "android/soong/testing/code_metadata_internal_proto",
+    deps: [
+            "golang-protobuf-reflect-protoreflect",
+            "golang-protobuf-runtime-protoimpl",
+        ],
+    srcs: [
+        "code_metadata_internal.pb.go",
+    ],
+}
diff --git a/testing/code_metadata_internal_proto/OWNERS b/testing/code_metadata_internal_proto/OWNERS
new file mode 100644
index 0000000..03bcdf1
--- /dev/null
+++ b/testing/code_metadata_internal_proto/OWNERS
@@ -0,0 +1,4 @@
+dariofreni@google.com
+joeo@google.com
+ronish@google.com
+caditya@google.com
diff --git a/testing/code_metadata_internal_proto/code_metadata_internal.pb.go b/testing/code_metadata_internal_proto/code_metadata_internal.pb.go
new file mode 100644
index 0000000..ecb8b86
--- /dev/null
+++ b/testing/code_metadata_internal_proto/code_metadata_internal.pb.go
@@ -0,0 +1,277 @@
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.30.0
+// 	protoc        v3.21.12
+// source: code_metadata_internal.proto
+
+package code_metadata_internal_proto
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type CodeMetadataInternal struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// List of all code targets and their metadata.
+	TargetOwnershipList []*CodeMetadataInternal_TargetOwnership `protobuf:"bytes,1,rep,name=target_ownership_list,json=targetOwnershipList" json:"target_ownership_list,omitempty"`
+}
+
+func (x *CodeMetadataInternal) Reset() {
+	*x = CodeMetadataInternal{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_code_metadata_internal_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CodeMetadataInternal) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CodeMetadataInternal) ProtoMessage() {}
+
+func (x *CodeMetadataInternal) ProtoReflect() protoreflect.Message {
+	mi := &file_code_metadata_internal_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CodeMetadataInternal.ProtoReflect.Descriptor instead.
+func (*CodeMetadataInternal) Descriptor() ([]byte, []int) {
+	return file_code_metadata_internal_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *CodeMetadataInternal) GetTargetOwnershipList() []*CodeMetadataInternal_TargetOwnership {
+	if x != nil {
+		return x.TargetOwnershipList
+	}
+	return nil
+}
+
+type CodeMetadataInternal_TargetOwnership struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// REQUIRED: Name of the build target
+	TargetName *string `protobuf:"bytes,1,opt,name=target_name,json=targetName" json:"target_name,omitempty"`
+	// REQUIRED: Code location of the target.
+	// To be used to support legacy/backup systems that use OWNERS file and is
+	// also required for our dashboard to support per code location basis UI
+	Path *string `protobuf:"bytes,2,opt,name=path" json:"path,omitempty"`
+	// REQUIRED: Team ID of the team that owns this target.
+	TrendyTeamId *string `protobuf:"bytes,3,opt,name=trendy_team_id,json=trendyTeamId" json:"trendy_team_id,omitempty"`
+	// OPTIONAL: The src files of the target.
+	// To be used to determine ownership of a file for ownership
+	SourceFiles []string `protobuf:"bytes,4,rep,name=source_files,json=sourceFiles" json:"source_files,omitempty"`
+	// OPTIONAL: Specify if multiple ownerships of the source files are allowed.
+	MultiOwnership *bool `protobuf:"varint,5,opt,name=multi_ownership,json=multiOwnership" json:"multi_ownership,omitempty"`
+}
+
+func (x *CodeMetadataInternal_TargetOwnership) Reset() {
+	*x = CodeMetadataInternal_TargetOwnership{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_code_metadata_internal_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CodeMetadataInternal_TargetOwnership) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CodeMetadataInternal_TargetOwnership) ProtoMessage() {}
+
+func (x *CodeMetadataInternal_TargetOwnership) ProtoReflect() protoreflect.Message {
+	mi := &file_code_metadata_internal_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CodeMetadataInternal_TargetOwnership.ProtoReflect.Descriptor instead.
+func (*CodeMetadataInternal_TargetOwnership) Descriptor() ([]byte, []int) {
+	return file_code_metadata_internal_proto_rawDescGZIP(), []int{0, 0}
+}
+
+func (x *CodeMetadataInternal_TargetOwnership) GetTargetName() string {
+	if x != nil && x.TargetName != nil {
+		return *x.TargetName
+	}
+	return ""
+}
+
+func (x *CodeMetadataInternal_TargetOwnership) GetPath() string {
+	if x != nil && x.Path != nil {
+		return *x.Path
+	}
+	return ""
+}
+
+func (x *CodeMetadataInternal_TargetOwnership) GetTrendyTeamId() string {
+	if x != nil && x.TrendyTeamId != nil {
+		return *x.TrendyTeamId
+	}
+	return ""
+}
+
+func (x *CodeMetadataInternal_TargetOwnership) GetSourceFiles() []string {
+	if x != nil {
+		return x.SourceFiles
+	}
+	return nil
+}
+
+func (x *CodeMetadataInternal_TargetOwnership) GetMultiOwnership() bool {
+	if x != nil && x.MultiOwnership != nil {
+		return *x.MultiOwnership
+	}
+	return false
+}
+
+var File_code_metadata_internal_proto protoreflect.FileDescriptor
+
+var file_code_metadata_internal_proto_rawDesc = []byte{
+	0x0a, 0x1c, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f,
+	0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c,
+	0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x69, 0x6e,
+	0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc9, 0x02, 0x0a,
+	0x14, 0x43, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x49, 0x6e, 0x74,
+	0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x76, 0x0a, 0x15, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f,
+	0x6f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61,
+	0x64, 0x61, 0x74, 0x61, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+	0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4f,
+	0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x13, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
+	0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0xb8, 0x01,
+	0x0a, 0x0f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69,
+	0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61,
+	0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x79,
+	0x5f, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
+	0x74, 0x72, 0x65, 0x6e, 0x64, 0x79, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c,
+	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03,
+	0x28, 0x09, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12,
+	0x27, 0x0a, 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68,
+	0x69, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x4f,
+	0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x42, 0x34, 0x5a, 0x32, 0x61, 0x6e, 0x64, 0x72,
+	0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e,
+	0x67, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f,
+	0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+}
+
+var (
+	file_code_metadata_internal_proto_rawDescOnce sync.Once
+	file_code_metadata_internal_proto_rawDescData = file_code_metadata_internal_proto_rawDesc
+)
+
+func file_code_metadata_internal_proto_rawDescGZIP() []byte {
+	file_code_metadata_internal_proto_rawDescOnce.Do(func() {
+		file_code_metadata_internal_proto_rawDescData = protoimpl.X.CompressGZIP(file_code_metadata_internal_proto_rawDescData)
+	})
+	return file_code_metadata_internal_proto_rawDescData
+}
+
+var file_code_metadata_internal_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_code_metadata_internal_proto_goTypes = []interface{}{
+	(*CodeMetadataInternal)(nil),                 // 0: code_metadata_internal_proto.CodeMetadataInternal
+	(*CodeMetadataInternal_TargetOwnership)(nil), // 1: code_metadata_internal_proto.CodeMetadataInternal.TargetOwnership
+}
+var file_code_metadata_internal_proto_depIdxs = []int32{
+	1, // 0: code_metadata_internal_proto.CodeMetadataInternal.target_ownership_list:type_name -> code_metadata_internal_proto.CodeMetadataInternal.TargetOwnership
+	1, // [1:1] is the sub-list for method output_type
+	1, // [1:1] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_code_metadata_internal_proto_init() }
+func file_code_metadata_internal_proto_init() {
+	if File_code_metadata_internal_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_code_metadata_internal_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CodeMetadataInternal); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_code_metadata_internal_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CodeMetadataInternal_TargetOwnership); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_code_metadata_internal_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_code_metadata_internal_proto_goTypes,
+		DependencyIndexes: file_code_metadata_internal_proto_depIdxs,
+		MessageInfos:      file_code_metadata_internal_proto_msgTypes,
+	}.Build()
+	File_code_metadata_internal_proto = out.File
+	file_code_metadata_internal_proto_rawDesc = nil
+	file_code_metadata_internal_proto_goTypes = nil
+	file_code_metadata_internal_proto_depIdxs = nil
+}
diff --git a/testing/code_metadata_internal_proto/code_metadata_internal.proto b/testing/code_metadata_internal_proto/code_metadata_internal.proto
new file mode 100644
index 0000000..14edc0f
--- /dev/null
+++ b/testing/code_metadata_internal_proto/code_metadata_internal.proto
@@ -0,0 +1,40 @@
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto2";
+package code_metadata_internal_proto;
+option go_package = "android/soong/testing/code_metadata_internal_proto";
+
+message CodeMetadataInternal {
+
+  message TargetOwnership {
+    // REQUIRED: Name of the build target
+    optional string target_name = 1;
+
+    // REQUIRED: Code location of the target.
+    // To be used to support legacy/backup systems that use OWNERS file and is
+    // also required for our dashboard to support per code location basis UI
+    optional string path = 2;
+
+    // REQUIRED: Team ID of the team that owns this target.
+    optional string trendy_team_id = 3;
+
+    // OPTIONAL: The src files of the target.
+    // To be used to determine ownership of a file for ownership
+    repeated string source_files = 4;
+
+    // OPTIONAL: Specify if multiple ownerships of the source files are allowed.
+    optional bool multi_ownership = 5;
+  }
+
+  // List of all code targets and their metadata.
+  repeated TargetOwnership target_ownership_list = 1;
+}
diff --git a/testing/code_metadata_internal_proto/go.mod b/testing/code_metadata_internal_proto/go.mod
new file mode 100644
index 0000000..7e9129d
--- /dev/null
+++ b/testing/code_metadata_internal_proto/go.mod
@@ -0,0 +1,3 @@
+module android/soong/testing/code_metadata_internal_proto
+
+go 1.18
diff --git a/testing/code_metadata_internal_proto/regen.sh b/testing/code_metadata_internal_proto/regen.sh
new file mode 100644
index 0000000..f101a02
--- /dev/null
+++ b/testing/code_metadata_internal_proto/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. code_metadata_internal.proto
diff --git a/testing/code_metadata_proto/Android.bp b/testing/code_metadata_proto/Android.bp
new file mode 100644
index 0000000..8fcca19
--- /dev/null
+++ b/testing/code_metadata_proto/Android.bp
@@ -0,0 +1,29 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-testing-code_metadata_proto",
+    pkgPath: "android/soong/testing/code_metadata_proto",
+    deps: [
+            "golang-protobuf-reflect-protoreflect",
+            "golang-protobuf-runtime-protoimpl",
+        ],
+    srcs: [
+        "code_metadata.pb.go",
+    ],
+}
diff --git a/testing/code_metadata_proto/OWNERS b/testing/code_metadata_proto/OWNERS
new file mode 100644
index 0000000..03bcdf1
--- /dev/null
+++ b/testing/code_metadata_proto/OWNERS
@@ -0,0 +1,4 @@
+dariofreni@google.com
+joeo@google.com
+ronish@google.com
+caditya@google.com
diff --git a/testing/code_metadata_proto/code_metadata.pb.go b/testing/code_metadata_proto/code_metadata.pb.go
new file mode 100644
index 0000000..711bf7a
--- /dev/null
+++ b/testing/code_metadata_proto/code_metadata.pb.go
@@ -0,0 +1,263 @@
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.30.0
+// 	protoc        v3.21.12
+// source: code_metadata.proto
+
+package code_metadata_proto
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type CodeMetadata struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// List of all code targets and their metadata.
+	TargetOwnershipList []*CodeMetadata_TargetOwnership `protobuf:"bytes,1,rep,name=target_ownership_list,json=targetOwnershipList" json:"target_ownership_list,omitempty"`
+}
+
+func (x *CodeMetadata) Reset() {
+	*x = CodeMetadata{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_code_metadata_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CodeMetadata) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CodeMetadata) ProtoMessage() {}
+
+func (x *CodeMetadata) ProtoReflect() protoreflect.Message {
+	mi := &file_code_metadata_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CodeMetadata.ProtoReflect.Descriptor instead.
+func (*CodeMetadata) Descriptor() ([]byte, []int) {
+	return file_code_metadata_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *CodeMetadata) GetTargetOwnershipList() []*CodeMetadata_TargetOwnership {
+	if x != nil {
+		return x.TargetOwnershipList
+	}
+	return nil
+}
+
+type CodeMetadata_TargetOwnership struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// REQUIRED: Name of the build target
+	TargetName *string `protobuf:"bytes,1,opt,name=target_name,json=targetName" json:"target_name,omitempty"`
+	// REQUIRED: Code location of the target.
+	// To be used to support legacy/backup systems that use OWNERS file and is
+	// also required for our dashboard to support per code location basis UI
+	Path *string `protobuf:"bytes,2,opt,name=path" json:"path,omitempty"`
+	// REQUIRED: Team ID of the team that owns this target.
+	TrendyTeamId *string `protobuf:"bytes,3,opt,name=trendy_team_id,json=trendyTeamId" json:"trendy_team_id,omitempty"`
+	// OPTIONAL: The src files of the target.
+	// To be used to determine ownership of a file for ownership
+	SourceFiles []string `protobuf:"bytes,4,rep,name=source_files,json=sourceFiles" json:"source_files,omitempty"`
+}
+
+func (x *CodeMetadata_TargetOwnership) Reset() {
+	*x = CodeMetadata_TargetOwnership{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_code_metadata_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CodeMetadata_TargetOwnership) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CodeMetadata_TargetOwnership) ProtoMessage() {}
+
+func (x *CodeMetadata_TargetOwnership) ProtoReflect() protoreflect.Message {
+	mi := &file_code_metadata_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CodeMetadata_TargetOwnership.ProtoReflect.Descriptor instead.
+func (*CodeMetadata_TargetOwnership) Descriptor() ([]byte, []int) {
+	return file_code_metadata_proto_rawDescGZIP(), []int{0, 0}
+}
+
+func (x *CodeMetadata_TargetOwnership) GetTargetName() string {
+	if x != nil && x.TargetName != nil {
+		return *x.TargetName
+	}
+	return ""
+}
+
+func (x *CodeMetadata_TargetOwnership) GetPath() string {
+	if x != nil && x.Path != nil {
+		return *x.Path
+	}
+	return ""
+}
+
+func (x *CodeMetadata_TargetOwnership) GetTrendyTeamId() string {
+	if x != nil && x.TrendyTeamId != nil {
+		return *x.TrendyTeamId
+	}
+	return ""
+}
+
+func (x *CodeMetadata_TargetOwnership) GetSourceFiles() []string {
+	if x != nil {
+		return x.SourceFiles
+	}
+	return nil
+}
+
+var File_code_metadata_proto protoreflect.FileDescriptor
+
+var file_code_metadata_proto_rawDesc = []byte{
+	0x0a, 0x13, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61,
+	0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x87, 0x02, 0x0a, 0x0c, 0x43,
+	0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x65, 0x0a, 0x15, 0x74,
+	0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x5f,
+	0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x63, 0x6f, 0x64,
+	0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x2e, 0x43, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61,
+	0x72, 0x67, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x13, 0x74,
+	0x61, 0x72, 0x67, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x4c, 0x69,
+	0x73, 0x74, 0x1a, 0x8f, 0x01, 0x0a, 0x0f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4f, 0x77, 0x6e,
+	0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
+	0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72,
+	0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x24, 0x0a, 0x0e, 0x74,
+	0x72, 0x65, 0x6e, 0x64, 0x79, 0x5f, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x79, 0x54, 0x65, 0x61, 0x6d, 0x49,
+	0x64, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65,
+	0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46,
+	0x69, 0x6c, 0x65, 0x73, 0x42, 0x2b, 0x5a, 0x29, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f,
+	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2f, 0x63, 0x6f,
+	0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74,
+	0x6f,
+}
+
+var (
+	file_code_metadata_proto_rawDescOnce sync.Once
+	file_code_metadata_proto_rawDescData = file_code_metadata_proto_rawDesc
+)
+
+func file_code_metadata_proto_rawDescGZIP() []byte {
+	file_code_metadata_proto_rawDescOnce.Do(func() {
+		file_code_metadata_proto_rawDescData = protoimpl.X.CompressGZIP(file_code_metadata_proto_rawDescData)
+	})
+	return file_code_metadata_proto_rawDescData
+}
+
+var file_code_metadata_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_code_metadata_proto_goTypes = []interface{}{
+	(*CodeMetadata)(nil),                 // 0: code_metadata_proto.CodeMetadata
+	(*CodeMetadata_TargetOwnership)(nil), // 1: code_metadata_proto.CodeMetadata.TargetOwnership
+}
+var file_code_metadata_proto_depIdxs = []int32{
+	1, // 0: code_metadata_proto.CodeMetadata.target_ownership_list:type_name -> code_metadata_proto.CodeMetadata.TargetOwnership
+	1, // [1:1] is the sub-list for method output_type
+	1, // [1:1] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_code_metadata_proto_init() }
+func file_code_metadata_proto_init() {
+	if File_code_metadata_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_code_metadata_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CodeMetadata); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_code_metadata_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CodeMetadata_TargetOwnership); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_code_metadata_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_code_metadata_proto_goTypes,
+		DependencyIndexes: file_code_metadata_proto_depIdxs,
+		MessageInfos:      file_code_metadata_proto_msgTypes,
+	}.Build()
+	File_code_metadata_proto = out.File
+	file_code_metadata_proto_rawDesc = nil
+	file_code_metadata_proto_goTypes = nil
+	file_code_metadata_proto_depIdxs = nil
+}
diff --git a/testing/code_metadata_proto/code_metadata.proto b/testing/code_metadata_proto/code_metadata.proto
new file mode 100644
index 0000000..2548363
--- /dev/null
+++ b/testing/code_metadata_proto/code_metadata.proto
@@ -0,0 +1,37 @@
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto2";
+package code_metadata_proto;
+option go_package = "android/soong/testing/code_metadata_proto";
+
+message CodeMetadata {
+
+  message TargetOwnership {
+    // REQUIRED: Name of the build target
+    optional string target_name = 1;
+
+    // REQUIRED: Code location of the target.
+    // To be used to support legacy/backup systems that use OWNERS file and is
+    // also required for our dashboard to support per code location basis UI
+    optional string path = 2;
+
+    // REQUIRED: Team ID of the team that owns this target.
+    optional string trendy_team_id = 3;
+
+    // OPTIONAL: The src files of the target.
+    // To be used to determine ownership of a file for ownership
+    repeated string source_files = 4;
+  }
+
+  // List of all code targets and their metadata.
+  repeated TargetOwnership target_ownership_list = 1;
+}
diff --git a/testing/code_metadata_proto/go.mod b/testing/code_metadata_proto/go.mod
new file mode 100644
index 0000000..ada2411
--- /dev/null
+++ b/testing/code_metadata_proto/go.mod
@@ -0,0 +1,3 @@
+module android/soong/testing/code_metadata_proto
+
+go 1.18
diff --git a/testing/code_metadata_proto/regen.sh b/testing/code_metadata_proto/regen.sh
new file mode 100644
index 0000000..ffe06f7
--- /dev/null
+++ b/testing/code_metadata_proto/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. code_metadata.proto
diff --git a/testing/init.go b/testing/init.go
index 206b430..edcbf59 100644
--- a/testing/init.go
+++ b/testing/init.go
@@ -28,6 +28,8 @@
 }
 
 func RegisterBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("code_metadata", CodeMetadataFactory)
 	ctx.RegisterModuleType("test_spec", TestSpecFactory)
+	ctx.RegisterParallelSingletonType("all_code_metadata", AllCodeMetadataFactory)
 	ctx.RegisterParallelSingletonType("all_test_specs", AllTestSpecsFactory)
 }
diff --git a/testing/test.go b/testing/test.go
index 44824e4..cd97a8f 100644
--- a/testing/test.go
+++ b/testing/test.go
@@ -18,4 +18,4 @@
 	"android/soong/android"
 )
 
-var PrepareForTestWithTestSpecBuildComponents = android.FixtureRegisterWithContext(RegisterBuildComponents)
+var PrepareForTestWithTestingBuildComponents = android.FixtureRegisterWithContext(RegisterBuildComponents)
diff --git a/testing/test_spec_proto/go.mod b/testing/test_spec_proto/go.mod
new file mode 100644
index 0000000..b581aac
--- /dev/null
+++ b/testing/test_spec_proto/go.mod
@@ -0,0 +1,3 @@
+module android/soong/testing/test_spec_proto
+
+go 1.18
diff --git a/ui/build/build.go b/ui/build/build.go
index 9d5c330..54a4c27 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -283,11 +283,16 @@
 	}
 
 	rbeCh := make(chan bool)
+	var rbePanic any
 	if config.StartRBE() {
 		cleanupRBELogsDir(ctx, config)
+		checkRBERequirements(ctx, config)
 		go func() {
+			defer func() {
+				rbePanic = recover()
+				close(rbeCh)
+			}()
 			startRBE(ctx, config)
-			close(rbeCh)
 		}()
 		defer DumpRBEMetrics(ctx, config, filepath.Join(config.LogsDir(), "rbe_metrics.pb"))
 	} else {
@@ -345,6 +350,11 @@
 	}
 
 	<-rbeCh
+	if rbePanic != nil {
+		// If there was a ctx.Fatal in startRBE, rethrow it.
+		panic(rbePanic)
+	}
+
 	if what&RunNinja != 0 {
 		if what&RunKati != 0 {
 			installCleanIfNecessary(ctx, config)
diff --git a/ui/build/config.go b/ui/build/config.go
index d345415..613fc65 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -384,10 +384,14 @@
 	// Configure Java-related variables, including adding it to $PATH
 	java8Home := filepath.Join("prebuilts/jdk/jdk8", ret.HostPrebuiltTag())
 	java17Home := filepath.Join("prebuilts/jdk/jdk17", ret.HostPrebuiltTag())
+	java21Home := filepath.Join("prebuilts/jdk/jdk21", ret.HostPrebuiltTag())
 	javaHome := func() string {
 		if override, ok := ret.environ.Get("OVERRIDE_ANDROID_JAVA_HOME"); ok {
 			return override
 		}
+		if ret.environ.IsEnvTrue("EXPERIMENTAL_USE_OPENJDK21_TOOLCHAIN") {
+			return java21Home
+		}
 		if toolchain11, ok := ret.environ.Get("EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN"); ok && toolchain11 != "true" {
 			ctx.Fatalln("The environment variable EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN is no longer supported. An OpenJDK 11 toolchain is now the global default.")
 		}
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 19a54df..fa04207 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -95,14 +95,10 @@
 	}
 }
 
-func startRBE(ctx Context, config Config) {
+func checkRBERequirements(ctx Context, config Config) {
 	if !config.GoogleProdCredsExist() && prodCredsAuthType(config) {
 		ctx.Fatalf("Unable to start RBE reproxy\nFAILED: Missing LOAS credentials.")
 	}
-	ctx.BeginTrace(metrics.RunSetupTool, "rbe_bootstrap")
-	defer ctx.EndTrace()
-
-	ctx.Status.Status("Starting rbe...")
 
 	if u := ulimitOrFatal(ctx, config, "-u"); u < rbeLeastNProcs {
 		ctx.Fatalf("max user processes is insufficient: %d; want >= %d.\n", u, rbeLeastNProcs)
@@ -115,6 +111,13 @@
 			ctx.Fatalf("Unable to create logs dir (%v) for RBE: %v", config.rbeProxyLogsDir, err)
 		}
 	}
+}
+
+func startRBE(ctx Context, config Config) {
+	ctx.BeginTrace(metrics.RunSetupTool, "rbe_bootstrap")
+	defer ctx.EndTrace()
+
+	ctx.Status.Status("Starting rbe...")
 
 	cmd := Command(ctx, config, "startRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd))
 
diff --git a/ui/status/ninja.go b/ui/status/ninja.go
index 7b25d50..1e97908 100644
--- a/ui/status/ninja.go
+++ b/ui/status/ninja.go
@@ -20,6 +20,7 @@
 	"io"
 	"os"
 	"regexp"
+	"runtime"
 	"strings"
 	"syscall"
 	"time"
@@ -178,7 +179,28 @@
 			// msgChan is closed
 			break
 		}
-		// Ignore msg.BuildStarted
+
+		if msg.BuildStarted != nil {
+			parallelism := uint32(runtime.NumCPU())
+			if msg.BuildStarted.GetParallelism() > 0 {
+				parallelism = msg.BuildStarted.GetParallelism()
+			}
+			// It is estimated from total time / parallelism assumming the build is packing enough.
+			estimatedDurationFromTotal := time.Duration(msg.BuildStarted.GetEstimatedTotalTime()/parallelism) * time.Millisecond
+			// It is estimated from critical path time which is useful for small size build.
+			estimatedDurationFromCriticalPath := time.Duration(msg.BuildStarted.GetCriticalPathTime()) * time.Millisecond
+			// Select the longer one.
+			estimatedDuration := max(estimatedDurationFromTotal, estimatedDurationFromCriticalPath)
+
+			if estimatedDuration > 0 {
+				n.status.SetEstimatedTime(time.Now().Add(estimatedDuration))
+				n.status.Verbose(fmt.Sprintf("parallelism: %d, estimiated from total time: %s, critical path time: %s",
+					parallelism,
+					estimatedDurationFromTotal,
+					estimatedDurationFromCriticalPath))
+
+			}
+		}
 		if msg.TotalEdges != nil {
 			n.status.SetTotalActions(int(msg.TotalEdges.GetTotalEdges()))
 		}
diff --git a/ui/status/ninja_frontend/frontend.pb.go b/ui/status/ninja_frontend/frontend.pb.go
index d0c4953..d8344c8 100644
--- a/ui/status/ninja_frontend/frontend.pb.go
+++ b/ui/status/ninja_frontend/frontend.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.1
+// 	protoc-gen-go v1.30.0
 // 	protoc        v3.21.12
 // source: frontend.proto
 
@@ -240,6 +240,10 @@
 	Parallelism *uint32 `protobuf:"varint,1,opt,name=parallelism" json:"parallelism,omitempty"`
 	// Verbose value passed to ninja.
 	Verbose *bool `protobuf:"varint,2,opt,name=verbose" json:"verbose,omitempty"`
+	// Critical path's running time in milliseconds
+	CriticalPathTime *uint32 `protobuf:"varint,3,opt,name=critical_path_time,json=criticalPathTime" json:"critical_path_time,omitempty"`
+	// Total running time of every need-to-build edge in milliseconds
+	EstimatedTotalTime *uint32 `protobuf:"varint,4,opt,name=estimated_total_time,json=estimatedTotalTime" json:"estimated_total_time,omitempty"`
 }
 
 func (x *Status_BuildStarted) Reset() {
@@ -288,6 +292,20 @@
 	return false
 }
 
+func (x *Status_BuildStarted) GetCriticalPathTime() uint32 {
+	if x != nil && x.CriticalPathTime != nil {
+		return *x.CriticalPathTime
+	}
+	return 0
+}
+
+func (x *Status_BuildStarted) GetEstimatedTotalTime() uint32 {
+	if x != nil && x.EstimatedTotalTime != nil {
+		return *x.EstimatedTotalTime
+	}
+	return 0
+}
+
 type Status_BuildFinished struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -660,7 +678,7 @@
 
 var file_frontend_proto_rawDesc = []byte{
 	0x0a, 0x0e, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-	0x12, 0x05, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x22, 0xc8, 0x0a, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74,
+	0x12, 0x05, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x22, 0xa9, 0x0b, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74,
 	0x75, 0x73, 0x12, 0x39, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x64, 0x67, 0x65,
 	0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x2e,
 	0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x45, 0x64, 0x67, 0x65,
@@ -687,67 +705,73 @@
 	0x67, 0x65, 0x1a, 0x2d, 0x0a, 0x0a, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x73,
 	0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x64, 0x67, 0x65, 0x73, 0x18,
 	0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x45, 0x64, 0x67, 0x65,
-	0x73, 0x1a, 0x4a, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65,
-	0x64, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c,
-	0x69, 0x73, 0x6d, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x1a, 0x0f, 0x0a,
-	0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x1a, 0xb6,
-	0x01, 0x0a, 0x0b, 0x45, 0x64, 0x67, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x0e,
-	0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d,
-	0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x0d, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a,
-	0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x69,
-	0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73,
-	0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x12,
-	0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64,
-	0x65, 0x73, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x06,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x18, 0x0a,
-	0x07, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07,
-	0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x1a, 0xf3, 0x03, 0x0a, 0x0c, 0x45, 0x64, 0x67, 0x65,
-	0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f,
-	0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54,
-	0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20,
-	0x01, 0x28, 0x11, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6f,
-	0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74,
-	0x70, 0x75, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65,
-	0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65,
-	0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18,
-	0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x69, 0x6d,
-	0x65, 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x73, 0x73, 0x5f, 0x6b, 0x62, 0x18,
-	0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x52, 0x73, 0x73, 0x4b, 0x62, 0x12,
-	0x2a, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61,
-	0x75, 0x6c, 0x74, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x69, 0x6e, 0x6f,
-	0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d,
-	0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73,
-	0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x50, 0x61, 0x67,
-	0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x69, 0x6f, 0x5f, 0x69, 0x6e,
-	0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6f,
-	0x49, 0x6e, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x6f, 0x5f, 0x6f, 0x75,
-	0x74, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x69,
-	0x6f, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x3c, 0x0a, 0x1a, 0x76, 0x6f, 0x6c,
-	0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x73,
-	0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x18, 0x76,
-	0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x53,
-	0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x69, 0x6e, 0x76, 0x6f, 0x6c,
-	0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x73,
-	0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1a, 0x69,
-	0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78,
-	0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67,
-	0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x1a, 0x92, 0x01,
-	0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x05, 0x6c, 0x65, 0x76,
-	0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6e, 0x69, 0x6e, 0x6a, 0x61,
-	0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e,
-	0x4c, 0x65, 0x76, 0x65, 0x6c, 0x3a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x52, 0x05, 0x6c, 0x65, 0x76,
-	0x65, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x34, 0x0a, 0x05,
-	0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x00, 0x12,
-	0x0b, 0x0a, 0x07, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05,
-	0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47,
-	0x10, 0x03, 0x42, 0x2a, 0x48, 0x03, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f,
-	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2f,
-	0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x5f, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64,
+	0x73, 0x1a, 0xaa, 0x01, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74,
+	0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73,
+	0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65,
+	0x6c, 0x69, 0x73, 0x6d, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x2c,
+	0x0a, 0x12, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f,
+	0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x63, 0x72, 0x69, 0x74,
+	0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x30, 0x0a, 0x14,
+	0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f,
+	0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x65, 0x73, 0x74, 0x69,
+	0x6d, 0x61, 0x74, 0x65, 0x64, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x1a, 0x0f,
+	0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x1a,
+	0xb6, 0x01, 0x0a, 0x0b, 0x45, 0x64, 0x67, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12,
+	0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12,
+	0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x0d, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16,
+	0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06,
+	0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74,
+	0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73,
+	0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+	0x64, 0x65, 0x73, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18,
+	0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x18,
+	0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52,
+	0x07, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x1a, 0xf3, 0x03, 0x0a, 0x0c, 0x45, 0x64, 0x67,
+	0x65, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64,
+	0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x65, 0x6e, 0x64,
+	0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x11, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06,
+	0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75,
+	0x74, 0x70, 0x75, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d,
+	0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x54, 0x69, 0x6d,
+	0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x74, 0x69, 0x6d, 0x65,
+	0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x69,
+	0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x73, 0x73, 0x5f, 0x6b, 0x62,
+	0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x52, 0x73, 0x73, 0x4b, 0x62,
+	0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66,
+	0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x69, 0x6e,
+	0x6f, 0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11,
+	0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74,
+	0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x50, 0x61,
+	0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x69, 0x6f, 0x5f, 0x69,
+	0x6e, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x69,
+	0x6f, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x6f, 0x5f, 0x6f,
+	0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a,
+	0x69, 0x6f, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x3c, 0x0a, 0x1a, 0x76, 0x6f,
+	0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f,
+	0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x18,
+	0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
+	0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x69, 0x6e, 0x76, 0x6f,
+	0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f,
+	0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1a,
+	0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65,
+	0x78, 0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61,
+	0x67, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x1a, 0x92,
+	0x01, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x05, 0x6c, 0x65,
+	0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6e, 0x69, 0x6e, 0x6a,
+	0x61, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+	0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x3a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x52, 0x05, 0x6c, 0x65,
+	0x76, 0x65, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x34, 0x0a,
+	0x05, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x00,
+	0x12, 0x0b, 0x0a, 0x07, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a,
+	0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55,
+	0x47, 0x10, 0x03, 0x42, 0x2a, 0x48, 0x03, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
+	0x2f, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x5f, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64,
 }
 
 var (
diff --git a/ui/status/ninja_frontend/frontend.proto b/ui/status/ninja_frontend/frontend.proto
index 6cb4a0d..42b251b 100644
--- a/ui/status/ninja_frontend/frontend.proto
+++ b/ui/status/ninja_frontend/frontend.proto
@@ -30,6 +30,10 @@
     optional uint32 parallelism = 1;
     // Verbose value passed to ninja.
     optional bool verbose = 2;
+    // Critical path's running time in milliseconds
+    optional uint32 critical_path_time = 3;
+    // Total running time of every need-to-build edge in milliseconds
+    optional uint32 estimated_total_time = 4;
   }
 
   message BuildFinished {
diff --git a/ui/status/status.go b/ui/status/status.go
index f3e58b6..da78994 100644
--- a/ui/status/status.go
+++ b/ui/status/status.go
@@ -19,6 +19,7 @@
 
 import (
 	"sync"
+	"time"
 )
 
 // Action describes an action taken (or as Ninja calls them, Edges).
@@ -107,6 +108,8 @@
 	// FinishedActions are the number of actions that have been finished
 	// with FinishAction.
 	FinishedActions int
+
+	EstimatedTime time.Time
 }
 
 // ToolStatus is the interface used by tools to report on their Actions, and to
@@ -118,6 +121,7 @@
 	// This call be will ignored if it sets a number that is less than the
 	// current number of started actions.
 	SetTotalActions(total int)
+	SetEstimatedTime(estimatedTime time.Time)
 
 	// StartAction specifies that the associated action has been started by
 	// the tool.
@@ -267,6 +271,13 @@
 	s.counts.TotalActions += diff
 }
 
+func (s *Status) SetEstimatedTime(estimatedTime time.Time) {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	s.counts.EstimatedTime = estimatedTime
+}
+
 func (s *Status) startAction(action *Action) {
 	s.lock.Lock()
 	defer s.lock.Unlock()
@@ -329,6 +340,10 @@
 	}
 }
 
+func (d *toolStatus) SetEstimatedTime(estimatedTime time.Time) {
+	d.status.SetEstimatedTime(estimatedTime)
+}
+
 func (d *toolStatus) StartAction(action *Action) {
 	totalDiff := 0
 
diff --git a/ui/terminal/format.go b/ui/terminal/format.go
index 4205bdc..5391023 100644
--- a/ui/terminal/format.go
+++ b/ui/terminal/format.go
@@ -25,6 +25,7 @@
 type formatter struct {
 	format string
 	quiet  bool
+	smart  bool
 	start  time.Time
 }
 
@@ -32,10 +33,11 @@
 // the terminal in a format similar to Ninja.
 // format takes nearly all the same options as NINJA_STATUS.
 // %c is currently unsupported.
-func newFormatter(format string, quiet bool) formatter {
+func newFormatter(format string, quiet bool, smart bool) formatter {
 	return formatter{
 		format: format,
 		quiet:  quiet,
+		smart:  smart,
 		start:  time.Now(),
 	}
 }
@@ -51,9 +53,23 @@
 	return ""
 }
 
+func remainingTimeString(t time.Time) string {
+	now := time.Now()
+	if t.After(now) {
+		return t.Sub(now).Round(time.Duration(time.Second)).String()
+	}
+	return time.Duration(0).Round(time.Duration(time.Second)).String()
+}
 func (s formatter) progress(counts status.Counts) string {
 	if s.format == "" {
-		return fmt.Sprintf("[%3d%% %d/%d] ", 100*counts.FinishedActions/counts.TotalActions, counts.FinishedActions, counts.TotalActions)
+		output := fmt.Sprintf("[%3d%% %d/%d", 100*counts.FinishedActions/counts.TotalActions, counts.FinishedActions, counts.TotalActions)
+		// Not to break parsing logic in the build bot
+		// TODO(b/313981966): make buildbot more flexible for output format
+		if s.smart && !counts.EstimatedTime.IsZero() {
+			output += fmt.Sprintf(" %s remaining", remainingTimeString(counts.EstimatedTime))
+		}
+		output += "] "
+		return output
 	}
 
 	buf := &strings.Builder{}
@@ -93,6 +109,13 @@
 			fmt.Fprintf(buf, "%3d%%", 100*counts.FinishedActions/counts.TotalActions)
 		case 'e':
 			fmt.Fprintf(buf, "%.3f", time.Since(s.start).Seconds())
+		case 'l':
+			if counts.EstimatedTime.IsZero() {
+				// No esitimated data
+				buf.WriteRune('?')
+			} else {
+				fmt.Fprintf(buf, "%s", remainingTimeString(counts.EstimatedTime))
+			}
 		default:
 			buf.WriteString("unknown placeholder '")
 			buf.WriteByte(c)
diff --git a/ui/terminal/status.go b/ui/terminal/status.go
index 2ad174f..810e3c9 100644
--- a/ui/terminal/status.go
+++ b/ui/terminal/status.go
@@ -27,9 +27,10 @@
 // statusFormat takes nearly all the same options as NINJA_STATUS.
 // %c is currently unsupported.
 func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild, forceKeepANSI bool) status.StatusOutput {
-	formatter := newFormatter(statusFormat, quietBuild)
+	useSmartStatus := !forceSimpleOutput && isSmartTerminal(w)
+	formatter := newFormatter(statusFormat, quietBuild, useSmartStatus)
 
-	if !forceSimpleOutput && isSmartTerminal(w) {
+	if useSmartStatus {
 		return NewSmartStatusOutput(w, formatter)
 	} else {
 		return NewSimpleStatusOutput(w, formatter, forceKeepANSI)