Merge "Make ‘rustfmt.toml’ available for other modules"
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index ccfad00..a5cea17 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -39,6 +39,7 @@
 	Bp2buildDefaultConfig = Bp2BuildConfig{
 		"prebuilts/runtime/mainline/platform/sdk":            Bp2BuildDefaultTrueRecursively,
 		"art/libartpalette":                                  Bp2BuildDefaultTrueRecursively,
+		"art/libartbase":                                     Bp2BuildDefaultTrueRecursively,
 		"art/libdexfile":                                     Bp2BuildDefaultTrueRecursively,
 		"art/libnativebridge":                                Bp2BuildDefaultTrueRecursively,
 		"art/runtime":                                        Bp2BuildDefaultTrueRecursively,
@@ -108,6 +109,7 @@
 		"external/eigen":                                     Bp2BuildDefaultTrueRecursively,
 		"external/erofs-utils":                               Bp2BuildDefaultTrueRecursively,
 		"external/error_prone":                               Bp2BuildDefaultTrueRecursively,
+		"external/expat":                                     Bp2BuildDefaultTrueRecursively,
 		"external/f2fs-tools":                                Bp2BuildDefaultTrue,
 		"external/flac":                                      Bp2BuildDefaultTrueRecursively,
 		"external/fmtlib":                                    Bp2BuildDefaultTrueRecursively,
@@ -146,7 +148,7 @@
 		"external/zlib":                                      Bp2BuildDefaultTrueRecursively,
 		"external/zopfli":                                    Bp2BuildDefaultTrueRecursively,
 		"external/zstd":                                      Bp2BuildDefaultTrueRecursively,
-		"frameworks/av/media/codecs/g711/decoder":            Bp2BuildDefaultTrueRecursively,
+		"frameworks/av/media/codecs":                         Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/services/minijail":                    Bp2BuildDefaultTrueRecursively,
 		"frameworks/base/media/tests/MediaDump":              Bp2BuildDefaultTrue,
 		"frameworks/base/startop/apps/test":                  Bp2BuildDefaultTrue,
@@ -280,39 +282,49 @@
 		"flatbuffer_headers",
 		"gemmlowp_headers",
 		"gl_headers",
+		"libandroid_runtime_lazy",
 		"libandroid_runtime_vm_headers",
 		"libaudioclient_aidl_conversion_util",
 		"libaudioutils_fixedfft",
+		"libbinder_headers",
 		"libbinder_headers_platform_shared",
 		"libbluetooth-types-header",
+		"libbufferhub_headers",
+		"libcodec2",
 		"libcodec2_headers",
 		"libcodec2_internal",
 		"libdmabufheap",
+		"libdvr_headers",
 		"libgsm",
 		"libgui_bufferqueue_sources",
+		"libhardware",
 		"libhardware_headers",
+		"libincfs_headers",
 		"libnativeloader-headers",
 		"libnativewindow_headers",
 		"libneuralnetworks_headers",
 		"libopus",
+		"libpdx_headers",
 		"libprocpartition",
+		"libruy_static",
 		"libserviceutils",
-		"libstagefright_amrnb_common",
-		"libstagefright_amrwbdec",
 		"libstagefright_enc_common",
 		"libstagefright_foundation_headers",
 		"libstagefright_headers",
-		"libstagefright_m4vh263dec",
-		"libstagefright_m4vh263enc",
-		"libstagefright_mp3dec_headers",
+		"libsurfaceflinger_headers",
 		"libsync",
 		"libtextclassifier_hash_headers",
 		"libtextclassifier_hash_static",
+		"libtflite_kernel_utils",
+		"libtinyxml2",
 		"libui-types",
+		"libui_headers",
 		"libvorbisidec",
 		"media_ndk_headers",
+		"media_plugin_headers",
 		"mediaswcodec.policy",
 		"mediaswcodec.xml",
+		"philox_random",
 		"philox_random_headers",
 		"server_configurable_flags",
 		"tensorflow_headers",
@@ -361,10 +373,13 @@
 		"gen-kotlin-build-file.py",                  // TODO(b/198619163) module has same name as source
 		"libgtest_ndk_c++", "libgtest_main_ndk_c++", // TODO(b/201816222): Requires sdk_version support.
 		"linkerconfig", "mdnsd", // TODO(b/202876379): has arch-variant static_executable
-		"linker",       // TODO(b/228316882): cc_binary uses link_crt
-		"libdebuggerd", // TODO(b/228314770): support product variable-specific header_libs
-		"versioner",    // TODO(b/228313961):  depends on prebuilt shared library libclang-cpp_host as a shared library, which does not supply expected providers for a shared library
-		"apexer_test",  // Requires aapt2
+		"linker",            // TODO(b/228316882): cc_binary uses link_crt
+		"libdebuggerd",      // TODO(b/228314770): support product variable-specific header_libs
+		"versioner",         // TODO(b/228313961):  depends on prebuilt shared library libclang-cpp_host as a shared library, which does not supply expected providers for a shared library
+		"libspeexresampler", // TODO(b/231995978): Filter out unknown cflags
+		"libjpeg", "libvpx", // TODO(b/233948256): Convert .asm files
+		"art_libartbase_headers", // TODO(b/236268577): Header libraries do not support export_shared_libs_headers
+		"apexer_test",            // Requires aapt2
 		"apexer_test_host_tools",
 		"host_apex_verifier",
 
@@ -383,6 +398,9 @@
 		"prebuilt_platform-robolectric-4.4-prebuilt",   // aosp/1999250, needs .aar support in Jars
 		"prebuilt_platform-robolectric-4.5.1-prebuilt", // aosp/1999250, needs .aar support in Jars
 
+		// proto support
+		"libstats_proto_host", // TODO(b/236055697): handle protos from other packages
+
 		// path property for filegroups
 		"conscrypt",                        // TODO(b/210751803), we don't handle path property for filegroups
 		"conscrypt-for-host",               // TODO(b/210751803), we don't handle path property for filegroups
@@ -451,9 +469,11 @@
 		"stats-log-api-gen",                         // depends on unconverted modules: libstats_proto_host
 		"statslog.cpp", "statslog.h", "statslog.rs", // depends on unconverted modules: stats-log-api-gen
 		"statslog_art.cpp", "statslog_art.h", "statslog_header.rs", // depends on unconverted modules: stats-log-api-gen
-		"timezone-host",       // depends on unconverted modules: art.module.api.annotations
-		"truth-host-prebuilt", // depends on unconverted modules: truth-prebuilt
-		"truth-prebuilt",      // depends on unconverted modules: asm-7.0, guava
+		"timezone-host",         // depends on unconverted modules: art.module.api.annotations
+		"truth-host-prebuilt",   // depends on unconverted modules: truth-prebuilt
+		"truth-prebuilt",        // depends on unconverted modules: asm-7.0, guava
+		"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
 
 		// b/215723302; awaiting tz{data,_version} to then rename targets conflicting with srcs
 		"tzdata",
@@ -463,6 +483,8 @@
 	Bp2buildCcLibraryStaticOnlyList = []string{}
 
 	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
diff --git a/android/androidmk.go b/android/androidmk.go
index 6b675a6..832c7df 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -945,7 +945,10 @@
 	return !module.Enabled() ||
 		module.commonProperties.HideFromMake ||
 		// Make does not understand LinuxBionic
-		module.Os() == LinuxBionic
+		module.Os() == LinuxBionic ||
+		// Make does not understand LinuxMusl, except when we are building with USE_HOST_MUSL=true
+		// and all host binaries are LinuxMusl
+		(module.Os() == LinuxMusl && module.Target().HostCross)
 }
 
 // A utility func to format LOCAL_TEST_DATA outputs. See the comments on DataPath to understand how
diff --git a/android/apex.go b/android/apex.go
index 019efdd..c53ceb3 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -836,40 +836,6 @@
 	ctx.Phony(fmt.Sprintf("%s-depsinfo", ctx.ModuleName()), d.fullListPath, d.flatListPath)
 }
 
-// TODO(b/158059172): remove minSdkVersion allowlist
-var minSdkVersionAllowlist = func(apiMap map[string]int) map[string]ApiLevel {
-	list := make(map[string]ApiLevel, len(apiMap))
-	for name, finalApiInt := range apiMap {
-		list[name] = uncheckedFinalApiLevel(finalApiInt)
-	}
-	return list
-}(map[string]int{
-	"androidx-constraintlayout_constraintlayout-solver-nodeps": 29,
-	"apache-commons-compress":                                  29,
-	"bouncycastle_ike_digests":                                 30,
-	"brotli-java":                                              29,
-	"flatbuffer_headers":                                       30,
-	"gemmlowp_headers":                                         30,
-	"ike-internals":                                            30,
-	"libbrotli":                                                30,
-	"libcrypto_static":                                         30,
-	"libeigen":                                                 30,
-	"liblz4":                                                   30,
-	"libmdnssd":                                                30,
-	"libprocpartition":                                         30,
-	"libprotobuf-java-lite":                                    30,
-	"libprotoutil":                                             30,
-	"libtextclassifier_hash_headers":                           30,
-	"libtextclassifier_hash_static":                            30,
-	"libtflite_kernel_utils":                                   30,
-	"libzstd":                                                  30,
-	"net-utils-framework-common":                               29,
-	"philox_random_headers":                                    30,
-	"philox_random":                                            30,
-	"tensorflow_headers":                                       30,
-	"xz-java":                                                  29,
-})
-
 // Function called while walking an APEX's payload dependencies.
 //
 // Return true if the `to` module should be visited, false otherwise.
@@ -921,15 +887,13 @@
 		}
 		if err := to.ShouldSupportSdkVersion(ctx, minSdkVersion); err != nil {
 			toName := ctx.OtherModuleName(to)
-			if ver, ok := minSdkVersionAllowlist[toName]; !ok || ver.GreaterThan(minSdkVersion) {
-				ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v."+
-					"\n\nDependency path: %s\n\n"+
-					"Consider adding 'min_sdk_version: %q' to %q",
-					minSdkVersion, ctx.ModuleName(), err.Error(),
-					ctx.GetPathString(false),
-					minSdkVersion, toName)
-				return false
-			}
+			ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v."+
+				"\n\nDependency path: %s\n\n"+
+				"Consider adding 'min_sdk_version: %q' to %q",
+				minSdkVersion, ctx.ModuleName(), err.Error(),
+				ctx.GetPathString(false),
+				minSdkVersion, toName)
+			return false
 		}
 		return true
 	})
diff --git a/android/api_levels.go b/android/api_levels.go
index 41b338c..da50b19 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -54,6 +54,14 @@
 	isPreview bool
 }
 
+func (this ApiLevel) FinalInt() int {
+	if this.IsPreview() {
+		panic("Requested a final int from a non-final ApiLevel")
+	} else {
+		return this.number
+	}
+}
+
 func (this ApiLevel) FinalOrFutureInt() int {
 	if this.IsPreview() {
 		return FutureApiLevelInt
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index f1ec55e..3be9805 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -34,6 +34,21 @@
 	"android/soong/bazel"
 )
 
+var (
+	writeBazelFile = pctx.AndroidStaticRule("bazelWriteFileRule", blueprint.RuleParams{
+		Command:        `sed "s/\\\\n/\n/g" ${out}.rsp >${out}`,
+		Rspfile:        "${out}.rsp",
+		RspfileContent: "${content}",
+	}, "content")
+	_                 = pctx.HostBinToolVariable("bazelBuildRunfilesTool", "build-runfiles")
+	buildRunfilesRule = pctx.AndroidStaticRule("bazelBuildRunfiles", blueprint.RuleParams{
+		Command:     "${bazelBuildRunfilesTool} ${in} ${outDir}",
+		Depfile:     "",
+		Description: "",
+		CommandDeps: []string{"${bazelBuildRunfilesTool}"},
+	}, "outDir")
+)
+
 func init() {
 	RegisterMixedBuildsMutator(InitRegistrationContext)
 }
@@ -173,26 +188,26 @@
 	LabelToPythonBinary map[string]string
 }
 
