Merge "Export Minimal Runtime Flags" into main
diff --git a/aconfig/init.go b/aconfig/init.go
index 3ed5faf..37167aa 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -30,7 +30,7 @@
 				` ${declarations}` +
 				` ${values}` +
 				` --cache ${out}.tmp` +
-				` && ( if cmp -s ${out}.tmp ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
+				` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
 			//				` --build-id ${release_version}` +
 			CommandDeps: []string{
 				"${aconfig}",
@@ -44,6 +44,7 @@
 			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` +
@@ -53,7 +54,7 @@
 				"$soong_zip",
 			},
 			Restat: true,
-		})
+		}, "mode")
 
 	// For java_aconfig_library: Generate java file
 	cppRule = pctx.AndroidStaticRule("cc_aconfig_library",
diff --git a/aconfig/java_aconfig_library.go b/aconfig/java_aconfig_library.go
index 53b2b10..53f8bd1 100644
--- a/aconfig/java_aconfig_library.go
+++ b/aconfig/java_aconfig_library.go
@@ -30,6 +30,9 @@
 type JavaAconfigDeclarationsLibraryProperties struct {
 	// name of the aconfig_declarations module to generate a library for
 	Aconfig_declarations string
+
+	// whether to generate test mode version of the library
+	Test bool
 }
 
 type JavaAconfigDeclarationsLibraryCallbacks struct {
@@ -61,11 +64,20 @@
 
 	// Generate the action to build the srcjar
 	srcJarPath := android.PathForModuleGen(ctx, ctx.ModuleName()+".srcjar")
+	var mode string
+	if callbacks.properties.Test {
+		mode = "test"
+	} else {
+		mode = "production"
+	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        javaRule,
 		Input:       declarations.IntermediatePath,
 		Output:      srcJarPath,
 		Description: "aconfig.srcjar",
+		Args: map[string]string{
+			"mode": mode,
+		},
 	})
 
 	// Tell the java module about the .aconfig files, so they can be propagated up the dependency chain.
diff --git a/aconfig/java_aconfig_library_test.go b/aconfig/java_aconfig_library_test.go
index 1808290..af50848 100644
--- a/aconfig/java_aconfig_library_test.go
+++ b/aconfig/java_aconfig_library_test.go
@@ -15,6 +15,7 @@
 package aconfig
 
 import (
+	"fmt"
 	"strings"
 	"testing"
 
@@ -152,3 +153,39 @@
 
 	runJavaAndroidMkTest(t, bp)
 }
+
+func testCodegenMode(t *testing.T, bpMode string, ruleMode string) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithAconfigBuildComponents,
+		java.PrepareForTestWithJavaDefaultModules).
+		ExtendWithErrorHandler(android.FixtureExpectsNoErrors).
+		RunTestWithBp(t, fmt.Sprintf(`
+			aconfig_declarations {
+				name: "my_aconfig_declarations",
+				package: "com.example.package",
+				srcs: ["foo.aconfig"],
+			}
+
+			java_aconfig_library {
+				name: "my_java_aconfig_library",
+				aconfig_declarations: "my_aconfig_declarations",
+				%s
+			}
+		`, bpMode))
+
+	module := result.ModuleForTests("my_java_aconfig_library", "android_common")
+	rule := module.Rule("java_aconfig_library")
+	android.AssertStringEquals(t, "rule must contain test mode", rule.Args["mode"], ruleMode)
+}
+
+func TestDefaultProdMode(t *testing.T) {
+	testCodegenMode(t, "", "production")
+}
+
+func TestProdMode(t *testing.T) {
+	testCodegenMode(t, "test: false,", "production")
+}
+
+func TestTestMode(t *testing.T) {
+	testCodegenMode(t, "test: true,", "test")
+}
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 71f451b..d37dc02 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -121,6 +121,7 @@
 		"development/sdk":                             Bp2BuildDefaultTrueRecursively,
 
 		"external/aac":                           Bp2BuildDefaultTrueRecursively,
+		"external/abseil-cpp":                    Bp2BuildDefaultTrueRecursively,
 		"external/arm-optimized-routines":        Bp2BuildDefaultTrueRecursively,
 		"external/auto":                          Bp2BuildDefaultTrue,
 		"external/auto/android-annotation-stubs": Bp2BuildDefaultTrueRecursively,
@@ -189,7 +190,11 @@
 		"external/ow2-asm":                       Bp2BuildDefaultTrueRecursively,
 		"external/pcre":                          Bp2BuildDefaultTrueRecursively,
 		"external/protobuf":                      Bp2BuildDefaultTrueRecursively,
+		"external/python/pyyaml/lib/yaml":        Bp2BuildDefaultTrueRecursively,
 		"external/python/six":                    Bp2BuildDefaultTrueRecursively,
+		"external/python/jinja/src":              Bp2BuildDefaultTrueRecursively,
+		"external/python/markupsafe/src":         Bp2BuildDefaultTrueRecursively,
+		"external/python/setuptools":             Bp2BuildDefaultTrueRecursively,
 		"external/rappor":                        Bp2BuildDefaultTrueRecursively,
 		"external/scudo":                         Bp2BuildDefaultTrueRecursively,
 		"external/selinux/checkpolicy":           Bp2BuildDefaultTrueRecursively,
@@ -813,6 +818,8 @@
 		"chre_flatbuffers",
 		"event_logger",
 		"hal_unit_tests",
+
+		"merge_annotation_zips_test",
 	}
 
 	Bp2buildModuleTypeAlwaysConvertList = []string{
@@ -836,6 +843,10 @@
 	// the "prebuilt_" prefix to the name, so that it's differentiable from
 	// the source versions within Soong's module graph.
 	Bp2buildModuleDoNotConvertList = []string{
+		// TODO(b/263326760): Failed already.
+		"minijail_compiler_unittest",
+		"minijail_parser_unittest",
+
 		// Depends on unconverted libandroid, libgui
 		"dvr_buffer_queue-test",
 		"dvr_display-test",
@@ -917,7 +928,10 @@
 		"libart_headers",                                             // depends on unconverted modules: art_libartbase_headers
 		"libartbase-art-gtest",                                       // depends on unconverted modules: libgtest_isolated, libart, libart-compiler, libdexfile, libprofile
 		"libartbased-art-gtest",                                      // depends on unconverted modules: libgtest_isolated, libartd, libartd-compiler, libdexfiled, libprofiled
+		"libart-runtime",                                             // depends on unconverted modules: apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, heapprofd_client_api, art_operator_srcs, libcpu_features, libodrstatslog, libelffile, art_cmdlineparser_headers, cpp-define-generator-definitions, libdexfile, libnativebridge, libnativeloader, libsigchain, libartbase, libprofile, cpp-define-generator-asm-support
+		"libart-runtime-for-test",                                    // depends on unconverted modules: apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, heapprofd_client_api, art_operator_srcs, libcpu_features, libodrstatslog, libelffile, art_cmdlineparser_headers, cpp-define-generator-definitions, libdexfile, libnativebridge, libnativeloader, libsigchain, libartbase, libprofile, cpp-define-generator-asm-support
 		"libartd",                                                    // depends on unconverted modules: art_operator_srcs, libcpu_features, libodrstatslog, libelffiled, art_cmdlineparser_headers, cpp-define-generator-definitions, libdexfiled, libnativebridge, libnativeloader, libsigchain, libartbased, libprofiled, cpp-define-generator-asm-support, apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, heapprofd_client_api
+		"libartd-runtime",                                            // depends on unconverted modules: art_operator_srcs, libcpu_features, libodrstatslog, libelffiled, art_cmdlineparser_headers, cpp-define-generator-definitions, libdexfiled, libnativebridge, libnativeloader, libsigchain, libartbased, libprofiled, cpp-define-generator-asm-support, apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, heapprofd_client_api
 		"libartd-runtime-gtest",                                      // depends on unconverted modules: libgtest_isolated, libartd-compiler, libdexfiled, libprofiled, libartbased, libartbased-art-gtest
 		"libdebuggerd",                                               // depends on unconverted module: libdexfile
 		"libdebuggerd_handler",                                       // depends on unconverted module libdebuggerd_handler_core
@@ -1513,73 +1527,6 @@
 		"libartd-runtime-gtest",
 	}
 
-	MixedBuildsDisabledList = []string{
-		"libruy_static", "libtflite_kernel_utils", // TODO(b/237315968); Depend on prebuilt stl, not from source
-
-		"art_libdexfile_dex_instruction_list_header", // breaks libart_mterp.armng, header not found
-
-		"libbrotli",               // http://b/198585397, ld.lld: error: bionic/libc/arch-arm64/generic/bionic/memmove.S:95:(.text+0x10): relocation R_AARCH64_CONDBR19 out of range: -1404176 is not in [-1048576, 1048575]; references __memcpy
-		"minijail_constants_json", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
-
-		"cap_names.h",                                  // TODO(b/204913827) runfiles need to be handled in mixed builds
-		"libcap",                                       // TODO(b/204913827) runfiles need to be handled in mixed builds
-		"libprotobuf-cpp-full", "libprotobuf-cpp-lite", // Unsupported product&vendor suffix. b/204811222 and b/204810610.
-
-		// Depends on libprotobuf-cpp-*
-		"libadb_pairing_connection",
-		"libadb_pairing_connection_static",
-		"libadb_pairing_server", "libadb_pairing_server_static",
-
-		// java_import[_host] issues
-		// tradefed prebuilts depend on libprotobuf
-		"prebuilt_tradefed",
-		"prebuilt_tradefed-test-framework",
-		// handcrafted BUILD.bazel files in //prebuilts/...
-		"prebuilt_r8lib-prebuilt",
-		"prebuilt_sdk-core-lambda-stubs",
-		"prebuilt_android-support-collections-nodeps",
-		"prebuilt_android-arch-core-common-nodeps",
-		"prebuilt_android-arch-lifecycle-common-java8-nodeps",
-		"prebuilt_android-arch-lifecycle-common-nodeps",
-		"prebuilt_android-support-annotations-nodeps",
-		"prebuilt_android-arch-paging-common-nodeps",
-		"prebuilt_android-arch-room-common-nodeps",
-		// TODO(b/217750501) exclude_dirs property not supported
-		"prebuilt_kotlin-reflect",
-		"prebuilt_kotlin-stdlib",
-		"prebuilt_kotlin-stdlib-jdk7",
-		"prebuilt_kotlin-stdlib-jdk8",
-		"prebuilt_kotlin-test",
-		// TODO(b/217750501) exclude_files property not supported
-		"prebuilt_currysrc_org.eclipse",
-
-		// TODO(b/266459895): re-enable libunwindstack
-		"libunwindstack",
-		"libunwindstack_stdout_log",
-		"libunwindstack_no_dex",
-		"libunwindstack_utils",
-		"unwind_reg_info",
-		"libunwindstack_local",
-		"unwind_for_offline",
-		"unwind",
-		"unwind_info",
-		"unwind_symbols",
-		"libEGL",
-		"libGLESv2",
-		"libc_malloc_debug",
-		"libcodec2_hidl@1.0",
-		"libcodec2_hidl@1.1",
-		"libcodec2_hidl@1.2",
-		"libfdtrack",
-		"libgui",
-		"libgui_bufferqueue_static",
-		"libmedia_codecserviceregistrant",
-		"libstagefright_bufferqueue_helper_novndk",
-		"libutils_test",
-		"libutilscallstack",
-		"mediaswcodec",
-	}
-
 	// Bazel prod-mode allowlist. Modules in this list are built by Bazel
 	// in either prod mode or staging mode.
 	ProdMixedBuildsEnabledList = []string{
@@ -1640,4 +1587,14 @@
 		"art_":        DEFAULT_PRIORITIZED_WEIGHT,
 		"ndk_library": DEFAULT_PRIORITIZED_WEIGHT,
 	}
+
+	BazelSandwichTargets = []struct {
+		Label string
+		Host  bool
+	}{
+		{
+			Label: "//build/bazel/examples/partitions:system_image",
+			Host:  false,
+		},
+	}
 )
diff --git a/android/bazel.go b/android/bazel.go
index 0d2c777..df30ff2 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -22,6 +22,7 @@
 
 	"android/soong/ui/metrics/bp2build_metrics_proto"
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android/allowlists"
@@ -426,8 +427,23 @@
 	return ModuleIncompatibility
 }
 
+func isGoModule(module blueprint.Module) bool {
+	if _, ok := module.(*bootstrap.GoPackage); ok {
+		return true
+	}
+	if _, ok := module.(*bootstrap.GoBinary); ok {
+		return true
+	}
+	return false
+}
+
 // ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel.
 func convertedToBazel(ctx BazelConversionContext, module blueprint.Module) bool {
+	// Special-case bootstrap_go_package and bootstrap_go_binary
+	// These do not implement Bazelable, but have been converted
+	if isGoModule(module) {
+		return true
+	}
 	b, ok := module.(Bazelable)
 	if !ok {
 		return false
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 94bc88b..fda8a22 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -22,6 +22,7 @@
 	"os"
 	"path"
 	"path/filepath"
+	"regexp"
 	"runtime"
 	"sort"
 	"strings"
@@ -186,6 +187,8 @@
 
 	// Returns the depsets defined in Bazel's aquery response.
 	AqueryDepsets() []bazel.AqueryDepset
+
+	QueueBazelSandwichCqueryRequests(config Config) error
 }
 
 type bazelRunner interface {
@@ -264,6 +267,10 @@
 	m.BazelRequests[key] = true
 }
 
+func (m MockBazelContext) QueueBazelSandwichCqueryRequests(config Config) error {
+	panic("unimplemented")
+}
+
 func (m MockBazelContext) GetOutputFiles(label string, _ configKey) ([]string, error) {
 	result, ok := m.LabelToOutputFiles[label]
 	if !ok {
@@ -424,6 +431,10 @@
 	panic("unimplemented")
 }
 
+func (n noopBazelContext) QueueBazelSandwichCqueryRequests(config Config) error {
+	panic("unimplemented")
+}
+
 func (n noopBazelContext) GetOutputFiles(_ string, _ configKey) ([]string, error) {
 	panic("unimplemented")
 }
@@ -1042,6 +1053,64 @@
 	allBazelCommands = []bazelCommand{aqueryCmd, cqueryCmd, buildCmd}
 )
 
+func GetBazelSandwichCqueryRequests(config Config) ([]cqueryKey, error) {
+	result := make([]cqueryKey, 0, len(allowlists.BazelSandwichTargets))
+	labelRegex := regexp.MustCompile("^@?//([a-zA-Z0-9/_-]+):[a-zA-Z0-9_-]+$")
+	// Note that bazel "targets" are different from soong "targets", the bazel targets are
+	// synonymous with soong modules, and soong targets are a configuration a module is built in.
+	for _, target := range allowlists.BazelSandwichTargets {
+		match := labelRegex.FindStringSubmatch(target.Label)
+		if match == nil {
+			return nil, fmt.Errorf("invalid label, must match `^@?//([a-zA-Z0-9/_-]+):[a-zA-Z0-9_-]+$`: %s", target.Label)
+		}
+		if _, err := os.Stat(absolutePath(match[1])); err != nil {
+			if os.IsNotExist(err) {
+				// Ignore bazel sandwich targets that don't exist.
+				continue
+			} else {
+				return nil, err
+			}
+		}
+
+		var soongTarget Target
+		if target.Host {
+			soongTarget = config.BuildOSTarget
+		} else {
+			soongTarget = config.AndroidCommonTarget
+			if soongTarget.Os.Class != Device {
+				// kernel-build-tools seems to set the AndroidCommonTarget to a linux host
+				// target for some reason, disable device builds in that case.
+				continue
+			}
+		}
+
+		result = append(result, cqueryKey{
+			label:       target.Label,
+			requestType: cquery.GetOutputFiles,
+			configKey: configKey{
+				arch:   soongTarget.Arch.String(),
+				osType: soongTarget.Os,
+			},
+		})
+	}
+	return result, nil
+}
+
+// QueueBazelSandwichCqueryRequests queues cquery requests for all the bazel labels in
+// bazel_sandwich_targets. These will later be given phony targets so that they can be built on the
+// command line.
+func (context *mixedBuildBazelContext) QueueBazelSandwichCqueryRequests(config Config) error {
+	requests, err := GetBazelSandwichCqueryRequests(config)
+	if err != nil {
+		return err
+	}
+	for _, request := range requests {
+		context.QueueBazelRequest(request.label, request.requestType, request.configKey)
+	}
+
+	return nil
+}
+
 // Issues commands to Bazel to receive results for all cquery requests
 // queued in the BazelContext.
 func (context *mixedBuildBazelContext) InvokeBazel(config Config, ctx invokeBazelContext) error {
@@ -1255,6 +1324,11 @@
 
 	executionRoot := path.Join(ctx.Config().BazelContext.OutputBase(), "execroot", "__main__")
 	bazelOutDir := path.Join(executionRoot, "bazel-out")
+	rel, err := filepath.Rel(ctx.Config().OutDir(), executionRoot)
+	if err != nil {
+		ctx.Errorf("%s", err.Error())
+	}
+	dotdotsToOutRoot := strings.Repeat("../", strings.Count(rel, "/")+1)
 	for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
 		// nil build statements are a valid case where we do not create an action because it is
 		// unnecessary or handled by other processing
@@ -1286,7 +1360,8 @@
 					})
 				}
 			}
-			createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx, depsetHashToDepset)
+			createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx, depsetHashToDepset, dotdotsToOutRoot)
+
 			desc := fmt.Sprintf("%s: %s", buildStatement.Mnemonic, buildStatement.OutputPaths)
 			rule.Build(fmt.Sprintf("bazel %d", index), desc)
 			continue
@@ -1331,6 +1406,24 @@
 			panic(fmt.Sprintf("unhandled build statement: %v", buildStatement))
 		}
 	}
+
+	// Create phony targets for all the bazel sandwich output files
+	requests, err := GetBazelSandwichCqueryRequests(ctx.Config())
+	if err != nil {
+		ctx.Errorf(err.Error())
+	}
+	for _, request := range requests {
+		files, err := ctx.Config().BazelContext.GetOutputFiles(request.label, request.configKey)
+		if err != nil {
+			ctx.Errorf(err.Error())
+		}
+		filesAsPaths := make([]Path, 0, len(files))
+		for _, file := range files {
+			filesAsPaths = append(filesAsPaths, PathForBazelOut(ctx, file))
+		}
+		ctx.Phony("bazel_sandwich", filesAsPaths...)
+	}
+	ctx.Phony("checkbuild", PathForPhony(ctx, "bazel_sandwich"))
 }
 
 // Returns a out dir path for a sandboxed mixed build action
@@ -1344,7 +1437,7 @@
 }
 
 // Register bazel-owned build statements (obtained from the aquery invocation).