-func (m MockBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
+func (m MockBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) {
 	panic("unimplemented")
 }
 
-func (m MockBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
+func (m MockBazelContext) GetOutputFiles(label string, _ configKey) ([]string, error) {
 	result, _ := m.LabelToOutputFiles[label]
 	return result, nil
 }
 
-func (m MockBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
+func (m MockBazelContext) GetCcInfo(label string, _ configKey) (cquery.CcInfo, error) {
 	result, _ := m.LabelToCcInfo[label]
 	return result, nil
 }
 
-func (m MockBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
+func (m MockBazelContext) GetPythonBinary(label string, _ configKey) (string, error) {
 	result, _ := m.LabelToPythonBinary[label]
 	return result, nil
 }
 
-func (m MockBazelContext) InvokeBazel(config Config) error {
+func (m MockBazelContext) InvokeBazel(_ Config) error {
 	panic("unimplemented")
 }
 
@@ -246,23 +261,23 @@
 	return "", fmt.Errorf("no bazel response found for %v", key)
 }
 
-func (n noopBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
+func (n noopBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) {
 	panic("unimplemented")
 }
 
-func (n noopBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
+func (n noopBazelContext) GetOutputFiles(_ string, _ configKey) ([]string, error) {
 	panic("unimplemented")
 }
 
-func (n noopBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
+func (n noopBazelContext) GetCcInfo(_ string, _ configKey) (cquery.CcInfo, error) {
 	panic("unimplemented")
 }
 
-func (n noopBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
+func (n noopBazelContext) GetPythonBinary(_ string, _ configKey) (string, error) {
 	panic("unimplemented")
 }
 
-func (n noopBazelContext) InvokeBazel(config Config) error {
+func (n noopBazelContext) InvokeBazel(_ Config) error {
 	panic("unimplemented")
 }
 
@@ -304,7 +319,7 @@
 	p := bazelPaths{
 		soongOutDir: c.soongOutDir,
 	}
-	missingEnvVars := []string{}
+	var missingEnvVars []string
 	if len(c.Getenv("BAZEL_HOME")) > 1 {
 		p.homeDir = c.Getenv("BAZEL_HOME")
 	} else {
@@ -365,10 +380,8 @@
 	extraFlags          []string
 }
 
-func (r *mockBazelRunner) issueBazelCommand(paths *bazelPaths,
-	runName bazel.RunName,
-	command bazelCommand,
-	extraFlags ...string) (string, string, error) {
+func (r *mockBazelRunner) issueBazelCommand(_ *bazelPaths, _ bazel.RunName,
+	command bazelCommand, extraFlags ...string) (string, string, error) {
 	r.commands = append(r.commands, command)
 	r.extraFlags = append(r.extraFlags, strings.Join(extraFlags, " "))
 	if ret, ok := r.bazelCommandResults[command]; ok {
@@ -396,26 +409,30 @@
 		command.command,
 	}
 	cmdFlags = append(cmdFlags, command.expression)
-	cmdFlags = append(cmdFlags, "--profile="+shared.BazelMetricsFilename(paths, runName))
+	cmdFlags = append(cmdFlags,
+		// TODO(asmundak): is it needed in every build?
+		"--profile="+shared.BazelMetricsFilename(paths, runName),
 
-	// Set default platforms to canonicalized values for mixed builds requests.
-	// If these are set in the bazelrc, they will have values that are
-	// non-canonicalized to @sourceroot labels, and thus be invalid when
-	// referenced from the buildroot.
-	//
-	// The actual platform values here may be overridden by configuration
-	// transitions from the buildroot.
-	cmdFlags = append(cmdFlags,
-		fmt.Sprintf("--platforms=%s", "//build/bazel/platforms:android_target"))
-	cmdFlags = append(cmdFlags,
-		fmt.Sprintf("--extra_toolchains=%s", "//prebuilts/clang/host/linux-x86:all"))
-	// This should be parameterized on the host OS, but let's restrict to linux
-	// to keep things simple for now.
-	cmdFlags = append(cmdFlags,
-		fmt.Sprintf("--host_platform=%s", "//build/bazel/platforms:linux_x86_64"))
+		// Set default platforms to canonicalized values for mixed builds requests.
+		// If these are set in the bazelrc, they will have values that are
+		// non-canonicalized to @sourceroot labels, and thus be invalid when
+		// referenced from the buildroot.
+		//
+		// The actual platform values here may be overridden by configuration
+		// transitions from the buildroot.
+		fmt.Sprintf("--platforms=%s", "//build/bazel/platforms:android_target"),
+		fmt.Sprintf("--extra_toolchains=%s", "//prebuilts/clang/host/linux-x86:all"),
 
-	// Explicitly disable downloading rules (such as canonical C++ and Java rules) from the network.
-	cmdFlags = append(cmdFlags, "--experimental_repository_disable_download")
+		// This should be parameterized on the host OS, but let's restrict to linux
+		// to keep things simple for now.
+		fmt.Sprintf("--host_platform=%s", "//build/bazel/platforms:linux_x86_64"),
+
+		// Explicitly disable downloading rules (such as canonical C++ and Java rules) from the network.
+		"--experimental_repository_disable_download",
+
+		// Suppress noise
+		"--ui_event_filters=-INFO",
+		"--noshow_progress")
 	cmdFlags = append(cmdFlags, extraFlags...)
 
 	bazelCmd := exec.Command(paths.bazelPath, cmdFlags...)
@@ -682,8 +699,6 @@
 func (context *bazelContext) InvokeBazel(config Config) error {
 	context.results = make(map[cqueryKey]string)
 
-	var cqueryOutput string
-	var cqueryErr string
 	var err error
 
 	soongInjectionPath := absolutePath(context.paths.injectedFilesDir())
@@ -700,45 +715,27 @@
 			return err
 		}
 	}
-	err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666)
-	if err != nil {
+	if err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666); err != nil {
 		return err
 	}
-
-	err = ioutil.WriteFile(
-		filepath.Join(mixedBuildsPath, "main.bzl"),
-		context.mainBzlFileContents(), 0666)
-	if err != nil {
+	if err = ioutil.WriteFile(filepath.Join(mixedBuildsPath, "main.bzl"), context.mainBzlFileContents(), 0666); err != nil {
 		return err
 	}
-
-	err = ioutil.WriteFile(
-		filepath.Join(mixedBuildsPath, "BUILD.bazel"),
-		context.mainBuildFileContents(), 0666)
-	if err != nil {
+	if err = ioutil.WriteFile(filepath.Join(mixedBuildsPath, "BUILD.bazel"), context.mainBuildFileContents(), 0666); err != nil {
 		return err
 	}
 	cqueryFileRelpath := filepath.Join(context.paths.injectedFilesDir(), "buildroot.cquery")
-	err = ioutil.WriteFile(
-		absolutePath(cqueryFileRelpath),
-		context.cqueryStarlarkFileContents(), 0666)
-	if err != nil {
+	if err = ioutil.WriteFile(absolutePath(cqueryFileRelpath), context.cqueryStarlarkFileContents(), 0666); err != nil {
 		return err
 	}
 
-	buildrootLabel := "@soong_injection//mixed_builds:buildroot"
-	cqueryOutput, cqueryErr, err = context.issueBazelCommand(
-		context.paths,
-		bazel.CqueryBuildRootRunName,
-		bazelCommand{"cquery", fmt.Sprintf("deps(%s, 2)", buildrootLabel)},
-		"--output=starlark",
-		"--starlark:file="+absolutePath(cqueryFileRelpath))
-	err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"),
-		[]byte(cqueryOutput), 0666)
+	const buildrootLabel = "@soong_injection//mixed_builds:buildroot"
+	cqueryCmd := bazelCommand{"cquery", fmt.Sprintf("deps(%s, 2)", buildrootLabel)}
+	cqueryOutput, cqueryErr, err := context.issueBazelCommand(context.paths, bazel.CqueryBuildRootRunName, cqueryCmd,
+		"--output=starlark", "--starlark:file="+absolutePath(cqueryFileRelpath))
 	if err != nil {
-		return err
+		err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryOutput), 0666)
 	}
-
 	if err != nil {
 		return err
 	}
@@ -750,7 +747,6 @@
 			cqueryResults[splitLine[0]] = splitLine[1]
 		}
 	}
-
 	for val := range context.requests {
 		if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok {
 			context.results[val] = cqueryResult
@@ -762,37 +758,27 @@
 
 	// Issue an aquery command to retrieve action information about the bazel build tree.
 	//
-	var aqueryOutput string
-	var coverageFlags []string
+	// Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
+	// proto sources, which would add a number of unnecessary dependencies.
+	extraFlags := []string{"--output=jsonproto", "--include_file_write_contents"}
 	if Bool(config.productVariables.ClangCoverage) {
-		coverageFlags = append(coverageFlags, "--collect_code_coverage")
-		if len(config.productVariables.NativeCoveragePaths) > 0 ||
-			len(config.productVariables.NativeCoverageExcludePaths) > 0 {
-			includePaths := JoinWithPrefixAndSeparator(config.productVariables.NativeCoveragePaths, "+", ",")
-			excludePaths := JoinWithPrefixAndSeparator(config.productVariables.NativeCoverageExcludePaths, "-", ",")
-			if len(includePaths) > 0 && len(excludePaths) > 0 {
-				includePaths += ","
-			}
-			coverageFlags = append(coverageFlags, fmt.Sprintf(`--instrumentation_filter=%s`,
-				includePaths+excludePaths))
+		extraFlags = append(extraFlags, "--collect_code_coverage")
+		paths := make([]string, 0, 2)
+		if p := config.productVariables.NativeCoveragePaths; len(p) > 0 {
+			paths = append(paths, JoinWithPrefixAndSeparator(p, "+", ","))
+		}
+		if p := config.productVariables.NativeCoverageExcludePaths; len(p) > 0 {
+			paths = append(paths, JoinWithPrefixAndSeparator(p, "-", ","))
+		}
+		if len(paths) > 0 {
+			extraFlags = append(extraFlags, "--instrumentation_filter="+strings.Join(paths, ","))
 		}
 	}
-
-	extraFlags := append([]string{"--output=jsonproto"}, coverageFlags...)
-
-	aqueryOutput, _, err = context.issueBazelCommand(
-		context.paths,
-		bazel.AqueryBuildRootRunName,
-		bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)},
-		// Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
-		// proto sources, which would add a number of unnecessary dependencies.
-		extraFlags...)
-
-	if err != nil {
-		return err
+	aqueryCmd := bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)}
+	if aqueryOutput, _, err := context.issueBazelCommand(context.paths, bazel.AqueryBuildRootRunName, aqueryCmd,
+		extraFlags...); err == nil {
+		context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
 	}
-
-	context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
 	if err != nil {
 		return err
 	}
@@ -800,12 +786,8 @@
 	// Issue a build command of the phony root to generate symlink forests for dependencies of the
 	// Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
 	// but some of symlinks may be required to resolve source dependencies of the build.
-	_, _, err = context.issueBazelCommand(
-		context.paths,
-		bazel.BazelBuildPhonyRootRunName,
-		bazelCommand{"build", "@soong_injection//mixed_builds:phonyroot"})
-
-	if err != nil {
+	buildCmd := bazelCommand{"build", "@soong_injection//mixed_builds:phonyroot"}
+	if _, _, err = context.issueBazelCommand(context.paths, bazel.BazelBuildPhonyRootRunName, buildCmd); err != nil {
 		return err
 	}
 
@@ -874,13 +856,56 @@
 	executionRoot := path.Join(ctx.Config().BazelContext.OutputBase(), "execroot", "__main__")
 	bazelOutDir := path.Join(executionRoot, "bazel-out")
 	for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
-		if len(buildStatement.Command) < 1 {
+		if len(buildStatement.Command) > 0 {
+			rule := NewRuleBuilder(pctx, ctx)
+			createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx)
+			desc := fmt.Sprintf("%s: %s", buildStatement.Mnemonic, buildStatement.OutputPaths)
+			rule.Build(fmt.Sprintf("bazel %d", index), desc)
+			continue
+		}
+		// Certain actions returned by aquery (for instance FileWrite) do not contain a command
+		// and thus require special treatment. If BuildStatement were an interface implementing
+		// buildRule(ctx) function, the code here would just call it.
+		// Unfortunately, the BuildStatement is defined in
+		// the 'bazel' package, which cannot depend on 'android' package where ctx is defined,
+		// because this would cause circular dependency. So, until we move aquery processing
+		// to the 'android' package, we need to handle special cases here.
+		if buildStatement.Mnemonic == "FileWrite" || buildStatement.Mnemonic == "SourceSymlinkManifest" {
+			// Pass file contents as the value of the rule's "content" argument.
+			// Escape newlines and $ in the contents (the action "writeBazelFile" restores "\\n"
+			// back to the newline, and Ninja reads $$ as $.
+			escaped := strings.ReplaceAll(strings.ReplaceAll(buildStatement.FileContents, "\n", "\\n"),
+				"$", "$$")
+			ctx.Build(pctx, BuildParams{
+				Rule:        writeBazelFile,
+				Output:      PathForBazelOut(ctx, buildStatement.OutputPaths[0]),
+				Description: fmt.Sprintf("%s %s", buildStatement.Mnemonic, buildStatement.OutputPaths[0]),
+				Args: map[string]string{
+					"content": escaped,
+				},
+			})
+		} else if buildStatement.Mnemonic == "SymlinkTree" {
+			// build-runfiles arguments are the manifest file and the target directory
+			// where it creates the symlink tree according to this manifest (and then
+			// writes the MANIFEST file to it).
+			outManifest := PathForBazelOut(ctx, buildStatement.OutputPaths[0])
+			outManifestPath := outManifest.String()
+			if !strings.HasSuffix(outManifestPath, "MANIFEST") {
+				panic("the base name of the symlink tree action should be MANIFEST, got " + outManifestPath)
+			}
+			outDir := filepath.Dir(outManifestPath)
+			ctx.Build(pctx, BuildParams{
+				Rule:        buildRunfilesRule,
+				Output:      outManifest,
+				Inputs:      []Path{PathForBazelOut(ctx, buildStatement.InputPaths[0])},
+				Description: "symlink tree for " + outDir,
+				Args: map[string]string{
+					"outDir": outDir,
+				},
+			})
+		} else {
 			panic(fmt.Sprintf("unhandled build statement: %v", buildStatement))
 		}
-		rule := NewRuleBuilder(pctx, ctx)
-		createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx)
-		desc := fmt.Sprintf("%s: %s", buildStatement.Mnemonic, buildStatement.OutputPaths)
-		rule.Build(fmt.Sprintf("bazel %d", index), desc)
 	}
 }
 
diff --git a/android/sdk.go b/android/sdk.go
index 2dc0bd7..07b94b2 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -661,6 +661,10 @@
 	// an Android.bp file.
 	RequiresBpProperty() bool
 
+	// SupportedBuildReleases returns the string representation of a set of target build releases that
+	// support this member type.
+	SupportedBuildReleases() string
+
 	// UsableWithSdkAndSdkSnapshot returns true if the member type supports the sdk/sdk_snapshot,
 	// false otherwise.
 	UsableWithSdkAndSdkSnapshot() bool
@@ -673,6 +677,10 @@
 	// SupportedLinkages returns the names of the linkage variants supported by this module.
 	SupportedLinkages() []string
 
+	// ArePrebuiltsRequired returns true if prebuilts are required in the sdk snapshot, false
+	// otherwise.
+	ArePrebuiltsRequired() bool
+
 	// AddDependencies adds dependencies from the SDK module to all the module variants the member
 	// type contributes to the SDK. `names` is the list of module names given in the member type
 	// property (as returned by SdkPropertyName()) in the SDK module. The exact set of variants
@@ -773,7 +781,17 @@
 	// property to be specifiable in an Android.bp file.
 	BpPropertyNotRequired bool
 
-	SupportsSdk     bool
+	// The name of the first targeted build release.
+	//
+	// If not specified then it is assumed to be available on all targeted build releases.
+	SupportedBuildReleaseSpecification string
+
+	// Set to true if this must be usable with the sdk/sdk_snapshot module types. Otherwise, it will
+	// only be usable with module_exports/module_exports_snapshots module types.
+	SupportsSdk bool
+
+	// Set to true if prebuilt host artifacts of this member may be specific to the host OS. Only
+	// applicable to modules where HostSupported() is true.
 	HostOsDependent bool
 
 	// When set to true UseSourceModuleTypeInSnapshot indicates that the member type creates a source
@@ -781,6 +799,11 @@
 	// code from automatically adding a prefer: true flag.
 	UseSourceModuleTypeInSnapshot bool
 
+	// Set to proptools.BoolPtr(false) if this member does not generate prebuilts but is only provided
+	// to allow the sdk to gather members from this member's dependencies. If not specified then
+	// defaults to true.
+	PrebuiltsRequired *bool
+
 	// The list of supported traits.
 	Traits []SdkMemberTrait
 }
@@ -793,6 +816,10 @@
 	return !b.BpPropertyNotRequired
 }
 
+func (b *SdkMemberTypeBase) SupportedBuildReleases() string {
+	return b.SupportedBuildReleaseSpecification
+}
+
 func (b *SdkMemberTypeBase) UsableWithSdkAndSdkSnapshot() bool {
 	return b.SupportsSdk
 }
@@ -801,6 +828,10 @@
 	return b.HostOsDependent
 }
 
+func (b *SdkMemberTypeBase) ArePrebuiltsRequired() bool {
+	return proptools.BoolDefault(b.PrebuiltsRequired, true)
+}
+
 func (b *SdkMemberTypeBase) UsesSourceModuleTypeInSnapshot() bool {
 	return b.UseSourceModuleTypeInSnapshot
 }
diff --git a/apex/Android.bp b/apex/Android.bp
index fcdf8e6..6533c61 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -23,6 +23,7 @@
     srcs: [
         "androidmk.go",
         "apex.go",
+        "apex_sdk_member.go",
         "apex_singleton.go",
         "builder.go",
         "constants.go",
diff --git a/apex/apex.go b/apex/apex.go
index 5678b06..7b6707c 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -606,6 +606,18 @@
 	// replacement. This is needed because some prebuilt modules do not provide all the information
 	// needed by the apex.
 	sourceOnly bool
+
+	// If not-nil and an APEX is a member of an SDK then dependencies of that APEX with this tag will
+	// also be added as exported members of that SDK.
+	memberType android.SdkMemberType
+}
+
+func (d *dependencyTag) SdkMemberType(_ android.Module) android.SdkMemberType {
+	return d.memberType
+}
+
+func (d *dependencyTag) ExportMember() bool {
+	return true
 }
 
 func (d *dependencyTag) String() string {
@@ -617,6 +629,7 @@
 }
 
 var _ android.ReplaceSourceWithPrebuilt = &dependencyTag{}
+var _ android.SdkMemberDependencyTag = &dependencyTag{}
 
 var (
 	androidAppTag   = &dependencyTag{name: "androidApp", payload: true}
@@ -624,8 +637,8 @@
 	certificateTag  = &dependencyTag{name: "certificate"}
 	executableTag   = &dependencyTag{name: "executable", payload: true}
 	fsTag           = &dependencyTag{name: "filesystem", payload: true}
-	bcpfTag         = &dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true}
-	sscpfTag        = &dependencyTag{name: "systemserverclasspathFragment", payload: true, sourceOnly: true}
+	bcpfTag         = &dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true, memberType: java.BootclasspathFragmentSdkMemberType}
+	sscpfTag        = &dependencyTag{name: "systemserverclasspathFragment", payload: true, sourceOnly: true, memberType: java.SystemServerClasspathFragmentSdkMemberType}
 	compatConfigTag = &dependencyTag{name: "compatConfig", payload: true, sourceOnly: true}
 	javaLibTag      = &dependencyTag{name: "javaLib", payload: true}
 	jniLibTag       = &dependencyTag{name: "jniLib", payload: true}
diff --git a/apex/apex_sdk_member.go b/apex/apex_sdk_member.go
new file mode 100644
index 0000000..284158f
--- /dev/null
+++ b/apex/apex_sdk_member.go
@@ -0,0 +1,58 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package apex
+
+import (
+	"android/soong/android"
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+// This file contains support for using apex modules within an sdk.
+
+func init() {
+	// Register sdk member types.
+	android.RegisterSdkMemberType(&apexSdkMemberType{
+		SdkMemberTypeBase: android.SdkMemberTypeBase{
+			PropertyName: "apexes",
+			SupportsSdk:  true,
+
+			// The apexes property does not need to be included in the snapshot as adding an apex to an
+			// sdk does not produce any prebuilts of the apex.
+			PrebuiltsRequired: proptools.BoolPtr(false),
+		},
+	})
+}
+
+type apexSdkMemberType struct {
+	android.SdkMemberTypeBase
+}
+
+func (mt *apexSdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
+}
+
+func (mt *apexSdkMemberType) IsInstance(module android.Module) bool {
+	_, ok := module.(*apexBundle)
+	return ok
+}
+
+func (mt *apexSdkMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
+	panic("Sdk does not create prebuilts of the apexes in its snapshot")
+}
+
+func (mt *apexSdkMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
+	panic("Sdk does not create prebuilts of the apexes in its snapshot")
+}
diff --git a/apex/builder.go b/apex/builder.go
index fc9bb3b..dda342f 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -82,12 +82,22 @@
 		Description: "prepare ${out}",
 	}, "provideNativeLibs", "requireNativeLibs", "opt")
 
+	stripCommentsApexManifestRule = pctx.StaticRule("stripCommentsApexManifestRule", blueprint.RuleParams{
+		Command:     `sed '/^\s*\/\//d' $in > $out`,
+		Description: "strip lines starting with // ${in}=>${out}",
+	})
+
 	stripApexManifestRule = pctx.StaticRule("stripApexManifestRule", blueprint.RuleParams{
 		Command:     `rm -f $out && ${conv_apex_manifest} strip $in -o $out`,
 		CommandDeps: []string{"${conv_apex_manifest}"},
 		Description: "strip ${in}=>${out}",
 	})
 
+	setVersionApexManifestRule = pctx.StaticRule("setVersionApexManifestRule", blueprint.RuleParams{
+		Command:     `sed 's/\"version\":\s*0\([^0-9]*\)$$/\"version\":\ ${default_version}\1/' $in > $out`,
+		Description: "Replace 'version: 0' with the correct version // ${in}=>${out}",
+	}, "default_version")
+
 	pbApexManifestRule = pctx.StaticRule("pbApexManifestRule", blueprint.RuleParams{
 		Command:     `rm -f $out && ${conv_apex_manifest} proto $in -o $out`,
 		CommandDeps: []string{"${conv_apex_manifest}"},
@@ -107,6 +117,7 @@
 			`--canned_fs_config ${canned_fs_config} ` +
 			`--include_build_info ` +
 			`--payload_type image ` +
+			`--apex_version ${apex_version} ` +
 			`--key ${key} ${opt_flags} ${image_dir} ${out} `,
 		CommandDeps: []string{"${apexer}", "${avbtool}", "${e2fsdroid}", "${merge_zips}",
 			"${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}", "${sload_f2fs}", "${make_erofs}",
@@ -114,7 +125,7 @@
 		Rspfile:        "${out}.copy_commands",
 		RspfileContent: "${copy_commands}",
 		Description:    "APEX ${image_dir} => ${out}",
-	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key", "opt_flags", "manifest", "payload_fs_type")
+	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key", "opt_flags", "manifest", "payload_fs_type", "apex_version")
 
 	zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{
 		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
@@ -122,12 +133,13 @@
 			`APEXER_TOOL_PATH=${tool_path} ` +
 			`${apexer} --force --manifest ${manifest} ` +
 			`--payload_type zip ` +
+			`--apex_version ${apex_version} ` +
 			`${image_dir} ${out} `,
 		CommandDeps:    []string{"${apexer}", "${merge_zips}", "${soong_zip}", "${zipalign}", "${aapt2}"},
 		Rspfile:        "${out}.copy_commands",
 		RspfileContent: "${copy_commands}",
 		Description:    "ZipAPEX ${image_dir} => ${out}",
-	}, "tool_path", "image_dir", "copy_commands", "manifest")
+	}, "tool_path", "image_dir", "copy_commands", "manifest", "apex_version")
 
 	apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule",
 		blueprint.RuleParams{
@@ -205,10 +217,27 @@
 		optCommands = append(optCommands, "-a jniLibs "+strings.Join(jniLibs, " "))
 	}
 
+	manifestJsonCommentsStripped := android.PathForModuleOut(ctx, "apex_manifest_comments_stripped.json")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   stripCommentsApexManifestRule,
+		Input:  src,
+		Output: manifestJsonCommentsStripped,
+	})
+
+	manifestJsonVersionChanged := android.PathForModuleOut(ctx, "apex_manifest_version_changed.json")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   setVersionApexManifestRule,
+		Input:  manifestJsonCommentsStripped,
+		Output: manifestJsonVersionChanged,
+		Args: map[string]string{
+			"default_version": defaultManifestVersion,
+		},
+	})
+
 	manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   apexManifestRule,
-		Input:  src,
+		Input:  manifestJsonVersionChanged,
 		Output: manifestJsonFullOut,
 		Args: map[string]string{
 			"provideNativeLibs": strings.Join(provideNativeLibs, " "),
@@ -655,8 +684,6 @@
 			optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String())
 		}
 
-		optFlags = append(optFlags, "--apex_version "+defaultManifestVersion)
-
 		optFlags = append(optFlags, "--payload_fs_type "+a.payloadFsType.string())
 
 		ctx.Build(pctx, android.BuildParams{
@@ -672,6 +699,7 @@
 				"file_contexts":    fileContexts.String(),
 				"canned_fs_config": cannedFsConfig.String(),
 				"key":              a.privateKeyFile.String(),
+				"apex_version":     defaultManifestVersion,
 				"opt_flags":        strings.Join(optFlags, " "),
 			},
 		})
@@ -768,6 +796,7 @@
 				"image_dir":     imageDir.String(),
 				"copy_commands": strings.Join(copyCommands, " && "),
 				"manifest":      a.manifestPbOut.String(),
+				"apex_version":  defaultManifestVersion,
 			},
 		})
 	}
diff --git a/bazel/aquery.go b/bazel/aquery.go
index 1d1f49c..ae2b107 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -21,7 +21,6 @@
 	"fmt"
 	"path/filepath"
 	"reflect"
-	"regexp"
 	"sort"
 	"strings"
 
@@ -83,6 +82,7 @@
 	OutputIds            []artifactId
 	TemplateContent      string
 	Substitutions        []KeyValuePair
+	FileContents         string
 }
 
 // actionGraphContainer contains relevant portions of Bazel's aquery proto, ActionGraphContainer.
@@ -110,6 +110,7 @@
 	// input path string, but not both.
 	InputDepsetHashes []string
 	InputPaths        []string
+	FileContents      string
 }
 
 // A helper type for aquery processing which facilitates retrieval of path IDs from their