-func createCommand(cmd *RuleBuilderCommand, buildStatement *bazel.BuildStatement, executionRoot string, bazelOutDir string, ctx BuilderContext, depsetHashToDepset map[string]bazel.AqueryDepset) {
+func createCommand(cmd *RuleBuilderCommand, buildStatement *bazel.BuildStatement, executionRoot string, bazelOutDir string, ctx BuilderContext, depsetHashToDepset map[string]bazel.AqueryDepset, dotdotsToOutRoot string) {
 	// executionRoot is the action cwd.
 	if buildStatement.ShouldRunInSbox {
 		// mkdir -p ensures that the directory exists when run via sbox
@@ -1367,14 +1460,17 @@
 		cmd.Flag(pair.Key + "=" + pair.Value)
 	}
 
+	command := buildStatement.Command
+	command = strings.ReplaceAll(command, "{DOTDOTS_TO_OUTPUT_ROOT}", dotdotsToOutRoot)
+
 	// The actual Bazel action.
-	if len(buildStatement.Command) > 16*1024 {
+	if len(command) > 16*1024 {
 		commandFile := PathForBazelOut(ctx, buildStatement.OutputPaths[0]+".sh")
-		WriteFileRule(ctx, commandFile, buildStatement.Command)
+		WriteFileRule(ctx, commandFile, command)
 
 		cmd.Text("bash").Text(buildStatement.OutputPaths[0] + ".sh").Implicit(commandFile)
 	} else {
-		cmd.Text(buildStatement.Command)
+		cmd.Text(command)
 	}
 
 	for _, outputPath := range buildStatement.OutputPaths {
@@ -1403,6 +1499,9 @@
 			cmd.Implicit(PathForPhony(ctx, otherDepsetName))
 		}
 	}
+	for _, implicitPath := range buildStatement.ImplicitDeps {
+		cmd.Implicit(PathForArbitraryOutput(ctx, implicitPath))
+	}
 
 	if depfile := buildStatement.Depfile; depfile != nil {
 		// The paths in depfile are relative to `executionRoot`.
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index e08a471..9a3c8fc 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -181,7 +181,7 @@
 
 		cmd := RuleBuilderCommand{}
 		ctx := builderContextForTests{PathContextForTesting(TestConfig("out", nil, "", nil))}
-		createCommand(&cmd, got[0], "test/exec_root", "test/bazel_out", ctx, map[string]bazel.AqueryDepset{})
+		createCommand(&cmd, got[0], "test/exec_root", "test/bazel_out", ctx, map[string]bazel.AqueryDepset{}, "")
 		if actual, expected := cmd.buf.String(), testCase.command; expected != actual {
 			t.Errorf("expected: [%s], actual: [%s]", expected, actual)
 		}
@@ -224,7 +224,7 @@
 
 	cmd := RuleBuilderCommand{}
 	ctx := builderContextForTests{PathContextForTesting(TestConfig("out", nil, "", nil))}
-	createCommand(&cmd, statement, "test/exec_root", "test/bazel_out", ctx, map[string]bazel.AqueryDepset{})
+	createCommand(&cmd, statement, "test/exec_root", "test/bazel_out", ctx, map[string]bazel.AqueryDepset{}, "")
 	// Assert that the output is generated in an intermediate directory
 	// fe05bcdcdc4928012781a5f1a2a77cbb5398e106 is the sha1 checksum of "one"
 	if actual, expected := cmd.outputs[0].String(), "out/soong/mixed_build_sbox_intermediates/fe05bcdcdc4928012781a5f1a2a77cbb5398e106/test/exec_root/one"; expected != actual {
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index 8956a18..b08a4ca 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -124,6 +124,7 @@
 		labels.Includes = []bazel.Label{}
 		return labels
 	}
+	modules = FirstUniqueStrings(modules)
 	for _, module := range modules {
 		bpText := module
 		if m := SrcIsModule(module); m == "" {
@@ -430,7 +431,7 @@
 
 func BazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module) string {
 	// TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
-	if !convertedToBazel(ctx, module) {
+	if !convertedToBazel(ctx, module) || isGoModule(module) {
 		return bp2buildModuleLabel(ctx, module)
 	}
 	b, _ := module.(Bazelable)
diff --git a/android/config.go b/android/config.go
index 2a243ee..eb89493 100644
--- a/android/config.go
+++ b/android/config.go
@@ -170,6 +170,19 @@
 	return c.config.TestProductVariables != nil
 }
 
+// DisableHiddenApiChecks returns true if hiddenapi checks have been disabled.
+// For 'eng' target variant hiddenapi checks are disabled by default for performance optimisation,
+// but can be enabled by setting environment variable ENABLE_HIDDENAPI_FLAGS=true.
+// For other target variants hiddenapi check are enabled by default but can be disabled by
+// setting environment variable UNSAFE_DISABLE_HIDDENAPI_FLAGS=true.
+// If both ENABLE_HIDDENAPI_FLAGS=true and UNSAFE_DISABLE_HIDDENAPI_FLAGS=true, then
+// ENABLE_HIDDENAPI_FLAGS=true will be triggered and hiddenapi checks will be considered enabled.
+func (c Config) DisableHiddenApiChecks() bool {
+	return !c.IsEnvTrue("ENABLE_HIDDENAPI_FLAGS") &&
+		(c.IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") ||
+			Bool(c.productVariables.Eng))
+}
+
 // MaxPageSizeSupported returns the max page size supported by the device. This
 // value will define the ELF segment alignment for binaries (executables and
 // shared libraries).
@@ -401,6 +414,12 @@
 	return nil
 }
 
+type productVariableStarlarkRepresentation struct {
+	soongType   string
+	selectable  bool
+	archVariant bool
+}
+
 func saveToBazelConfigFile(config *ProductVariables, outDir string) error {
 	dir := filepath.Join(outDir, bazel.SoongInjectionDirName, "product_config")
 	err := createDirIfNonexistent(dir, os.ModePerm)
@@ -408,32 +427,43 @@
 		return fmt.Errorf("Could not create dir %s: %s", dir, err)
 	}
 
-	nonArchVariantProductVariables := []string{}
-	archVariantProductVariables := []string{}
+	allProductVariablesType := reflect.TypeOf((*ProductVariables)(nil)).Elem()
+	productVariablesInfo := make(map[string]productVariableStarlarkRepresentation)
 	p := variableProperties{}
 	t := reflect.TypeOf(p.Product_variables)
 	for i := 0; i < t.NumField(); i++ {
 		f := t.Field(i)
-		nonArchVariantProductVariables = append(nonArchVariantProductVariables, strings.ToLower(f.Name))
-		if proptools.HasTag(f, "android", "arch_variant") {
-			archVariantProductVariables = append(archVariantProductVariables, strings.ToLower(f.Name))
+		if f.Name == "Pdk" {
+			// Pdk is deprecated and has no effect as of aosp/1319667
+			continue
+		}
+		archVariant := proptools.HasTag(f, "android", "arch_variant")
+		if mainProductVariablesStructField, ok := allProductVariablesType.FieldByName(f.Name); ok {
+			productVariablesInfo[f.Name] = productVariableStarlarkRepresentation{
+				soongType:   stringRepresentationOfSimpleType(mainProductVariablesStructField.Type),
+				selectable:  true,
+				archVariant: archVariant,
+			}
+		} else {
+			panic("Unknown variable " + f.Name)
 		}
 	}
 
-	nonArchVariantProductVariablesJson := starlark_fmt.PrintStringList(nonArchVariantProductVariables, 0)
-	if err != nil {
-		return fmt.Errorf("cannot marshal product variable data: %s", err.Error())
-	}
-
-	archVariantProductVariablesJson := starlark_fmt.PrintStringList(archVariantProductVariables, 0)
-	if err != nil {
-		return fmt.Errorf("cannot marshal arch variant product variable data: %s", err.Error())
-	}
-
 	err = pathtools.WriteFileIfChanged(filepath.Join(dir, "product_variable_constants.bzl"), []byte(fmt.Sprintf(`
-product_var_constraints = %s
-arch_variant_product_var_constraints = %s
-`, nonArchVariantProductVariablesJson, archVariantProductVariablesJson)), 0644)
+# product_var_constant_info is a map of product variables to information about them. The fields are:
+# - soongType: The type of the product variable as it appears in soong's ProductVariables struct.
+#              examples are string, bool, int, *bool, *string, []string, etc. This may be an overly
+#              conservative estimation of the type, for example a *bool could oftentimes just be a
+#              bool that defaults to false.
+# - selectable: if this product variable can be selected on in Android.bp/build files. This means
+#               it's listed in the "variableProperties" soong struct. Currently all variables in
+#               this list are selectable because we only need the selectable ones at the moment,
+#               but the list may be expanded later.
+# - archVariant: If the variable is tagged as arch variant in the "variableProperties" struct.
+product_var_constant_info = %s
+product_var_constraints = [k for k, v in product_var_constant_info.items() if v.selectable]
+arch_variant_product_var_constraints = [k for k, v in product_var_constant_info.items() if v.selectable and v.archVariant]
+`, starlark_fmt.PrintAny(productVariablesInfo, 0))), 0644)
 	if err != nil {
 		return fmt.Errorf("Could not write .bzl config file %s", err)
 	}
@@ -446,6 +476,23 @@
 	return nil
 }
 
+func stringRepresentationOfSimpleType(ty reflect.Type) string {
+	switch ty.Kind() {
+	case reflect.String:
+		return "string"
+	case reflect.Bool:
+		return "bool"
+	case reflect.Int:
+		return "int"
+	case reflect.Slice:
+		return "[]" + stringRepresentationOfSimpleType(ty.Elem())
+	case reflect.Pointer:
+		return "*" + stringRepresentationOfSimpleType(ty.Elem())
+	default:
+		panic("unimplemented type: " + ty.Kind().String())
+	}
+}
+
 // NullConfig returns a mostly empty Config for use by standalone tools like dexpreopt_gen that
 // use the android package.
 func NullConfig(outDir, soongOutDir string) Config {
@@ -1909,6 +1956,10 @@
 	return c.config.productVariables.RequiresInsecureExecmemForSwiftshader
 }
 
+func (c *deviceConfig) Release_aidl_use_unfrozen() bool {
+	return Bool(c.config.productVariables.Release_aidl_use_unfrozen)
+}
+
 func (c *config) SelinuxIgnoreNeverallows() bool {
 	return c.productVariables.SelinuxIgnoreNeverallows
 }
diff --git a/android/module.go b/android/module.go
index 384776a..b982019 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1373,15 +1373,15 @@
 		}
 	}
 
-	productConfigEnabledLabels := []bazel.Label{}
+	productConfigEnabledAttribute := bazel.LabelListAttribute{}
 	// TODO(b/234497586): Soong config variables and product variables have different overriding behavior, we
 	// should handle it correctly
 	if !proptools.BoolDefault(enabledProperty.Value, true) && !neitherHostNorDevice {
 		// If the module is not enabled by default, then we can check if a
 		// product variable enables it
-		productConfigEnabledLabels = productVariableConfigEnableLabels(ctx)
+		productConfigEnabledAttribute = productVariableConfigEnableAttribute(ctx)
 
-		if len(productConfigEnabledLabels) > 0 {
+		if len(productConfigEnabledAttribute.ConfigurableValues) > 0 {
 			// In this case, an existing product variable configuration overrides any
 			// module-level `enable: false` definition
 			newValue := true
@@ -1389,10 +1389,6 @@
 		}
 	}
 
-	productConfigEnabledAttribute := bazel.MakeLabelListAttribute(bazel.LabelList{
-		productConfigEnabledLabels, nil,
-	})
-
 	platformEnabledAttribute, err := enabledProperty.ToLabelListAttribute(
 		bazel.LabelList{[]bazel.Label{{Label: "@platforms//:incompatible"}}, nil},
 		bazel.LabelList{[]bazel.Label{}, nil})
@@ -1423,31 +1419,28 @@
 
 // Check product variables for `enabled: true` flag override.
 // Returns a list of the constraint_value targets who enable this override.
-func productVariableConfigEnableLabels(ctx *topDownMutatorContext) []bazel.Label {
+func productVariableConfigEnableAttribute(ctx *topDownMutatorContext) bazel.LabelListAttribute {
+	result := bazel.LabelListAttribute{}
 	productVariableProps := ProductVariableProperties(ctx, ctx.Module())
-	productConfigEnablingTargets := []bazel.Label{}
-	const propName = "Enabled"
-	if productConfigProps, exists := productVariableProps[propName]; exists {
+	if productConfigProps, exists := productVariableProps["Enabled"]; exists {
 		for productConfigProp, prop := range productConfigProps {
 			flag, ok := prop.(*bool)
 			if !ok {
-				ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName))
+				ctx.ModuleErrorf("Could not convert product variable enabled property")
 			}
 
 			if *flag {
 				axis := productConfigProp.ConfigurationAxis()
-				targetLabel := axis.SelectKey(productConfigProp.SelectKey())
-				productConfigEnablingTargets = append(productConfigEnablingTargets, bazel.Label{
-					Label: targetLabel,
-				})
+				result.SetSelectValue(axis, bazel.ConditionsDefaultConfigKey, bazel.MakeLabelList([]bazel.Label{{Label: "@platforms//:incompatible"}}))
+				result.SetSelectValue(axis, productConfigProp.SelectKey(), bazel.LabelList{Includes: []bazel.Label{}})
 			} else {
 				// TODO(b/210546943): handle negative case where `enabled: false`
-				ctx.ModuleErrorf("`enabled: false` is not currently supported for configuration variables. See b/210546943", proptools.PropertyNameForField(propName))
+				ctx.ModuleErrorf("`enabled: false` is not currently supported for configuration variables. See b/210546943")
 			}
 		}
 	}
 
-	return productConfigEnablingTargets
+	return result
 }
 
 // A ModuleBase object contains the properties that are common to all Android
@@ -4029,43 +4022,26 @@
 	JavaBp2buildTargetName() string
 }
 
-// PartitionXsdSrcs partitions srcs into xsd_config modules and others
-// Since xsd_config are soong modules, we cannot use file extension for partitioning
-func PartitionXsdSrcs(ctx BazelConversionPathContext, srcs []string) ([]string, []string) {
-	//isXsd returns true if src is a soong module of type xsd_config
-	isXsd := func(src string) bool {
-		mod, exists := ctx.ModuleFromName(src)
+// XsdModuleToTargetName is a function that takes an XsdConfigBp2buildTarget
+type XsdModuleToTargetName func(xsd XsdConfigBp2buildTargets) string
+
+// XsdLabelMapper returns a bazel.LabelMapper for partitioning XSD sources/headers given an
+// XsdModuleToTargetName function.
+func XsdLabelMapper(targetName XsdModuleToTargetName) bazel.LabelMapper {
+	return func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
+		mod, exists := ctx.ModuleFromName(label.OriginalModuleName)
 		if !exists {
-			return false
+			return label.Label, false
 		}
-		_, _isXsd := mod.(XsdConfigBp2buildTargets)
-		return _isXsd
-	}
-	nonXsd := []string{}
-	xsd := []string{}
-
-	for _, src := range srcs {
-		if isXsd(src) {
-			xsd = append(xsd, src)
-		} else {
-			nonXsd = append(nonXsd, src)
+		xsdMod, isXsd := mod.(XsdConfigBp2buildTargets)
+		if !isXsd {
+			return label.Label, false
 		}
-	}
 
-	return nonXsd, xsd
-}
-
-// Replaces //a/b/my_xsd_config with //a/b/my_xsd_config-{cpp|java}
-// The new target name is provided by the `targetName` callback function
-func XsdConfigBp2buildTarget(ctx BazelConversionPathContext, mod blueprint.Module, targetName func(xsd XsdConfigBp2buildTargets) string) string {
-	xsd, isXsd := mod.(XsdConfigBp2buildTargets)
-	if !isXsd {
-		ctx.ModuleErrorf("xsdConfigJavaTarget called on %v, which is not an xsd_config", mod)
+		// Remove the base module name
+		ret := strings.TrimSuffix(label.Label, mod.Name())
+		// Append the language specific target name
+		ret += targetName(xsdMod)
+		return ret, true
 	}
-	ret := BazelModuleLabel(ctx, mod)
-	// Remove the base module name
-	ret = strings.TrimSuffix(ret, mod.Name())
-	// Append the language specific target name
-	ret += targetName(xsd)
-	return ret
 }
diff --git a/android/paths.go b/android/paths.go
index e16cb37..325a953 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1029,16 +1029,16 @@
 	return p
 }
 
+func (p basePath) RelativeToTop() Path {
+	ensureTestOnly()
+	return p
+}
+
 // SourcePath is a Path representing a file path rooted from SrcDir
 type SourcePath struct {
 	basePath
 }
 
-func (p SourcePath) RelativeToTop() Path {
-	ensureTestOnly()
-	return p
-}
-
 var _ Path = SourcePath{}
 
 func (p SourcePath) withRel(rel string) SourcePath {
@@ -1126,6 +1126,16 @@
 	return path
 }
 
+// PathForArbitraryOutput creates a path for the given components. Unlike PathForOutput,
+// the path is relative to the root of the output folder, not the out/soong folder.
+func PathForArbitraryOutput(ctx PathContext, pathComponents ...string) Path {
+	p, err := validatePath(pathComponents...)
+	if err != nil {
+		reportPathError(ctx, err)
+	}
+	return basePath{path: filepath.Join(ctx.Config().OutDir(), p)}
+}
+
 // MaybeExistentPathForSource joins the provided path components and validates that the result
 // neither escapes the source dir nor is in the out dir.
 // It does not validate whether the path exists.
diff --git a/android/variable.go b/android/variable.go
index f07ab56..03a80c1 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -160,6 +160,7 @@
 			}
 		}
 
+		// Deprecated, has no effect as of aosp/1319667
 		Pdk struct {
 			Enabled *bool `android:"arch_variant"`
 		} `android:"arch_variant"`
@@ -184,6 +185,13 @@
 			Srcs         []string `android:"arch_variant"`
 			Exclude_srcs []string `android:"arch_variant"`
 		} `android:"arch_variant"`
+
+		// release_aidl_use_unfrozen is "true" when a device can
+		// use the unfrozen versions of AIDL interfaces.
+		Release_aidl_use_unfrozen struct {
+			Cflags []string
+			Cmd    *string
+		}
 	} `android:"arch_variant"`
 }
 
@@ -462,6 +470,8 @@
 
 	SelinuxIgnoreNeverallows bool `json:",omitempty"`
 
+	Release_aidl_use_unfrozen *bool `json:",omitempty"`
+
 	SepolicyFreezeTestExtraDirs         []string `json:",omitempty"`
 	SepolicyFreezeTestExtraPrebuiltDirs []string `json:",omitempty"`
 
@@ -734,7 +744,9 @@
 			dst = append(dst, src...)
 			(*p)[propertyName][key] = dst
 		default:
-			panic(fmt.Errorf("TODO: handle merging value %#v", existing))
+			if existing != propertyValue {
+				panic(fmt.Errorf("TODO: handle merging value %#v", existing))
+			}
 		}
 	} else {
 		(*p)[propertyName][key] = propertyValue
@@ -947,7 +959,7 @@
 						productConfigProperties.AddSoongConfigProperty(propertyName, namespace, soongConfigVariableName, soongConfigVariableValue, os.Name, property.Interface())
 					}
 				}
-			} else {
+			} else if !archOrOsSpecificStruct.IsZero() {
 				// One problem with supporting additional fields is that if multiple branches of
 				// "target" overlap, we don't want them to be in the same select statement (aka
 				// configuration axis). "android" and "host" are disjoint, so it's ok that we only
diff --git a/apex/apex.go b/apex/apex.go
index 325ca00..b26d1d2 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -993,7 +993,7 @@
 	// the non-system APEXes because the VNDK libraries won't be included (and duped) in the
 	// APEX, but shared across APEXes via the VNDK APEX.
 	useVndk := a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && mctx.Config().EnforceProductPartitionInterface())
-	excludeVndkLibs := useVndk && proptools.Bool(a.properties.Use_vndk_as_stable)
+	excludeVndkLibs := useVndk && a.useVndkAsStable(mctx)
 	if proptools.Bool(a.properties.Use_vndk_as_stable) {
 		if !useVndk {
 			mctx.PropertyErrorf("use_vndk_as_stable", "not supported for system/system_ext APEXes")
@@ -2394,7 +2394,7 @@
 	// tags used below are private (e.g. `cc.sharedDepTag`).
 	if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) {
 		if ch, ok := child.(*cc.Module); ok {
-			if ch.UseVndk() && proptools.Bool(a.properties.Use_vndk_as_stable) && ch.IsVndk() {
+			if ch.UseVndk() && a.useVndkAsStable(ctx) && ch.IsVndk() {
 				vctx.requireNativeLibs = append(vctx.requireNativeLibs, ":vndk")
 				return false
 			}
@@ -3716,3 +3716,12 @@
 func (a *apexBundle) IsTestApex() bool {
 	return a.testApex
 }
+
+func (a *apexBundle) useVndkAsStable(ctx android.BaseModuleContext) bool {
+	// VNDK cannot be linked if it is deprecated
+	if ctx.Config().IsVndkDeprecated() {
+		return false
+	}
+
+	return proptools.Bool(a.properties.Use_vndk_as_stable)
+}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index df138e0..9dba08e 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -3029,7 +3029,11 @@
 			vendor: true,
 			shared_libs: ["libvndk", "libvendor"],
 		}
-	`)
+	`,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.TestProductVariables.KeepVndk = proptools.BoolPtr(true)
+		}),
+	)
 
 	vendorVariant := "android_vendor.29_arm64_armv8-a"
 
@@ -6790,6 +6794,10 @@
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
 		}
+		override_apex {
+			name: "myoverrideapex",
+			base: "bar",
+		}
 	`)
 
 	fooManifestRule := result.ModuleForTests("foo", "android_common_foo_image").Rule("apexManifestRule")
@@ -6806,6 +6814,12 @@
 	if barActualDefaultVersion != barExpectedDefaultVersion {
 		t.Errorf("expected to find defaultVersion %q; got %q", barExpectedDefaultVersion, barActualDefaultVersion)
 	}
+
+	overrideBarManifestRule := result.ModuleForTests("bar", "android_common_myoverrideapex_bar_image").Rule("apexManifestRule")
+	overrideBarActualDefaultVersion := overrideBarManifestRule.Args["default_version"]
+	if overrideBarActualDefaultVersion != barExpectedDefaultVersion {
+		t.Errorf("expected to find defaultVersion %q; got %q", barExpectedDefaultVersion, barActualDefaultVersion)
+	}
 }
 
 func TestApexAvailable_ApexAvailableName(t *testing.T) {
diff --git a/bazel/aquery.go b/bazel/aquery.go
index 3428328..d77d59a 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -17,15 +17,15 @@
 import (
 	"crypto/sha256"
 	"encoding/base64"
+	"encoding/json"
 	"fmt"
 	"path/filepath"
+	analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
 	"reflect"
 	"sort"
 	"strings"
 	"sync"
 
-	analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
-
 	"github.com/google/blueprint/metrics"
 	"github.com/google/blueprint/proptools"
 	"google.golang.org/protobuf/proto"
@@ -119,6 +119,10 @@
 	// If ShouldRunInSbox is true, Soong will use sbox to created an isolated environment
 	// and run the mixed build action there
 	ShouldRunInSbox bool
+	// A list of files to add as implicit deps to the outputs of this BuildStatement.
+	// Unlike most properties in BuildStatement, these paths must be relative to the root of
+	// the whole out/ folder, instead of relative to ctx.Config().BazelContext.OutputBase()
+	ImplicitDeps []string
 }
 
 // A helper type for aquery processing which facilitates retrieval of path IDs from their
@@ -459,7 +463,7 @@
 // escapes the args received from aquery and creates a command string
 func commandString(actionEntry *analysis_v2_proto.Action) string {
 	switch actionEntry.Mnemonic {
-	case "GoCompilePkg":
+	case "GoCompilePkg", "GoStdlib":
 		argsEscaped := []string{}
 		for _, arg := range actionEntry.Arguments {
 			if arg == "" {
@@ -581,6 +585,72 @@
 	}, nil
 }
 
+type bazelSandwichJson struct {
+	Target         string   `json:"target"`
+	DependOnTarget *bool    `json:"depend_on_target,omitempty"`
+	ImplicitDeps   []string `json:"implicit_deps"`
+}
+
+func (a *aqueryArtifactHandler) unresolvedSymlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
+	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
+	if err != nil {
+		return nil, err
+	}
+	if len(actionEntry.InputDepSetIds) != 0 || len(outputPaths) != 1 {
+		return nil, fmt.Errorf("expected 0 inputs and 1 output to symlink action, got: input %q, output %q", actionEntry.InputDepSetIds, outputPaths)
+	}
+	target := actionEntry.UnresolvedSymlinkTarget
+	if target == "" {
+		return nil, fmt.Errorf("expected an unresolved_symlink_target, but didn't get one")
+	}
+	if filepath.Clean(target) != target {
+		return nil, fmt.Errorf("expected %q, got %q", filepath.Clean(target), target)
+	}
+	if strings.HasPrefix(target, "/") {
+		return nil, fmt.Errorf("no absolute symlinks allowed: %s", target)
+	}
+
+	out := outputPaths[0]
+	outDir := filepath.Dir(out)
+	var implicitDeps []string
+	if strings.HasPrefix(target, "bazel_sandwich:") {
+		j := bazelSandwichJson{}
+		err := json.Unmarshal([]byte(target[len("bazel_sandwich:"):]), &j)
+		if err != nil {
+			return nil, err
+		}
+		if proptools.BoolDefault(j.DependOnTarget, true) {
+			implicitDeps = append(implicitDeps, j.Target)
+		}
+		implicitDeps = append(implicitDeps, j.ImplicitDeps...)
+		dotDotsToReachCwd := ""
+		if outDir != "." {
+			dotDotsToReachCwd = strings.Repeat("../", strings.Count(outDir, "/")+1)
+		}
+		target = proptools.ShellEscapeIncludingSpaces(j.Target)
+		target = "{DOTDOTS_TO_OUTPUT_ROOT}" + dotDotsToReachCwd + target
+	} else {
+		target = proptools.ShellEscapeIncludingSpaces(target)
+	}
+
+	outDir = proptools.ShellEscapeIncludingSpaces(outDir)
+	out = proptools.ShellEscapeIncludingSpaces(out)
+	// Use absolute paths, because some soong actions don't play well with relative paths (for example, `cp -d`).
+	command := fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, target)
+	symlinkPaths := outputPaths[:]
+
+	buildStatement := &BuildStatement{
+		Command:      command,
+		Depfile:      depfile,
+		OutputPaths:  outputPaths,
+		Env:          actionEntry.EnvironmentVariables,
+		Mnemonic:     actionEntry.Mnemonic,
+		SymlinkPaths: symlinkPaths,
+		ImplicitDeps: implicitDeps,
+	}
+	return buildStatement, nil
+}
+
 func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
 	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
 	if err != nil {
@@ -690,6 +760,8 @@
 		return a.fileWriteActionBuildStatement(actionEntry)
 	case "SymlinkTree":
 		return a.symlinkTreeActionBuildStatement(actionEntry)
+	case "UnresolvedSymlink":
+		return a.unresolvedSymlinkActionBuildStatement(actionEntry)
 	}
 
 	if len(actionEntry.Arguments) < 1 {
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index 19a584f..32c87a0 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -357,9 +357,11 @@
 	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 	if expected := 1; len(actual) != expected {
 		t.Fatalf("Expected %d build statements, got %d", expected, len(actual))
+		return
 	}
 
 	bs := actual[0]
@@ -544,6 +546,7 @@
 	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 	assertBuildStatements(t, []*BuildStatement{
 		&BuildStatement{
@@ -756,9 +759,11 @@
 	actualBuildStatements, actualDepsets, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 	if expected := 2; len(actualBuildStatements) != expected {
 		t.Fatalf("Expected %d build statements, got %d %#v", expected, len(actualBuildStatements), actualBuildStatements)
+		return
 	}
 
 	expectedDepsetFiles := [][]string{
@@ -859,6 +864,7 @@
 
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 
 	expectedBuildStatements := []*BuildStatement{
@@ -907,6 +913,7 @@
 	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 
 	expectedBuildStatements := []*BuildStatement{
@@ -1017,6 +1024,7 @@
 	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 
 	expectedBuildStatements := []*BuildStatement{
@@ -1088,6 +1096,7 @@
 	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 	assertBuildStatements(t, []*BuildStatement{
 		&BuildStatement{
@@ -1126,6 +1135,7 @@
 	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
+		return
 	}
 	assertBuildStatements(t, []*BuildStatement{
 		&BuildStatement{
@@ -1136,6 +1146,126 @@
 	}, actual)
 }
 
+func TestUnresolvedSymlink(t *testing.T) {
+	const inputString = `
+{
+ "artifacts": [
+   { "id": 1, "path_fragment_id": 1 }
+ ],
+ "actions": [{
+   "target_id": 1,
+   "action_key": "x",
+   "mnemonic": "UnresolvedSymlink",
+   "configuration_id": 1,
+   "output_ids": [1],
+   "primary_output_id": 1,
+   "execution_platform": "//build/bazel/platforms:linux_x86_64",
+   "unresolved_symlink_target": "symlink/target"
+ }],
+ "path_fragments": [
+   { "id": 1, "label": "path/to/symlink" }
+ ]
+}
+`
+	data, err := JsonToActionGraphContainer(inputString)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
+	if err != nil {
+		t.Errorf("Unexpected error %q", err)
+		return
+	}
+	assertBuildStatements(t, []*BuildStatement{{
+		Command:      "mkdir -p path/to && rm -f path/to/symlink && ln -sf symlink/target path/to/symlink",
+		OutputPaths:  []string{"path/to/symlink"},
+		Mnemonic:     "UnresolvedSymlink",
+		SymlinkPaths: []string{"path/to/symlink"},
+	}}, actual)
+}
+
+func TestUnresolvedSymlinkBazelSandwich(t *testing.T) {
+	const inputString = `
+{
+ "artifacts": [
+   { "id": 1, "path_fragment_id": 1 }
+ ],
+ "actions": [{
+   "target_id": 1,
+   "action_key": "x",
+   "mnemonic": "UnresolvedSymlink",
+   "configuration_id": 1,
+   "output_ids": [1],
+   "primary_output_id": 1,
+   "execution_platform": "//build/bazel/platforms:linux_x86_64",
+   "unresolved_symlink_target": "bazel_sandwich:{\"target\":\"target/product/emulator_x86_64/system\"}"
+ }],
+ "path_fragments": [
+   { "id": 1, "label": "path/to/symlink" }
+ ]
+}
+`
+	data, err := JsonToActionGraphContainer(inputString)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
+	if err != nil {
+		t.Errorf("Unexpected error %q", err)
+		return
+	}
+	assertBuildStatements(t, []*BuildStatement{{
+		Command:      "mkdir -p path/to && rm -f path/to/symlink && ln -sf {DOTDOTS_TO_OUTPUT_ROOT}../../target/product/emulator_x86_64/system path/to/symlink",
+		OutputPaths:  []string{"path/to/symlink"},
+		Mnemonic:     "UnresolvedSymlink",
+		SymlinkPaths: []string{"path/to/symlink"},
+		ImplicitDeps: []string{"target/product/emulator_x86_64/system"},
+	}}, actual)
+}
+
+func TestUnresolvedSymlinkBazelSandwichWithAlternativeDeps(t *testing.T) {
+	const inputString = `
+{
+ "artifacts": [
+   { "id": 1, "path_fragment_id": 1 }
+ ],
+ "actions": [{
+   "target_id": 1,
+   "action_key": "x",
+   "mnemonic": "UnresolvedSymlink",
+   "configuration_id": 1,
+   "output_ids": [1],
+   "primary_output_id": 1,
+   "execution_platform": "//build/bazel/platforms:linux_x86_64",
+   "unresolved_symlink_target": "bazel_sandwich:{\"depend_on_target\":false,\"implicit_deps\":[\"target/product/emulator_x86_64/obj/PACKAGING/systemimage_intermediates/staging_dir.stamp\"],\"target\":\"target/product/emulator_x86_64/system\"}"
+ }],
+ "path_fragments": [
+   { "id": 1, "label": "path/to/symlink" }
+ ]
+}
+`
+	data, err := JsonToActionGraphContainer(inputString)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
+	if err != nil {
+		t.Errorf("Unexpected error %q", err)
+		return
+	}
+	assertBuildStatements(t, []*BuildStatement{{
+		Command:      "mkdir -p path/to && rm -f path/to/symlink && ln -sf {DOTDOTS_TO_OUTPUT_ROOT}../../target/product/emulator_x86_64/system path/to/symlink",
+		OutputPaths:  []string{"path/to/symlink"},
+		Mnemonic:     "UnresolvedSymlink",
+		SymlinkPaths: []string{"path/to/symlink"},
+		// Note that the target of the symlink, target/product/emulator_x86_64/system, is not listed here
+		ImplicitDeps: []string{"target/product/emulator_x86_64/obj/PACKAGING/systemimage_intermediates/staging_dir.stamp"},
+	}}, actual)
+}
+
 func assertError(t *testing.T, err error, expected string) {
 	t.Helper()
 	if err == nil {
@@ -1201,6 +1331,9 @@
 	if !reflect.DeepEqual(sortedStrings(first.SymlinkPaths), sortedStrings(second.SymlinkPaths)) {
 		return "SymlinkPaths"
 	}
+	if !reflect.DeepEqual(sortedStrings(first.ImplicitDeps), sortedStrings(second.ImplicitDeps)) {
+		return "ImplicitDeps"
+	}
 	if first.Depfile != second.Depfile {
 		return "Depfile"
 	}
diff --git a/bazel/configurability.go b/bazel/configurability.go
index d962a1d..671e5c1 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -67,7 +67,7 @@
 
 	ConditionsDefaultSelectKey = "//conditions:default"
 
-	productVariableBazelPackage = "//build/bazel/product_variables"
+	productVariableBazelPackage = "//build/bazel/product_config/config_settings"
 
 	AndroidAndInApex = "android-in_apex"
 	AndroidPlatform  = "system"
diff --git a/bazel/properties.go b/bazel/properties.go
index 15af09b..702c31c 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -288,6 +288,41 @@
 	return result
 }
 
+// FirstUniqueBazelLabelListAttribute takes a LabelListAttribute and makes the LabelList for
+// each axis/configuration by keeping the first instance of a Label and omitting all subsequent
+// repetitions.
+func FirstUniqueBazelLabelListAttribute(attr LabelListAttribute) LabelListAttribute {
+	var result LabelListAttribute
+	result.Value = FirstUniqueBazelLabelList(attr.Value)
+	if attr.HasConfigurableValues() {
+		result.ConfigurableValues = make(configurableLabelLists)
+	}
+	for axis, configToLabels := range attr.ConfigurableValues {
+		for c, l := range configToLabels {
+			result.SetSelectValue(axis, c, FirstUniqueBazelLabelList(l))
+		}
+	}
+
+	return result
+}
+
+// SubtractBazelLabelListAttribute subtract needle from haystack for LabelList in each
+// axis/configuration.
+func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute {
+	var result LabelListAttribute
+	result.Value = SubtractBazelLabelList(haystack.Value, needle.Value)
+	if haystack.HasConfigurableValues() {
+		result.ConfigurableValues = make(configurableLabelLists)
+	}
+	for axis, configToLabels := range haystack.ConfigurableValues {
+		for haystackConfig, haystackLabels := range configToLabels {
+			result.SetSelectValue(axis, haystackConfig, SubtractBazelLabelList(haystackLabels, needle.SelectValue(axis, haystackConfig)))
+		}
+	}
+
+	return result
+}
+
 type Attribute interface {
 	HasConfigurableValues() bool
 }
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index c56d11f..c98ae0e 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -125,6 +125,63 @@
 		}
 	}
 }
+
+func TestSubtractBazelLabelListAttribute(t *testing.T) {
+	testCases := []struct {
+		haystack LabelListAttribute
+		needle   LabelListAttribute
+		expected LabelListAttribute
+	}{
+		{
+			haystack: LabelListAttribute{
+				Value: makeLabelList(
+					[]string{"a", "b", "a", "c"},
+					[]string{"x", "x", "y", "z"},
+				),
+				ConfigurableValues: configurableLabelLists{
+					ArchConfigurationAxis: labelListSelectValues{
+						"arm": makeLabelList([]string{"arm_1", "arm_2"}, []string{}),
+						"x86": makeLabelList([]string{"x86_3", "x86_4", "x86_5"}, []string{"x86_5"}),
+					},
+				},
+			},
+			needle: LabelListAttribute{
+				Value: makeLabelList(
+					[]string{"d", "a"},
+					[]string{"x", "y2", "z2"},
+				),
+				ConfigurableValues: configurableLabelLists{
+					ArchConfigurationAxis: labelListSelectValues{
+						"arm": makeLabelList([]string{"arm_1", "arm_3"}, []string{}),
+						"x86": makeLabelList([]string{"x86_3", "x86_4"}, []string{"x86_6"}),
+					},
+				},
+			},
+			expected: LabelListAttribute{
+				Value: makeLabelList(
+					[]string{"b", "c"},
+					[]string{"x", "x", "y", "z"},
+				),
+				ConfigurableValues: configurableLabelLists{
+					ArchConfigurationAxis: labelListSelectValues{
+						"arm": makeLabelList([]string{"arm_2"}, []string{}),
+						"x86": makeLabelList([]string{"x86_5"}, []string{"x86_5"}),
+					},
+				},
+				ForceSpecifyEmptyList: false,
+				EmitEmptyList:         false,
+				Prepend:               false,
+			},
+		},
+	}
+	for _, tc := range testCases {
+		got := SubtractBazelLabelListAttribute(tc.haystack, tc.needle)
+		if !reflect.DeepEqual(tc.expected, got) {
+			t.Fatalf("Expected\n%v, but got\n%v", tc.expected, got)
+		}
+	}
+}
+
 func TestFirstUniqueBazelLabelList(t *testing.T) {
 	testCases := []struct {
 		originalLabelList       LabelList
@@ -167,6 +224,46 @@
 	}
 }
 
+func TestFirstUniqueBazelLabelListAttribute(t *testing.T) {
+	testCases := []struct {
+		originalLabelList       LabelListAttribute
+		expectedUniqueLabelList LabelListAttribute
+	}{
+		{
+			originalLabelList: LabelListAttribute{
+				Value: makeLabelList(
+					[]string{"a", "b", "a", "c"},
+					[]string{"x", "x", "y", "z"},
+				),
+				ConfigurableValues: configurableLabelLists{
+					ArchConfigurationAxis: labelListSelectValues{
+						"arm": makeLabelList([]string{"1", "2", "1"}, []string{}),
+						"x86": makeLabelList([]string{"3", "4", "4"}, []string{"5", "5"}),
+					},
+				},
+			},
+			expectedUniqueLabelList: LabelListAttribute{
+				Value: makeLabelList(
+					[]string{"a", "b", "c"},
+					[]string{"x", "y", "z"},
+				),
+				ConfigurableValues: configurableLabelLists{
+					ArchConfigurationAxis: labelListSelectValues{
+						"arm": makeLabelList([]string{"1", "2"}, []string{}),
+						"x86": makeLabelList([]string{"3", "4"}, []string{"5"}),
+					},
+				},
+			},
+		},
+	}
+	for _, tc := range testCases {
+		actualUniqueLabelList := FirstUniqueBazelLabelListAttribute(tc.originalLabelList)
+		if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) {
+			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList)
+		}
+	}
+}
+
 func TestUniqueSortedBazelLabelList(t *testing.T) {
 	testCases := []struct {
 		originalLabelList       LabelList
diff --git a/bp2build/apex_conversion_test.go b/bp2build/apex_conversion_test.go
index 84c7ea2..2383247 100644
--- a/bp2build/apex_conversion_test.go
+++ b/bp2build/apex_conversion_test.go
@@ -1555,7 +1555,7 @@
 				"file_contexts": `":foo-file_contexts"`,
 				"manifest":      `"apex_manifest.json"`,
 				"min_sdk_version": `select({
-        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": "30",
+        "//build/bazel/product_config/config_settings:android__library_linking_strategy__prefer_static": "30",
         "//conditions:default": "31",
     })`,
 				"package_name": `"pkg_name"`,
@@ -1564,7 +1564,7 @@
 				"file_contexts":  `":foo-file_contexts"`,
 				"manifest":       `"apex_manifest.json"`,
 				"min_sdk_version": `select({
-        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": "30",
+        "//build/bazel/product_config/config_settings:android__library_linking_strategy__prefer_static": "30",
         "//conditions:default": "31",
     })`,
 				"package_name": `"override_pkg_name"`,
diff --git a/bp2build/bp2build_product_config.go b/bp2build/bp2build_product_config.go
index f56e6d8..db478db 100644
--- a/bp2build/bp2build_product_config.go
+++ b/bp2build/bp2build_product_config.go
@@ -2,6 +2,7 @@
 
 import (
 	"android/soong/android"
+	"android/soong/android/soongconfig"
 	"android/soong/starlark_import"
 	"encoding/json"
 	"fmt"
@@ -51,7 +52,7 @@
 		"{VARIANT}", targetBuildVariant,
 		"{PRODUCT_FOLDER}", currentProductFolder)
 
-	platformMappingContent, err := platformMappingContent(productReplacer.Replace("@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}"), &productVariables)
+	platformMappingContent, err := platformMappingContent(productReplacer.Replace("@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}"), &productVariables, ctx.Config().Bp2buildSoongConfigDefinitions)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -97,6 +98,7 @@
 android_product(
     name = "mixed_builds_product-{VARIANT}",
     soong_variables = _soong_variables,
+    extra_constraints = ["@//build/bazel/platforms:mixed_builds"],
 )
 `)),
 		newFile(
@@ -147,20 +149,20 @@
 	return injectionDirFiles, bp2buildDirFiles, nil
 }
 
-func platformMappingContent(mainProductLabel string, mainProductVariables *android.ProductVariables) (string, error) {
+func platformMappingContent(mainProductLabel string, mainProductVariables *android.ProductVariables, soongConfigDefinitions soongconfig.Bp2BuildSoongConfigDefinitions) (string, error) {
 	productsForTesting, err := starlark_import.GetStarlarkValue[map[string]map[string]starlark.Value]("products_for_testing")
 	if err != nil {
 		return "", err
 	}
 	var result strings.Builder
 	result.WriteString("platforms:\n")
-	platformMappingSingleProduct(mainProductLabel, mainProductVariables, &result)
+	platformMappingSingleProduct(mainProductLabel, mainProductVariables, soongConfigDefinitions, &result)
 	for product, productVariablesStarlark := range productsForTesting {
 		productVariables, err := starlarkMapToProductVariables(productVariablesStarlark)
 		if err != nil {
 			return "", err
 		}
-		platformMappingSingleProduct("@//build/bazel/tests/products:"+product, &productVariables, &result)
+		platformMappingSingleProduct("@//build/bazel/tests/products:"+product, &productVariables, soongConfigDefinitions, &result)
 	}
 	return result.String(), nil
 }
@@ -179,7 +181,7 @@
 	"_windows_x86_64",
 }
 
-func platformMappingSingleProduct(label string, productVariables *android.ProductVariables, result *strings.Builder) {
+func platformMappingSingleProduct(label string, productVariables *android.ProductVariables, soongConfigDefinitions soongconfig.Bp2BuildSoongConfigDefinitions, result *strings.Builder) {
 	targetBuildVariant := "user"
 	if proptools.Bool(productVariables.Eng) {
 		targetBuildVariant = "eng"
@@ -193,27 +195,72 @@
 		result.WriteString(suffix)
 		result.WriteString("\n")
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:always_use_prebuilt_sdks=%t\n", proptools.Bool(productVariables.Always_use_prebuilt_sdks)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:arc=%t\n", proptools.Bool(productVariables.Arc)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:apex_global_min_sdk_version_override=%s\n", proptools.String(productVariables.ApexGlobalMinSdkVersionOverride)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:binder32bit=%t\n", proptools.Bool(productVariables.Binder32bit)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:build_from_text_stub=%t\n", proptools.Bool(productVariables.Build_from_text_stub)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:build_id=%s\n", proptools.String(productVariables.BuildId)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:build_version_tags=%s\n", strings.Join(productVariables.BuildVersionTags, ",")))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:certificate_overrides=%s\n", strings.Join(productVariables.CertificateOverrides, ",")))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:cfi_exclude_paths=%s\n", strings.Join(productVariables.CFIExcludePaths, ",")))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:cfi_include_paths=%s\n", strings.Join(productVariables.CFIIncludePaths, ",")))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:compressed_apex=%t\n", proptools.Bool(productVariables.CompressedApex)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:debuggable=%t\n", proptools.Bool(productVariables.Debuggable)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:default_app_certificate=%s\n", proptools.String(productVariables.DefaultAppCertificate)))
 		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.Device_page_size_agnostic)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_product=%s\n", proptools.String(productVariables.DeviceProduct)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:enable_cfi=%t\n", proptools.BoolDefault(productVariables.EnableCFI, true)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:enforce_vintf_manifest=%t\n", proptools.Bool(productVariables.Enforce_vintf_manifest)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:eng=%t\n", proptools.Bool(productVariables.Eng)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:malloc_not_svelte=%t\n", proptools.Bool(productVariables.Malloc_not_svelte)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:malloc_pattern_fill_contents=%t\n", proptools.Bool(productVariables.Malloc_pattern_fill_contents)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:malloc_zero_contents=%t\n", proptools.Bool(productVariables.Malloc_zero_contents)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:manifest_package_name_overrides=%s\n", strings.Join(productVariables.ManifestPackageNameOverrides, ",")))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:native_coverage=%t\n", proptools.Bool(productVariables.Native_coverage)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:platform_version_name=%s\n", proptools.String(productVariables.Platform_version_name)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:product_brand=%s\n", productVariables.ProductBrand))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:product_manufacturer=%s\n", productVariables.ProductManufacturer))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:platform_sdk_version=%d\n", *productVariables.Platform_sdk_version))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:safestack=%t\n", proptools.Bool(productVariables.Safestack)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:target_build_variant=%s\n", targetBuildVariant))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:treble_linker_namespaces=%t\n", proptools.Bool(productVariables.Treble_linker_namespaces)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:tidy_checks=%s\n", proptools.String(productVariables.TidyChecks)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:uml=%t\n", proptools.Bool(productVariables.Uml)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:unbundled_build=%t\n", proptools.Bool(productVariables.Unbundled_build)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:unbundled_build_apps=%s\n", strings.Join(productVariables.Unbundled_build_apps, ",")))
+		for namespace, namespaceContents := range productVariables.VendorVars {
+			for variable, value := range namespaceContents {
+				key := namespace + "__" + variable
+				_, hasBool := soongConfigDefinitions.BoolVars[key]
+				_, hasString := soongConfigDefinitions.StringVars[key]
+				_, hasValue := soongConfigDefinitions.ValueVars[key]
+				if !hasBool && !hasString && !hasValue {
+					// Not all soong config variables are defined in Android.bp files. For example,
+					// prebuilt_bootclasspath_fragment uses soong config variables in a nonstandard
+					// way, that causes them to be present in the soong.variables file but not
+					// defined in an Android.bp file. There's also nothing stopping you from setting
+					// a variable in make that doesn't exist in soong. We only generate build
+					// settings for the ones that exist in soong, so skip all others.
+					continue
+				}
+				if hasBool && hasString || hasBool && hasValue || hasString && hasValue {
+					panic(fmt.Sprintf("Soong config variable %s:%s appears to be of multiple types. bool? %t, string? %t, value? %t", namespace, variable, hasBool, hasString, hasValue))
+				}
+				if hasBool {
+					// Logic copied from soongConfig.Bool()
+					value = strings.ToLower(value)
+					if value == "1" || value == "y" || value == "yes" || value == "on" || value == "true" {
+						value = "true"
+					} else {
+						value = "false"
+					}
+				}
+				result.WriteString(fmt.Sprintf("    --//build/bazel/product_config/soong_config_variables:%s=%s\n", strings.ToLower(key), value))
+			}
+		}
 	}
 }
 
@@ -224,12 +271,20 @@
 		field := productVarsReflect.Field(i)
 		fieldType := productVarsReflect.Type().Field(i)
 		name := fieldType.Name
-		if name == "BootJars" || name == "ApexBootJars" || name == "VendorVars" ||
-			name == "VendorSnapshotModules" || name == "RecoverySnapshotModules" {
+		if name == "BootJars" || name == "ApexBootJars" || name == "VendorSnapshotModules" ||
+			name == "RecoverySnapshotModules" {
 			// These variables have more complicated types, and we don't need them right now
 			continue
 		}
 		if _, ok := in[name]; ok {
+			if name == "VendorVars" {
+				vendorVars, err := starlark_import.Unmarshal[map[string]map[string]string](in[name])
+				if err != nil {
+					return result, err
+				}
+				field.Set(reflect.ValueOf(vendorVars))
+				continue
+			}
 			switch field.Type().Kind() {
 			case reflect.Bool:
 				val, err := starlark_import.Unmarshal[bool](in[name])
@@ -281,5 +336,9 @@
 		}
 	}
 
+	result.Native_coverage = proptools.BoolPtr(
+		proptools.Bool(result.GcovCoverage) ||
+			proptools.Bool(result.ClangCoverage))
+
 	return result, nil
 }
diff --git a/bp2build/bp2build_product_config_test.go b/bp2build/bp2build_product_config_test.go
index 3dd53ce..02d83b4 100644
--- a/bp2build/bp2build_product_config_test.go
+++ b/bp2build/bp2build_product_config_test.go
@@ -69,6 +69,7 @@
 			t.Error(err)
 			continue
 		}
+		testCase.result.Native_coverage = proptools.BoolPtr(false)
 		if !reflect.DeepEqual(testCase.result, productVariables) {
 			expected, err := json.Marshal(testCase.result)
 			if err != nil {
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 0e6596b..09a9d04 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -353,7 +353,104 @@
 	Importpath             bazel.StringAttribute
 	Srcs                   bazel.LabelListAttribute
 	Deps                   bazel.LabelListAttribute
+	Data                   bazel.LabelListAttribute
 	Target_compatible_with bazel.LabelListAttribute
+
+	// attributes for the dynamically generated go_test target
+	Embed bazel.LabelListAttribute
+}
+
+type goTestProperties struct {
+	name           string
+	dir            string
+	testSrcs       []string
+	linuxTestSrcs  []string
+	darwinTestSrcs []string
+	testData       []string
+	// Name of the target that should be compiled together with the test
+	embedName string
+}
+
+// Creates a go_test target for bootstrap_go_package / blueprint_go_binary
+func generateBazelTargetsGoTest(ctx *android.Context, goModulesMap nameToGoLibraryModule, gp goTestProperties) (BazelTarget, error) {
+	ca := android.CommonAttributes{
+		Name: gp.name,
+	}
+	ga := goAttributes{
+		Srcs: goSrcLabels(ctx.Config(), gp.dir, gp.testSrcs, gp.linuxTestSrcs, gp.darwinTestSrcs),
+		Data: goSrcLabels(ctx.Config(), gp.dir, gp.testData, []string{}, []string{}),
+		Embed: bazel.MakeLabelListAttribute(
+			bazel.MakeLabelList(
+				[]bazel.Label{bazel.Label{Label: ":" + gp.embedName}},
+			),
+		),
+		Target_compatible_with: targetNotCompatibleWithAndroid(),
+	}
+
+	libTest := goBazelTarget{
+		targetName:            gp.name,
+		targetPackage:         gp.dir,
+		bazelRuleClass:        "go_test",
+		bazelRuleLoadLocation: "@io_bazel_rules_go//go:def.bzl",
+		bazelAttributes:       []interface{}{&ca, &ga},
+	}
+	return generateBazelTarget(ctx, libTest)
+}
+
+// TODO - b/288491147: testSrcs of certain bootstrap_go_package/blueprint_go_binary are not hermetic and depend on
+// testdata checked into the filesystem.
+// Denylist the generation of go_test targets for these Soong modules.
+// The go_library/go_binary will still be generated, since those are hermitic.
+var (
+	goTestsDenylist = []string{
+		"android-archive-zip",
+		"bazel_notice_gen",
+		"blueprint-bootstrap-bpdoc",
+		"blueprint-microfactory",
+		"blueprint-pathtools",
+		"bssl_ar",
+		"compliance_checkmetadata",
+		"compliance_checkshare",
+		"compliance_dumpgraph",
+		"compliance_dumpresolutions",
+		"compliance_listshare",
+		"compliance-module",
+		"compliancenotice_bom",
+		"compliancenotice_shippedlibs",
+		"compliance_rtrace",
+		"compliance_sbom",
+		"golang-protobuf-internal-fuzz-jsonfuzz",
+		"golang-protobuf-internal-fuzz-textfuzz",
+		"golang-protobuf-internal-fuzz-wirefuzz",
+		"htmlnotice",
+		"protoc-gen-go",
+		"rbcrun-module",
+		"spdx-tools-builder",
+		"spdx-tools-builder2v1",
+		"spdx-tools-builder2v2",
+		"spdx-tools-builder2v3",
+		"spdx-tools-idsearcher",
+		"spdx-tools-spdx-json",
+		"spdx-tools-utils",
+		"soong-ui-build",
+		"textnotice",
+		"xmlnotice",
+	}
+)
+
+func testOfGoPackageIsIncompatible(g *bootstrap.GoPackage) bool {
+	return android.InList(g.Name(), goTestsDenylist) ||
+		// Denylist tests of soong_build
+		// Theses tests have a guard that prevent usage outside a test environment
+		// The guard (`ensureTestOnly`) looks for a `-test` in os.Args, which is present in soong's gotestrunner, but missing in `b test`
+		g.IsPluginFor("soong_build") ||
+		// soong-android is a dep of soong_build
+		// This dependency is created by soong_build by listing it in its deps explicitly in Android.bp, and not via `plugin_for` in `soong-android`
+		g.Name() == "soong-android"
+}
+
+func testOfGoBinaryIsIncompatible(g *bootstrap.GoBinary) bool {
+	return android.InList(g.Name(), goTestsDenylist)
 }
 
 func generateBazelTargetsGoPackage(ctx *android.Context, g *bootstrap.GoPackage, goModulesMap nameToGoLibraryModule) ([]BazelTarget, []error) {
@@ -390,12 +487,33 @@
 		bazelRuleLoadLocation: "@io_bazel_rules_go//go:def.bzl",
 		bazelAttributes:       []interface{}{&ca, &ga},
 	}
-	// TODO - b/284483729: Create go_test target from testSrcs
-	libTarget, err := generateBazelTarget(ctx, lib)
-	if err != nil {
-		return []BazelTarget{}, []error{err}
+	retTargets := []BazelTarget{}
+	var retErrs []error
+	if libTarget, err := generateBazelTarget(ctx, lib); err == nil {
+		retTargets = append(retTargets, libTarget)
+	} else {
+		retErrs = []error{err}
 	}
-	return []BazelTarget{libTarget}, nil
+
+	// If the library contains test srcs, create an additional go_test target
+	if !testOfGoPackageIsIncompatible(g) && (len(g.TestSrcs()) > 0 || len(g.LinuxTestSrcs()) > 0 || len(g.DarwinTestSrcs()) > 0) {
+		gp := goTestProperties{
+			name:           g.Name() + "-test",
+			dir:            ctx.ModuleDir(g),
+			testSrcs:       g.TestSrcs(),
+			linuxTestSrcs:  g.LinuxTestSrcs(),
+			darwinTestSrcs: g.DarwinTestSrcs(),
+			testData:       g.TestData(),
+			embedName:      g.Name(), // embed the source go_library in the test so that its .go files are included in the compilation unit
+		}
+		if libTestTarget, err := generateBazelTargetsGoTest(ctx, goModulesMap, gp); err == nil {
+			retTargets = append(retTargets, libTestTarget)
+		} else {
+			retErrs = append(retErrs, err)
+		}
+	}
+
+	return retTargets, retErrs
 }
 
 type goLibraryModule struct {
@@ -440,6 +558,9 @@
 		Name: g.Name(),
 	}
 
+	retTargets := []BazelTarget{}
+	var retErrs []error
+
 	// For this bootstrap_go_package dep chain,
 	// A --> B --> C ( ---> depends on)
 	// Soong provides the convenience of only listing B as deps of A even if a src file of A imports C
@@ -450,12 +571,70 @@
 	// bp2build does not have sufficient info on whether C is a direct dep of A or not, so for now collect all transitive deps and add them to deps
 	transitiveDeps := transitiveGoDeps(g.Deps(), goModulesMap)
 
+	goSource := ""
+	// If the library contains test srcs, create an additional go_test target
+	// The go_test target will embed a go_source containining the source .go files it tests
+	if !testOfGoBinaryIsIncompatible(g) && (len(g.TestSrcs()) > 0 || len(g.LinuxTestSrcs()) > 0 || len(g.DarwinTestSrcs()) > 0) {
+		// Create a go_source containing the source .go files of go_library
+		// This target will be an `embed` of the go_binary and go_test
+		goSource = g.Name() + "-source"
+		ca := android.CommonAttributes{
+			Name: goSource,
+		}
+		ga := goAttributes{
+			Srcs:                   goSrcLabels(ctx.Config(), ctx.ModuleDir(g), g.Srcs(), g.LinuxSrcs(), g.DarwinSrcs()),
+			Deps:                   goDepLabels(transitiveDeps, goModulesMap),
+			Target_compatible_with: targetNotCompatibleWithAndroid(),
+		}
+		libTestSource := goBazelTarget{
+			targetName:            goSource,
+			targetPackage:         ctx.ModuleDir(g),
+			bazelRuleClass:        "go_source",
+			bazelRuleLoadLocation: "@io_bazel_rules_go//go:def.bzl",
+			bazelAttributes:       []interface{}{&ca, &ga},
+		}
+		if libSourceTarget, err := generateBazelTarget(ctx, libTestSource); err == nil {
+			retTargets = append(retTargets, libSourceTarget)
+		} else {
+			retErrs = append(retErrs, err)
+		}
+
+		// Create a go_test target
+		gp := goTestProperties{
+			name:           g.Name() + "-test",
+			dir:            ctx.ModuleDir(g),
+			testSrcs:       g.TestSrcs(),
+			linuxTestSrcs:  g.LinuxTestSrcs(),
+			darwinTestSrcs: g.DarwinTestSrcs(),
+			testData:       g.TestData(),
+			// embed the go_source in the test
+			embedName: g.Name() + "-source",
+		}
+		if libTestTarget, err := generateBazelTargetsGoTest(ctx, goModulesMap, gp); err == nil {
+			retTargets = append(retTargets, libTestTarget)
+		} else {
+			retErrs = append(retErrs, err)
+		}
+
+	}
+
+	// Create a go_binary target
 	ga := goAttributes{
-		Srcs:                   goSrcLabels(ctx.Config(), ctx.ModuleDir(g), g.Srcs(), g.LinuxSrcs(), g.DarwinSrcs()),
 		Deps:                   goDepLabels(transitiveDeps, goModulesMap),
 		Target_compatible_with: targetNotCompatibleWithAndroid(),
 	}
 
+	// If the binary has testSrcs, embed the common `go_source`
+	if goSource != "" {
+		ga.Embed = bazel.MakeLabelListAttribute(
+			bazel.MakeLabelList(
+				[]bazel.Label{bazel.Label{Label: ":" + goSource}},
+			),
+		)
+	} else {
+		ga.Srcs = goSrcLabels(ctx.Config(), ctx.ModuleDir(g), g.Srcs(), g.LinuxSrcs(), g.DarwinSrcs())
+	}
+
 	bin := goBazelTarget{
 		targetName:            g.Name(),
 		targetPackage:         ctx.ModuleDir(g),
@@ -463,12 +642,14 @@
 		bazelRuleLoadLocation: "@io_bazel_rules_go//go:def.bzl",
 		bazelAttributes:       []interface{}{&ca, &ga},
 	}
-	// TODO - b/284483729: Create go_test target from testSrcs
-	binTarget, err := generateBazelTarget(ctx, bin)
-	if err != nil {
-		return []BazelTarget{}, []error{err}
+
+	if binTarget, err := generateBazelTarget(ctx, bin); err == nil {
+		retTargets = append(retTargets, binTarget)
+	} else {
+		retErrs = []error{err}
 	}
-	return []BazelTarget{binTarget}, nil
+
+	return retTargets, retErrs
 }
 
 func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (conversionResults, []error) {
@@ -563,10 +744,12 @@
 				targets, targetErrs = generateBazelTargetsGoPackage(bpCtx, glib, nameToGoLibMap)
 				errs = append(errs, targetErrs...)
 				metrics.IncrementRuleClassCount("go_library")
+				metrics.AddConvertedModule(glib, "go_library", dir)
 			} else if gbin, ok := m.(*bootstrap.GoBinary); ok {
 				targets, targetErrs = generateBazelTargetsGoBinary(bpCtx, gbin, nameToGoLibMap)
 				errs = append(errs, targetErrs...)
 				metrics.IncrementRuleClassCount("go_binary")
+				metrics.AddConvertedModule(gbin, "go_binary", dir)
 			} else {
 				metrics.AddUnconvertedModule(m, moduleType, dir, android.UnconvertedReason{
 					ReasonType: int(bp2build_metrics_proto.UnconvertedReasonType_TYPE_UNSUPPORTED),
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index e127fd5..4d1d171 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -640,7 +640,10 @@
 }`,
 			ExpectedBazelTargets: []string{
 				MakeBazelTargetNoRestrictions("custom", "foo", AttrNameToString{
-					"target_compatible_with": `["//build/bazel/product_variables:unbundled_build"]`,
+					"target_compatible_with": `select({
+        "//build/bazel/product_config/config_settings:unbundled_build": [],
+        "//conditions:default": ["@platforms//:incompatible"],
+    })`,
 				}),
 			},
 		},
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 490cd91..6b17fc4 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -1260,14 +1260,14 @@
         "//build/bazel/platforms/arch:arm": [],
         "//conditions:default": [":arm_static_lib_excludes_bp2build_cc_library_static"],
     }) + select({
-        "//build/bazel/product_variables:malloc_not_svelte": [],
+        "//build/bazel/product_config/config_settings:malloc_not_svelte": [],
         "//conditions:default": [":malloc_not_svelte_static_lib_excludes_bp2build_cc_library_static"],
     })`,
 			"implementation_dynamic_deps": `select({
         "//build/bazel/platforms/arch:arm": [],
         "//conditions:default": [":arm_shared_lib_excludes"],
     }) + select({
-        "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_shared_lib"],
+        "//build/bazel/product_config/config_settings:malloc_not_svelte": [":malloc_not_svelte_shared_lib"],
         "//conditions:default": [],
     })`,
 			"srcs_c": `["common.c"]`,
@@ -1275,7 +1275,7 @@
         "//build/bazel/platforms/arch:arm": [],
         "//conditions:default": [":arm_whole_static_lib_excludes_bp2build_cc_library_static"],
     }) + select({
-        "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_whole_static_lib_bp2build_cc_library_static"],
+        "//build/bazel/product_config/config_settings:malloc_not_svelte": [":malloc_not_svelte_whole_static_lib_bp2build_cc_library_static"],
         "//conditions:default": [":malloc_not_svelte_whole_static_lib_excludes_bp2build_cc_library_static"],
     })`,
 		}),
@@ -1307,7 +1307,7 @@
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("foo_static", AttrNameToString{
 			"implementation_deps": `select({
-        "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_header_lib"],
+        "//build/bazel/product_config/config_settings:malloc_not_svelte": [":malloc_not_svelte_header_lib"],
         "//conditions:default": [],
     })`,
 			"srcs_c":                 `["common.c"]`,
@@ -4631,7 +4631,7 @@
         "-Wextra",
         "-DDEBUG_ONLY_CODE=0",
     ] + select({
-        "//build/bazel/product_variables:eng": [
+        "//build/bazel/product_config/config_settings:eng": [
             "-UDEBUG_ONLY_CODE",
             "-DDEBUG_ONLY_CODE=1",
         ],
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index 8084a5d..d5c40eb 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -1003,6 +1003,38 @@
 	})
 }
 
+func TestCcLibraryStaticGeneratedHeadersMultipleExports(t *testing.T) {
+	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+		Blueprint: soongCcLibraryStaticPreamble + `
+genrule {
+    name: "generated_hdr",
+    cmd: "nothing to see here",
+    export_include_dirs: ["foo", "bar"],
+    bazel_module: { bp2build_available: false },
+}
+
+genrule {
+    name: "export_generated_hdr",
+    cmd: "nothing to see here",
+    export_include_dirs: ["a", "b"],
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library_static {
+    name: "foo_static",
+    generated_headers: ["generated_hdr", "export_generated_hdr"],
+    export_generated_headers: ["export_generated_hdr"],
+    include_build_directory: false,
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
+				"deps":                `[":export_generated_hdr__header_library"]`,
+				"implementation_deps": `[":generated_hdr__header_library"]`,
+			}),
+		},
+	})
+}
+
 // generated_headers has "variant_prepend" tag. In bp2build output,
 // variant info(select) should go before general info.
 func TestCcLibraryStaticArchSrcsExcludeSrcsGeneratedFiles(t *testing.T) {
@@ -1156,13 +1188,13 @@
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"copts": `select({
-        "//build/bazel/product_variables:binder32bit": ["-Wbinder32bit"],
+        "//build/bazel/product_config/config_settings:binder32bit": ["-Wbinder32bit"],
         "//conditions:default": [],
     }) + select({
-        "//build/bazel/product_variables:malloc_not_svelte": ["-Wmalloc_not_svelte"],
+        "//build/bazel/product_config/config_settings:malloc_not_svelte": ["-Wmalloc_not_svelte"],
         "//conditions:default": [],
     }) + select({
-        "//build/bazel/product_variables:malloc_zero_contents": ["-Wmalloc_zero_contents"],
+        "//build/bazel/product_config/config_settings:malloc_zero_contents": ["-Wmalloc_zero_contents"],
         "//conditions:default": [],
     })`,
 				"srcs_c": `["common.c"]`,
@@ -1216,19 +1248,19 @@
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"copts": `select({
-        "//build/bazel/product_variables:malloc_not_svelte": ["-Wmalloc_not_svelte"],
+        "//build/bazel/product_config/config_settings:malloc_not_svelte": ["-Wmalloc_not_svelte"],
         "//conditions:default": [],
     }) + select({
-        "//build/bazel/product_variables:malloc_not_svelte-android": ["-Wandroid_malloc_not_svelte"],
+        "//build/bazel/product_config/config_settings:malloc_not_svelte-android": ["-Wandroid_malloc_not_svelte"],
         "//conditions:default": [],
     }) + select({
-        "//build/bazel/product_variables:malloc_not_svelte-arm": ["-Wlib32_malloc_not_svelte"],
+        "//build/bazel/product_config/config_settings:malloc_not_svelte-arm": ["-Wlib32_malloc_not_svelte"],
         "//conditions:default": [],
     }) + select({
-        "//build/bazel/product_variables:malloc_not_svelte-arm64": ["-Warm64_malloc_not_svelte"],
+        "//build/bazel/product_config/config_settings:malloc_not_svelte-arm64": ["-Warm64_malloc_not_svelte"],
         "//conditions:default": [],
     }) + select({
-        "//build/bazel/product_variables:malloc_not_svelte-x86": ["-Wlib32_malloc_not_svelte"],
+        "//build/bazel/product_config/config_settings:malloc_not_svelte-x86": ["-Wlib32_malloc_not_svelte"],
         "//conditions:default": [],
     })`,
 				"srcs_c": `["common.c"]`,
@@ -1255,7 +1287,7 @@
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_static", "foo_static", AttrNameToString{
 				"asflags": `select({
-        "//build/bazel/product_variables:platform_sdk_version": ["-DPLATFORM_SDK_VERSION=$(Platform_sdk_version)"],
+        "//build/bazel/product_config/config_settings:platform_sdk_version": ["-DPLATFORM_SDK_VERSION=$(Platform_sdk_version)"],
         "//conditions:default": [],
     })`,
 				"srcs_as": `["common.S"]`,
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index eab84e1..ecfcb5a 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -200,7 +200,7 @@
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_object", "foo", AttrNameToString{
 				"asflags": `select({
-        "//build/bazel/product_variables:platform_sdk_version": ["-DPLATFORM_SDK_VERSION=$(Platform_sdk_version)"],
+        "//build/bazel/product_config/config_settings:platform_sdk_version": ["-DPLATFORM_SDK_VERSION=$(Platform_sdk_version)"],
         "//conditions:default": [],
     })`,
 				"copts":               `["-fno-addrsig"]`,
diff --git a/bp2build/cc_test_conversion_test.go b/bp2build/cc_test_conversion_test.go
index 20eb092..3c037b4 100644
--- a/bp2build/cc_test_conversion_test.go
+++ b/bp2build/cc_test_conversion_test.go
@@ -120,7 +120,6 @@
         "//build/bazel/platforms/os:windows": [":hostlib"],
         "//conditions:default": [],
     })`,
-				"gtest":          "True",
 				"local_includes": `["."]`,
 				"dynamic_deps": `[":cc_test_lib2"] + select({
         "//build/bazel/platforms/os:android": [":foolib"],
@@ -182,7 +181,6 @@
 				"tags":           `["no-remote"]`,
 				"local_includes": `["."]`,
 				"srcs":           `["test.cpp"]`,
-				"gtest":          "True",
 				"deps": `[
         ":libgtest_main",
         ":libgtest",
@@ -209,7 +207,6 @@
 			simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest"),
 		targets: []testBazelTarget{
 			{"cc_test", "mytest", AttrNameToString{
-				"gtest":                  "True",
 				"local_includes":         `["."]`,
 				"srcs":                   `["test.cpp"]`,
 				"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
@@ -239,7 +236,6 @@
 			simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest"),
 		targets: []testBazelTarget{
 			{"cc_test", "mytest", AttrNameToString{
-				"gtest":                  "True",
 				"local_includes":         `["."]`,
 				"srcs":                   `["test.cpp"]`,
 				"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
@@ -273,7 +269,6 @@
 		targets: []testBazelTarget{
 			{"cc_test", "mytest", AttrNameToString{
 				"auto_generate_test_config": "True",
-				"gtest":                     "True",
 				"local_includes":            `["."]`,
 				"srcs":                      `["test.cpp"]`,
 				"target_compatible_with":    `["//build/bazel/platforms/os:android"]`,
@@ -304,7 +299,6 @@
 			simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest"),
 		targets: []testBazelTarget{
 			{"cc_test", "mytest", AttrNameToString{
-				"gtest":                  "True",
 				"local_includes":         `["."]`,
 				"srcs":                   `["test.cpp"]`,
 				"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
@@ -332,7 +326,6 @@
 			simpleModuleDoNotConvertBp2build("cc_library", "liblog"),
 		targets: []testBazelTarget{
 			{"cc_test", "mytest", AttrNameToString{
-				"gtest":                  "True",
 				"local_includes":         `["."]`,
 				"srcs":                   `["test.cpp"]`,
 				"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
@@ -344,3 +337,38 @@
 	})
 
 }
+
+func TestCcTest_GtestExplicitlySpecifiedInAndroidBp(t *testing.T) {
+	runCcTestTestCase(t, ccTestBp2buildTestCase{
+		description: "If `gtest` is explicit in Android.bp, it should be explicit in BUILD files as well",
+		blueprint: `
+cc_test {
+	name: "mytest_with_gtest",
+	gtest: true,
+}
+cc_test {
+	name: "mytest_with_no_gtest",
+	gtest: false,
+}
+` + simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest_main") +
+			simpleModuleDoNotConvertBp2build("cc_library_static", "libgtest"),
+		targets: []testBazelTarget{
+			{"cc_test", "mytest_with_gtest", AttrNameToString{
+				"local_includes": `["."]`,
+				"deps": `[
+        ":libgtest_main",
+        ":libgtest",
+    ]`,
+				"gtest":                  "True",
+				"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+			},
+			},
+			{"cc_test", "mytest_with_no_gtest", AttrNameToString{
+				"local_includes":         `["."]`,
+				"gtest":                  "False",
+				"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+			},
+			},
+		},
+	})
+}
diff --git a/bp2build/genrule_conversion_test.go b/bp2build/genrule_conversion_test.go
index 5cf4fb2..5a73969 100644
--- a/bp2build/genrule_conversion_test.go
+++ b/bp2build/genrule_conversion_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"path/filepath"
 	"testing"
 
 	"android/soong/android"
@@ -695,3 +696,79 @@
 			})
 	})
 }
+
+func TestGenruleWithExportIncludeDirs(t *testing.T) {
+	testCases := []struct {
+		moduleType string
+		factory    android.ModuleFactory
+		hod        android.HostOrDeviceSupported
+	}{
+		{
+			moduleType: "genrule",
+			factory:    genrule.GenRuleFactory,
+		},
+		{
+			moduleType: "cc_genrule",
+			factory:    cc.GenRuleFactory,
+			hod:        android.DeviceSupported,
+		},
+		{
+			moduleType: "java_genrule",
+			factory:    java.GenRuleFactory,
+			hod:        android.DeviceSupported,
+		},
+		{
+			moduleType: "java_genrule_host",
+			factory:    java.GenRuleFactoryHost,
+			hod:        android.HostSupported,
+		},
+	}
+
+	dir := "baz"
+
+	bp := `%s {
+    name: "foo",
+    out: ["foo.out.h"],
+    srcs: ["foo.in"],
+    cmd: "cp $(in) $(out)",
+    export_include_dirs: ["foo", "bar", "."],
+    bazel_module: { bp2build_available: true },
+}`
+
+	for _, tc := range testCases {
+		moduleAttrs := AttrNameToString{
+			"cmd":  `"cp $(SRCS) $(OUTS)"`,
+			"outs": `["foo.out.h"]`,
+			"srcs": `["foo.in"]`,
+		}
+
+		expectedBazelTargets := []string{
+			makeBazelTargetHostOrDevice("genrule", "foo", moduleAttrs, tc.hod),
+			makeBazelTargetHostOrDevice("cc_library_headers", "foo__header_library", AttrNameToString{
+				"hdrs": `[":foo"]`,
+				"export_includes": `[
+        "foo",
+        "baz/foo",
+        "bar",
+        "baz/bar",
+        ".",
+        "baz",
+    ]`,
+			},
+				tc.hod),
+		}
+
+		t.Run(tc.moduleType, func(t *testing.T) {
+			RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+				Bp2buildTestCase{
+					ModuleTypeUnderTest:        tc.moduleType,
+					ModuleTypeUnderTestFactory: tc.factory,
+					Filesystem: map[string]string{
+						filepath.Join(dir, "Android.bp"): fmt.Sprintf(bp, tc.moduleType),
+					},
+					Dir:                  dir,
+					ExpectedBazelTargets: expectedBazelTargets,
+				})
+		})
+	}
+}
diff --git a/bp2build/go_conversion_test.go b/bp2build/go_conversion_test.go
index 507fbf0..2387641 100644
--- a/bp2build/go_conversion_test.go
+++ b/bp2build/go_conversion_test.go
@@ -45,11 +45,17 @@
 		srcs: [
 			"foo_linux.go",
 		],
+		testSrcs: [
+			"foo_linux_test.go",
+		],
 	},
 	darwin: {
 		srcs: [
 			"foo_darwin.go",
 		],
+		testSrcs: [
+			"foo_darwin_test.go",
+		],
 	},
 	testSrcs: [
 		"foo1_test.go",
@@ -84,7 +90,21 @@
     })`,
 			},
 			android.HostSupported,
-		)},
+		),
+			makeBazelTargetHostOrDevice("go_test", "foo-test",
+				AttrNameToString{
+					"embed": `[":foo"]`,
+					"srcs": `[
+        "foo1_test.go",
+        "foo2_test.go",
+    ] + select({
+        "//build/bazel/platforms/os:darwin": ["foo_darwin_test.go"],
+        "//build/bazel/platforms/os:linux_glibc": ["foo_linux_test.go"],
+        "//conditions:default": [],
+    })`,
+				},
+				android.HostSupported,
+			)},
 	})
 }
 
@@ -125,6 +145,44 @@
 	})
 }
 
+func TestConvertGoBinaryWithTestSrcs(t *testing.T) {
+	bp := `
+blueprint_go_binary {
+	name: "foo",
+	srcs: ["main.go"],
+	testSrcs: ["main_test.go"],
+}
+`
+	t.Parallel()
+	runGoTests(t, Bp2buildTestCase{
+		Description: "Convert blueprint_go_binary with testSrcs",
+		Blueprint:   bp,
+		ExpectedBazelTargets: []string{
+			makeBazelTargetHostOrDevice("go_binary", "foo",
+				AttrNameToString{
+					"deps":  `[]`,
+					"embed": `[":foo-source"]`,
+				},
+				android.HostSupported,
+			),
+			makeBazelTargetHostOrDevice("go_source", "foo-source",
+				AttrNameToString{
+					"deps": `[]`,
+					"srcs": `["main.go"]`,
+				},
+				android.HostSupported,
+			),
+			makeBazelTargetHostOrDevice("go_test", "foo-test",
+				AttrNameToString{
+					"embed": `[":foo-source"]`,
+					"srcs":  `["main_test.go"]`,
+				},
+				android.HostSupported,
+			),
+		},
+	})
+}
+
 func TestConvertGoBinaryWithSrcInDifferentPackage(t *testing.T) {
 	bp := `
 blueprint_go_binary {
diff --git a/bp2build/prebuilt_etc_conversion_test.go b/bp2build/prebuilt_etc_conversion_test.go
index aa0a5b7..5b2d609 100644
--- a/bp2build/prebuilt_etc_conversion_test.go
+++ b/bp2build/prebuilt_etc_conversion_test.go
@@ -149,7 +149,7 @@
 			MakeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
 				"filename": `"tz_version"`,
 				"src": `select({
-        "//build/bazel/product_variables:native_coverage": "src1",
+        "//build/bazel/product_config/config_settings:native_coverage": "src1",
         "//conditions:default": "version/tz_version",
     })`,
 				"dir": `"etc"`,
@@ -318,7 +318,7 @@
 				"dir":      `"etc"`,
 				"src": `select({
         "//build/bazel/platforms/arch:arm": "armSrc",
-        "//build/bazel/product_variables:native_coverage-arm": "nativeCoverageArmSrc",
+        "//build/bazel/product_config/config_settings:native_coverage-arm": "nativeCoverageArmSrc",
         "//conditions:default": None,
     })`,
 			})}})
diff --git a/bp2build/soong_config_module_type_conversion_test.go b/bp2build/soong_config_module_type_conversion_test.go
index 813773d..1ff47f2 100644
--- a/bp2build/soong_config_module_type_conversion_test.go
+++ b/bp2build/soong_config_module_type_conversion_test.go
@@ -91,7 +91,7 @@
 		ExpectedBazelTargets: []string{`cc_library_static(
     name = "foo",
     copts = select({
-        "//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
+        "//build/bazel/product_config/config_settings:acme__feature1": ["-DFEATURE1"],
         "//conditions:default": ["-DDEFAULT1"],
     }),
     local_includes = ["."],
@@ -140,7 +140,7 @@
 		ExpectedBazelTargets: []string{`cc_library_static(
     name = "foo",
     copts = select({
-        "//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
+        "//build/bazel/product_config/config_settings:acme__feature1": ["-DFEATURE1"],
         "//conditions:default": ["-DDEFAULT1"],
     }),
     local_includes = ["."],
@@ -191,9 +191,9 @@
 		ExpectedBazelTargets: []string{`cc_library_static(
     name = "foo",
     copts = select({
-        "//build/bazel/product_variables:acme__board__soc_a": ["-DSOC_A"],
-        "//build/bazel/product_variables:acme__board__soc_b": ["-DSOC_B"],
-        "//build/bazel/product_variables:acme__board__soc_c": [],
+        "//build/bazel/product_config/config_settings:acme__board__soc_a": ["-DSOC_A"],
+        "//build/bazel/product_config/config_settings:acme__board__soc_b": ["-DSOC_B"],
+        "//build/bazel/product_config/config_settings:acme__board__soc_c": [],
         "//conditions:default": ["-DSOC_DEFAULT"],
     }),
     local_includes = ["."],
@@ -240,7 +240,7 @@
 		ExpectedBazelTargets: []string{`cc_library_static(
     name = "foo",
     copts = select({
-        "//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
+        "//build/bazel/product_config/config_settings:acme__feature1": ["-DFEATURE1"],
         "//conditions:default": ["-DDEFAULT1"],
     }),
     local_includes = ["."],
@@ -310,15 +310,15 @@
 		ExpectedBazelTargets: []string{`cc_library_static(
     name = "foo",
     copts = select({
-        "//build/bazel/product_variables:acme__board__soc_a": ["-DSOC_A"],
-        "//build/bazel/product_variables:acme__board__soc_b": ["-DSOC_B"],
-        "//build/bazel/product_variables:acme__board__soc_c": [],
+        "//build/bazel/product_config/config_settings:acme__board__soc_a": ["-DSOC_A"],
+        "//build/bazel/product_config/config_settings:acme__board__soc_b": ["-DSOC_B"],
+        "//build/bazel/product_config/config_settings:acme__board__soc_c": [],
         "//conditions:default": ["-DSOC_DEFAULT"],
     }) + select({
-        "//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
+        "//build/bazel/product_config/config_settings:acme__feature1": ["-DFEATURE1"],
         "//conditions:default": ["-DDEFAULT1"],
     }) + select({
-        "//build/bazel/product_variables:acme__feature2": ["-DFEATURE2"],
+        "//build/bazel/product_config/config_settings:acme__feature2": ["-DFEATURE2"],
         "//conditions:default": ["-DDEFAULT2"],
     }),
     local_includes = ["."],
@@ -380,15 +380,15 @@
 		ExpectedBazelTargets: []string{`cc_library_static(
     name = "foo",
     copts = select({
-        "//build/bazel/product_variables:acme__board__soc_a": ["-DSOC_A"],
-        "//build/bazel/product_variables:acme__board__soc_b": ["-DSOC_B"],
-        "//build/bazel/product_variables:acme__board__soc_c": [],
+        "//build/bazel/product_config/config_settings:acme__board__soc_a": ["-DSOC_A"],
+        "//build/bazel/product_config/config_settings:acme__board__soc_b": ["-DSOC_B"],
+        "//build/bazel/product_config/config_settings:acme__board__soc_c": [],
         "//conditions:default": ["-DSOC_DEFAULT"],
     }),
     implementation_deps = select({
-        "//build/bazel/product_variables:acme__board__soc_a": ["//foo/bar:soc_a_dep"],
-        "//build/bazel/product_variables:acme__board__soc_b": ["//foo/bar:soc_b_dep"],
-        "//build/bazel/product_variables:acme__board__soc_c": [],
+        "//build/bazel/product_config/config_settings:acme__board__soc_a": ["//foo/bar:soc_a_dep"],
+        "//build/bazel/product_config/config_settings:acme__board__soc_b": ["//foo/bar:soc_b_dep"],
+        "//build/bazel/product_config/config_settings:acme__board__soc_c": [],
         "//conditions:default": ["//foo/bar:soc_default_static_dep"],
     }),
     local_includes = ["."],
@@ -446,7 +446,7 @@
 		ExpectedBazelTargets: []string{`cc_library_static(
     name = "lib",
     copts = select({
-        "//build/bazel/product_variables:vendor_foo__feature": [
+        "//build/bazel/product_config/config_settings:vendor_foo__feature": [
             "-cflag_feature_2",
             "-cflag_feature_1",
         ],
@@ -527,11 +527,11 @@
 		ExpectedBazelTargets: []string{`cc_library_static(
     name = "lib",
     asflags = select({
-        "//build/bazel/product_variables:acme__feature": ["-asflag_bar"],
+        "//build/bazel/product_config/config_settings:acme__feature": ["-asflag_bar"],
         "//conditions:default": ["-asflag_default_bar"],
     }),
     copts = select({
-        "//build/bazel/product_variables:acme__feature": [
+        "//build/bazel/product_config/config_settings:acme__feature": [
             "-cflag_foo",
             "-cflag_bar",
         ],
@@ -546,11 +546,11 @@
 			`cc_library_static(
     name = "lib2",
     asflags = select({
-        "//build/bazel/product_variables:acme__feature": ["-asflag_bar"],
+        "//build/bazel/product_config/config_settings:acme__feature": ["-asflag_bar"],
         "//conditions:default": ["-asflag_default_bar"],
     }),
     copts = select({
-        "//build/bazel/product_variables:acme__feature": [
+        "//build/bazel/product_config/config_settings:acme__feature": [
             "-cflag_bar",
             "-cflag_foo",
         ],
@@ -643,13 +643,13 @@
 		ExpectedBazelTargets: []string{`cc_library_static(
     name = "lib",
     copts = select({
-        "//build/bazel/product_variables:vendor_bar__feature": ["-DVENDOR_BAR_FEATURE"],
+        "//build/bazel/product_config/config_settings:vendor_bar__feature": ["-DVENDOR_BAR_FEATURE"],
         "//conditions:default": ["-DVENDOR_BAR_DEFAULT"],
     }) + select({
-        "//build/bazel/product_variables:vendor_foo__feature": ["-DVENDOR_FOO_FEATURE"],
+        "//build/bazel/product_config/config_settings:vendor_foo__feature": ["-DVENDOR_FOO_FEATURE"],
         "//conditions:default": ["-DVENDOR_FOO_DEFAULT"],
     }) + select({
-        "//build/bazel/product_variables:vendor_qux__feature": ["-DVENDOR_QUX_FEATURE"],
+        "//build/bazel/product_config/config_settings:vendor_qux__feature": ["-DVENDOR_QUX_FEATURE"],
         "//conditions:default": ["-DVENDOR_QUX_DEFAULT"],
     }),
     local_includes = ["."],
@@ -697,7 +697,7 @@
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("custom", "foo", AttrNameToString{
 				"string_literal_prop": `select({
-        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": "29",
+        "//build/bazel/product_config/config_settings:android__library_linking_strategy__prefer_static": "29",
         "//conditions:default": "30",
     })`,
 			}),
@@ -779,7 +779,7 @@
 		ExpectedBazelTargets: []string{`cc_binary(
     name = "library_linking_strategy_sample_binary",
     dynamic_deps = select({
-        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+        "//build/bazel/product_config/config_settings:android__library_linking_strategy__prefer_static": [],
         "//conditions:default": [
             "//foo/bar:lib_b",
             "//foo/bar:lib_a",
@@ -868,7 +868,7 @@
 		ExpectedBazelTargets: []string{
 			MakeBazelTargetNoRestrictions("cc_binary", "library_linking_strategy_sample_binary", AttrNameToString{
 				"dynamic_deps": `select({
-        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+        "//build/bazel/product_config/config_settings:android__library_linking_strategy__prefer_static": [],
         "//conditions:default": [
             "//foo/bar:lib_b",
             "//foo/bar:lib_c",
@@ -877,7 +877,7 @@
 			}),
 			MakeBazelTargetNoRestrictions("cc_binary", "library_linking_strategy_sample_binary_with_excludes", AttrNameToString{
 				"dynamic_deps": `select({
-        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+        "//build/bazel/product_config/config_settings:android__library_linking_strategy__prefer_static": [],
         "//conditions:default": ["//foo/bar:lib_b"],
     })`,
 			}),
@@ -965,14 +965,14 @@
 		ExpectedBazelTargets: []string{`cc_binary(
     name = "library_linking_strategy_sample_binary",
     deps = select({
-        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [
+        "//build/bazel/product_config/config_settings:android__library_linking_strategy__prefer_static": [
             "//foo/bar:lib_b_bp2build_cc_library_static",
             "//foo/bar:lib_a_bp2build_cc_library_static",
         ],
         "//conditions:default": [],
     }),
     dynamic_deps = select({
-        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+        "//build/bazel/product_config/config_settings:android__library_linking_strategy__prefer_static": [],
         "//conditions:default": [
             "//foo/bar:lib_b",
             "//foo/bar:lib_a",
@@ -1046,14 +1046,14 @@
 		ExpectedBazelTargets: []string{`cc_binary(
     name = "library_linking_strategy_sample_binary",
     deps = select({
-        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [
+        "//build/bazel/product_config/config_settings:android__library_linking_strategy__prefer_static": [
             "//foo/bar:lib_a_bp2build_cc_library_static",
             "//foo/bar:lib_b_bp2build_cc_library_static",
         ],
         "//conditions:default": [],
     }),
     dynamic_deps = select({
-        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+        "//build/bazel/product_config/config_settings:android__library_linking_strategy__prefer_static": [],
         "//conditions:default": [
             "//foo/bar:lib_a",
             "//foo/bar:lib_b",
@@ -1134,13 +1134,13 @@
 		ExpectedBazelTargets: []string{`cc_binary(
     name = "alphabet_binary",
     deps = select({
-        "//build/bazel/product_variables:android__alphabet__a": [],
-        "//build/bazel/product_variables:android__alphabet__b": [],
+        "//build/bazel/product_config/config_settings:android__alphabet__a": [],
+        "//build/bazel/product_config/config_settings:android__alphabet__b": [],
         "//conditions:default": ["//foo/bar:lib_default_bp2build_cc_library_static"],
     }),
     dynamic_deps = select({
-        "//build/bazel/product_variables:android__alphabet__a": ["//foo/bar:lib_a"],
-        "//build/bazel/product_variables:android__alphabet__b": ["//foo/bar:lib_b"],
+        "//build/bazel/product_config/config_settings:android__alphabet__a": ["//foo/bar:lib_a"],
+        "//build/bazel/product_config/config_settings:android__alphabet__b": ["//foo/bar:lib_b"],
         "//conditions:default": [],
     }),
     local_includes = ["."],
@@ -1199,7 +1199,7 @@
     name = "alphabet_binary",
     local_includes = ["."],
     srcs = ["main.cc"],
-    target_compatible_with = ["//build/bazel/product_variables:alphabet_module__special_build"] + select({
+    target_compatible_with = select({
         "//build/bazel/platforms/os_arch:android_x86_64": ["@platforms//:incompatible"],
         "//build/bazel/platforms/os_arch:darwin_arm64": ["@platforms//:incompatible"],
         "//build/bazel/platforms/os_arch:darwin_x86_64": ["@platforms//:incompatible"],
@@ -1208,6 +1208,9 @@
         "//build/bazel/platforms/os_arch:linux_musl_x86_64": ["@platforms//:incompatible"],
         "//build/bazel/platforms/os_arch:windows_x86_64": ["@platforms//:incompatible"],
         "//conditions:default": [],
+    }) + select({
+        "//build/bazel/product_config/config_settings:alphabet_module__special_build": [],
+        "//conditions:default": ["@platforms//:incompatible"],
     }),
 )`}})
 }
@@ -1252,7 +1255,10 @@
     name = "alphabet_binary",
     local_includes = ["."],
     srcs = ["main.cc"],
-    target_compatible_with = ["//build/bazel/product_variables:alphabet_module__special_build"],
+    target_compatible_with = select({
+        "//build/bazel/product_config/config_settings:alphabet_module__special_build": [],
+        "//conditions:default": ["@platforms//:incompatible"],
+    }),
 )`}})
 }
 
@@ -1389,16 +1395,16 @@
         "//build/bazel/platforms/os:android": ["-DFOO"],
         "//conditions:default": [],
     }) + select({
-        "//build/bazel/product_variables:my_namespace__my_bool_variable__android": ["-DBAR"],
-        "//build/bazel/product_variables:my_namespace__my_bool_variable__conditions_default__android": ["-DBAZ"],
+        "//build/bazel/product_config/config_settings:my_namespace__my_bool_variable__android": ["-DBAR"],
+        "//build/bazel/product_config/config_settings:my_namespace__my_bool_variable__conditions_default__android": ["-DBAZ"],
         "//conditions:default": [],
     }) + select({
-        "//build/bazel/product_variables:my_namespace__my_string_variable__value1": ["-DVALUE1_NOT_ANDROID"],
+        "//build/bazel/product_config/config_settings:my_namespace__my_string_variable__value1": ["-DVALUE1_NOT_ANDROID"],
         "//conditions:default": [],
     }) + select({
-        "//build/bazel/product_variables:my_namespace__my_string_variable__conditions_default__android": ["-DSTRING_VAR_CONDITIONS_DEFAULT"],
-        "//build/bazel/product_variables:my_namespace__my_string_variable__value1__android": ["-DVALUE1"],
-        "//build/bazel/product_variables:my_namespace__my_string_variable__value2__android": ["-DVALUE2"],
+        "//build/bazel/product_config/config_settings:my_namespace__my_string_variable__conditions_default__android": ["-DSTRING_VAR_CONDITIONS_DEFAULT"],
+        "//build/bazel/product_config/config_settings:my_namespace__my_string_variable__value1__android": ["-DVALUE1"],
+        "//build/bazel/product_config/config_settings:my_namespace__my_string_variable__value2__android": ["-DVALUE2"],
         "//conditions:default": [],
     }),
     local_includes = ["."],
@@ -1406,3 +1412,111 @@
     target_compatible_with = ["//build/bazel/platforms/os:android"],
 )`}})
 }
+
+// If we have
+// A. a soong_config_module_type with target.android_<arch>.* in properties
+// B. a module that uses this module type but does not set target.android_<arch>.* via soong config vars
+// Then we should not panic
+func TestPanicsIfSoongConfigModuleTypeHasArchSpecificProperties(t *testing.T) {
+	commonBp := `
+soong_config_bool_variable {
+	name: "my_bool_variable",
+}
+soong_config_module_type {
+	name: "special_cc_defaults",
+	module_type: "cc_defaults",
+	config_namespace: "my_namespace",
+	bool_variables: ["my_bool_variable"],
+	properties: [
+		"cflags",
+		"target.android_arm64.shared_libs",
+	],
+}
+cc_binary {
+	name: "my_binary",
+	defaults: ["my_special_cc_defaults"],
+}
+`
+	testCases := []struct {
+		desc            string
+		additionalBp    string
+		isPanicExpected bool
+	}{
+		{
+			desc: "target.android_arm64 is not set, bp2build should not panic",
+			additionalBp: `
+special_cc_defaults {
+	name: "my_special_cc_defaults",
+	soong_config_variables: {
+		my_bool_variable: {
+			cflags: ["-DFOO"],
+			conditions_default: {
+				cflags: ["-DBAR"],
+			}
+		}
+	},
+}
+			`,
+			isPanicExpected: false,
+		},
+		{
+			desc: "target.android_arm64 is set using the bool soong config var, bp2build should panic",
+			additionalBp: `
+special_cc_defaults {
+	name: "my_special_cc_defaults",
+	soong_config_variables: {
+		my_bool_variable: {
+			cflags: ["-DFOO"],
+			target: {
+				android_arm64: {
+					shared_libs: ["liblog"],
+				},
+			},
+			conditions_default: {
+				cflags: ["-DBAR"],
+			}
+		}
+	},
+}
+			`,
+			isPanicExpected: true,
+		},
+		{
+			desc: "target.android_arm64 is set using conditions_default for the bool soong config var, bp2build should panic",
+			additionalBp: `
+special_cc_defaults {
+	name: "my_special_cc_defaults",
+	soong_config_variables: {
+		my_bool_variable: {
+			cflags: ["-DFOO"],
+			conditions_default: {
+				cflags: ["-DBAR"],
+				target: {
+					android_arm64: {
+						shared_libs: ["liblog"],
+					},
+				},
+			}
+		}
+	},
+}
+			`,
+			isPanicExpected: true,
+		},
+	}
+	for _, tc := range testCases {
+		bp2buildTestCase := Bp2buildTestCase{
+			Description:                tc.desc,
+			ModuleTypeUnderTest:        "cc_binary",
+			ModuleTypeUnderTestFactory: cc.BinaryFactory,
+			Blueprint:                  commonBp + tc.additionalBp,
+			// Check in `foo` dir so that we can check whether it panics or not and not trip over an empty `ExpectedBazelTargets`
+			Dir:                  "foo",
+			ExpectedBazelTargets: []string{},
+		}
+		if tc.isPanicExpected {
+			bp2buildTestCase.ExpectedErr = fmt.Errorf("TODO: support other target types in soong config variable structs: Android_arm64")
+		}
+		runSoongConfigModuleTypeTest(t, bp2buildTestCase)
+	}
+}
diff --git a/cc/afdo.go b/cc/afdo.go
index bc7cd52..23d196d 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -131,6 +131,10 @@
 		return
 	}
 
+	if !c.afdo.afdoEnabled() {
+		return
+	}
+
 	ctx.VisitDirectDepsWithTag(FdoProfileTag, func(m android.Module) {
 		if ctx.OtherModuleHasProvider(m, FdoProfileProvider) {
 			info := ctx.OtherModuleProvider(m, FdoProfileProvider).(FdoProfileInfo)
diff --git a/cc/api_level.go b/cc/api_level.go
index a5571f3..69a0d3a 100644
--- a/cc/api_level.go
+++ b/cc/api_level.go
@@ -31,7 +31,11 @@
 	case android.Arm64, android.X86_64:
 		return android.FirstLp64Version
 	case android.Riscv64:
-		return android.FutureApiLevel
+		apiLevel, err := android.ApiLevelFromUser(ctx, "VanillaIceCream")
+		if err != nil {
+			panic(err)
+		}
+		return apiLevel
 	default:
 		panic(fmt.Errorf("Unknown arch %q", arch))
 	}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 6e00aa8..9e485d6 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -22,6 +22,7 @@
 	"android/soong/android"
 	"android/soong/bazel"
 	"android/soong/cc/config"
+	"android/soong/genrule"
 
 	"github.com/google/blueprint"
 
@@ -43,6 +44,12 @@
 
 	rScriptSrcPartition = "renderScript"
 
+	xsdSrcPartition = "xsd"
+
+	genrulePartition = "genrule"
+
+	hdrPartition = "hdr"
+
 	stubsSuffix = "_stub_libs_current"
 )
 
@@ -155,6 +162,7 @@
 		lSrcPartition:       bazel.LabelPartition{Extensions: []string{".l"}},
 		llSrcPartition:      bazel.LabelPartition{Extensions: []string{".ll"}},
 		rScriptSrcPartition: bazel.LabelPartition{Extensions: []string{".fs", ".rscript"}},
+		xsdSrcPartition:     bazel.LabelPartition{LabelMapper: android.XsdLabelMapper(xsdConfigCppTarget)},
 		// C++ is the "catch-all" group, and comprises generated sources because we don't
 		// know the language of these sources until the genrule is executed.
 		cppSrcPartition:     bazel.LabelPartition{Extensions: []string{".cpp", ".cc", ".cxx", ".mm"}, LabelMapper: addSuffixForFilegroup("_cpp_srcs"), Keep_remainder: true},
@@ -165,6 +173,15 @@
 	return bazel.PartitionLabelListAttribute(ctx, &srcs, labels)
 }
 
+func partitionHeaders(ctx android.BazelConversionPathContext, hdrs bazel.LabelListAttribute) bazel.PartitionToLabelListAttribute {
+	labels := bazel.LabelPartitions{
+		xsdSrcPartition:  bazel.LabelPartition{LabelMapper: android.XsdLabelMapper(xsdConfigCppTarget)},
+		genrulePartition: bazel.LabelPartition{LabelMapper: genrule.GenruleCcHeaderLabelMapper},
+		hdrPartition:     bazel.LabelPartition{Keep_remainder: true},
+	}
+	return bazel.PartitionLabelListAttribute(ctx, &hdrs, labels)
+}
+
 // bp2BuildParseLibProps returns the attributes for a variant of a cc_library.
 func bp2BuildParseLibProps(ctx android.BazelConversionPathContext, module *Module, isStatic bool) staticOrSharedAttributes {
 	lib, ok := module.compiler.(*libraryDecorator)
@@ -403,7 +420,12 @@
 	srcs     bazel.LabelListAttribute
 
 	// xsd config sources
-	xsdInSrcs bazel.StringListAttribute
+	xsdSrcs       bazel.LabelListAttribute
+	exportXsdSrcs bazel.LabelListAttribute
+
+	// genrule headers
+	genruleHeaders       bazel.LabelListAttribute
+	exportGenruleHeaders bazel.LabelListAttribute
 
 	// Lex sources and options
 	lSrcs   bazel.LabelListAttribute
@@ -494,14 +516,11 @@
 func (ca *compilerAttributes) bp2buildForAxisAndConfig(ctx android.BazelConversionPathContext, axis bazel.ConfigurationAxis, config string, props *BaseCompilerProperties) {
 	// If there's arch specific srcs or exclude_srcs, generate a select entry for it.
 	// TODO(b/186153868): do this for OS specific srcs and exclude_srcs too.
-	srcsList, xsdList, ok := parseSrcs(ctx, props)
+	srcsList, ok := parseSrcs(ctx, props)
 
 	if ok {
 		ca.srcs.SetSelectValue(axis, config, srcsList)
 	}
-	if len(xsdList) > 0 {
-		ca.xsdInSrcs.SetSelectValue(axis, config, xsdList)
-	}
 
 	localIncludeDirs := props.Local_include_dirs
 	if axis == bazel.NoConfigAxis {
@@ -568,9 +587,11 @@
 	}
 }
 
-func (ca *compilerAttributes) finalize(ctx android.BazelConversionPathContext, implementationHdrs bazel.LabelListAttribute) {
+func (ca *compilerAttributes) finalize(ctx android.BazelConversionPathContext, implementationHdrs, exportHdrs bazel.LabelListAttribute) {
 	ca.srcs.ResolveExcludes()
 	partitionedSrcs := groupSrcsByExtension(ctx, ca.srcs)
+	partitionedImplHdrs := partitionHeaders(ctx, implementationHdrs)
+	partitionedHdrs := partitionHeaders(ctx, exportHdrs)
 
 	ca.protoSrcs = partitionedSrcs[protoSrcPartition]
 	ca.aidlSrcs = partitionedSrcs[aidlSrcPartition]
@@ -580,10 +601,22 @@
 		if lla.IsEmpty() {
 			continue
 		}
-		lla.Append(implementationHdrs)
+		lla.Append(partitionedImplHdrs[hdrPartition])
 		partitionedSrcs[p] = lla
 	}
 
+	ca.hdrs = partitionedHdrs[hdrPartition]
+
+	ca.includesFromHeaders(ctx, partitionedImplHdrs[hdrPartition], partitionedHdrs[hdrPartition])
+
+	xsdSrcs := bazel.SubtractBazelLabelListAttribute(partitionedSrcs[xsdSrcPartition], partitionedHdrs[xsdSrcPartition])
+	xsdSrcs.Append(partitionedImplHdrs[xsdSrcPartition])
+	ca.exportXsdSrcs = partitionedHdrs[xsdSrcPartition]
+	ca.xsdSrcs = bazel.FirstUniqueBazelLabelListAttribute(xsdSrcs)
+
+	ca.genruleHeaders = partitionedImplHdrs[genrulePartition]
+	ca.exportGenruleHeaders = partitionedHdrs[genrulePartition]
+
 	ca.srcs = partitionedSrcs[cppSrcPartition]
 	ca.cSrcs = partitionedSrcs[cSrcPartition]
 	ca.asSrcs = partitionedSrcs[asSrcPartition]
@@ -604,11 +637,11 @@
 }
 
 // Parse srcs from an arch or OS's props value.
-func parseSrcs(ctx android.BazelConversionPathContext, props *BaseCompilerProperties) (bazel.LabelList, []string, bool) {
+func parseSrcs(ctx android.BazelConversionPathContext, props *BaseCompilerProperties) (bazel.LabelList, bool) {
 	anySrcs := false
 	// Add srcs-like dependencies such as generated files.
 	// First create a LabelList containing these dependencies, then merge the values with srcs.
-	genSrcs, xsd := android.PartitionXsdSrcs(ctx, props.Generated_sources)
+	genSrcs := props.Generated_sources
 	generatedSrcsLabelList := android.BazelLabelForModuleDepsExcludes(ctx, genSrcs, props.Exclude_generated_sources)
 	if len(props.Generated_sources) > 0 || len(props.Exclude_generated_sources) > 0 {
 		anySrcs = true
@@ -620,7 +653,7 @@
 		anySrcs = true
 	}
 
-	return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedSrcsLabelList), xsd, anySrcs
+	return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedSrcsLabelList), anySrcs
 }
 
 func bp2buildStdVal(std *string, prefix string, useGnu bool) *string {
@@ -667,8 +700,43 @@
 	return split[0][2:], true
 }
 
-// includesFromLabelList extracts relative/absolute includes from a bazel.LabelList>
-func includesFromLabelList(labelList bazel.LabelList) (relative, absolute []string) {
+// includesFromHeaders gets the include directories needed from generated headers
+func (ca *compilerAttributes) includesFromHeaders(ctx android.BazelConversionPathContext, implHdrs, hdrs bazel.LabelListAttribute) {
+	local, absolute := includesFromLabelListAttribute(implHdrs, ca.localIncludes, ca.absoluteIncludes)
+	localExport, absoluteExport := includesFromLabelListAttribute(hdrs, ca.includes.Includes, ca.includes.AbsoluteIncludes)
+
+	ca.localIncludes = local
+	ca.absoluteIncludes = absolute
+
+	ca.includes.Includes = localExport
+	ca.includes.AbsoluteIncludes = absoluteExport
+}
+
+// includesFromLabelList extracts the packages from a LabelListAttribute that should be includes and
+// combines them with existing local/absolute includes.
+func includesFromLabelListAttribute(attr bazel.LabelListAttribute, existingLocal, existingAbsolute bazel.StringListAttribute) (bazel.StringListAttribute, bazel.StringListAttribute) {
+	localAttr := existingLocal.Clone()
+	absoluteAttr := existingAbsolute.Clone()
+	if !attr.Value.IsEmpty() {
+		l, a := includesFromLabelList(attr.Value, existingLocal.Value, existingAbsolute.Value)
+		localAttr.SetSelectValue(bazel.NoConfigAxis, "", l)
+		absoluteAttr.SetSelectValue(bazel.NoConfigAxis, "", a)
+	}
+	for axis, configToLabels := range attr.ConfigurableValues {
+		for c, labels := range configToLabels {
+			local := existingLocal.SelectValue(axis, c)
+			absolute := existingAbsolute.SelectValue(axis, c)
+			l, a := includesFromLabelList(labels, local, absolute)
+			localAttr.SetSelectValue(axis, c, l)
+			absoluteAttr.SetSelectValue(axis, c, a)
+		}
+	}
+	return *localAttr, *absoluteAttr
+}
+
+// includesFromLabelList extracts relative/absolute includes from a bazel.LabelList.
+func includesFromLabelList(labelList bazel.LabelList, existingRel, existingAbs []string) ([]string, []string) {
+	var relative, absolute []string
 	for _, hdr := range labelList.Includes {
 		if pkg, hasPkg := packageFromLabel(hdr.Label); hasPkg {
 			absolute = append(absolute, pkg)
@@ -676,6 +744,12 @@
 			relative = append(relative, pkg)
 		}
 	}
+	if len(relative)+len(existingRel) != 0 {
+		relative = android.FirstUniqueStrings(append(append([]string{}, existingRel...), relative...))
+	}
+	if len(absolute)+len(existingAbs) != 0 {
+		absolute = android.FirstUniqueStrings(append(append([]string{}, existingAbs...), absolute...))
+	}
 	return relative, absolute
 }
 
@@ -740,8 +814,6 @@
 	archVariantLinkerProps := module.GetArchVariantProperties(ctx, &BaseLinkerProperties{})
 	archVariantLibraryProperties := module.GetArchVariantProperties(ctx, &LibraryProperties{})
 
-	var implementationHdrs bazel.LabelListAttribute
-
 	axisToConfigs := map[bazel.ConfigurationAxis]map[string]bool{}
 	allAxesAndConfigs := func(cp android.ConfigurationAxisToArchVariantProperties) {
 		for axis, configMap := range cp {
@@ -761,6 +833,7 @@
 	linkerAttrs := linkerAttributes{}
 
 	var aidlLibs bazel.LabelList
+	var implementationHdrs, exportHdrs bazel.LabelListAttribute
 
 	// Iterate through these axes in a deterministic order. This is required
 	// because processing certain dependencies may result in concatenating
@@ -770,9 +843,9 @@
 	for _, axis := range bazel.SortedConfigurationAxes(axisToConfigs) {
 		configs := axisToConfigs[axis]
 		for cfg := range configs {
-			var allHdrs, allHdrsXsd []string
+			var allHdrs []string
 			if baseCompilerProps, ok := archVariantCompilerProps[axis][cfg].(*BaseCompilerProperties); ok {
-				allHdrs, allHdrsXsd = android.PartitionXsdSrcs(ctx, baseCompilerProps.Generated_headers)
+				allHdrs = baseCompilerProps.Generated_headers
 
 				if baseCompilerProps.Lex != nil {
 					compilerAttrs.lexopts.SetSelectValue(axis, cfg, baseCompilerProps.Lex.Flags)
@@ -786,36 +859,17 @@
 				aidlLibs.Append(android.BazelLabelForModuleDeps(ctx, baseCompilerProps.Aidl.Libs))
 			}
 
-			var exportHdrs, exportHdrsXsd []string
+			var exportedHdrs []string
 
 			if baseLinkerProps, ok := archVariantLinkerProps[axis][cfg].(*BaseLinkerProperties); ok {
-				exportHdrs, exportHdrsXsd = android.PartitionXsdSrcs(ctx, baseLinkerProps.Export_generated_headers)
+				exportedHdrs = baseLinkerProps.Export_generated_headers
 				(&linkerAttrs).bp2buildForAxisAndConfig(ctx, module, axis, cfg, baseLinkerProps)
 			}
 
-			// in the synthetic bp2build workspace, xsd sources are compiled to a static library
-			xsdList := compilerAttrs.xsdInSrcs.SelectValue(axis, cfg)
-			allHdrsXsd = android.FirstUniqueStrings(append(xsdList, allHdrsXsd...))
-			headers := maybePartitionExportedAndImplementationsDeps(ctx, !module.Binary(), allHdrs, exportHdrs, android.BazelLabelForModuleDeps)
-			xsdConfigLibs := maybePartitionExportedAndImplementationsDeps(ctx, !module.Binary(), allHdrsXsd, exportHdrsXsd, bazelLabelForXsdConfig)
+			headers := maybePartitionExportedAndImplementationsDeps(ctx, !module.Binary(), allHdrs, exportedHdrs, android.BazelLabelForModuleDeps)
 
 			implementationHdrs.SetSelectValue(axis, cfg, headers.implementation)
-			compilerAttrs.hdrs.SetSelectValue(axis, cfg, headers.export)
-
-			exportIncludes, exportAbsoluteIncludes := includesFromLabelList(headers.export)
-			compilerAttrs.includes.Includes.SetSelectValue(axis, cfg, exportIncludes)
-			compilerAttrs.includes.AbsoluteIncludes.SetSelectValue(axis, cfg, exportAbsoluteIncludes)
-
-			includes, absoluteIncludes := includesFromLabelList(headers.implementation)
-			currAbsoluteIncludes := compilerAttrs.absoluteIncludes.SelectValue(axis, cfg)
-			currAbsoluteIncludes = android.FirstUniqueStrings(append(currAbsoluteIncludes, absoluteIncludes...))
-
-			compilerAttrs.absoluteIncludes.SetSelectValue(axis, cfg, currAbsoluteIncludes)
-
-			currIncludes := compilerAttrs.localIncludes.SelectValue(axis, cfg)
-			currIncludes = android.FirstUniqueStrings(append(currIncludes, includes...))
-
-			compilerAttrs.localIncludes.SetSelectValue(axis, cfg, currIncludes)
+			exportHdrs.SetSelectValue(axis, cfg, headers.export)
 
 			if libraryProps, ok := archVariantLibraryProperties[axis][cfg].(*LibraryProperties); ok {
 				if axis == bazel.NoConfigAxis {
@@ -835,14 +889,6 @@
 				}
 			}
 
-			if len(allHdrsXsd) > 0 {
-				wholeStaticLibs := linkerAttrs.implementationWholeArchiveDeps.SelectValue(axis, cfg)
-				(&wholeStaticLibs).Append(xsdConfigLibs.implementation)
-				linkerAttrs.implementationWholeArchiveDeps.SetSelectValue(axis, cfg, wholeStaticLibs)
-				wholeStaticLibs = linkerAttrs.wholeArchiveDeps.SelectValue(axis, cfg)
-				(&wholeStaticLibs).Append(xsdConfigLibs.export)
-				linkerAttrs.wholeArchiveDeps.SetSelectValue(axis, cfg, wholeStaticLibs)
-			}
 		}
 	}
 
@@ -860,11 +906,17 @@
 	(&compilerAttrs).convertProductVariables(ctx, productVariableProps)
 	(&linkerAttrs).convertProductVariables(ctx, productVariableProps)
 
-	(&compilerAttrs).finalize(ctx, implementationHdrs)
+	(&compilerAttrs).finalize(ctx, implementationHdrs, exportHdrs)
 	(&linkerAttrs).finalize(ctx)
 
 	(&compilerAttrs.srcs).Add(bp2BuildYasm(ctx, module, compilerAttrs))
 
+	(&linkerAttrs).deps.Append(compilerAttrs.exportGenruleHeaders)
+	(&linkerAttrs).implementationDeps.Append(compilerAttrs.genruleHeaders)
+
+	(&linkerAttrs).wholeArchiveDeps.Append(compilerAttrs.exportXsdSrcs)
+	(&linkerAttrs).implementationWholeArchiveDeps.Append(compilerAttrs.xsdSrcs)
+
 	protoDep := bp2buildProto(ctx, module, compilerAttrs.protoSrcs)
 
 	// bp2buildProto will only set wholeStaticLib or implementationWholeStaticLib, but we don't know
@@ -1748,16 +1800,8 @@
 	return label
 }
 
-// Replaces //a/b/my_xsd_config with //a/b/my_xsd_config-cpp
-func xsdConfigCppTarget(ctx android.BazelConversionPathContext, mod blueprint.Module) string {
-	callback := func(xsd android.XsdConfigBp2buildTargets) string {
-		return xsd.CppBp2buildTargetName()
-	}
-	return android.XsdConfigBp2buildTarget(ctx, mod, callback)
-}
-
-func bazelLabelForXsdConfig(ctx android.BazelConversionPathContext, modules []string) bazel.LabelList {
-	return android.BazelLabelForModuleDepsWithFn(ctx, modules, xsdConfigCppTarget)
+func xsdConfigCppTarget(xsd android.XsdConfigBp2buildTargets) string {
+	return xsd.CppBp2buildTargetName()
 }
 
 func bazelLabelForWholeDeps(ctx android.BazelConversionPathContext, modules []string) bazel.LabelList {
diff --git a/cc/compiler.go b/cc/compiler.go
index 16f4a6e..5bed8a7 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -189,13 +189,13 @@
 			// build the recovery variant of the C/C++ module.
 			Exclude_generated_sources []string
 		}
-		Vendor_ramdisk struct {
+		Ramdisk, Vendor_ramdisk struct {
 			// list of source files that should not be used to
-			// build the vendor ramdisk variant of the C/C++ module.
+			// build the ramdisk variants of the C/C++ module.
 			Exclude_srcs []string `android:"path"`
 
-			// List of additional cflags that should be used to build the vendor ramdisk
-			// variant of the C/C++ module.
+			// List of additional cflags that should be used to build the ramdisk
+			// variants of the C/C++ module.
 			Cflags []string
 		}
 		Platform struct {
@@ -351,6 +351,7 @@
 	CheckBadCompilerFlags(ctx, "vendor.cflags", compiler.Properties.Target.Vendor.Cflags)
 	CheckBadCompilerFlags(ctx, "product.cflags", compiler.Properties.Target.Product.Cflags)
 	CheckBadCompilerFlags(ctx, "recovery.cflags", compiler.Properties.Target.Recovery.Cflags)
+	CheckBadCompilerFlags(ctx, "ramdisk.cflags", compiler.Properties.Target.Ramdisk.Cflags)
 	CheckBadCompilerFlags(ctx, "vendor_ramdisk.cflags", compiler.Properties.Target.Vendor_ramdisk.Cflags)
 	CheckBadCompilerFlags(ctx, "platform.cflags", compiler.Properties.Target.Platform.Cflags)
 
@@ -536,6 +537,9 @@
 	if ctx.inVendorRamdisk() {
 		flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Target.Vendor_ramdisk.Cflags)...)
 	}
+	if ctx.inRamdisk() {
+		flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Target.Ramdisk.Cflags)...)
+	}
 	if !ctx.useSdk() {
 		flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Target.Platform.Cflags)...)
 	}
diff --git a/cc/config/global.go b/cc/config/global.go
index 266d278..ff5ab05 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -149,6 +149,7 @@
 	commonGlobalLldflags = []string{
 		"-fuse-ld=lld",
 		"-Wl,--icf=safe",
+		"-Wl,--no-demangle",
 	}
 
 	deviceGlobalCppflags = []string{
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index dd612ce..f9b3eac 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -21,6 +21,7 @@
 var VndkMustUseVendorVariantList = []string{
 	"android.hardware.nfc@1.2",
 	"libbinder",
+	"libdumpstateutil",
 	"libcrypto",
 	"libexpat",
 	"libgatekeeper",
diff --git a/cc/coverage.go b/cc/coverage.go
index c0f6973..cbd8a6f 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -48,6 +48,7 @@
 func getGcovProfileLibraryName(ctx ModuleContextIntf) string {
 	// This function should only ever be called for a cc.Module, so the
 	// following statement should always succeed.
+	// LINT.IfChange
 	if ctx.useSdk() {
 		return "libprofile-extras_ndk"
 	} else {
@@ -63,6 +64,7 @@
 	} else {
 		return "libprofile-clang-extras"
 	}
+	// LINT.ThenChange(library.go)
 }
 
 func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps {
diff --git a/cc/image.go b/cc/image.go
index e65a9aa..f91762a 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -678,10 +678,17 @@
 	}
 }
 
+func squashRamdiskSrcs(m *Module) {
+	if lib, ok := m.compiler.(*libraryDecorator); ok {
+		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs, lib.baseCompiler.Properties.Target.Ramdisk.Exclude_srcs...)
+	}
+}
+
 func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
 	m := module.(*Module)
 	if variant == android.RamdiskVariation {
 		m.MakeAsPlatform()
+		squashRamdiskSrcs(m)
 	} else if variant == android.VendorRamdiskVariation {
 		m.MakeAsPlatform()
 		squashVendorRamdiskSrcs(m)
diff --git a/cc/library.go b/cc/library.go
index 266fa75..df1dbc5 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -32,6 +32,20 @@
 	"github.com/google/blueprint/proptools"
 )
 
+var (
+	alwaysLinkLibraries = map[string]bool{
+		// Coverage libraries are _always_ added as a whole_static_dep. By converting as these as
+		// alwayslink = True, we can add these as to deps (e.g. as a regular static dep) in Bazel
+		// without any extra complications in cc_shared_library roots to prevent linking the same
+		// library repeatedly.
+		"libprofile-extras_ndk":               true,
+		"libprofile-extras":                   true,
+		"libprofile-clang-extras_ndk":         true,
+		"libprofile-clang-extras_cfi_support": true,
+		"libprofile-clang-extras":             true,
+	}
+)
+
 // LibraryProperties is a collection of properties shared by cc library rules/cc.
 type LibraryProperties struct {
 	// local file name to pass to the linker as -unexported_symbols_list
@@ -435,6 +449,10 @@
 		Bzl_load_location: "//build/bazel/rules/cc:cc_library_shared.bzl",
 	}
 
+	if _, ok := alwaysLinkLibraries[m.Name()]; ok {
+		staticTargetAttrs.Alwayslink = proptools.BoolPtr(true)
+	}
+
 	var tagsForStaticVariant bazel.StringListAttribute
 	if compilerAttrs.stubsSymbolFile == nil && len(compilerAttrs.stubsVersions.Value) == 0 {
 		tagsForStaticVariant = android.ApexAvailableTagsWithoutTestApexes(ctx, m)
@@ -2951,6 +2969,10 @@
 	var attrs interface{}
 	if isStatic {
 		commonAttrs.Deps.Add(baseAttributes.protoDependency)
+		var alwayslink *bool
+		if _, ok := alwaysLinkLibraries[module.Name()]; ok && isStatic {
+			alwayslink = proptools.BoolPtr(true)
+		}
 		attrs = &bazelCcLibraryStaticAttributes{
 			staticOrSharedAttributes: commonAttrs,
 			Rtti:                     compilerAttrs.rtti,
@@ -2964,8 +2986,10 @@
 			Conlyflags: compilerAttrs.conlyFlags,
 			Asflags:    asFlags,
 
-			Features: *features,
+			Alwayslink: alwayslink,
+			Features:   *features,
 		}
+
 	} else {
 		commonAttrs.Dynamic_deps.Add(baseAttributes.protoDependency)
 
@@ -3047,7 +3071,8 @@
 	Conlyflags bazel.StringListAttribute
 	Asflags    bazel.StringListAttribute
 
-	Features bazel.StringListAttribute
+	Alwayslink *bool
+	Features   bazel.StringListAttribute
 }
 
 // TODO(b/199902614): Can this be factored to share with the other Attributes?
diff --git a/cc/lto.go b/cc/lto.go
index 44361db..df9ca0a 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -135,10 +135,14 @@
 			ltoLdFlags = append(ltoLdFlags, cachePolicyFormat+policy)
 		}
 
-		// If the module does not have a profile, be conservative and limit cross TU inline
-		// limit to 5 LLVM IR instructions, to balance binary size increase and performance.
-		if !ctx.Darwin() && !ctx.isPgoCompile() && !ctx.isAfdoCompile() {
-			ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=5")
+		// Reduce the inlining threshold for a better balance of binary size and
+		// performance.
+		if !ctx.Darwin() {
+			if ctx.isPgoCompile() || ctx.isAfdoCompile() {
+				ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=40")
+			} else {
+				ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=5")
+			}
 		}
 
 		flags.Local.CFlags = append(flags.Local.CFlags, ltoCFlags...)
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index f0b7cc5..9281aeb 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -31,9 +31,8 @@
 
 func init() {
 	pctx.HostBinToolVariable("ndkStubGenerator", "ndkstubgen")
-	pctx.HostBinToolVariable("abidiff", "abidiff")
-	pctx.HostBinToolVariable("abitidy", "abitidy")
-	pctx.HostBinToolVariable("abidw", "abidw")
+	pctx.HostBinToolVariable("stg", "stg")
+	pctx.HostBinToolVariable("stgdiff", "stgdiff")
 }
 
 var (
@@ -44,28 +43,20 @@
 			CommandDeps: []string{"$ndkStubGenerator"},
 		}, "arch", "apiLevel", "apiMap", "flags")
 
-	abidw = pctx.AndroidStaticRule("abidw",
+	stg = pctx.AndroidStaticRule("stg",
 		blueprint.RuleParams{
-			Command: "$abidw --type-id-style hash --no-corpus-path " +
-				"--no-show-locs --no-comp-dir-path -w $symbolList " +
-				"$in --out-file $out",
-			CommandDeps: []string{"$abidw"},
+			Command:     "$stg -S :$symbolList --elf $in -o $out",
+			CommandDeps: []string{"$stg"},
 		}, "symbolList")
 
-	abitidy = pctx.AndroidStaticRule("abitidy",
-		blueprint.RuleParams{
-			Command:     "$abitidy --all $flags -i $in -o $out",
-			CommandDeps: []string{"$abitidy"},
-		}, "flags")
-
-	abidiff = pctx.AndroidStaticRule("abidiff",
+	stgdiff = pctx.AndroidStaticRule("stgdiff",
 		blueprint.RuleParams{
 			// Need to create *some* output for ninja. We don't want to use tee
 			// because we don't want to spam the build output with "nothing
 			// changed" messages, so redirect output message to $out, and if
 			// changes were detected print the output and fail.
-			Command:     "$abidiff $args $in > $out || (cat $out && false)",
-			CommandDeps: []string{"$abidiff"},
+			Command:     "$stgdiff $args --stg $in -o $out || (cat $out && false)",
+			CommandDeps: []string{"$stgdiff"},
 		}, "args")
 
 	ndkLibrarySuffix = ".ndk"
@@ -107,12 +98,6 @@
 	// https://github.com/android-ndk/ndk/issues/265.
 	Unversioned_until *string
 
-	// If true, does not emit errors when APIs lacking type information are
-	// found. This is false by default and should not be enabled outside bionic,
-	// where it is enabled pending a fix for http://b/190554910 (no debug info
-	// for asm implemented symbols).
-	Allow_untyped_symbols *bool
-
 	// Headers presented by this library to the Public API Surface
 	Export_header_libs []string
 }
@@ -326,7 +311,7 @@
 	apiLevel android.ApiLevel) android.OptionalPath {
 
 	subpath := filepath.Join("prebuilts/abi-dumps/ndk", apiLevel.String(),
-		ctx.Arch().ArchType.String(), this.libraryName(ctx), "abi.xml")
+		ctx.Arch().ArchType.String(), this.libraryName(ctx), "abi.stg")
 	return android.ExistentPathForSource(ctx, subpath)
 }
 
@@ -359,34 +344,17 @@
 
 func (this *stubDecorator) dumpAbi(ctx ModuleContext, symbolList android.Path) {
 	implementationLibrary := this.findImplementationLibrary(ctx)
-	abiRawPath := getNdkAbiDumpInstallBase(ctx).Join(ctx,
-		this.apiLevel.String(), ctx.Arch().ArchType.String(),
-		this.libraryName(ctx), "abi.raw.xml")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        abidw,
-		Description: fmt.Sprintf("abidw %s", implementationLibrary),
-		Input:       implementationLibrary,
-		Output:      abiRawPath,
-		Implicit:    symbolList,
-		Args: map[string]string{
-			"symbolList": symbolList.String(),
-		},
-	})
-
 	this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx,
 		this.apiLevel.String(), ctx.Arch().ArchType.String(),
-		this.libraryName(ctx), "abi.xml")
-	untypedFlag := "--abort-on-untyped-symbols"
-	if proptools.BoolDefault(this.properties.Allow_untyped_symbols, false) {
-		untypedFlag = ""
-	}
+		this.libraryName(ctx), "abi.stg")
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        abitidy,
-		Description: fmt.Sprintf("abitidy %s", implementationLibrary),
-		Input:       abiRawPath,
+		Rule:        stg,
+		Description: fmt.Sprintf("stg %s", implementationLibrary),
+		Input:       implementationLibrary,
+		Implicit:    symbolList,
 		Output:      this.abiDumpPath,
 		Args: map[string]string{
-			"flags": untypedFlag,
+			"symbolList": symbolList.String(),
 		},
 	})
 }
@@ -405,7 +373,7 @@
 func (this *stubDecorator) diffAbi(ctx ModuleContext) {
 	// Catch any ABI changes compared to the checked-in definition of this API
 	// level.
-	abiDiffPath := android.PathForModuleOut(ctx, "abidiff.timestamp")
+	abiDiffPath := android.PathForModuleOut(ctx, "stgdiff.timestamp")
 	prebuiltAbiDump := this.findPrebuiltAbiDump(ctx, this.apiLevel)
 	missingPrebuiltError := fmt.Sprintf(
 		"Did not find prebuilt ABI dump for %q (%q). Generate with "+
@@ -421,11 +389,14 @@
 		})
 	} else {
 		ctx.Build(pctx, android.BuildParams{
-			Rule: abidiff,
-			Description: fmt.Sprintf("abidiff %s %s", prebuiltAbiDump,
+			Rule: stgdiff,
+			Description: fmt.Sprintf("Comparing ABI %s %s", prebuiltAbiDump,
 				this.abiDumpPath),
 			Output: abiDiffPath,
 			Inputs: android.Paths{prebuiltAbiDump.Path(), this.abiDumpPath},
+			Args: map[string]string{
+				"args": "--format=small",
+			},
 		})
 	}
 	this.abiDiffPaths = append(this.abiDiffPaths, abiDiffPath)
@@ -452,13 +423,13 @@
 			})
 		} else {
 			ctx.Build(pctx, android.BuildParams{
-				Rule: abidiff,
+				Rule: stgdiff,
 				Description: fmt.Sprintf("abidiff %s %s", this.abiDumpPath,
 					nextAbiDump),
 				Output: nextAbiDiffPath,
 				Inputs: android.Paths{this.abiDumpPath, nextAbiDump.Path()},
 				Args: map[string]string{
-					"args": "--no-added-syms",
+					"args": "--format=small --ignore=interface_addition",
 				},
 			})
 		}
diff --git a/cc/rs.go b/cc/rs.go
index 6507259..93acdc7 100644
--- a/cc/rs.go
+++ b/cc/rs.go
@@ -101,11 +101,12 @@
 func rsFlags(ctx ModuleContext, flags Flags, properties *BaseCompilerProperties) Flags {
 	targetApi := String(properties.Renderscript.Target_api)
 	if targetApi == "" && ctx.useSdk() {
-		switch ctx.sdkVersion() {
-		case "current", "system_current", "test_current":
-			// Nothing
-		default:
-			targetApi = android.GetNumericSdkVersion(ctx.sdkVersion())
+		targetApiLevel := android.ApiLevelOrPanic(ctx, ctx.sdkVersion())
+		if targetApiLevel.IsCurrent() || targetApiLevel.IsPreview() {
+			// If the target level is current or preview, leave the 'target-api' unset.
+			// This signals to llvm-rs-cc that the development API should be used.
+		} else {
+			targetApi = targetApiLevel.String()
 		}
 	}
 
diff --git a/cc/sanitize.go b/cc/sanitize.go
index d7b69e4..6806e63 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -56,10 +56,6 @@
 		// higher number of "optimized out" stack variables.
 		// b/112437883.
 		"-instcombine-lower-dbg-declare=0",
-		// TODO(b/159343917): HWASan and GlobalISel don't play nicely, and
-		// GlobalISel is the default at -O0 on aarch64.
-		"--aarch64-enable-global-isel-at-O=-1",
-		"-fast-isel=false",
 		"-hwasan-use-after-scope=1",
 		"-dom-tree-reachability-max-bbs-to-explore=128",
 	}
diff --git a/cc/stl.go b/cc/stl.go
index ffc7c76..8f92dcb 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -80,8 +80,7 @@
 			return ""
 		}
 		s = deduplicateStlInput(s)
-		archHasNDKStl := ctx.Arch().ArchType != android.Riscv64
-		if ctx.useSdk() && ctx.Device() && archHasNDKStl {
+		if ctx.useSdk() && ctx.Device() {
 			switch s {
 			case "", "system":
 				return "ndk_system"
diff --git a/cc/test.go b/cc/test.go
index 53a097a..0be2301 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -682,7 +682,7 @@
 type testBinaryAttributes struct {
 	binaryAttributes
 
-	Gtest bool
+	Gtest *bool
 
 	tidyAttributes
 	tradefed.TestConfigAttributes
@@ -720,15 +720,15 @@
 
 	m.convertTidyAttributes(ctx, &testBinaryAttrs.tidyAttributes)
 
-	gtestIsolated := m.linker.(*testBinary).isolated(ctx)
-	for _, propIntf := range m.GetProperties() {
-		if testLinkerProps, ok := propIntf.(*TestLinkerProperties); ok {
-			testBinaryAttrs.Gtest = proptools.BoolDefault(testLinkerProps.Gtest, true)
-			break
-		}
-	}
+	testBinary := m.linker.(*testBinary)
+	gtest := testBinary.gtest()
+	gtestIsolated := testBinary.isolated(ctx)
+	// Use the underling bool pointer for Gtest in attrs
+	// This ensures that if this property is not set in Android.bp file, it will not be set in BUILD file either
+	// cc_test macro will default gtest to True
+	testBinaryAttrs.Gtest = testBinary.LinkerProperties.Gtest
 
-	addImplicitGtestDeps(ctx, &testBinaryAttrs, gtestIsolated)
+	addImplicitGtestDeps(ctx, &testBinaryAttrs, gtest, gtestIsolated)
 
 	for _, testProps := range m.GetProperties() {
 		if p, ok := testProps.(*TestBinaryProperties); ok {
@@ -764,7 +764,7 @@
 
 // cc_test that builds using gtest needs some additional deps
 // addImplicitGtestDeps makes these deps explicit in the generated BUILD files
-func addImplicitGtestDeps(ctx android.BazelConversionPathContext, attrs *testBinaryAttributes, gtestIsolated bool) {
+func addImplicitGtestDeps(ctx android.BazelConversionPathContext, attrs *testBinaryAttributes, gtest, gtestIsolated bool) {
 	addDepsAndDedupe := func(lla *bazel.LabelListAttribute, modules []string) {
 		moduleLabels := android.BazelLabelForModuleDeps(ctx, modules)
 		lla.Value.Append(moduleLabels)
@@ -773,7 +773,7 @@
 	}
 	// this must be kept in sync with Soong's implementation in:
 	// https://cs.android.com/android/_/android/platform/build/soong/+/460fb2d6d546b5ab493a7e5479998c4933a80f73:cc/test.go;l=300-313;drc=ec7314336a2b35ea30ce5438b83949c28e3ac429;bpv=1;bpt=0
-	if attrs.Gtest {
+	if gtest {
 		// TODO - b/244433197: Handle canUseSdk
 		if gtestIsolated {
 			addDepsAndDedupe(&attrs.Deps, []string{"libgtest_isolated_main"})
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 5ea84bc..62b3333 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -121,6 +121,10 @@
 	defer ctx.EventHandler.End("mixed_build")
 
 	bazelHook := func() error {
+		err := ctx.Config().BazelContext.QueueBazelSandwichCqueryRequests(ctx.Config())
+		if err != nil {
+			return err
+		}
 		return ctx.Config().BazelContext.InvokeBazel(ctx.Config(), ctx)
 	}
 	ctx.SetBeforePrepareBuildActionsHook(bazelHook)
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 6306c27..69ba1e9 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -994,6 +994,7 @@
 
 	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, m)
 
+	bazelName := m.Name()
 	if ctx.ModuleType() == "gensrcs" {
 		props := bazel.BazelTargetModuleProperties{
 			Rule_class:        "gensrcs",
@@ -1021,7 +1022,6 @@
 				break
 			}
 		}
-		bazelName := m.Name()
 		for _, out := range outs {
 			if out == bazelName {
 				// This is a workaround to circumvent a Bazel warning where a genrule's
@@ -1046,6 +1046,54 @@
 			Tags: tags,
 		}, attrs)
 	}
+
+	if m.needsCcLibraryHeadersBp2build() {
+		includeDirs := make([]string, len(m.properties.Export_include_dirs)*2)
+		for i, dir := range m.properties.Export_include_dirs {
+			includeDirs[i*2] = dir
+			includeDirs[i*2+1] = filepath.Clean(filepath.Join(ctx.ModuleDir(), dir))
+		}
+		attrs := &ccHeaderLibraryAttrs{
+			Hdrs:            []string{":" + bazelName},
+			Export_includes: includeDirs,
+		}
+		props := bazel.BazelTargetModuleProperties{
+			Rule_class:        "cc_library_headers",
+			Bzl_load_location: "//build/bazel/rules/cc:cc_library_headers.bzl",
+		}
+		ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+			Name: m.Name() + genruleHeaderLibrarySuffix,
+			Tags: tags,
+		}, attrs)
+
+	}
+}
+
+const genruleHeaderLibrarySuffix = "__header_library"
+
+func (m *Module) needsCcLibraryHeadersBp2build() bool {
+	return len(m.properties.Export_include_dirs) > 0
+}
+
+// GenruleCcHeaderMapper is a bazel.LabelMapper function to map genrules to a cc_library_headers
+// target when they export multiple include directories.
+func GenruleCcHeaderLabelMapper(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
+	mod, exists := ctx.ModuleFromName(label.OriginalModuleName)
+	if !exists {
+		return label.Label, false
+	}
+	if m, ok := mod.(*Module); ok {
+		if m.needsCcLibraryHeadersBp2build() {
+			return label.Label + genruleHeaderLibrarySuffix, true
+		}
+	}
+	return label.Label, false
+}
+
+type ccHeaderLibraryAttrs struct {
+	Hdrs []string
+
+	Export_includes []string
 }
 
 var Bool = proptools.Bool
@@ -1099,6 +1147,7 @@
 		}
 	}).(*sandboxingAllowlistSets)
 }
+
 func getSandboxedRuleBuilder(ctx android.ModuleContext, r *android.RuleBuilder) *android.RuleBuilder {
 	if !ctx.DeviceConfig().GenruleSandboxing() {
 		return r.SandboxTools()
diff --git a/java/aapt2.go b/java/aapt2.go
index 7845a0b..4cff8a7 100644
--- a/java/aapt2.go
+++ b/java/aapt2.go
@@ -150,7 +150,8 @@
 			`${config.Aapt2Cmd} link -o $out $flags --java $genDir --proguard $proguardOptions ` +
 			`--output-text-symbols ${rTxt} $inFlags && ` +
 			`${config.SoongZipCmd} -write_if_changed -jar -o $genJar -C $genDir -D $genDir &&` +
-			`${config.ExtractJarPackagesCmd} -i $genJar -o $extraPackages --prefix '--extra-packages '`,
+			`${config.ExtractJarPackagesCmd} -i $genJar -o $extraPackages --prefix '--extra-packages ' && ` +
+			`rm -rf $genDir`,
 
 		CommandDeps: []string{
 			"${config.Aapt2Cmd}",
diff --git a/java/app_builder.go b/java/app_builder.go
index d20a6bf..d397ff7 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -225,8 +225,6 @@
 	})
 }
 
-const jniJarOutputPathString = "jniJarOutput.zip"
-
 func TransformJniLibsToJar(
 	ctx android.ModuleContext,
 	outputFile android.WritablePath,
@@ -258,7 +256,10 @@
 		rule = zipRE
 		args["implicits"] = strings.Join(deps.Strings(), ",")
 	}
-	jniJarPath := android.PathForModuleOut(ctx, jniJarOutputPathString)
+	var jniJarPath android.WritablePath = android.PathForModuleOut(ctx, "jniJarOutput.zip")
+	if len(prebuiltJniPackages) == 0 {
+		jniJarPath = outputFile
+	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        rule,
 		Description: "zip jni libs",
@@ -266,12 +267,26 @@
 		Implicits:   deps,
 		Args:        args,
 	})
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        mergeAssetsRule,
-		Description: "merge prebuilt JNI packages",
-		Inputs:      append(prebuiltJniPackages, jniJarPath),
-		Output:      outputFile,
-	})
+	if len(prebuiltJniPackages) > 0 {
+		var mergeJniJarPath android.WritablePath = android.PathForModuleOut(ctx, "mergeJniJarOutput.zip")
+		if !uncompressJNI {
+			mergeJniJarPath = outputFile
+		}
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        mergeAssetsRule,
+			Description: "merge prebuilt JNI packages",
+			Inputs:      append(prebuiltJniPackages, jniJarPath),
+			Output:      mergeJniJarPath,
+		})
+
+		if uncompressJNI {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   uncompressEmbeddedJniLibsRule,
+				Input:  mergeJniJarPath,
+				Output: outputFile,
+			})
+		}
+	}
 }
 
 func (a *AndroidApp) generateJavaUsedByApex(ctx android.ModuleContext) {
diff --git a/java/app_test.go b/java/app_test.go
index 4627ff6..8474ea7 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -1742,7 +1742,7 @@
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
 			app := ctx.ModuleForTests(test.name, "android_common")
-			jniLibZip := app.Output(jniJarOutputPathString)
+			jniLibZip := app.Output("jnilibs.zip")
 			var abis []string
 			args := strings.Fields(jniLibZip.Args["jarArgs"])
 			for i := 0; i < len(args); i++ {
@@ -1875,7 +1875,7 @@
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
 			app := ctx.ModuleForTests(test.name, "android_common")
-			jniLibZip := app.MaybeOutput(jniJarOutputPathString)
+			jniLibZip := app.MaybeOutput("jnilibs.zip")
 			if g, w := (jniLibZip.Rule != nil), test.packaged; g != w {
 				t.Errorf("expected jni packaged %v, got %v", w, g)
 			}
@@ -1966,7 +1966,7 @@
 		t.Run(test.name, func(t *testing.T) {
 			app := ctx.ModuleForTests(test.name, "android_common")
 
-			jniLibZip := app.MaybeOutput(jniJarOutputPathString)
+			jniLibZip := app.MaybeOutput("jnilibs.zip")
 			if len(jniLibZip.Implicits) != 1 {
 				t.Fatalf("expected exactly one jni library, got %q", jniLibZip.Implicits.Strings())
 			}
@@ -2986,7 +2986,7 @@
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
 			app := ctx.ModuleForTests(test.name, "android_common")
-			jniLibZip := app.Output(jniJarOutputPathString)
+			jniLibZip := app.Output("jnilibs.zip")
 			var jnis []string
 			args := strings.Fields(jniLibZip.Args["jarArgs"])
 			for i := 0; i < len(args); i++ {
diff --git a/java/builder.go b/java/builder.go
index afbd69e..debf49a 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -55,7 +55,7 @@
 				`$zipTemplate${config.SoongZipCmd} -jar -o $out.tmp -C $outDir -D $outDir && ` +
 				`if ! cmp -s "$out.tmp" "$out"; then mv "$out.tmp" "$out"; fi && ` +
 				`if ! cmp -s "$annoSrcJar.tmp" "$annoSrcJar"; then mv "$annoSrcJar.tmp" "$annoSrcJar"; fi && ` +
-				`rm -rf "$srcJarDir"`,
+				`rm -rf "$srcJarDir" "$outDir"`,
 			CommandDeps: []string{
 				"${config.JavacCmd}",
 				"${config.SoongZipCmd}",
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index eadd9c6..de9a82d 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -146,12 +146,43 @@
     ],
 }
 
+java_defaults {
+    name: "core.module_lib.stubs.defaults",
+    visibility: ["//visibility:private"],
+    sdk_version: "none",
+    system_modules: "none",
+}
+
+
 // A stubs target containing the parts of the public SDK & @SystemApi(MODULE_LIBRARIES) API
 // provided by the core libraries.
 //
 // Don't use this directly, use "sdk_version: module_current".
 java_library {
     name: "core.module_lib.stubs",
+    defaults: [
+        "core.module_lib.stubs.defaults",
+    ],
+    static_libs: [
+        "core.module_lib.stubs.from-source",
+    ],
+    product_variables: {
+        build_from_text_stub: {
+            static_libs: [
+                "core.module_lib.stubs.from-text",
+            ],
+            exclude_static_libs: [
+                "core.module_lib.stubs.from-source",
+            ],
+        },
+    },
+}
+
+java_library {
+    name: "core.module_lib.stubs.from-source",
+    defaults: [
+        "core.module_lib.stubs.defaults",
+    ],
     static_libs: [
         "art.module.public.api.stubs.module_lib",
 
@@ -161,9 +192,6 @@
         "conscrypt.module.public.api.stubs",
         "i18n.module.public.api.stubs",
     ],
-    sdk_version: "none",
-    system_modules: "none",
-    visibility: ["//visibility:private"],
 }
 
 // Produces a dist file that is used by the
@@ -249,10 +277,10 @@
     product_variables: {
         build_from_text_stub: {
             static_libs: [
-                "stable.core.platform.api.stubs.from-text",
+                "legacy.core.platform.api.stubs.from-text",
             ],
             exclude_static_libs: [
-                "stable.core.platform.api.stubs.from-source",
+                "legacy.core.platform.api.stubs.from-source",
             ],
         },
     },
diff --git a/java/core-libraries/TxtStubLibraries.bp b/java/core-libraries/TxtStubLibraries.bp
index 0cf0f36..c46f8b8 100644
--- a/java/core-libraries/TxtStubLibraries.bp
+++ b/java/core-libraries/TxtStubLibraries.bp
@@ -57,19 +57,23 @@
     ],
 }
 
-java_library {
+java_api_library {
     name: "core.module_lib.stubs.from-text",
-    static_libs: [
-        "art.module.public.api.stubs.module_lib.from-text",
+    api_surface: "module-lib",
+    api_contributions: [
+        "art.module.public.api.stubs.source.api.contribution",
+        "art.module.public.api.stubs.source.system.api.contribution",
+        "art.module.public.api.stubs.source.module_lib.api.contribution",
 
-        // Replace the following with the module-lib correspondence when Conscrypt or i18N module
+        // Add the module-lib correspondence when Conscrypt or i18N module
         // provides @SystemApi(MODULE_LIBRARIES). Currently, assume that only ART module provides
         // @SystemApi(MODULE_LIBRARIES).
-        "conscrypt.module.public.api.stubs.from-text",
-        "i18n.module.public.api.stubs.from-text",
+        "conscrypt.module.public.api.stubs.source.api.contribution",
+        "i18n.module.public.api.stubs.source.api.contribution",
     ],
-    sdk_version: "none",
-    system_modules: "none",
+    libs: [
+        "stub-annotations",
+    ],
     visibility: ["//visibility:private"],
 }
 
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index d25096b..4d08b83 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -106,7 +106,7 @@
 	h.uncompressDexState = uncompressedDexState
 
 	// If hiddenapi processing is disabled treat this as inactive.
-	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+	if ctx.Config().DisableHiddenApiChecks() {
 		return
 	}
 
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 714634f..8ec1797 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -121,8 +121,8 @@
 
 // hiddenAPI singleton rules
 func (h *hiddenAPISingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	// Don't run any hiddenapi rules if UNSAFE_DISABLE_HIDDENAPI_FLAGS=true
-	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+	// Don't run any hiddenapi rules if hiddenapi checks are disabled
+	if ctx.Config().DisableHiddenApiChecks() {
 		return
 	}
 
diff --git a/java/java.go b/java/java.go
index 860155c..f29f738 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2819,12 +2819,8 @@
 	hasKotlin bool
 }
 
-// Replaces //a/b/my_xsd_config with //a/b/my_xsd_config-java
-func xsdConfigJavaTarget(ctx android.BazelConversionPathContext, mod blueprint.Module) string {
-	callback := func(xsd android.XsdConfigBp2buildTargets) string {
-		return xsd.JavaBp2buildTargetName()
-	}
-	return android.XsdConfigBp2buildTarget(ctx, mod, callback)
+func javaXsdTargetName(xsd android.XsdConfigBp2buildTargets) string {
+	return xsd.JavaBp2buildTargetName()
 }
 
 // convertLibraryAttrsBp2Build returns a javaCommonAttributes struct with
@@ -2835,21 +2831,14 @@
 func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) (*javaCommonAttributes, *bp2BuildJavaInfo) {
 	var srcs bazel.LabelListAttribute
 	var deps bazel.LabelListAttribute
-	var staticDeps bazel.LabelList
+	var staticDeps bazel.LabelListAttribute
 
 	archVariantProps := m.GetArchVariantProperties(ctx, &CommonProperties{})
 	for axis, configToProps := range archVariantProps {
 		for config, _props := range configToProps {
 			if archProps, ok := _props.(*CommonProperties); ok {
-				srcsNonXsd, srcsXsd := android.PartitionXsdSrcs(ctx, archProps.Srcs)
-				excludeSrcsNonXsd, _ := android.PartitionXsdSrcs(ctx, archProps.Exclude_srcs)
-				archSrcs := android.BazelLabelForModuleSrcExcludes(ctx, srcsNonXsd, excludeSrcsNonXsd)
+				archSrcs := android.BazelLabelForModuleSrcExcludes(ctx, archProps.Srcs, archProps.Exclude_srcs)
 				srcs.SetSelectValue(axis, config, archSrcs)
-
-				// Add to static deps
-				xsdJavaConfigLibraryLabels := android.BazelLabelForModuleDepsWithFn(ctx, srcsXsd, xsdConfigJavaTarget)
-				staticDeps.Append(xsdJavaConfigLibraryLabels)
-
 			}
 		}
 	}
@@ -2857,6 +2846,7 @@
 
 	javaSrcPartition := "java"
 	protoSrcPartition := "proto"
+	xsdSrcPartition := "xsd"
 	logtagSrcPartition := "logtag"
 	aidlSrcPartition := "aidl"
 	kotlinPartition := "kotlin"
@@ -2865,6 +2855,7 @@
 		logtagSrcPartition: bazel.LabelPartition{Extensions: []string{".logtags", ".logtag"}},
 		protoSrcPartition:  android.ProtoSrcLabelPartition,
 		aidlSrcPartition:   android.AidlSrcLabelPartition,
+		xsdSrcPartition:    bazel.LabelPartition{LabelMapper: android.XsdLabelMapper(javaXsdTargetName)},
 		kotlinPartition:    bazel.LabelPartition{Extensions: []string{".kt"}},
 	})
 
@@ -2872,6 +2863,8 @@
 	kotlinSrcs := srcPartitions[kotlinPartition]
 	javaSrcs.Append(kotlinSrcs)
 
+	staticDeps.Append(srcPartitions[xsdSrcPartition])
+
 	if !srcPartitions[logtagSrcPartition].IsEmpty() {
 		logtagsLibName := m.Name() + "_logtags"
 		ctx.CreateBazelTargetModule(
@@ -2925,7 +2918,7 @@
 			},
 		)
 
-		staticDeps.Add(&bazel.Label{Label: ":" + javaAidlLibName})
+		staticDeps.Append(bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + javaAidlLibName}))
 	}
 
 	var javacopts bazel.StringListAttribute //[]string
@@ -2980,7 +2973,9 @@
 	// by protoc are included directly in the resulting JAR. Thus upstream dependencies
 	// that depend on a java_library with proto sources can link directly to the protobuf API,
 	// and so this should be a static dependency.
-	staticDeps.Add(protoDepLabel)
+	if protoDepLabel != nil {
+		staticDeps.Append(bazel.MakeSingleLabelListAttribute(*protoDepLabel))
+	}
 
 	depLabels := &javaDependencyLabels{}
 	depLabels.Deps = deps
@@ -2995,7 +2990,7 @@
 			}
 		}
 	}
-	depLabels.StaticDeps.Value.Append(staticDeps)
+	depLabels.StaticDeps.Append(staticDeps)
 
 	hasKotlin := !kotlinSrcs.IsEmpty()
 	commonAttrs.kotlinAttributes = &kotlinAttributes{
diff --git a/java/kotlin.go b/java/kotlin.go
index f28d6c7..3637e2e 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -42,7 +42,7 @@
 			` -P plugin:org.jetbrains.kotlin.jvm.abi:outputDir=$headerClassesDir && ` +
 			`${config.SoongZipCmd} -jar -o $out -C $classesDir -D $classesDir -write_if_changed && ` +
 			`${config.SoongZipCmd} -jar -o $headerJar -C $headerClassesDir -D $headerClassesDir -write_if_changed && ` +
-			`rm -rf "$srcJarDir"`,
+			`rm -rf "$srcJarDir" "$classesDir" "$headerClassesDir"`,
 		CommandDeps: []string{
 			"${config.KotlincCmd}",
 			"${config.KotlinCompilerJar}",
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index a4bba48..ade7395 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -113,7 +113,7 @@
 }
 
 func (b *platformBootclasspathModule) hiddenAPIDepsMutator(ctx android.BottomUpMutatorContext) {
-	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+	if ctx.Config().DisableHiddenApiChecks() {
 		return
 	}
 
@@ -275,10 +275,10 @@
 
 	bootDexJarByModule := extractBootDexJarsFromModules(ctx, modules)
 
-	// Don't run any hiddenapi rules if UNSAFE_DISABLE_HIDDENAPI_FLAGS=true. This is a performance
+	// Don't run any hiddenapi rules if hidden api checks are disabled. This is a performance
 	// optimization that can be used to reduce the incremental build time but as its name suggests it
 	// can be unsafe to use, e.g. when the changes affect anything that goes on the bootclasspath.
-	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+	if ctx.Config().DisableHiddenApiChecks() {
 		paths := android.OutputPaths{b.hiddenAPIFlagsCSV, b.hiddenAPIIndexCSV, b.hiddenAPIMetadataCSV}
 		for _, path := range paths {
 			ctx.Build(pctx, android.BuildParams{
diff --git a/python/bp2build.go b/python/bp2build.go
index cd3f2a1..60cabc4 100644
--- a/python/bp2build.go
+++ b/python/bp2build.go
@@ -223,7 +223,8 @@
 
 	props := bazel.BazelTargetModuleProperties{
 		// Use the native py_binary rule.
-		Rule_class: "py_test",
+		Rule_class:        "py_test",
+		Bzl_load_location: "//build/bazel/rules/python:py_test.bzl",
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 59585aa..c2bf6af 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -61,15 +61,18 @@
 		"${cc_config.ClangBase}/${bindgenHostPrebuiltTag}/${bindgenClangVersion}/${bindgenClangLibdir}")
 
 	//TODO(ivanlozano) Switch this to RuleBuilder
+	//
+	//TODO Pass the flag files directly to bindgen e.g. with @file when it supports that.
+	//See https://github.com/rust-lang/rust-bindgen/issues/2508.
 	bindgen = pctx.AndroidStaticRule("bindgen",
 		blueprint.RuleParams{
 			Command: "CLANG_PATH=$bindgenClang LIBCLANG_PATH=$bindgenLibClang RUSTFMT=${config.RustBin}/rustfmt " +
-				"$cmd $flags $in -o $out -- -MD -MF $out.d $cflags",
+				"$cmd $flags $$(cat $flagfiles) $in -o $out -- -MD -MF $out.d $cflags",
 			CommandDeps: []string{"$cmd"},
 			Deps:        blueprint.DepsGCC,
 			Depfile:     "$out.d",
 		},
-		"cmd", "flags", "cflags")
+		"cmd", "flags", "flagfiles", "cflags")
 )
 
 func init() {
@@ -90,6 +93,9 @@
 	// list of bindgen-specific flags and options
 	Bindgen_flags []string `android:"arch_variant"`
 
+	// list of files containing extra bindgen flags
+	Bindgen_flag_files []string `android:"arch_variant"`
+
 	// module name of a custom binary/script which should be used instead of the 'bindgen' binary. This custom
 	// binary must expect arguments in a similar fashion to bindgen, e.g.
 	//
@@ -216,6 +222,14 @@
 	bindgenFlags := defaultBindgenFlags
 	bindgenFlags = append(bindgenFlags, esc(b.Properties.Bindgen_flags)...)
 
+	// cat reads from stdin if its command line is empty,
+	// so we pass in /dev/null if there are no other flag files
+	bindgenFlagFiles := []string{"/dev/null"}
+	for _, flagFile := range b.Properties.Bindgen_flag_files {
+		bindgenFlagFiles = append(bindgenFlagFiles, android.PathForModuleSrc(ctx, flagFile).String())
+		implicits = append(implicits, android.PathForModuleSrc(ctx, flagFile))
+	}
+
 	wrapperFile := android.OptionalPathForModuleSrc(ctx, b.Properties.Wrapper_src)
 	if !wrapperFile.Valid() {
 		ctx.PropertyErrorf("wrapper_src", "invalid path to wrapper source")
@@ -261,9 +275,10 @@
 		Input:       wrapperFile.Path(),
 		Implicits:   implicits,
 		Args: map[string]string{
-			"cmd":    cmd,
-			"flags":  strings.Join(bindgenFlags, " "),
-			"cflags": strings.Join(cflags, " "),
+			"cmd":       cmd,
+			"flags":     strings.Join(bindgenFlags, " "),
+			"flagfiles": strings.Join(bindgenFlagFiles, " "),
+			"cflags":    strings.Join(cflags, " "),
 		},
 	})
 
diff --git a/rust/bindgen_test.go b/rust/bindgen_test.go
index af04cfc..12cdb3c 100644
--- a/rust/bindgen_test.go
+++ b/rust/bindgen_test.go
@@ -168,3 +168,28 @@
 		}
 	`)
 }
+
+func TestBindgenFlagFile(t *testing.T) {
+	ctx := testRust(t, `
+		rust_bindgen {
+			name: "libbindgen",
+			wrapper_src: "src/any.h",
+			crate_name: "bindgen",
+			stem: "libbindgen",
+			source_stem: "bindings",
+			bindgen_flag_files: [
+				"flag_file.txt",
+			],
+		}
+	`)
+	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a_source").Output("bindings.rs")
+
+	if !strings.Contains(libbindgen.Args["flagfiles"], "/dev/null") {
+		t.Errorf("missing /dev/null in rust_bindgen rule: flags %#v", libbindgen.Args["flagfiles"])
+	}
+	if !strings.Contains(libbindgen.Args["flagfiles"], "flag_file.txt") {
+		t.Errorf("missing bindgen flags file in rust_bindgen rule: flags %#v", libbindgen.Args["flagfiles"])
+	}
+	// TODO: The best we can do right now is check $flagfiles. Once bindgen.go switches to RuleBuilder,
+	// we may be able to check libbinder.RuleParams.Command to see if it contains $(cat /dev/null flag_file.txt)
+}
diff --git a/rust/builder.go b/rust/builder.go
index c31bc88..fbceecc 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -228,6 +228,17 @@
 		pkgVersion := ctx.RustModule().compiler.CargoPkgVersion()
 		if pkgVersion != "" {
 			envVars = append(envVars, "CARGO_PKG_VERSION="+pkgVersion)
+
+			// Ensure the version is in the form of "x.y.z" (approximately semver compliant).
+			//
+			// For our purposes, we don't care to enforce that these are integers since they may
+			// include other characters at times (e.g. sometimes the patch version is more than an integer).
+			if strings.Count(pkgVersion, ".") == 2 {
+				var semver_parts = strings.Split(pkgVersion, ".")
+				envVars = append(envVars, "CARGO_PKG_VERSION_MAJOR="+semver_parts[0])
+				envVars = append(envVars, "CARGO_PKG_VERSION_MINOR="+semver_parts[1])
+				envVars = append(envVars, "CARGO_PKG_VERSION_PATCH="+semver_parts[2])
+			}
 		}
 	}
 
diff --git a/rust/rust.go b/rust/rust.go
index 05fceee..dab3532 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -1306,12 +1306,17 @@
 				}
 			}
 			linkObject := ccDep.OutputFile()
-			linkPath := linkPathFromFilePath(linkObject.Path())
-
 			if !linkObject.Valid() {
-				ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
+				if !ctx.Config().AllowMissingDependencies() {
+					ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
+				} else {
+					ctx.AddMissingDependencies([]string{depName})
+				}
+				return
 			}
 
+			linkPath := linkPathFromFilePath(linkObject.Path())
+
 			exportDep := false
 			switch {
 			case cc.IsStaticDepTag(depTag):
@@ -1356,6 +1361,14 @@
 				// Re-get linkObject as ChooseStubOrImpl actually tells us which
 				// object (either from stub or non-stub) to use.
 				linkObject = android.OptionalPathForPath(sharedLibraryInfo.SharedLibrary)
+				if !linkObject.Valid() {
+					if !ctx.Config().AllowMissingDependencies() {
+						ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
+					} else {
+						ctx.AddMissingDependencies([]string{depName})
+					}
+					return
+				}
 				linkPath = linkPathFromFilePath(linkObject.Path())
 
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
diff --git a/rust/sanitize.go b/rust/sanitize.go
index 0f7cf6e..862baf7 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -223,11 +223,16 @@
 	if !sanitize.Properties.SanitizerEnabled {
 		return flags, deps
 	}
+
 	if Bool(sanitize.Properties.Sanitize.Fuzzer) {
 		flags.RustFlags = append(flags.RustFlags, fuzzerFlags...)
-	} else if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+	}
+
+	if Bool(sanitize.Properties.Sanitize.Hwaddress) {
 		flags.RustFlags = append(flags.RustFlags, hwasanFlags...)
-	} else if Bool(sanitize.Properties.Sanitize.Address) {
+	}
+
+	if Bool(sanitize.Properties.Sanitize.Address) {
 		flags.RustFlags = append(flags.RustFlags, asanFlags...)
 	}
 	return flags, deps
@@ -267,14 +272,12 @@
 		var depTag blueprint.DependencyTag
 		var deps []string
 
-		if mod.IsSanitizerEnabled(cc.Asan) ||
-			(mod.IsSanitizerEnabled(cc.Fuzzer) && (mctx.Arch().ArchType != android.Arm64 || !mctx.Os().Bionic())) {
+		if mod.IsSanitizerEnabled(cc.Asan) {
 			variations = append(variations,
 				blueprint.Variation{Mutator: "link", Variation: "shared"})
 			depTag = cc.SharedDepTag()
 			deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan")}
-		} else if mod.IsSanitizerEnabled(cc.Hwasan) ||
-			(mod.IsSanitizerEnabled(cc.Fuzzer) && mctx.Arch().ArchType == android.Arm64 && mctx.Os().Bionic()) {
+		} else if mod.IsSanitizerEnabled(cc.Hwasan) {
 			// TODO(b/204776996): HWASan for static Rust binaries isn't supported yet.
 			if binary, ok := mod.compiler.(binaryInterface); ok {
 				if binary.staticallyLinked() {
diff --git a/starlark_fmt/format.go b/starlark_fmt/format.go
index 4209507..0224bcf 100644
--- a/starlark_fmt/format.go
+++ b/starlark_fmt/format.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"reflect"
 	"sort"
 	"strconv"
 	"strings"
@@ -33,6 +34,72 @@
 	return strings.Repeat(" ", level*indent)
 }
 
+func PrintAny(value any, indentLevel int) string {
+	return printAnyRecursive(reflect.ValueOf(value), indentLevel)
+}
+
+func printAnyRecursive(value reflect.Value, indentLevel int) string {
+	switch value.Type().Kind() {
+	case reflect.String:
+		val := value.String()
+		if strings.Contains(val, "\"") || strings.Contains(val, "\n") {
+			return `'''` + val + `'''`
+		}
+		return `"` + val + `"`
+	case reflect.Bool:
+		if value.Bool() {
+			return "True"
+		} else {
+			return "False"
+		}
+	case reflect.Int:
+		return fmt.Sprintf("%d", value.Int())
+	case reflect.Slice:
+		if value.Len() == 0 {
+			return "[]"
+		} else if value.Len() == 1 {
+			return "[" + printAnyRecursive(value.Index(0), indentLevel) + "]"
+		}
+		list := make([]string, 0, value.Len()+2)
+		list = append(list, "[")
+		innerIndent := Indention(indentLevel + 1)
+		for i := 0; i < value.Len(); i++ {
+			list = append(list, innerIndent+printAnyRecursive(value.Index(i), indentLevel+1)+`,`)
+		}
+		list = append(list, Indention(indentLevel)+"]")
+		return strings.Join(list, "\n")
+	case reflect.Map:
+		if value.Len() == 0 {
+			return "{}"
+		}
+		items := make([]string, 0, value.Len())
+		for _, key := range value.MapKeys() {
+			items = append(items, fmt.Sprintf(`%s%s: %s,`, Indention(indentLevel+1), printAnyRecursive(key, indentLevel+1), printAnyRecursive(value.MapIndex(key), indentLevel+1)))
+		}
+		sort.Strings(items)
+		return fmt.Sprintf(`{
+%s
+%s}`, strings.Join(items, "\n"), Indention(indentLevel))
+	case reflect.Struct:
+		if value.NumField() == 0 {
+			return "struct()"
+		}
+		items := make([]string, 0, value.NumField()+2)
+		items = append(items, "struct(")
+		for i := 0; i < value.NumField(); i++ {
+			if value.Type().Field(i).Anonymous {
+				panic("anonymous fields aren't supported")
+			}
+			name := value.Type().Field(i).Name
+			items = append(items, fmt.Sprintf(`%s%s = %s,`, Indention(indentLevel+1), name, printAnyRecursive(value.Field(i), indentLevel+1)))
+		}
+		items = append(items, Indention(indentLevel)+")")
+		return strings.Join(items, "\n")
+	default:
+		panic("Unhandled kind: " + value.Kind().String())
+	}
+}
+
 // PrintBool returns a Starlark compatible bool string.
 func PrintBool(item bool) string {
 	if item {
diff --git a/tests/lib.sh b/tests/lib.sh
index 40b317b..f337c74 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -52,6 +52,10 @@
   cp -R "$REAL_TOP/$dir" "$MOCK_TOP/$parent"
 }
 
+function delete_directory {
+  rm -rf "$MOCK_TOP/$1"
+}
+
 function symlink_file {
   local file="$1"
 
@@ -138,6 +142,9 @@
   copy_directory build/bazel
   copy_directory build/bazel_common_rules
 
+  # This requires pulling more tools into the mock top to build partitions
+  delete_directory build/bazel/examples/partitions
+
   symlink_directory packages/modules/common/build
   symlink_directory prebuilts/bazel
   symlink_directory prebuilts/clang
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index 48f654e..6b9ff8b 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -22,7 +22,5 @@
 "$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_arm" "armv7-a"
 "$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_cf_arm64_phone" "armv8-a" "cortex-a53"
 
-"$TOP/build/soong/tests/sbom_test.sh"
-
 "$TOP/build/bazel/ci/b_test.sh"
 
diff --git a/tests/sbom_test.sh b/tests/sbom_test.sh
index afec6b1..2534b20 100755
--- a/tests/sbom_test.sh
+++ b/tests/sbom_test.sh
@@ -238,10 +238,45 @@
     diff_files "$file_list_file" "$files_in_spdx_file" "$partition_name"
   done
 
+  verify_package_verification_code "$product_out/sbom.spdx"
+
   # Teardown
   cleanup "${out_dir}"
 }
 
+function verify_package_verification_code {
+  local sbom_file="$1"; shift
+
+  local -a file_checksums
+  local package_product_found=
+  while read -r line;
+  do
+    if grep -q 'PackageVerificationCode' <<<"$line"
+    then
+      package_product_found=true
+    fi
+    if [ -n "$package_product_found" ]
+    then
+      if grep -q 'FileChecksum' <<< "$line"
+      then
+        checksum=$(echo $line | sed 's/^.*: //')
+        file_checksums+=("$checksum")
+      fi
+    fi
+  done <<< "$(grep -E 'PackageVerificationCode|FileChecksum' $sbom_file)"
+  IFS=$'\n' file_checksums=($(sort <<<"${file_checksums[*]}")); unset IFS
+  IFS= expected_package_verification_code=$(printf "${file_checksums[*]}" | sha1sum | sed 's/[[:space:]]*-//'); unset IFS
+
+  actual_package_verification_code=$(grep PackageVerificationCode $sbom_file | sed 's/PackageVerificationCode: //g')
+  if [ $actual_package_verification_code = $expected_package_verification_code ]
+  then
+    echo "Package verification code is correct."
+  else
+    echo "Unexpected package verification code."
+    exit 1
+  fi
+}
+
 function test_sbom_unbundled_apex {
   # Setup
   out_dir="$(setup)"