@@ -139,9 +140,6 @@
 	"%python_binary%": "python3",
 }
 
-// This pattern matches the MANIFEST file created for a py_binary target.
-var manifestFilePattern = regexp.MustCompile(".*/.+\\.runfiles/MANIFEST$")
-
 // The file name of py3wrapper.sh, which is used by py_binary targets.
 const py3wrapperFileName = "/py3wrapper.sh"
 
@@ -225,20 +223,12 @@
 			// Swap middleman artifacts with their corresponding depsets and drop the middleman artifacts.
 			transitiveDepsetIds = append(transitiveDepsetIds, depsetsToUse...)
 		} else if strings.HasSuffix(path, py3wrapperFileName) ||
-			manifestFilePattern.MatchString(path) ||
 			strings.HasPrefix(path, "../bazel_tools") {
 			// Drop these artifacts.
 			// See go/python-binary-host-mixed-build for more details.
-			// 1) For py3wrapper.sh, there is no action for creating py3wrapper.sh in the aquery output of
-			// Bazel py_binary targets, so there is no Ninja build statements generated for creating it.
-			// 2) For MANIFEST file, SourceSymlinkManifest action is in aquery output of Bazel py_binary targets,
-			// but it doesn't contain sufficient information so no Ninja build statements are generated
-			// for creating it.
-			// So in mixed build mode, when these two are used as input of some Ninja build statement,
-			// since there is no build statement to create them, they should be removed from input paths.
-			// TODO(b/197135294): Clean up this custom runfiles handling logic when
-			// SourceSymlinkManifest and SymlinkTree actions are supported.
-			// 3) ../bazel_tools: they have MODIFY timestamp 10years in the future and would cause the
+			// 1) Drop py3wrapper.sh, just use python binary, the launcher script generated by the
+			// TemplateExpandAction handles everything necessary to launch a Pythin application.
+			// 2) ../bazel_tools: they have MODIFY timestamp 10years in the future and would cause the
 			// containing depset to always be considered newer than their outputs.
 		} else {
 			directArtifactPaths = append(directArtifactPaths, path)
@@ -346,12 +336,14 @@
 		}
 
 		var buildStatement BuildStatement
-		if isSymlinkAction(actionEntry) {
+		if actionEntry.isSymlinkAction() {
 			buildStatement, err = aqueryHandler.symlinkActionBuildStatement(actionEntry)
-		} else if isTemplateExpandAction(actionEntry) && len(actionEntry.Arguments) < 1 {
+		} else if actionEntry.isTemplateExpandAction() && len(actionEntry.Arguments) < 1 {
 			buildStatement, err = aqueryHandler.templateExpandActionBuildStatement(actionEntry)
-		} else if isPythonZipperAction(actionEntry) {
-			buildStatement, err = aqueryHandler.pythonZipperActionBuildStatement(actionEntry, buildStatements)
+		} else if actionEntry.isFileWriteAction() {
+			buildStatement, err = aqueryHandler.fileWriteActionBuildStatement(actionEntry)
+		} else if actionEntry.isSymlinkTreeAction() {
+			buildStatement, err = aqueryHandler.symlinkTreeActionBuildStatement(actionEntry)
 		} else if len(actionEntry.Arguments) < 1 {
 			return nil, nil, fmt.Errorf("received action with no command: [%s]", actionEntry.Mnemonic)
 		} else {
@@ -452,54 +444,6 @@
 	return buildStatement, nil
 }
 
-func (a *aqueryArtifactHandler) pythonZipperActionBuildStatement(actionEntry action, prevBuildStatements []BuildStatement) (BuildStatement, error) {
-	inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
-	if err != nil {
-		return BuildStatement{}, err
-	}
-	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
-	if err != nil {
-		return BuildStatement{}, err
-	}
-
-	if len(inputPaths) < 1 || len(outputPaths) != 1 {
-		return BuildStatement{}, fmt.Errorf("Expect 1+ input and 1 output to python zipper action, got: input %q, output %q", inputPaths, outputPaths)
-	}
-	command := strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " ")
-	inputPaths, command = removePy3wrapperScript(inputPaths, command)
-	command = addCommandForPyBinaryRunfilesDir(command, outputPaths[0])
-	// Add the python zip file as input of the corresponding python binary stub script in Ninja build statements.
-	// In Ninja build statements, the outputs of dependents of a python binary have python binary stub script as input,
-	// which is not sufficient without the python zip file from which runfiles directory is created for py_binary.
-	//
-	// The following logic relies on that Bazel aquery output returns actions in the order that
-	// PythonZipper is after TemplateAction of creating Python binary stub script. If later Bazel doesn't return actions
-	// in that order, the following logic might not find the build statement generated for Python binary
-	// stub script and the build might fail. So the check of pyBinaryFound is added to help debug in case later Bazel might change aquery output.
-	// See go/python-binary-host-mixed-build for more details.
-	pythonZipFilePath := outputPaths[0]
-	pyBinaryFound := false
-	for i := range prevBuildStatements {
-		if len(prevBuildStatements[i].OutputPaths) == 1 && prevBuildStatements[i].OutputPaths[0]+".zip" == pythonZipFilePath {
-			prevBuildStatements[i].InputPaths = append(prevBuildStatements[i].InputPaths, pythonZipFilePath)
-			pyBinaryFound = true
-		}
-	}
-	if !pyBinaryFound {
-		return BuildStatement{}, fmt.Errorf("Could not find the correspondinging Python binary stub script of PythonZipper: %q", outputPaths)
-	}
-
-	buildStatement := BuildStatement{
-		Command:     command,
-		Depfile:     depfile,
-		OutputPaths: outputPaths,
-		InputPaths:  inputPaths,
-		Env:         actionEntry.EnvironmentVariables,
-		Mnemonic:    actionEntry.Mnemonic,
-	}
-	return buildStatement, nil
-}
-
 func (a *aqueryArtifactHandler) templateExpandActionBuildStatement(actionEntry action) (BuildStatement, error) {
 	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
 	if err != nil {
@@ -532,6 +476,47 @@
 	return buildStatement, nil
 }
 
+func (a *aqueryArtifactHandler) fileWriteActionBuildStatement(actionEntry action) (BuildStatement, error) {
+	outputPaths, _, err := a.getOutputPaths(actionEntry)
+	var depsetHashes []string
+	if err == nil {
+		depsetHashes, err = a.depsetContentHashes(actionEntry.InputDepSetIds)
+	}
+	if err != nil {
+		return BuildStatement{}, err
+	}
+	return BuildStatement{
+		Depfile:           nil,
+		OutputPaths:       outputPaths,
+		Env:               actionEntry.EnvironmentVariables,
+		Mnemonic:          actionEntry.Mnemonic,
+		InputDepsetHashes: depsetHashes,
+		FileContents:      actionEntry.FileContents,
+	}, nil
+}
+
+func (a *aqueryArtifactHandler) symlinkTreeActionBuildStatement(actionEntry action) (BuildStatement, error) {
+	outputPaths, _, err := a.getOutputPaths(actionEntry)
+	if err != nil {
+		return BuildStatement{}, err
+	}
+	inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
+	if err != nil {
+		return BuildStatement{}, err
+	}
+	if len(inputPaths) != 1 || len(outputPaths) != 1 {
+		return BuildStatement{}, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths)
+	}
+	// The actual command is generated in bazelSingleton.GenerateBuildActions
+	return BuildStatement{
+		Depfile:     nil,
+		OutputPaths: outputPaths,
+		Env:         actionEntry.EnvironmentVariables,
+		Mnemonic:    actionEntry.Mnemonic,
+		InputPaths:  inputPaths,
+	}, nil
+}
+
 func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry action) (BuildStatement, error) {
 	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
 	if err != nil {
@@ -614,76 +599,35 @@
 	return replacer.Replace(str)
 }
 
-// removePy3wrapperScript removes py3wrapper.sh from the input paths and command of the action of
-// creating python zip file in mixed build mode. py3wrapper.sh is returned as input by aquery but
-// there is no action returned by aquery for creating it. So in mixed build "python3" is used
-// as the PYTHON_BINARY in python binary stub script, and py3wrapper.sh is not needed and should be
-// removed from input paths and command of creating python zip file.
-// See go/python-binary-host-mixed-build for more details.
-// TODO(b/205879240) remove this after py3wrapper.sh could be created in the mixed build mode.
-func removePy3wrapperScript(inputPaths []string, command string) (newInputPaths []string, newCommand string) {
-	// Remove from inputs
-	var filteredInputPaths []string
-	for _, path := range inputPaths {
-		if !strings.HasSuffix(path, py3wrapperFileName) {
-			filteredInputPaths = append(filteredInputPaths, path)
-		}
-	}
-	newInputPaths = filteredInputPaths
-
-	// Remove from command line
-	var re = regexp.MustCompile(`\S*` + py3wrapperFileName)
-	newCommand = re.ReplaceAllString(command, "")
-	return
-}
-
-// addCommandForPyBinaryRunfilesDir adds commands creating python binary runfiles directory.
-// runfiles directory is created by using MANIFEST file and MANIFEST file is the output of
-// SourceSymlinkManifest action is in aquery output of Bazel py_binary targets,
-// but since SourceSymlinkManifest doesn't contain sufficient information
-// so MANIFEST file could not be created, which also blocks the creation of runfiles directory.
-// See go/python-binary-host-mixed-build for more details.
-// TODO(b/197135294) create runfiles directory from MANIFEST file once it can be created from SourceSymlinkManifest action.
-func addCommandForPyBinaryRunfilesDir(oldCommand string, zipFilePath string) string {
-	// Unzip the zip file, zipFilePath looks like <python_binary>.zip
-	runfilesDirName := zipFilePath[0:len(zipFilePath)-4] + ".runfiles"
-	command := fmt.Sprintf("%s x %s -d %s", "../bazel_tools/tools/zip/zipper/zipper", zipFilePath, runfilesDirName)
-	// Create a symbolic link in <python_binary>.runfiles/, which is the expected structure
-	// when running the python binary stub script.
-	command += fmt.Sprintf(" && ln -sf runfiles/__main__ %s", runfilesDirName)
-	return oldCommand + " && " + command
-}
-
-func isSymlinkAction(a action) bool {
+func (a action) isSymlinkAction() bool {
 	return a.Mnemonic == "Symlink" || a.Mnemonic == "SolibSymlink" || a.Mnemonic == "ExecutableSymlink"
 }
 
-func isTemplateExpandAction(a action) bool {
+func (a action) isTemplateExpandAction() bool {
 	return a.Mnemonic == "TemplateExpand"
 }
 
-func isPythonZipperAction(a action) bool {
-	return a.Mnemonic == "PythonZipper"
+func (a action) isFileWriteAction() bool {
+	return a.Mnemonic == "FileWrite" || a.Mnemonic == "SourceSymlinkManifest"
+}
+
+func (a action) isSymlinkTreeAction() bool {
+	return a.Mnemonic == "SymlinkTree"
 }
 
 func shouldSkipAction(a action) bool {
-	// TODO(b/180945121): Handle complex symlink actions.
-	if a.Mnemonic == "SymlinkTree" || a.Mnemonic == "SourceSymlinkManifest" {
-		return true
-	}
 	// Middleman actions are not handled like other actions; they are handled separately as a
 	// preparatory step so that their inputs may be relayed to actions depending on middleman
 	// artifacts.
 	if a.Mnemonic == "Middleman" {
 		return true
 	}
-	// Skip "Fail" actions, which are placeholder actions designed to always fail.
-	if a.Mnemonic == "Fail" {
+	// PythonZipper is bogus action returned by aquery, ignore it (b/236198693)
+	if a.Mnemonic == "PythonZipper" {
 		return true
 	}
-	// TODO(b/180946980): Handle FileWrite. The aquery proto currently contains no information
-	// about the contents that are written.
-	if a.Mnemonic == "FileWrite" {
+	// Skip "Fail" actions, which are placeholder actions designed to always fail.
+	if a.Mnemonic == "Fail" {
 		return true
 	}
 	if a.Mnemonic == "BaselineCoverage" {
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index c759d56..3a2bf0f 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -460,6 +460,43 @@
 	}
 }
 
+func TestSymlinkTree(t *testing.T) {
+	const inputString = `
+{
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 }],
+  "actions": [{
+    "targetId": 1,
+    "actionKey": "x",
+    "mnemonic": "SymlinkTree",
+    "configurationId": 1,
+    "inputDepSetIds": [1],
+    "outputIds": [2],
+    "primaryOutputId": 2,
+    "executionPlatform": "//build/bazel/platforms:linux_x86_64"
+  }],
+  "pathFragments": [
+    { "id": 1, "label": "foo.manifest" },
+    { "id": 2, "label": "foo.runfiles/MANIFEST" }],
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1] }]
+}
+`
+	actual, _, err := AqueryBuildStatements([]byte(inputString))
+	if err != nil {
+		t.Errorf("Unexpected error %q", err)
+	}
+	assertBuildStatements(t, []BuildStatement{
+		{
+			Command:     "",
+			OutputPaths: []string{"foo.runfiles/MANIFEST"},
+			Mnemonic:    "SymlinkTree",
+			InputPaths:  []string{"foo.manifest"},
+		},
+	}, actual)
+}
+
 func TestBazelOutRemovalFromInputDepsets(t *testing.T) {
 	const inputString = `{
   "artifacts": [{
@@ -861,151 +898,67 @@
 	assertError(t, err, `Expect 1 output to template expand action, got: output []`)
 }
 
-func TestPythonZipperActionSuccess(t *testing.T) {
+func TestFileWrite(t *testing.T) {
 	const inputString = `
 {
   "artifacts": [
-    { "id": 1, "pathFragmentId": 1 },
-    { "id": 2, "pathFragmentId": 2 },
-    { "id": 3, "pathFragmentId": 3 },
-    { "id": 4, "pathFragmentId": 4 },
-    { "id": 5, "pathFragmentId": 10 },
-    { "id": 10, "pathFragmentId": 20 }],
+    { "id": 1, "pathFragmentId": 1 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
-    "mnemonic": "TemplateExpand",
+    "mnemonic": "FileWrite",
     "configurationId": 1,
     "outputIds": [1],
     "primaryOutputId": 1,
     "executionPlatform": "//build/bazel/platforms:linux_x86_64",
-    "templateContent": "Test template substitutions: %token1%, %python_binary%",
-    "substitutions": [{
-      "key": "%token1%",
-      "value": "abcd"
-    },{
-      "key": "%python_binary%",
-      "value": "python3"
-    }]
-  },{
-    "targetId": 1,
-    "actionKey": "x",
-    "mnemonic": "PythonZipper",
-    "configurationId": 1,
-    "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"],
-    "outputIds": [2],
-    "inputDepSetIds": [1],
-    "primaryOutputId": 2
+    "fileContents": "file data\n"
   }],
-  "depSetOfFiles": [
-    { "id": 1, "directArtifactIds": [4, 3, 5] }],
   "pathFragments": [
-    { "id": 1, "label": "python_binary" },
-    { "id": 2, "label": "python_binary.zip" },
-    { "id": 3, "label": "python_binary.py" },
-    { "id": 9, "label": ".." },
-    { "id": 8, "label": "bazel_tools", "parentId": 9 },
-    { "id": 7, "label": "tools", "parentId": 8 },
-    { "id": 6, "label": "zip", "parentId": 7  },
-    { "id": 5, "label": "zipper", "parentId": 6 },
-    { "id": 4, "label": "zipper", "parentId": 5 },
-    { "id": 16, "label": "bazel-out" },
-    { "id": 15, "label": "bazel_tools", "parentId": 16 },
-    { "id": 14, "label": "k8-fastbuild", "parentId": 15 },
-    { "id": 13, "label": "bin", "parentId": 14 },
-    { "id": 12, "label": "tools", "parentId": 13 },
-    { "id": 11, "label": "python", "parentId": 12 },
-    { "id": 10, "label": "py3wrapper.sh", "parentId": 11 },
-    { "id": 20, "label": "python_binary" }]
-}`
+    { "id": 1, "label": "foo.manifest" }]
+}
+`
 	actual, _, err := AqueryBuildStatements([]byte(inputString))
-
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
 	}
+	assertBuildStatements(t, []BuildStatement{
+		{
+			OutputPaths:  []string{"foo.manifest"},
+			Mnemonic:     "FileWrite",
+			FileContents: "file data\n",
+		},
+	}, actual)
+}
 
-	expectedBuildStatements := []BuildStatement{
-		{
-			Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > python_binary && " +
-				"chmod a+x python_binary'",
-			InputPaths:  []string{"python_binary.zip"},
-			OutputPaths: []string{"python_binary"},
-			Mnemonic:    "TemplateExpand",
-		},
-		{
-			Command: "../bazel_tools/tools/zip/zipper/zipper cC python_binary.zip __main__.py=bazel-out/k8-fastbuild/bin/python_binary.temp " +
-				"__init__.py= runfiles/__main__/__init__.py= runfiles/__main__/python_binary.py=python_binary.py  && " +
-				"../bazel_tools/tools/zip/zipper/zipper x python_binary.zip -d python_binary.runfiles && ln -sf runfiles/__main__ python_binary.runfiles",
-			InputPaths:  []string{"python_binary.py"},
-			OutputPaths: []string{"python_binary.zip"},
-			Mnemonic:    "PythonZipper",
-		},
+func TestSourceSymlinkManifest(t *testing.T) {
+	const inputString = `
+{
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 }],
+  "actions": [{
+    "targetId": 1,
+    "actionKey": "x",
+    "mnemonic": "SourceSymlinkManifest",
+    "configurationId": 1,
+    "outputIds": [1],
+    "primaryOutputId": 1,
+    "executionPlatform": "//build/bazel/platforms:linux_x86_64",
+    "fileContents": "symlink target\n"
+  }],
+  "pathFragments": [
+    { "id": 1, "label": "foo.manifest" }]
+}
+`
+	actual, _, err := AqueryBuildStatements([]byte(inputString))
+	if err != nil {
+		t.Errorf("Unexpected error %q", err)
 	}
-	assertBuildStatements(t, expectedBuildStatements, actual)
-}
-
-func TestPythonZipperActionNoInput(t *testing.T) {
-	const inputString = `
-{
-  "artifacts": [
-    { "id": 1, "pathFragmentId": 1 },
-    { "id": 2, "pathFragmentId": 2 }],
-  "actions": [{
-    "targetId": 1,
-    "actionKey": "x",
-    "mnemonic": "PythonZipper",
-    "configurationId": 1,
-    "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"],
-    "outputIds": [2],
-    "primaryOutputId": 2
-  }],
-  "pathFragments": [
-    { "id": 1, "label": "python_binary" },
-    { "id": 2, "label": "python_binary.zip" }]
-}`
-	_, _, err := AqueryBuildStatements([]byte(inputString))
-	assertError(t, err, `Expect 1+ input and 1 output to python zipper action, got: input [], output ["python_binary.zip"]`)
-}
-
-func TestPythonZipperActionNoOutput(t *testing.T) {
-	const inputString = `
-{
-  "artifacts": [
-    { "id": 1, "pathFragmentId": 1 },
-    { "id": 2, "pathFragmentId": 2 },
-    { "id": 3, "pathFragmentId": 3 },
-    { "id": 4, "pathFragmentId": 4 },
-    { "id": 5, "pathFragmentId": 10 }],
-  "actions": [{
-    "targetId": 1,
-    "actionKey": "x",
-    "mnemonic": "PythonZipper",
-    "configurationId": 1,
-    "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"],
-    "inputDepSetIds": [1]
-  }],
-  "depSetOfFiles": [
-    { "id": 1, "directArtifactIds": [4, 3, 5]}],
-  "pathFragments": [
-    { "id": 1, "label": "python_binary" },
-    { "id": 2, "label": "python_binary.zip" },
-    { "id": 3, "label": "python_binary.py" },
-    { "id": 9, "label": ".." },
-    { "id": 8, "label": "bazel_tools", "parentId": 9 },
-    { "id": 7, "label": "tools", "parentId": 8 },
-    { "id": 6, "label": "zip", "parentId": 7 },
-    { "id": 5, "label": "zipper", "parentId": 6 },
-    { "id": 4, "label": "zipper", "parentId": 5 },
-    { "id": 16, "label": "bazel-out" },
-    { "id": 15, "label": "bazel_tools", "parentId": 16 },
-    { "id": 14, "label": "k8-fastbuild", "parentId": 15 },
-    { "id": 13, "label": "bin", "parentId": 14 },
-    { "id": 12, "label": "tools", "parentId": 13 },
-    { "id": 11, "label": "python", "parentId": 12 },
-    { "id": 10, "label": "py3wrapper.sh", "parentId": 11 }]
-}`
-	_, _, err := AqueryBuildStatements([]byte(inputString))
-	assertError(t, err, `Expect 1+ input and 1 output to python zipper action, got: input ["python_binary.py"], output []`)
+	assertBuildStatements(t, []BuildStatement{
+		{
+			OutputPaths: []string{"foo.manifest"},
+			Mnemonic:    "SourceSymlinkManifest",
+		},
+	}, actual)
 }
 
 func assertError(t *testing.T, err error, expected string) {
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index be10e86..36c46a4 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -18,6 +18,7 @@
 	"android/soong/android"
 	"android/soong/cc"
 	"android/soong/genrule"
+	"fmt"
 
 	"testing"
 )
@@ -204,8 +205,8 @@
         ":whole_static_lib_1",
         ":whole_static_lib_2",
     ]`,
-        "sdk_version": `"current"`,
-        "min_sdk_version": `"29"`,
+				"sdk_version":     `"current"`,
+				"min_sdk_version": `"29"`,
 			}),
 		},
 	})
@@ -1489,3 +1490,72 @@
 		},
 	})
 }
+
+func TestCcLibraryStaticStl(t *testing.T) {
+	testCases := []struct {
+		desc string
+		prop string
+		attr attrNameToString
+	}{
+		{
+			desc: "c++_shared deduped to libc++",
+			prop: `stl: "c++_shared",`,
+			attr: attrNameToString{
+				"stl": `"libc++"`,
+			},
+		},
+		{
+			desc: "libc++ to libc++",
+			prop: `stl: "libc++",`,
+			attr: attrNameToString{
+				"stl": `"libc++"`,
+			},
+		},
+		{
+			desc: "c++_static to libc++_static",
+			prop: `stl: "c++_static",`,
+			attr: attrNameToString{
+				"stl": `"libc++_static"`,
+			},
+		},
+		{
+			desc: "libc++_static to libc++_static",
+			prop: `stl: "libc++_static",`,
+			attr: attrNameToString{
+				"stl": `"libc++_static"`,
+			},
+		},
+		{
+			desc: "system to system",
+			prop: `stl: "system",`,
+			attr: attrNameToString{
+				"stl": `"system"`,
+			},
+		},
+		{
+			desc: "none to none",
+			prop: `stl: "none",`,
+			attr: attrNameToString{
+				"stl": `"none"`,
+			},
+		},
+		{
+			desc: "empty to empty",
+			attr: attrNameToString{},
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(*testing.T) {
+			runCcLibraryStaticTestCase(t, bp2buildTestCase{
+				blueprint: fmt.Sprintf(`cc_library_static {
+	name: "foo",
+	include_build_directory: false,
+	%s
+}`, tc.prop),
+				expectedBazelTargets: []string{
+					makeBazelTarget("cc_library_static", "foo", tc.attr),
+				},
+			})
+		})
+	}
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index d891007..fa30d09 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -376,7 +376,8 @@
 				return
 			}
 			if ca.stl == nil {
-				ca.stl = stlProps.Stl
+				stl := deduplicateStlInput(*stlProps.Stl)
+				ca.stl = &stl
 			} else if ca.stl != stlProps.Stl {
 				ctx.ModuleErrorf("Unsupported conversion: module with different stl for different variants: %s and %s", *ca.stl, stlProps.Stl)
 			}
diff --git a/cc/stl.go b/cc/stl.go
index 0f2a878..85a06da 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -25,6 +25,16 @@
 	return family
 }
 
+func deduplicateStlInput(stl string) string {
+	switch stl {
+	case "c++_shared":
+		return "libc++"
+	case "c++_static":
+		return "libc++_static"
+	}
+	return stl
+}
+
 func getNdkStlFamilyAndLinkType(m LinkableInterface) (string, string) {
 	stl := m.SelectedStl()
 	switch stl {
@@ -66,18 +76,18 @@
 		} else if ctx.header() {
 			s = "none"
 		}
+		if s == "none" {
+			return ""
+		}
+		s = deduplicateStlInput(s)
 		if ctx.useSdk() && ctx.Device() {
 			switch s {
 			case "", "system":
 				return "ndk_system"
-			case "c++_shared", "c++_static":
-				return "ndk_lib" + s
 			case "libc++":
 				return "ndk_libc++_shared"
 			case "libc++_static":
 				return "ndk_libc++_static"
-			case "none":
-				return ""
 			default:
 				ctx.ModuleErrorf("stl: %q is not a supported STL with sdk_version set", s)
 				return ""
@@ -87,8 +97,6 @@
 			case "libc++", "libc++_static", "":
 				// Only use static libc++ for Windows.
 				return "libc++_static"
-			case "none":
-				return ""
 			default:
 				ctx.ModuleErrorf("stl: %q is not a supported STL for windows", s)
 				return ""
@@ -97,12 +105,6 @@
 			switch s {
 			case "libc++", "libc++_static":
 				return s
-			case "c++_shared":
-				return "libc++"
-			case "c++_static":
-				return "libc++_static"
-			case "none":
-				return ""
 			case "", "system":
 				if ctx.static() {
 					return "libc++_static"
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index de139c4..d8011d6 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -394,10 +394,14 @@
 	if !android.PrefixInList(preoptFlags, "--compiler-filter=") {
 		var compilerFilter string
 		if systemServerJars.ContainsJar(module.Name) {
-			// Jars of system server, use the product option if it is set, speed otherwise.
 			if global.SystemServerCompilerFilter != "" {
+				// Use the product option if it is set.
 				compilerFilter = global.SystemServerCompilerFilter
+			} else if profile != nil {
+				// Use "speed-profile" for system server jars that have a profile.
+				compilerFilter = "speed-profile"
 			} else {
+				// Use "speed" for system server jars that do not have a profile.
 				compilerFilter = "speed"
 			}
 		} else if contains(global.SpeedApps, module.Name) || contains(global.SystemServerApps, module.Name) {
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index 700cdf0..8e54d80 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -18,6 +18,7 @@
 
 import (
 	"encoding/json"
+	"fmt"
 	"sort"
 	"strings"
 
@@ -59,9 +60,65 @@
 	Dir          string
 }
 
+type PrivilegedLevel string
+
+const (
+	// Environment with the most minimal permissions.
+	Constrained PrivilegedLevel = "Constrained"
+	// Typical execution environment running unprivileged code.
+	Unprivileged = "Unprivileged"
+	// May have access to elevated permissions.
+	Privileged = "Privileged"
+	// Trusted computing base.
+	Tcb = "TCB"
+	// Bootloader chain.
+	Bootloader = "Bootloader"
+	// Tusted execution environment.
+	Tee = "Tee"
+	// Secure enclave.
+	Se = "Se"
+	// Other.
+	Other = "Other"
+)
+
+func IsValidConfig(fuzzModule FuzzPackagedModule, moduleName string) bool {
+	var config = fuzzModule.FuzzProperties.Fuzz_config
+	if config != nil {
+		var level = PrivilegedLevel(config.Privilege_level)
+		if level != "" {
+			switch level {
+			case Constrained, Unprivileged, Privileged, Tcb, Bootloader, Tee, Se, Other:
+				return true
+			}
+			panic(fmt.Errorf("Invalid privileged level in fuzz config in %s", moduleName))
+		}
+		return true
+	} else {
+		return false
+	}
+}
+
 type FuzzConfig struct {
 	// Email address of people to CC on bugs or contact about this fuzz target.
 	Cc []string `json:"cc,omitempty"`
+	// A brief description of what the fuzzed code does.
+	Description string `json:"description,omitempty"`
+	// Can this code be triggered remotely or only locally.
+	Remotely_accessible bool `json:"remotely_accessible,omitempty"`
+	// Is the fuzzed code host only, i.e. test frameworks or support utilities.
+	Host_only bool `json:"access_vector,omitempty"`
+	// Can third party/untrusted apps supply data to fuzzed code.
+	Untrusted_data bool `json:"untrusted_data,omitempty"`
+	// Is the code being fuzzed in a privileged, constrained or any other
+	// context from:
+	// https://source.android.com/security/overview/updates-resources#context_types.
+	Privilege_level PrivilegedLevel `json:"privilege_level,omitempty"`
+	// Can the fuzzed code isolated or can be called by multiple users/processes.
+	Isolated bool `json:"users_isolation,omitempty"`
+	// When code was relaeased or will be released.
+	Production_date string `json:"production_date,omitempty"`
+	// Prevents critical service functionality like phone calls, bluetooth, etc.
+	Critical bool `json:"critical,omitempty"`
 	// Specify whether to enable continuous fuzzing on devices. Defaults to true.
 	Fuzz_on_haiku_device *bool `json:"fuzz_on_haiku_device,omitempty"`
 	// Specify whether to enable continuous fuzzing on host. Defaults to true.
@@ -157,7 +214,7 @@
 	}
 
 	// Additional fuzz config.
-	if fuzzModule.Config != nil {
+	if fuzzModule.Config != nil && IsValidConfig(fuzzModule, module.Name()) {
 		files = append(files, FileToZip{fuzzModule.Config, ""})
 	}
 
diff --git a/java/base.go b/java/base.go
index db10411..a391f64 100644
--- a/java/base.go
+++ b/java/base.go
@@ -1072,6 +1072,13 @@
 		kotlincFlags := j.properties.Kotlincflags
 		CheckKotlincFlags(ctx, kotlincFlags)
 
+		// This is needed for code that depends on libraries that use @JvmDefault
+		// -Xjvm-default=all will be the default in a future version of kotlin
+		// according to https://blog.jetbrains.com/kotlin/2020/07/kotlin-1-4-m3-generating-default-methods-in-interfaces/
+		if flags.javaVersion >= JAVA_VERSION_8 {
+			kotlincFlags = append(kotlincFlags, "-Xjvm-default=all")
+		}
+
 		// Workaround for KT-46512
 		kotlincFlags = append(kotlincFlags, "-Xsam-conversions=class")
 
@@ -1485,11 +1492,30 @@
 	}
 
 	if ctx.Device() {
-		lintSDKVersion := func(sdkSpec android.SdkSpec) android.ApiLevel {
+		lintSDKVersion := func(sdkSpec android.SdkSpec) int {
 			if v := sdkSpec.ApiLevel; !v.IsPreview() {
-				return v
+				return v.FinalInt()
 			} else {
-				return ctx.Config().DefaultAppTargetSdk(ctx)
+				// When running metalava, we pass --version-codename. When that value
+				// is not REL, metalava will add 1 to the --current-version argument.
+				// On old branches, PLATFORM_SDK_VERSION is the latest version (for that
+				// branch) and the codename is REL, except potentially on the most
+				// recent non-master branch. On that branch, it goes through two other
+				// phases before it gets to the phase previously described:
+				//  - PLATFORM_SDK_VERSION has not been updated yet, and the codename
+				//    is not rel. This happens for most of the internal branch's life
+				//    while the branch has been cut but is still under active development.
+				//  - PLATFORM_SDK_VERSION has been set, but the codename is still not
+				//    REL. This happens briefly during the release process. During this
+				//    state the code to add --current-version is commented out, and then
+				//    that commenting out is reverted after the codename is set to REL.
+				// On the master branch, the PLATFORM_SDK_VERSION always represents a
+				// prior version and the codename is always non-REL.
+				//
+				// We need to add one here to match metalava adding 1. Technically
+				// this means that in the state described in the second bullet point
+				// above, this number is 1 higher than it should be.
+				return ctx.Config().PlatformSdkVersion().FinalInt() + 1
 			}
 		}
 
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 0591012..f08b64b 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -32,12 +32,7 @@
 func init() {
 	registerBootclasspathFragmentBuildComponents(android.InitRegistrationContext)
 
-	android.RegisterSdkMemberType(&bootclasspathFragmentMemberType{
-		SdkMemberTypeBase: android.SdkMemberTypeBase{
-			PropertyName: "bootclasspath_fragments",
-			SupportsSdk:  true,
-		},
-	})
+	android.RegisterSdkMemberType(BootclasspathFragmentSdkMemberType)
 }
 
 func registerBootclasspathFragmentBuildComponents(ctx android.RegistrationContext) {
@@ -46,6 +41,15 @@
 	ctx.RegisterModuleType("prebuilt_bootclasspath_fragment", prebuiltBootclasspathFragmentFactory)
 }
 
+// BootclasspathFragmentSdkMemberType is the member type used to add bootclasspath_fragments to
+// the SDK snapshot. It is exported for use by apex.
+var BootclasspathFragmentSdkMemberType = &bootclasspathFragmentMemberType{
+	SdkMemberTypeBase: android.SdkMemberTypeBase{
+		PropertyName: "bootclasspath_fragments",
+		SupportsSdk:  true,
+	},
+}
+
 type bootclasspathFragmentContentDependencyTag struct {
 	blueprint.BaseDependencyTag
 }
diff --git a/java/java.go b/java/java.go
index 2897fd7..6e05159 100644
--- a/java/java.go
+++ b/java/java.go
@@ -164,6 +164,9 @@
 		android.SdkMemberTypeBase{
 			PropertyName: "java_systemserver_libs",
 			SupportsSdk:  true,
+
+			// This was only added in Tiramisu.
+			SupportedBuildReleaseSpecification: "Tiramisu+",
 		},
 		func(ctx android.SdkMemberContext, j *Library) android.Path {
 			// Java systemserver libs are only provided in the SDK to provide access to their dex
@@ -510,6 +513,20 @@
 	}
 }
 
+func (v javaVersion) StringForKotlinc() string {
+	// $ ./external/kotlinc/bin/kotlinc -jvm-target foo
+	// error: unknown JVM target version: foo
+	// Supported versions: 1.6, 1.8, 9, 10, 11, 12, 13, 14, 15, 16, 17
+	switch v {
+	case JAVA_VERSION_7:
+		return "1.6"
+	case JAVA_VERSION_9:
+		return "9"
+	default:
+		return v.String()
+	}
+}
+
 // Returns true if javac targeting this version uses system modules instead of a bootclasspath.
 func (v javaVersion) usesJavaModules() bool {
 	return v >= 9
diff --git a/java/kotlin.go b/java/kotlin.go
index 903c624..9bff5ea 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -119,9 +119,8 @@
 			"srcJarDir":         android.PathForModuleOut(ctx, "kotlinc", "srcJars").String(),
 			"kotlinBuildFile":   android.PathForModuleOut(ctx, "kotlinc-build.xml").String(),
 			"emptyDir":          android.PathForModuleOut(ctx, "kotlinc", "empty").String(),
-			// http://b/69160377 kotlinc only supports -jvm-target 1.6 and 1.8
-			"kotlinJvmTarget": "1.8",
-			"name":            kotlinName,
+			"kotlinJvmTarget":   flags.javaVersion.StringForKotlinc(),
+			"name":              kotlinName,
 		},
 	})
 }
diff --git a/java/lint.go b/java/lint.go
index 22c9ec4..e276345 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"sort"
+	"strconv"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
@@ -75,9 +76,9 @@
 	extraLintCheckJars      android.Paths
 	test                    bool
 	library                 bool
-	minSdkVersion           android.ApiLevel
-	targetSdkVersion        android.ApiLevel
-	compileSdkVersion       android.ApiLevel
+	minSdkVersion           int
+	targetSdkVersion        int
+	compileSdkVersion       int
 	compileSdkKind          android.SdkKind
 	javaLanguageLevel       string
 	kotlinLanguageLevel     string
@@ -299,8 +300,8 @@
 		Text(`echo "<?xml version='1.0' encoding='utf-8'?>" &&`).
 		Text(`echo "<manifest xmlns:android='http://schemas.android.com/apk/res/android'" &&`).
 		Text(`echo "    android:versionCode='1' android:versionName='1' >" &&`).
-		Textf(`echo "  <uses-sdk android:minSdkVersion='%s' android:targetSdkVersion='%s'/>" &&`,
-			l.minSdkVersion.String(), l.targetSdkVersion.String()).
+		Textf(`echo "  <uses-sdk android:minSdkVersion='%d' android:targetSdkVersion='%d'/>" &&`,
+			l.minSdkVersion, l.targetSdkVersion).
 		Text(`echo "</manifest>"`).
 		Text(") >").Output(manifestPath)
 
@@ -325,7 +326,7 @@
 		return
 	}
 
-	if l.minSdkVersion.CompareTo(l.compileSdkVersion) == -1 {
+	if l.minSdkVersion != l.compileSdkVersion {
 		l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, updatabilityChecks...)
 		_, filtered := android.FilterList(l.properties.Lint.Warning_checks, updatabilityChecks)
 		if len(filtered) != 0 {
@@ -427,7 +428,7 @@
 		FlagWithOutput("--html ", html).
 		FlagWithOutput("--text ", text).
 		FlagWithOutput("--xml ", xml).
-		FlagWithArg("--compile-sdk-version ", l.compileSdkVersion.String()).
+		FlagWithArg("--compile-sdk-version ", strconv.Itoa(l.compileSdkVersion)).
 		FlagWithArg("--java-language-level ", l.javaLanguageLevel).
 		FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
 		FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index fa61ea6..a2cd261 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -24,12 +24,7 @@
 func init() {
 	registerSystemserverClasspathBuildComponents(android.InitRegistrationContext)
 
-	android.RegisterSdkMemberType(&systemServerClasspathFragmentMemberType{
-		SdkMemberTypeBase: android.SdkMemberTypeBase{
-			PropertyName: "systemserverclasspath_fragments",
-			SupportsSdk:  true,
-		},
-	})
+	android.RegisterSdkMemberType(SystemServerClasspathFragmentSdkMemberType)
 }
 
 func registerSystemserverClasspathBuildComponents(ctx android.RegistrationContext) {
@@ -38,6 +33,17 @@
 	ctx.RegisterModuleType("prebuilt_systemserverclasspath_fragment", prebuiltSystemServerClasspathModuleFactory)
 }
 
+var SystemServerClasspathFragmentSdkMemberType = &systemServerClasspathFragmentMemberType{
+	SdkMemberTypeBase: android.SdkMemberTypeBase{
+		PropertyName: "systemserverclasspath_fragments",
+		SupportsSdk:  true,
+
+		// Support for adding systemserverclasspath_fragments to the sdk snapshot was only added in
+		// Tiramisu.
+		SupportedBuildReleaseSpecification: "Tiramisu+",
+	},
+}
+
 type platformSystemServerClasspathModule struct {
 	android.ModuleBase
 
diff --git a/rust/compiler.go b/rust/compiler.go
index 365f1e0..bf6a488 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -371,8 +371,9 @@
 
 	if !Bool(compiler.Properties.No_stdlibs) {
 		for _, stdlib := range config.Stdlibs {
-			// If we're building for the build host, use the prebuilt stdlibs
-			if ctx.Host() && !ctx.Target().HostCross {
+			// If we're building for the build host, use the prebuilt stdlibs, unless the host
+			// is linux_bionic which doesn't have prebuilts.
+			if ctx.Host() && !ctx.Target().HostCross && ctx.Target().Os != android.LinuxBionic {
 				stdlib = "prebuilt_" + stdlib
 			}
 			deps.Stdlibs = append(deps.Stdlibs, stdlib)
diff --git a/scripts/jsonmodify.py b/scripts/jsonmodify.py
index ba1109e..5132aa8 100755
--- a/scripts/jsonmodify.py
+++ b/scripts/jsonmodify.py
@@ -75,6 +75,14 @@
       raise ValueError(self + " should be a array.")
     cur[key].extend(args)
 
+# A JSONDecoder that supports line comments start with //
+class JSONWithCommentsDecoder(json.JSONDecoder):
+  def __init__(self, **kw):
+    super().__init__(**kw)
+
+  def decode(self, s: str):
+    s = '\n'.join(l for l in s.split('\n') if not l.lstrip(' ').startswith('//'))
+    return super().decode(s)
 
 def main():
   parser = argparse.ArgumentParser()
@@ -103,9 +111,9 @@
 
   if args.input:
     with open(args.input) as f:
-      obj = json.load(f, object_pairs_hook=collections.OrderedDict)
+      obj = json.load(f, object_pairs_hook=collections.OrderedDict, cls=JSONWithCommentsDecoder)
   else:
-    obj = json.load(sys.stdin, object_pairs_hook=collections.OrderedDict)
+    obj = json.load(sys.stdin, object_pairs_hook=collections.OrderedDict, cls=JSONWithCommentsDecoder)
 
   for p in args.patch:
     p[0].apply(obj, *p[1:])
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 93ad172..13ddbe7 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -190,7 +190,7 @@
 	android.AssertStringDoesContain(t, "boot jars package check", command, expectedCommandArgs)
 }
 
-func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) {
+func testSnapshotWithBootClasspathFragment_Contents(t *testing.T, sdk string, copyRules string) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		java.PrepareForTestWithJavaDefaultModules,
@@ -202,19 +202,7 @@
 		// Add a platform_bootclasspath that depends on the fragment.
 		fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"),
 
-		android.FixtureWithRootAndroidBp(`
-			sdk {
-				name: "mysdk",
-				bootclasspath_fragments: ["mybootclasspathfragment"],
-				java_sdk_libs: [
-					// This is not strictly needed as it should be automatically added to the sdk_snapshot as
-					// a java_sdk_libs module because it is used in the mybootclasspathfragment's
-					// api.stub_libs property. However, it is specified here to ensure that duplicates are
-					// correctly deduped.
-					"mysdklibrary",
-				],
-			}
-
+		android.FixtureWithRootAndroidBp(sdk+`
 			apex {
 				name: "myapex",
 				key: "myapex.key",
@@ -373,24 +361,7 @@
     },
 }
 		`),
-		checkAllCopyRules(`
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
-.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
-.intermediates/myothersdklibrary.stubs/android_common/javac/myothersdklibrary.stubs.jar -> sdk_library/public/myothersdklibrary-stubs.jar
-.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
-.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-removed.txt
-.intermediates/mysdklibrary.stubs/android_common/javac/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar
-.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
-.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
-.intermediates/mycoreplatform.stubs/android_common/javac/mycoreplatform.stubs.jar -> sdk_library/public/mycoreplatform-stubs.jar
-.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt
-.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
-`),
+		checkAllCopyRules(copyRules),
 		snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
 		snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
 			module := result.ModuleForTests("platform-bootclasspath", "android_common")
@@ -427,6 +398,89 @@
 	)
 }
 
+func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) {
+	t.Run("added-directly", func(t *testing.T) {
+		testSnapshotWithBootClasspathFragment_Contents(t, `
+			sdk {
+				name: "mysdk",
+				bootclasspath_fragments: ["mybootclasspathfragment"],
+				java_sdk_libs: [
+					// This is not strictly needed as it should be automatically added to the sdk_snapshot as
+					// a java_sdk_libs module because it is used in the mybootclasspathfragment's
+					// api.stub_libs property. However, it is specified here to ensure that duplicates are
+					// correctly deduped.
+					"mysdklibrary",
+				],
+			}
+		`, `
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
+.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
+.intermediates/myothersdklibrary.stubs/android_common/javac/myothersdklibrary.stubs.jar -> sdk_library/public/myothersdklibrary-stubs.jar
+.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
+.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-removed.txt
+.intermediates/mysdklibrary.stubs/android_common/javac/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar
+.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
+.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
+.intermediates/mycoreplatform.stubs/android_common/javac/mycoreplatform.stubs.jar -> sdk_library/public/mycoreplatform-stubs.jar
+.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt
+.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
+`)
+	})
+
+	copyBootclasspathFragmentFromApexVariantRules := `
+.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
+.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
+.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/index.csv -> hiddenapi/index.csv
+.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
+.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
+.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
+.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
+.intermediates/myothersdklibrary.stubs/android_common/javac/myothersdklibrary.stubs.jar -> sdk_library/public/myothersdklibrary-stubs.jar
+.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
+.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-removed.txt
+.intermediates/mysdklibrary.stubs/android_common/javac/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar
+.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
+.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
+.intermediates/mycoreplatform.stubs/android_common/javac/mycoreplatform.stubs.jar -> sdk_library/public/mycoreplatform-stubs.jar
+.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt
+.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
+`
+	t.Run("added-via-apex", func(t *testing.T) {
+		testSnapshotWithBootClasspathFragment_Contents(t, `
+			sdk {
+				name: "mysdk",
+				apexes: ["myapex"],
+			}
+		`, copyBootclasspathFragmentFromApexVariantRules)
+	})
+
+	t.Run("added-directly-and-indirectly", func(t *testing.T) {
+		testSnapshotWithBootClasspathFragment_Contents(t, `
+			sdk {
+				name: "mysdk",
+				apexes: ["myapex"],
+				// This is not strictly needed as it should be automatically added to the sdk_snapshot as
+				// a bootclasspath_fragments module because it is used in the myapex's
+				// bootclasspath_fragments property. However, it is specified here to ensure that duplicates
+				// are correctly deduped.
+				bootclasspath_fragments: ["mybootclasspathfragment"],
+				java_sdk_libs: [
+					// This is not strictly needed as it should be automatically added to the sdk_snapshot as
+					// a java_sdk_libs module because it is used in the mybootclasspathfragment's
+					// api.stub_libs property. However, it is specified here to ensure that duplicates are
+					// correctly deduped.
+					"mysdklibrary",
+				],
+			}
+		`, copyBootclasspathFragmentFromApexVariantRules)
+	})
+}
+
 // TestSnapshotWithBootClasspathFragment_Fragments makes sure that the fragments property of a
 // bootclasspath_fragment is correctly output to the sdk snapshot.
 func TestSnapshotWithBootClasspathFragment_Fragments(t *testing.T) {
diff --git a/sdk/systemserverclasspath_fragment_sdk_test.go b/sdk/systemserverclasspath_fragment_sdk_test.go
index 01692a3..1ac405d 100644
--- a/sdk/systemserverclasspath_fragment_sdk_test.go
+++ b/sdk/systemserverclasspath_fragment_sdk_test.go
@@ -22,28 +22,21 @@
 	"android/soong/java"
 )
 
-func TestSnapshotWithSystemServerClasspathFragment(t *testing.T) {
+func testSnapshotWithSystemServerClasspathFragment(t *testing.T, sdk string, targetBuildRelease string, expectedSdkSnapshot string) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		java.PrepareForTestWithJavaDefaultModules,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("mysdklibrary"),
 		dexpreopt.FixtureSetApexSystemServerJars("myapex:mylib", "myapex:mysdklibrary"),
+		android.FixtureModifyEnv(func(env map[string]string) {
+			if targetBuildRelease != "latest" {
+				env["SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE"] = targetBuildRelease
+			}
+		}),
 		prepareForSdkTestWithApex,
 
-		android.FixtureWithRootAndroidBp(`
-			sdk {
-				name: "mysdk",
-				systemserverclasspath_fragments: ["mysystemserverclasspathfragment"],
-				java_sdk_libs: [
-					// This is not strictly needed as it should be automatically added to the sdk_snapshot as
-					// a java_sdk_libs module because it is used in the mysystemserverclasspathfragment's
-					// contents property. However, it is specified here to ensure that duplicates are
-					// correctly deduped.
-					"mysdklibrary",
-				],
-			}
-
+		android.FixtureWithRootAndroidBp(sdk+`
 			apex {
 				name: "myapex",
 				key: "myapex.key",
@@ -83,7 +76,27 @@
 	).RunTest(t)
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkAndroidBpContents(`
+		checkAndroidBpContents(expectedSdkSnapshot),
+	)
+}
+
+func TestSnapshotWithSystemServerClasspathFragment(t *testing.T) {
+
+	commonSdk := `
+sdk {
+	name: "mysdk",
+	systemserverclasspath_fragments: ["mysystemserverclasspathfragment"],
+	java_sdk_libs: [
+		// This is not strictly needed as it should be automatically added to the sdk_snapshot as
+		// a java_sdk_libs module because it is used in the mysystemserverclasspathfragment's
+		// contents property. However, it is specified here to ensure that duplicates are
+		// correctly deduped.
+		"mysdklibrary",
+	],
+}
+	`
+
+	expectedLatestSnapshot := `
 // This is auto-generated. DO NOT EDIT.
 
 java_sdk_library_import {
@@ -120,6 +133,80 @@
         "mysdklibrary",
     ],
 }
-`),
-	)
+`
+
+	t.Run("target-s", func(t *testing.T) {
+		testSnapshotWithSystemServerClasspathFragment(t, commonSdk, "S", `
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+    name: "mysdklibrary",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["myapex"],
+    shared_library: false,
+    public: {
+        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
+        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
+        current_api: "sdk_library/public/mysdklibrary.txt",
+        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
+        sdk_version: "current",
+    },
+}
+`)
+	})
+
+	t.Run("target-t", func(t *testing.T) {
+		testSnapshotWithSystemServerClasspathFragment(t, commonSdk, "Tiramisu", `
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+    name: "mysdklibrary",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["myapex"],
+    shared_library: false,
+    public: {
+        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
+        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
+        current_api: "sdk_library/public/mysdklibrary.txt",
+        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
+        sdk_version: "current",
+    },
+}
+
+java_import {
+    name: "mylib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["myapex"],
+    jars: ["java_systemserver_libs/snapshot/jars/are/invalid/mylib.jar"],
+    permitted_packages: ["mylib"],
+}
+
+prebuilt_systemserverclasspath_fragment {
+    name: "mysystemserverclasspathfragment",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["myapex"],
+    contents: [
+        "mylib",
+        "mysdklibrary",
+    ],
+}
+`)
+	})
+
+	t.Run("added-directly", func(t *testing.T) {
+		testSnapshotWithSystemServerClasspathFragment(t, commonSdk, `latest`, expectedLatestSnapshot)
+	})
+
+	t.Run("added-via-apex", func(t *testing.T) {
+		testSnapshotWithSystemServerClasspathFragment(t, `
+			sdk {
+				name: "mysdk",
+				apexes: ["myapex"],
+			}
+		`, `latest`, expectedLatestSnapshot)
+	})
 }
diff --git a/sdk/update.go b/sdk/update.go
index 457828b..b9ef3d0 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -239,7 +239,7 @@
 // Finally, the member type slices are concatenated together to form a single slice. The order in
 // which they are concatenated is the order in which the member types were registered in the
 // android.SdkMemberTypesRegistry.
-func (s *sdk) groupMemberVariantsByMemberThenType(ctx android.ModuleContext, memberVariantDeps []sdkMemberVariantDep) []*sdkMember {
+func (s *sdk) groupMemberVariantsByMemberThenType(ctx android.ModuleContext, targetBuildRelease *buildRelease, memberVariantDeps []sdkMemberVariantDep) []*sdkMember {
 	byType := make(map[android.SdkMemberType][]*sdkMember)
 	byName := make(map[string]*sdkMember)
 
@@ -268,13 +268,39 @@
 	}
 	var members []*sdkMember
 	for _, memberListProperty := range s.memberTypeListProperties() {
-		membersOfType := byType[memberListProperty.memberType]
+		memberType := memberListProperty.memberType
+
+		if !isMemberTypeSupportedByTargetBuildRelease(memberType, targetBuildRelease) {
+			continue
+		}
+
+		membersOfType := byType[memberType]
 		members = append(members, membersOfType...)
 	}
 
 	return members
 }
 
+// isMemberTypeSupportedByTargetBuildRelease returns true if the member type is supported by the
+// target build release.
+func isMemberTypeSupportedByTargetBuildRelease(memberType android.SdkMemberType, targetBuildRelease *buildRelease) bool {
+	supportedByTargetBuildRelease := true
+	supportedBuildReleases := memberType.SupportedBuildReleases()
+	if supportedBuildReleases == "" {
+		supportedBuildReleases = "S+"
+	}
+
+	set, err := parseBuildReleaseSet(supportedBuildReleases)
+	if err != nil {
+		panic(fmt.Errorf("member type %s has invalid supported build releases %q: %s",
+			memberType.SdkPropertyName(), supportedBuildReleases, err))
+	}
+	if !set.contains(targetBuildRelease) {
+		supportedByTargetBuildRelease = false
+	}
+	return supportedByTargetBuildRelease
+}
+
 func appendUniqueVariants(variants []android.SdkAware, newVariant android.SdkAware) []android.SdkAware {
 	for _, v := range variants {
 		if v == newVariant {
@@ -401,12 +427,15 @@
 
 	// Group the variants for each member module together and then group the members of each member
 	// type together.
-	members := s.groupMemberVariantsByMemberThenType(ctx, memberVariantDeps)
+	members := s.groupMemberVariantsByMemberThenType(ctx, targetBuildRelease, memberVariantDeps)
 
 	// Create the prebuilt modules for each of the member modules.
 	traits := s.gatherTraits()
 	for _, member := range members {
 		memberType := member.memberType
+		if !memberType.ArePrebuiltsRequired() {
+			continue
+		}
 
 		name := member.name
 		requiredTraits := traits[name]
@@ -1293,6 +1322,119 @@
 	}
 }
 
+// TODO(187910671): BEGIN - Remove once modules do not have an APEX and default variant.
+// variantCoordinate contains the coordinates used to identify a variant of an SDK member.
+type variantCoordinate struct {
+	// osType identifies the OS target of a variant.
+	osType android.OsType
+	// archId identifies the architecture and whether it is for the native bridge.
+	archId archId
+	// image is the image variant name.
+	image string
+	// linkType is the link type name.
+	linkType string
+}
+
+func getVariantCoordinate(ctx *memberContext, variant android.Module) variantCoordinate {
+	linkType := ""
+	if len(ctx.MemberType().SupportedLinkages()) > 0 {
+		linkType = getLinkType(variant)
+	}
+	return variantCoordinate{
+		osType:   variant.Target().Os,
+		archId:   archIdFromTarget(variant.Target()),
+		image:    variant.ImageVariation().Variation,
+		linkType: linkType,
+	}
+}
+
+// selectApexVariantsWhereAvailable filters the input list of variants by selecting the APEX
+// specific variant for a specific variantCoordinate when there is both an APEX and default variant.
+//
+// There is a long-standing issue where a module that is added to an APEX has both an APEX and
+// default/platform variant created even when the module does not require a platform variant. As a
+// result an indirect dependency onto a module via the APEX will use the APEX variant, whereas a
+// direct dependency onto the module will use the default/platform variant. That would result in a
+// failure while attempting to optimize the properties for a member as it would have two variants
+// when only one was expected.
+//
+// This function mitigates that problem by detecting when there are two variants that differ only
+// by apex variant, where one is the default/platform variant and one is the APEX variant. In that
+// case it picks the APEX variant. It picks the APEX variant because that is the behavior that would
+// be expected
+func selectApexVariantsWhereAvailable(ctx *memberContext, variants []android.SdkAware) []android.SdkAware {
+	moduleCtx := ctx.sdkMemberContext
+
+	// Group the variants by coordinates.
+	variantsByCoord := make(map[variantCoordinate][]android.SdkAware)
+	for _, variant := range variants {
+		coord := getVariantCoordinate(ctx, variant)
+		variantsByCoord[coord] = append(variantsByCoord[coord], variant)
+	}
+
+	toDiscard := make(map[android.SdkAware]struct{})
+	for coord, list := range variantsByCoord {
+		count := len(list)
+		if count == 1 {
+			continue
+		}
+
+		variantsByApex := make(map[string]android.SdkAware)
+		conflictDetected := false
+		for _, variant := range list {
+			apexInfo := moduleCtx.OtherModuleProvider(variant, android.ApexInfoProvider).(android.ApexInfo)
+			apexVariationName := apexInfo.ApexVariationName
+			// If there are two variants for a specific APEX variation then there is conflict.
+			if _, ok := variantsByApex[apexVariationName]; ok {
+				conflictDetected = true
+				break
+			}
+			variantsByApex[apexVariationName] = variant
+		}
+
+		// If there are more than 2 apex variations or one of the apex variations is not the
+		// default/platform variation then there is a conflict.
+		if len(variantsByApex) != 2 {
+			conflictDetected = true
+		} else if _, ok := variantsByApex[""]; !ok {
+			conflictDetected = true
+		}
+
+		// If there are no conflicts then add the default/platform variation to the list to remove.
+		if !conflictDetected {
+			toDiscard[variantsByApex[""]] = struct{}{}
+			continue
+		}
+
+		// There are duplicate variants at this coordinate and they are not the default and APEX variant
+		// so fail.
+		variantDescriptions := []string{}
+		for _, m := range list {
+			variantDescriptions = append(variantDescriptions, fmt.Sprintf("    %s", m.String()))
+		}
+
+		moduleCtx.ModuleErrorf("multiple conflicting variants detected for OsType{%s}, %s, Image{%s}, Link{%s}\n%s",
+			coord.osType, coord.archId.String(), coord.image, coord.linkType,
+			strings.Join(variantDescriptions, "\n"))
+	}
+
+	// If there are any variants to discard then remove them from the list of variants, while
+	// preserving the order.
+	if len(toDiscard) > 0 {
+		filtered := []android.SdkAware{}
+		for _, variant := range variants {
+			if _, ok := toDiscard[variant]; !ok {
+				filtered = append(filtered, variant)
+			}
+		}
+		variants = filtered
+	}
+
+	return variants
+}
+
+// TODO(187910671): END - Remove once modules do not have an APEX and default variant.
+
 type baseInfo struct {
 	Properties android.SdkMemberProperties
 }
@@ -1348,7 +1490,14 @@
 
 	if commonVariants, ok := variantsByArchId[commonArchId]; ok {
 		if len(osTypeVariants) != 1 {
-			panic(fmt.Errorf("Expected to only have 1 variant when arch type is common but found %d", len(osTypeVariants)))
+			variants := []string{}
+			for _, m := range osTypeVariants {
+				variants = append(variants, fmt.Sprintf("    %s", m.String()))
+			}
+			panic(fmt.Errorf("expected to only have 1 variant of %q when arch type is common but found %d\n%s",
+				ctx.Name(),
+				len(osTypeVariants),
+				strings.Join(variants, "\n")))
 		}
 
 		// A common arch type only has one variant and its properties should be treated
@@ -1828,7 +1977,8 @@
 	memberType := member.memberType
 
 	// Do not add the prefer property if the member snapshot module is a source module type.
-	config := ctx.sdkMemberContext.Config()
+	moduleCtx := ctx.sdkMemberContext
+	config := moduleCtx.Config()
 	if !memberType.UsesSourceModuleTypeInSnapshot() {
 		// Set the prefer based on the environment variable. This is a temporary work around to allow a
 		// snapshot to be created that sets prefer: true.
@@ -1853,9 +2003,10 @@
 		}
 	}
 
+	variants := selectApexVariantsWhereAvailable(ctx, member.variants)
+
 	// Group the variants by os type.
 	variantsByOsType := make(map[android.OsType][]android.Module)
-	variants := member.Variants()
 	for _, variant := range variants {
 		osType := variant.Target().Os
 		variantsByOsType[osType] = append(variantsByOsType[osType], variant)
@@ -1901,7 +2052,7 @@
 	}
 
 	// Extract properties which are common across all architectures and os types.
-	extractCommonProperties(ctx.sdkMemberContext, commonValueExtractor, commonProperties, osSpecificPropertiesContainers)
+	extractCommonProperties(moduleCtx, commonValueExtractor, commonProperties, osSpecificPropertiesContainers)
 
 	// Add the common properties to the module.
 	addSdkMemberPropertiesToSet(ctx, commonProperties, bpModule)
diff --git a/ui/build/config.go b/ui/build/config.go
index 8874209..cbf1986 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -156,10 +156,15 @@
 // experiments system to control Soong features dynamically.
 func fetchEnvConfig(ctx Context, config *configImpl, envConfigName string) error {
 	configName := envConfigName + "." + jsonSuffix
-	expConfigFetcher := &smpb.ExpConfigFetcher{}
+	expConfigFetcher := &smpb.ExpConfigFetcher{Filename: &configName}
 	defer func() {
 		ctx.Metrics.ExpConfigFetcher(expConfigFetcher)
 	}()
+	if !config.GoogleProdCredsExist() {
+		status := smpb.ExpConfigFetcher_MISSING_GCERT
+		expConfigFetcher.Status = &status
+		return nil
+	}
 
 	s, err := os.Stat(configFetcher)
 	if err != nil {
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index 4bc713b..2dd8299 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -220,9 +220,10 @@
 type ExpConfigFetcher_ConfigStatus int32
 
 const (
-	ExpConfigFetcher_NO_CONFIG ExpConfigFetcher_ConfigStatus = 0
-	ExpConfigFetcher_CONFIG    ExpConfigFetcher_ConfigStatus = 1
-	ExpConfigFetcher_ERROR     ExpConfigFetcher_ConfigStatus = 2
+	ExpConfigFetcher_NO_CONFIG     ExpConfigFetcher_ConfigStatus = 0
+	ExpConfigFetcher_CONFIG        ExpConfigFetcher_ConfigStatus = 1
+	ExpConfigFetcher_ERROR         ExpConfigFetcher_ConfigStatus = 2
+	ExpConfigFetcher_MISSING_GCERT ExpConfigFetcher_ConfigStatus = 3
 )
 
 // Enum value maps for ExpConfigFetcher_ConfigStatus.
@@ -231,11 +232,13 @@
 		0: "NO_CONFIG",
 		1: "CONFIG",
 		2: "ERROR",
+		3: "MISSING_GCERT",
 	}
 	ExpConfigFetcher_ConfigStatus_value = map[string]int32{
-		"NO_CONFIG": 0,
-		"CONFIG":    1,
-		"ERROR":     2,
+		"NO_CONFIG":     0,
+		"CONFIG":        1,
+		"ERROR":         2,
+		"MISSING_GCERT": 3,
 	}
 )
 
@@ -1578,7 +1581,7 @@
 	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72,
 	0x69, 0x63, 0x73, 0x2e, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49,
 	0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73,
-	0x49, 0x6e, 0x66, 0x6f, 0x22, 0xc8, 0x01, 0x0a, 0x10, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66,
+	0x49, 0x6e, 0x66, 0x6f, 0x22, 0xdb, 0x01, 0x0a, 0x10, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66,
 	0x69, 0x67, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x12, 0x4a, 0x0a, 0x06, 0x73, 0x74, 0x61,
 	0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, 0x73, 0x6f, 0x6f, 0x6e,
 	0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
@@ -1587,22 +1590,23 @@
 	0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d,
 	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d,
 	0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
-	0x04, 0x52, 0x06, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x22, 0x34, 0x0a, 0x0c, 0x43, 0x6f, 0x6e,
+	0x04, 0x52, 0x06, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x22, 0x47, 0x0a, 0x0c, 0x43, 0x6f, 0x6e,
 	0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x5f,
 	0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4e, 0x46,
-	0x49, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x22,
-	0x91, 0x01, 0x0a, 0x0f, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49,
-	0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x1b, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69,
-	0x6c, 0x64, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
-	0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42,
-	0x75, 0x69, 0x6c, 0x64, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
-	0x65, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c,
-	0x64, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
-	0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x19, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42,
-	0x75, 0x69, 0x6c, 0x64, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75,
-	0x6c, 0x65, 0x73, 0x42, 0x28, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73,
-	0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f,
-	0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x49, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12,
+	0x11, 0x0a, 0x0d, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x5f, 0x47, 0x43, 0x45, 0x52, 0x54,
+	0x10, 0x03, 0x22, 0x91, 0x01, 0x0a, 0x0f, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c,
+	0x64, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x1b, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f,
+	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f,
+	0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x6d, 0x69, 0x78,
+	0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f,
+	0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62,
+	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f,
+	0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x19, 0x6d, 0x69, 0x78,
+	0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d,
+	0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x42, 0x28, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+	0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+	0x63, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 }
 
 var (
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 51dd523..4f8fe7f 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -251,6 +251,7 @@
     NO_CONFIG = 0;
     CONFIG = 1;
     ERROR = 2;
+    MISSING_GCERT = 3;
   }
   // The result of the call to expconfigfetcher
   // NO_CONFIG - Not part of experiment