Merge "Add a new option `compilerFilter` to `bootImageConfig`."
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 7ce0d9b..89c2d19 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -199,6 +199,7 @@
 		"frameworks/base/tests/appwidgets/AppWidgetHostTest": Bp2BuildDefaultTrueRecursively,
 		"frameworks/base/tools/aapt2":                        Bp2BuildDefaultTrue,
 		"frameworks/base/tools/streaming_proto":              Bp2BuildDefaultTrueRecursively,
+		"frameworks/hardware/interfaces/stats/aidl":          Bp2BuildDefaultTrue,
 		"frameworks/native/libs/adbd_auth":                   Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/arect":                       Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/gui":                         Bp2BuildDefaultTrue,
@@ -257,30 +258,32 @@
 
 		"libnativehelper": Bp2BuildDefaultTrueRecursively,
 
-		"packages/apps/DevCamera":                          Bp2BuildDefaultTrue,
-		"packages/apps/HTMLViewer":                         Bp2BuildDefaultTrue,
-		"packages/apps/Protips":                            Bp2BuildDefaultTrue,
-		"packages/apps/SafetyRegulatoryInfo":               Bp2BuildDefaultTrue,
-		"packages/apps/WallpaperPicker":                    Bp2BuildDefaultTrue,
-		"packages/modules/NeuralNetworks/driver/cache":     Bp2BuildDefaultTrueRecursively,
-		"packages/modules/StatsD/lib/libstatssocket":       Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb":                             Bp2BuildDefaultTrue,
-		"packages/modules/adb/apex":                        Bp2BuildDefaultTrue,
-		"packages/modules/adb/crypto":                      Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/libs":                        Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/pairing_auth":                Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/pairing_connection":          Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/proto":                       Bp2BuildDefaultTrueRecursively,
-		"packages/modules/adb/tls":                         Bp2BuildDefaultTrueRecursively,
-		"packages/providers/MediaProvider/tools/dialogs":   Bp2BuildDefaultFalse, // TODO(b/242834374)
-		"packages/screensavers/Basic":                      Bp2BuildDefaultTrue,
-		"packages/services/Car/tests/SampleRearViewCamera": Bp2BuildDefaultFalse, // TODO(b/242834321)
+		"packages/apps/DevCamera":                            Bp2BuildDefaultTrue,
+		"packages/apps/HTMLViewer":                           Bp2BuildDefaultTrue,
+		"packages/apps/Protips":                              Bp2BuildDefaultTrue,
+		"packages/apps/SafetyRegulatoryInfo":                 Bp2BuildDefaultTrue,
+		"packages/apps/WallpaperPicker":                      Bp2BuildDefaultTrue,
+		"packages/modules/NeuralNetworks/driver/cache":       Bp2BuildDefaultTrueRecursively,
+		"packages/modules/StatsD/lib/libstatssocket":         Bp2BuildDefaultTrueRecursively,
+		"packages/modules/adb":                               Bp2BuildDefaultTrue,
+		"packages/modules/adb/apex":                          Bp2BuildDefaultTrue,
+		"packages/modules/adb/crypto":                        Bp2BuildDefaultTrueRecursively,
+		"packages/modules/adb/libs":                          Bp2BuildDefaultTrueRecursively,
+		"packages/modules/adb/pairing_auth":                  Bp2BuildDefaultTrueRecursively,
+		"packages/modules/adb/pairing_connection":            Bp2BuildDefaultTrueRecursively,
+		"packages/modules/adb/proto":                         Bp2BuildDefaultTrueRecursively,
+		"packages/modules/adb/tls":                           Bp2BuildDefaultTrueRecursively,
+		"packages/modules/NetworkStack/common/captiveportal": Bp2BuildDefaultTrue,
+		"packages/providers/MediaProvider/tools/dialogs":     Bp2BuildDefaultFalse, // TODO(b/242834374)
+		"packages/screensavers/Basic":                        Bp2BuildDefaultTrue,
+		"packages/services/Car/tests/SampleRearViewCamera":   Bp2BuildDefaultFalse, // TODO(b/242834321)
 
 		"platform_testing/tests/example": Bp2BuildDefaultTrueRecursively,
 
 		"prebuilts/clang/host/linux-x86":           Bp2BuildDefaultTrueRecursively,
 		"prebuilts/gradle-plugin":                  Bp2BuildDefaultTrueRecursively,
 		"prebuilts/runtime/mainline/platform/sdk":  Bp2BuildDefaultTrueRecursively,
+		"prebuilts/sdk/current/androidx":           Bp2BuildDefaultTrue,
 		"prebuilts/sdk/current/extras/app-toolkit": Bp2BuildDefaultTrue,
 		"prebuilts/sdk/current/support":            Bp2BuildDefaultTrue,
 		"prebuilts/tools":                          Bp2BuildDefaultTrue,
@@ -1427,4 +1430,20 @@
 	// It is implicit that all modules in ProdMixedBuildsEnabledList will
 	// also be built - do not add them to this list.
 	StagingMixedBuildsEnabledList = []string{}
+
+	// These should be the libs that are included by the apexes in the ProdMixedBuildsEnabledList
+	ProdDclaMixedBuildsEnabledList = []string{}
+
+	// These should be the libs that are included by the apexes in the StagingMixedBuildsEnabledList
+	StagingDclaMixedBuildsEnabledList = []string{
+		"libbase",
+		"libc++",
+		"libcrypto",
+		"libcutils",
+	}
+
+	// TODO(b/269342245): Enable the rest of the DCLA libs
+	// "libssl",
+	// "libstagefright_flacdec",
+	// "libutils",
 )
diff --git a/android/androidmk.go b/android/androidmk.go
index 6346401..14b2e82 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -501,6 +501,7 @@
 // generate and fill in AndroidMkEntries's in-struct data, ready to be flushed to a file.
 type fillInEntriesContext interface {
 	ModuleDir(module blueprint.Module) string
+	ModuleSubDir(module blueprint.Module) string
 	Config() Config
 	ModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{}
 	ModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool
@@ -528,7 +529,7 @@
 		fmt.Fprintf(&a.header, distString)
 	}
 
-	fmt.Fprintln(&a.header, "\ninclude $(CLEAR_VARS)  # "+ctx.ModuleType(mod))
+	fmt.Fprintf(&a.header, "\ninclude $(CLEAR_VARS)  # type: %s, name: %s, variant: %s\n", ctx.ModuleType(mod), base.BaseModuleName(), ctx.ModuleSubDir(mod))
 
 	// Collect make variable assignment entries.
 	a.SetString("LOCAL_PATH", ctx.ModuleDir(mod))
diff --git a/android/bazel.go b/android/bazel.go
index 52f50c5..b600758 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -352,11 +352,13 @@
 // metrics reporting.
 func MixedBuildsEnabled(ctx BaseModuleContext) bool {
 	module := ctx.Module()
+	apexInfo := ctx.Provider(ApexInfoProvider).(ApexInfo)
+	withinApex := !apexInfo.IsForPlatform()
 	mixedBuildEnabled := ctx.Config().IsMixedBuildsEnabled() &&
 		ctx.Os() != Windows && // Windows toolchains are not currently supported.
 		module.Enabled() &&
 		convertedToBazel(ctx, module) &&
-		ctx.Config().BazelContext.IsModuleNameAllowed(module.Name())
+		ctx.Config().BazelContext.IsModuleNameAllowed(module.Name(), withinApex)
 	ctx.Config().LogMixedBuild(ctx, mixedBuildEnabled)
 	return mixedBuildEnabled
 }
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 8c34c92..e7ff08f 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -109,12 +109,29 @@
 
 // Portion of cquery map key to describe target configuration.
 type configKey struct {
-	arch   string
-	osType OsType
+	arch    string
+	osType  OsType
+	apexKey ApexConfigKey
+}
+
+type ApexConfigKey struct {
+	WithinApex     bool
+	ApexSdkVersion string
+}
+
+func (c ApexConfigKey) String() string {
+	return fmt.Sprintf("%s_%s", withinApexToString(c.WithinApex), c.ApexSdkVersion)
+}
+
+func withinApexToString(withinApex bool) string {
+	if withinApex {
+		return "within_apex"
+	}
+	return ""
 }
 
 func (c configKey) String() string {
-	return fmt.Sprintf("%s::%s", c.arch, c.osType)
+	return fmt.Sprintf("%s::%s::%s", c.arch, c.osType, c.apexKey)
 }
 
 // Map key to describe bazel cquery requests.
@@ -182,13 +199,15 @@
 	// Note that this only implies "bazel mixed build" allowlisting. The caller
 	// should independently verify the module is eligible for Bazel handling
 	// (for example, that it is MixedBuildBuildable).
-	IsModuleNameAllowed(moduleName string) bool
+	IsModuleNameAllowed(moduleName string, withinApex bool) bool
+
+	IsModuleDclaAllowed(moduleName string) bool
 
 	// Returns the bazel output base (the root directory for all bazel intermediate outputs).
 	OutputBase() string
 
 	// Returns build statements which should get registered to reflect Bazel's outputs.
-	BuildStatementsToRegister() []bazel.BuildStatement
+	BuildStatementsToRegister() []*bazel.BuildStatement
 
 	// Returns the depsets defined in Bazel's aquery response.
 	AqueryDepsets() []bazel.AqueryDepset
@@ -222,7 +241,7 @@
 	results map[cqueryKey]string // Results of cquery requests after Bazel invocations
 
 	// Build statements which should get registered to reflect Bazel's outputs.
-	buildStatements []bazel.BuildStatement
+	buildStatements []*bazel.BuildStatement
 
 	// Depsets which should be used for Bazel's build statements.
 	depsets []bazel.AqueryDepset
@@ -235,6 +254,8 @@
 	bazelDisabledModules map[string]bool
 	// Per-module allowlist to opt modules in to bazel handling.
 	bazelEnabledModules map[string]bool
+	// DCLA modules are enabled when used in apex.
+	bazelDclaEnabledModules map[string]bool
 	// If true, modules are bazel-enabled by default, unless present in bazelDisabledModules.
 	modulesDefaultToBazel bool
 
@@ -258,10 +279,16 @@
 	LabelToPythonBinary map[string]string
 	LabelToApexInfo     map[string]cquery.ApexInfo
 	LabelToCcBinary     map[string]cquery.CcUnstrippedInfo
+
+	BazelRequests map[string]bool
 }
 
-func (m MockBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) {
-	panic("unimplemented")
+func (m MockBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
+	key := BuildMockBazelContextRequestKey(label, requestType, cfgKey.arch, cfgKey.osType, cfgKey.apexKey)
+	if m.BazelRequests == nil {
+		m.BazelRequests = make(map[string]bool)
+	}
+	m.BazelRequests[key] = true
 }
 
 func (m MockBazelContext) GetOutputFiles(label string, _ configKey) ([]string, error) {
@@ -272,10 +299,14 @@
 	return result, nil
 }
 
-func (m MockBazelContext) GetCcInfo(label string, _ configKey) (cquery.CcInfo, error) {
+func (m MockBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
 	result, ok := m.LabelToCcInfo[label]
 	if !ok {
-		return cquery.CcInfo{}, fmt.Errorf("no target with label %q in LabelToCcInfo", label)
+		key := BuildMockBazelContextResultKey(label, cfgKey.arch, cfgKey.osType, cfgKey.apexKey)
+		result, ok = m.LabelToCcInfo[key]
+		if !ok {
+			return cquery.CcInfo{}, fmt.Errorf("no target with label %q in LabelToCcInfo", label)
+		}
 	}
 	return result, nil
 }
@@ -308,14 +339,18 @@
 	panic("unimplemented")
 }
 
-func (m MockBazelContext) IsModuleNameAllowed(_ string) bool {
+func (m MockBazelContext) IsModuleNameAllowed(_ string, _ bool) bool {
+	return true
+}
+
+func (m MockBazelContext) IsModuleDclaAllowed(_ string) bool {
 	return true
 }
 
 func (m MockBazelContext) OutputBase() string { return m.OutputBaseDir }
 
-func (m MockBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
-	return []bazel.BuildStatement{}
+func (m MockBazelContext) BuildStatementsToRegister() []*bazel.BuildStatement {
+	return []*bazel.BuildStatement{}
 }
 
 func (m MockBazelContext) AqueryDepsets() []bazel.AqueryDepset {
@@ -324,6 +359,26 @@
 
 var _ BazelContext = MockBazelContext{}
 
+func BuildMockBazelContextRequestKey(label string, request cqueryRequest, arch string, osType OsType, apexKey ApexConfigKey) string {
+	cfgKey := configKey{
+		arch:    arch,
+		osType:  osType,
+		apexKey: apexKey,
+	}
+
+	return strings.Join([]string{label, request.Name(), cfgKey.String()}, "_")
+}
+
+func BuildMockBazelContextResultKey(label string, arch string, osType OsType, apexKey ApexConfigKey) string {
+	cfgKey := configKey{
+		arch:    arch,
+		osType:  osType,
+		apexKey: apexKey,
+	}
+
+	return strings.Join([]string{label, cfgKey.String()}, "_")
+}
+
 func (bazelCtx *mixedBuildBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
 	key := makeCqueryKey(label, requestType, cfgKey)
 	bazelCtx.requestMutex.Lock()
@@ -430,26 +485,31 @@
 	return ""
 }
 
-func (n noopBazelContext) IsModuleNameAllowed(_ string) bool {
+func (n noopBazelContext) IsModuleNameAllowed(_ string, _ bool) bool {
 	return false
 }
 
-func (m noopBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
-	return []bazel.BuildStatement{}
+func (n noopBazelContext) IsModuleDclaAllowed(_ string) bool {
+	return false
+}
+
+func (m noopBazelContext) BuildStatementsToRegister() []*bazel.BuildStatement {
+	return []*bazel.BuildStatement{}
 }
 
 func (m noopBazelContext) AqueryDepsets() []bazel.AqueryDepset {
 	return []bazel.AqueryDepset{}
 }
 
+func addToStringSet(set map[string]bool, items []string) {
+	for _, item := range items {
+		set[item] = true
+	}
+}
+
 func GetBazelEnabledAndDisabledModules(buildMode SoongBuildMode, forceEnabled map[string]struct{}) (map[string]bool, map[string]bool) {
 	disabledModules := map[string]bool{}
 	enabledModules := map[string]bool{}
-	addToStringSet := func(set map[string]bool, items []string) {
-		for _, item := range items {
-			set[item] = true
-		}
-	}
 
 	switch buildMode {
 	case BazelProdMode:
@@ -537,15 +597,24 @@
 	if c.HasDeviceProduct() {
 		targetProduct = c.DeviceProduct()
 	}
-
+	dclaMixedBuildsEnabledList := []string{}
+	if c.BuildMode == BazelProdMode {
+		dclaMixedBuildsEnabledList = allowlists.ProdDclaMixedBuildsEnabledList
+	} else if c.BuildMode == BazelStagingMode {
+		dclaMixedBuildsEnabledList = append(allowlists.ProdDclaMixedBuildsEnabledList,
+			allowlists.StagingDclaMixedBuildsEnabledList...)
+	}
+	dclaEnabledModules := map[string]bool{}
+	addToStringSet(dclaEnabledModules, dclaMixedBuildsEnabledList)
 	return &mixedBuildBazelContext{
-		bazelRunner:           &builtinBazelRunner{},
-		paths:                 &paths,
-		modulesDefaultToBazel: c.BuildMode == BazelDevMode,
-		bazelEnabledModules:   enabledModules,
-		bazelDisabledModules:  disabledModules,
-		targetProduct:         targetProduct,
-		targetBuildVariant:    targetBuildVariant,
+		bazelRunner:             &builtinBazelRunner{},
+		paths:                   &paths,
+		modulesDefaultToBazel:   c.BuildMode == BazelDevMode,
+		bazelEnabledModules:     enabledModules,
+		bazelDisabledModules:    disabledModules,
+		bazelDclaEnabledModules: dclaEnabledModules,
+		targetProduct:           targetProduct,
+		targetBuildVariant:      targetBuildVariant,
 	}, nil
 }
 
@@ -553,16 +622,24 @@
 	return p.metricsDir
 }
 
-func (context *mixedBuildBazelContext) IsModuleNameAllowed(moduleName string) bool {
+func (context *mixedBuildBazelContext) IsModuleNameAllowed(moduleName string, withinApex bool) bool {
 	if context.bazelDisabledModules[moduleName] {
 		return false
 	}
 	if context.bazelEnabledModules[moduleName] {
 		return true
 	}
+	if withinApex && context.IsModuleDclaAllowed(moduleName) {
+		return true
+	}
+
 	return context.modulesDefaultToBazel
 }
 
+func (context *mixedBuildBazelContext) IsModuleDclaAllowed(moduleName string) bool {
+	return context.bazelDclaEnabledModules[moduleName]
+}
+
 func pwdPrefix() string {
 	// Darwin doesn't have /proc
 	if runtime.GOOS != "darwin" {
@@ -696,21 +773,37 @@
 #####################################################
 # This file is generated by soong_build. Do not edit.
 #####################################################
-
 def _config_node_transition_impl(settings, attr):
     if attr.os == "android" and attr.arch == "target":
         target = "{PRODUCT}-{VARIANT}"
     else:
         target = "{PRODUCT}-{VARIANT}_%s_%s" % (attr.os, attr.arch)
-    return {
+    apex_name = ""
+    if attr.within_apex:
+        # //build/bazel/rules/apex:apex_name has to be set to a non_empty value,
+        # otherwise //build/bazel/rules/apex:non_apex will be true and the
+        # "-D__ANDROID_APEX__" compiler flag will be missing. Apex_name is used
+        # in some validation on bazel side which don't really apply in mixed
+        # build because soong will do the work, so we just set it to a fixed
+        # value here.
+        apex_name = "dcla_apex"
+    outputs = {
         "//command_line_option:platforms": "@soong_injection//product_config_platforms/products/{PRODUCT}-{VARIANT}:%s" % target,
+        "@//build/bazel/rules/apex:within_apex": attr.within_apex,
+        "@//build/bazel/rules/apex:min_sdk_version": attr.apex_sdk_version,
+        "@//build/bazel/rules/apex:apex_name": apex_name,
     }
 
+    return outputs
+
 _config_node_transition = transition(
     implementation = _config_node_transition_impl,
     inputs = [],
     outputs = [
         "//command_line_option:platforms",
+        "@//build/bazel/rules/apex:within_apex",
+        "@//build/bazel/rules/apex:min_sdk_version",
+        "@//build/bazel/rules/apex:apex_name",
     ],
 )
 
@@ -720,9 +813,11 @@
 config_node = rule(
     implementation = _passthrough_rule_impl,
     attrs = {
-        "arch" : attr.string(mandatory = True),
-        "os"   : attr.string(mandatory = True),
-        "deps" : attr.label_list(cfg = _config_node_transition, allow_files = True),
+        "arch"    : attr.string(mandatory = True),
+        "os"      : attr.string(mandatory = True),
+        "within_apex" : attr.bool(default = False),
+        "apex_sdk_version" : attr.string(mandatory = True),
+        "deps"    : attr.label_list(cfg = _config_node_transition, allow_files = True),
         "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
     },
 )
@@ -781,6 +876,8 @@
 config_node(name = "%s",
     arch = "%s",
     os = "%s",
+    within_apex = %s,
+    apex_sdk_version = "%s",
     deps = [%s],
     testonly = True, # Unblocks testonly deps.
 )
@@ -807,15 +904,28 @@
 	for _, configString := range sortedConfigs {
 		labels := labelsByConfig[configString]
 		configTokens := strings.Split(configString, "|")
-		if len(configTokens) != 2 {
+		if len(configTokens) < 2 {
 			panic(fmt.Errorf("Unexpected config string format: %s", configString))
 		}
 		archString := configTokens[0]
 		osString := configTokens[1]
+		withinApex := "False"
+		apexSdkVerString := ""
 		targetString := fmt.Sprintf("%s_%s", osString, archString)
+		if len(configTokens) > 2 {
+			targetString += "_" + configTokens[2]
+			if configTokens[2] == withinApexToString(true) {
+				withinApex = "True"
+			}
+		}
+		if len(configTokens) > 3 {
+			targetString += "_" + configTokens[3]
+			apexSdkVerString = configTokens[3]
+		}
 		allLabels = append(allLabels, fmt.Sprintf("\":%s\"", targetString))
 		labelsString := strings.Join(labels, ",\n            ")
-		configNodesSection += fmt.Sprintf(configNodeFormatString, targetString, archString, osString, labelsString)
+		configNodesSection += fmt.Sprintf(configNodeFormatString, targetString, archString, osString, withinApex, apexSdkVerString,
+			labelsString)
 	}
 
 	return []byte(fmt.Sprintf(formatString, configNodesSection, strings.Join(allLabels, ",\n            ")))
@@ -911,6 +1021,7 @@
   # Soong treats filegroups, but it may not be the case with manually-written
   # filegroup BUILD targets.
   buildoptions = build_options(target)
+
   if buildoptions == None:
     # File targets do not have buildoptions. File targets aren't associated with
     #  any specific platform architecture in mixed builds, so use the host.
@@ -927,15 +1038,26 @@
   if not platform_name.startswith("{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}"):
     fail("expected platform name of the form '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_android_<arch>' or '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_linux_<arch>', but was " + str(platforms))
   platform_name = platform_name.removeprefix("{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}").removeprefix("_")
+  config_key = ""
   if not platform_name:
-    return "target|android"
+    config_key = "target|android"
   elif platform_name.startswith("android_"):
-    return platform_name.removeprefix("android_") + "|android"
+    config_key = platform_name.removeprefix("android_") + "|android"
   elif platform_name.startswith("linux_"):
-    return platform_name.removeprefix("linux_") + "|linux"
+    config_key = platform_name.removeprefix("linux_") + "|linux"
   else:
     fail("expected platform name of the form '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_android_<arch>' or '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_linux_<arch>', but was " + str(platforms))
 
+  within_apex = buildoptions.get("//build/bazel/rules/apex:within_apex")
+  apex_sdk_version = buildoptions.get("//build/bazel/rules/apex:min_sdk_version")
+
+  if within_apex:
+    config_key += "|within_apex"
+  if apex_sdk_version != None and len(apex_sdk_version) > 0:
+    config_key += "|" + apex_sdk_version
+
+  return config_key
+
 def format(target):
   id_string = str(target.label) + "|" + get_arch(target)
 
@@ -1044,8 +1166,12 @@
 		return err
 	}
 
-	cqueryCommandWithFlag := context.createBazelCommand(config, context.paths, bazel.CqueryBuildRootRunName, cqueryCmd,
-		"--output=starlark", "--starlark:file="+absolutePath(cqueryFileRelpath))
+	extraFlags := []string{"--output=starlark", "--starlark:file=" + absolutePath(cqueryFileRelpath)}
+	if Bool(config.productVariables.ClangCoverage) {
+		extraFlags = append(extraFlags, "--collect_code_coverage")
+	}
+
+	cqueryCommandWithFlag := context.createBazelCommand(config, context.paths, bazel.CqueryBuildRootRunName, cqueryCmd, extraFlags...)
 	cqueryOutput, cqueryErrorMessage, cqueryErr := context.issueBazelCommand(cqueryCommandWithFlag, eventHandler)
 	if cqueryErr != nil {
 		return cqueryErr
@@ -1128,7 +1254,7 @@
 	return err
 }
 
-func (context *mixedBuildBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
+func (context *mixedBuildBazelContext) BuildStatementsToRegister() []*bazel.BuildStatement {
 	return context.buildStatements
 }
 
@@ -1196,6 +1322,11 @@
 	executionRoot := path.Join(ctx.Config().BazelContext.OutputBase(), "execroot", "__main__")
 	bazelOutDir := path.Join(executionRoot, "bazel-out")
 	for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
+		// nil build statements are a valid case where we do not create an action because it is
+		// unnecessary or handled by other processing
+		if buildStatement == nil {
+			continue
+		}
 		if len(buildStatement.Command) > 0 {
 			rule := NewRuleBuilder(pctx, ctx)
 			createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx)
@@ -1240,7 +1371,7 @@
 }
 
 // Register bazel-owned build statements (obtained from the aquery invocation).
-func createCommand(cmd *RuleBuilderCommand, buildStatement bazel.BuildStatement, executionRoot string, bazelOutDir string, ctx BuilderContext) {
+func createCommand(cmd *RuleBuilderCommand, buildStatement *bazel.BuildStatement, executionRoot string, bazelOutDir string, ctx BuilderContext) {
 	// executionRoot is the action cwd.
 	cmd.Text(fmt.Sprintf("cd '%s' &&", executionRoot))
 
@@ -1320,7 +1451,16 @@
 		// Use host OS, which is currently hardcoded to be linux.
 		osName = "linux"
 	}
-	return arch + "|" + osName
+	keyString := arch + "|" + osName
+	if key.configKey.apexKey.WithinApex {
+		keyString += "|" + withinApexToString(key.configKey.apexKey.WithinApex)
+	}
+
+	if len(key.configKey.apexKey.ApexSdkVersion) > 0 {
+		keyString += "|" + key.configKey.apexKey.ApexSdkVersion
+	}
+
+	return keyString
 }
 
 func GetConfigKey(ctx BaseModuleContext) configKey {
@@ -1331,6 +1471,19 @@
 	}
 }
 
+func GetConfigKeyApexVariant(ctx BaseModuleContext, apexKey *ApexConfigKey) configKey {
+	configKey := GetConfigKey(ctx)
+
+	if apexKey != nil {
+		configKey.apexKey = ApexConfigKey{
+			WithinApex:     apexKey.WithinApex,
+			ApexSdkVersion: apexKey.ApexSdkVersion,
+		}
+	}
+
+	return configKey
+}
+
 func bazelDepsetName(contentHash string) string {
 	return fmt.Sprintf("bazel_depset_%s", contentHash)
 }
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index 4a4ecb5..c67d7fb 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -24,20 +24,37 @@
 }
 
 func TestRequestResultsAfterInvokeBazel(t *testing.T) {
-	label := "@//foo:bar"
-	cfg := configKey{"arm64_armv8-a", Android}
+	label_foo := "@//foo:foo"
+	label_bar := "@//foo:bar"
+	apexKey := ApexConfigKey{
+		WithinApex:     true,
+		ApexSdkVersion: "29",
+	}
+	cfg_foo := configKey{"arm64_armv8-a", Android, apexKey}
+	cfg_bar := configKey{arch: "arm64_armv8-a", osType: Android}
+	cmd_results := []string{
+		`@//foo:foo|arm64_armv8-a|android|within_apex|29>>out/foo/foo.txt`,
+		`@//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`,
+	}
 	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
-		bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: `@//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`,
+		bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: strings.Join(cmd_results, "\n"),
 	})
-	bazelContext.QueueBazelRequest(label, cquery.GetOutputFiles, cfg)
+
+	bazelContext.QueueBazelRequest(label_foo, cquery.GetOutputFiles, cfg_foo)
+	bazelContext.QueueBazelRequest(label_bar, cquery.GetOutputFiles, cfg_bar)
 	err := bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
 	if err != nil {
 		t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
 	}
-	g, err := bazelContext.GetOutputFiles(label, cfg)
+	verifyCqueryResult(t, bazelContext, label_foo, cfg_foo, "out/foo/foo.txt")
+	verifyCqueryResult(t, bazelContext, label_bar, cfg_bar, "out/foo/bar.txt")
+}
+
+func verifyCqueryResult(t *testing.T, ctx *mixedBuildBazelContext, label string, cfg configKey, result string) {
+	g, err := ctx.GetOutputFiles(label, cfg)
 	if err != nil {
 		t.Errorf("Expected cquery results after running InvokeBazel(), but got err %v", err)
-	} else if w := []string{"out/foo/bar.txt"}; !reflect.DeepEqual(w, g) {
+	} else if w := []string{result}; !reflect.DeepEqual(w, g) {
 		t.Errorf("Expected output %s, got %s", w, g)
 	}
 }
@@ -178,14 +195,18 @@
 func TestBazelRequestsSorted(t *testing.T) {
 	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
 
-	bazelContext.QueueBazelRequest("zzz", cquery.GetOutputFiles, configKey{"arm64_armv8-a", Android})
-	bazelContext.QueueBazelRequest("ccc", cquery.GetApexInfo, configKey{"arm64_armv8-a", Android})
-	bazelContext.QueueBazelRequest("duplicate", cquery.GetOutputFiles, configKey{"arm64_armv8-a", Android})
-	bazelContext.QueueBazelRequest("duplicate", cquery.GetOutputFiles, configKey{"arm64_armv8-a", Android})
-	bazelContext.QueueBazelRequest("xxx", cquery.GetOutputFiles, configKey{"arm64_armv8-a", Linux})
-	bazelContext.QueueBazelRequest("aaa", cquery.GetOutputFiles, configKey{"arm64_armv8-a", Android})
-	bazelContext.QueueBazelRequest("aaa", cquery.GetOutputFiles, configKey{"otherarch", Android})
-	bazelContext.QueueBazelRequest("bbb", cquery.GetOutputFiles, configKey{"otherarch", Android})
+	cfgKeyArm64Android := configKey{arch: "arm64_armv8-a", osType: Android}
+	cfgKeyArm64Linux := configKey{arch: "arm64_armv8-a", osType: Linux}
+	cfgKeyOtherAndroid := configKey{arch: "otherarch", osType: Android}
+
+	bazelContext.QueueBazelRequest("zzz", cquery.GetOutputFiles, cfgKeyArm64Android)
+	bazelContext.QueueBazelRequest("ccc", cquery.GetApexInfo, cfgKeyArm64Android)
+	bazelContext.QueueBazelRequest("duplicate", cquery.GetOutputFiles, cfgKeyArm64Android)
+	bazelContext.QueueBazelRequest("duplicate", cquery.GetOutputFiles, cfgKeyArm64Android)
+	bazelContext.QueueBazelRequest("xxx", cquery.GetOutputFiles, cfgKeyArm64Linux)
+	bazelContext.QueueBazelRequest("aaa", cquery.GetOutputFiles, cfgKeyArm64Android)
+	bazelContext.QueueBazelRequest("aaa", cquery.GetOutputFiles, cfgKeyOtherAndroid)
+	bazelContext.QueueBazelRequest("bbb", cquery.GetOutputFiles, cfgKeyOtherAndroid)
 
 	if len(bazelContext.requests) != 7 {
 		t.Error("Expected 7 request elements, but got", len(bazelContext.requests))
@@ -201,6 +222,52 @@
 	}
 }
 
+func TestIsModuleNameAllowed(t *testing.T) {
+	libDisabled := "lib_disabled"
+	libEnabled := "lib_enabled"
+	libDclaWithinApex := "lib_dcla_within_apex"
+	libDclaNonApex := "lib_dcla_non_apex"
+	libNotConverted := "lib_not_converted"
+
+	disabledModules := map[string]bool{
+		libDisabled: true,
+	}
+	enabledModules := map[string]bool{
+		libEnabled: true,
+	}
+	dclaEnabledModules := map[string]bool{
+		libDclaWithinApex: true,
+		libDclaNonApex:    true,
+	}
+
+	bazelContext := &mixedBuildBazelContext{
+		modulesDefaultToBazel:   false,
+		bazelEnabledModules:     enabledModules,
+		bazelDisabledModules:    disabledModules,
+		bazelDclaEnabledModules: dclaEnabledModules,
+	}
+
+	if bazelContext.IsModuleNameAllowed(libDisabled, true) {
+		t.Fatalf("%s shouldn't be allowed for mixed build", libDisabled)
+	}
+
+	if !bazelContext.IsModuleNameAllowed(libEnabled, true) {
+		t.Fatalf("%s should be allowed for mixed build", libEnabled)
+	}
+
+	if !bazelContext.IsModuleNameAllowed(libDclaWithinApex, true) {
+		t.Fatalf("%s should be allowed for mixed build", libDclaWithinApex)
+	}
+
+	if bazelContext.IsModuleNameAllowed(libDclaNonApex, false) {
+		t.Fatalf("%s shouldn't be allowed for mixed build", libDclaNonApex)
+	}
+
+	if bazelContext.IsModuleNameAllowed(libNotConverted, true) {
+		t.Fatalf("%s shouldn't be allowed for mixed build", libNotConverted)
+	}
+}
+
 func verifyExtraFlags(t *testing.T, config Config, expected string) string {
 	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
 
diff --git a/android/filegroup.go b/android/filegroup.go
index d21d146..7d929bc 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -232,7 +232,7 @@
 	bazelCtx.QueueBazelRequest(
 		fg.GetBazelLabel(ctx, fg),
 		cquery.GetOutputFiles,
-		configKey{Common.String(), CommonOS})
+		configKey{arch: Common.String(), osType: CommonOS})
 }
 
 func (fg *fileGroup) IsMixedBuildSupported(ctx BaseModuleContext) bool {
@@ -252,7 +252,7 @@
 		relativeRoot = filepath.Join(relativeRoot, *fg.properties.Path)
 	}
 
-	filePaths, err := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), configKey{Common.String(), CommonOS})
+	filePaths, err := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), configKey{arch: Common.String(), osType: CommonOS})
 	if err != nil {
 		ctx.ModuleErrorf(err.Error())
 		return
diff --git a/android/module.go b/android/module.go
index 681f724..97c9706 100644
--- a/android/module.go
+++ b/android/module.go
@@ -502,6 +502,7 @@
 	InstallInRoot() bool
 	InstallInVendor() bool
 	InstallForceOS() (*OsType, *ArchType)
+	PartitionTag(DeviceConfig) string
 	HideFromMake()
 	IsHideFromMake() bool
 	IsSkipInstall() bool
diff --git a/apex/apex.go b/apex/apex.go
index 8a8b19d..d7d76d1 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -111,6 +111,18 @@
 
 	Multilib apexMultilibProperties
 
+	// List of runtime resource overlays (RROs) that are embedded inside this APEX.
+	Rros []string
+
+	// List of bootclasspath fragments that are embedded inside this APEX bundle.
+	Bootclasspath_fragments []string
+
+	// List of systemserverclasspath fragments that are embedded inside this APEX bundle.
+	Systemserverclasspath_fragments []string
+
+	// List of java libraries that are embedded inside this APEX bundle.
+	Java_libs []string
+
 	// List of sh binaries that are embedded inside this APEX bundle.
 	Sh_binaries []string
 
@@ -326,21 +338,9 @@
 	// List of prebuilt files that are embedded inside this APEX bundle.
 	Prebuilts []string
 
-	// List of runtime resource overlays (RROs) that are embedded inside this APEX.
-	Rros []string
-
 	// List of BPF programs inside this APEX bundle.
 	Bpfs []string
 
-	// List of bootclasspath fragments that are embedded inside this APEX bundle.
-	Bootclasspath_fragments []string
-
-	// List of systemserverclasspath fragments that are embedded inside this APEX bundle.
-	Systemserverclasspath_fragments []string
-
-	// List of java libraries that are embedded inside this APEX bundle.
-	Java_libs []string
-
 	// Names of modules to be overridden. Listed modules can only be other binaries (in Make or
 	// Soong). This does not completely prevent installation of the overridden binaries, but if
 	// both binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will
@@ -514,6 +514,7 @@
 	// buildFile is put in the installDir inside the APEX.
 	builtFile  android.Path
 	installDir string
+	partition  string
 	customStem string
 	symlinks   []string // additional symlinks
 
@@ -553,6 +554,7 @@
 	}
 	if module != nil {
 		ret.moduleDir = ctx.OtherModuleDir(module)
+		ret.partition = module.PartitionTag(ctx.DeviceConfig())
 		ret.requiredModuleNames = module.RequiredModuleNames()
 		ret.targetRequiredModuleNames = module.TargetRequiredModuleNames()
 		ret.hostRequiredModuleNames = module.HostRequiredModuleNames()
@@ -845,6 +847,10 @@
 
 	// Common-arch dependencies come next
 	commonVariation := ctx.Config().AndroidCommonTarget.Variations()
+	ctx.AddFarVariationDependencies(commonVariation, rroTag, a.properties.Rros...)
+	ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.properties.Bootclasspath_fragments...)
+	ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.properties.Systemserverclasspath_fragments...)
+	ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
 	ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
 	ctx.AddFarVariationDependencies(commonVariation, compatConfigTag, a.properties.Compat_configs...)
 }
@@ -858,10 +864,6 @@
 	commonVariation := ctx.Config().AndroidCommonTarget.Variations()
 	ctx.AddFarVariationDependencies(commonVariation, androidAppTag, a.overridableProperties.Apps...)
 	ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.overridableProperties.Bpfs...)
-	ctx.AddFarVariationDependencies(commonVariation, rroTag, a.overridableProperties.Rros...)
-	ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.overridableProperties.Bootclasspath_fragments...)
-	ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.overridableProperties.Systemserverclasspath_fragments...)
-	ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.overridableProperties.Java_libs...)
 	if prebuilts := a.overridableProperties.Prebuilts; len(prebuilts) > 0 {
 		// For prebuilt_etc, use the first variant (64 on 64/32bit device, 32 on 32bit device)
 		// regardless of the TARGET_PREFER_* setting. See b/144532908
@@ -3107,9 +3109,9 @@
 
 // Collect information for opening IDE project files in java/jdeps.go.
 func (a *apexBundle) IDEInfo(dpInfo *android.IdeInfo) {
-	dpInfo.Deps = append(dpInfo.Deps, a.overridableProperties.Java_libs...)
-	dpInfo.Deps = append(dpInfo.Deps, a.overridableProperties.Bootclasspath_fragments...)
-	dpInfo.Deps = append(dpInfo.Deps, a.overridableProperties.Systemserverclasspath_fragments...)
+	dpInfo.Deps = append(dpInfo.Deps, a.properties.Java_libs...)
+	dpInfo.Deps = append(dpInfo.Deps, a.properties.Bootclasspath_fragments...)
+	dpInfo.Deps = append(dpInfo.Deps, a.properties.Systemserverclasspath_fragments...)
 	dpInfo.Paths = append(dpInfo.Paths, a.modulePaths...)
 }
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index eec24b0..b7febe1 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -6294,9 +6294,6 @@
 			apps: ["app"],
 			bpfs: ["bpf"],
 			prebuilts: ["myetc"],
-			bootclasspath_fragments: ["mybootclasspath_fragment"],
-			systemserverclasspath_fragments: ["mysystemserverclasspath_fragment"],
-			java_libs: ["myjava_library"],
 			overrides: ["oldapex"],
 			updatable: false,
 		}
@@ -6307,9 +6304,6 @@
 			apps: ["override_app"],
 			bpfs: ["overrideBpf"],
 			prebuilts: ["override_myetc"],
-			bootclasspath_fragments: ["override_bootclasspath_fragment"],
-			systemserverclasspath_fragments: ["override_systemserverclasspath_fragment"],
-			java_libs: ["override_java_library"],
 			overrides: ["unknownapex"],
 			logging_parent: "com.foo.bar",
 			package_name: "test.overridden.package",
@@ -6368,78 +6362,6 @@
 			name: "override_myetc",
 			src: "override_myprebuilt",
 		}
-
-		java_library {
-			name: "bcplib",
-			srcs: ["a.java"],
-			compile_dex: true,
-			apex_available: ["myapex"],
-			permitted_packages: ["bcp.lib"],
-		}
-
-		bootclasspath_fragment {
-			name: "mybootclasspath_fragment",
-			contents: ["bcplib"],
-			apex_available: ["myapex"],
-			hidden_api: {
-				split_packages: ["*"],
-			},
-		}
-
-		java_library {
-			name: "override_bcplib",
-			srcs: ["a.java"],
-			compile_dex: true,
-			apex_available: ["myapex"],
-			permitted_packages: ["override.bcp.lib"],
-		}
-
-		bootclasspath_fragment {
-			name: "override_bootclasspath_fragment",
-			contents: ["override_bcplib"],
-			apex_available: ["myapex"],
-			hidden_api: {
-				split_packages: ["*"],
-			},
-		}
-
-		java_library {
-			name: "systemserverlib",
-			srcs: ["a.java"],
-			apex_available: ["myapex"],
-		}
-
-		systemserverclasspath_fragment {
-			name: "mysystemserverclasspath_fragment",
-			standalone_contents: ["systemserverlib"],
-			apex_available: ["myapex"],
-		}
-
-		java_library {
-			name: "override_systemserverlib",
-			srcs: ["a.java"],
-			apex_available: ["myapex"],
-		}
-
-		systemserverclasspath_fragment {
-			name: "override_systemserverclasspath_fragment",
-			standalone_contents: ["override_systemserverlib"],
-			apex_available: ["myapex"],
-		}
-
-		java_library {
-			name: "myjava_library",
-			srcs: ["a.java"],
-			compile_dex: true,
-			apex_available: ["myapex"],
-		}
-
-		java_library {
-			name: "override_java_library",
-			srcs: ["a.java"],
-			compile_dex: true,
-			apex_available: ["myapex"],
-		}
 	`, withManifestPackageNameOverrides([]string{"myapex:com.android.myapex"}))
 
 	originalVariant := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(android.OverridableModule)
@@ -6474,13 +6396,6 @@
 		t.Errorf("override_myapex should have logging parent (com.foo.bar), but was %q.", apexBundle.overridableProperties.Logging_parent)
 	}
 
-	android.AssertArrayString(t, "Bootclasspath_fragments does not match",
-		[]string{"override_bootclasspath_fragment"}, apexBundle.overridableProperties.Bootclasspath_fragments)
-	android.AssertArrayString(t, "Systemserverclasspath_fragments does not match",
-		[]string{"override_systemserverclasspath_fragment"}, apexBundle.overridableProperties.Systemserverclasspath_fragments)
-	android.AssertArrayString(t, "Java_libs does not match",
-		[]string{"override_java_library"}, apexBundle.overridableProperties.Java_libs)
-
 	optFlags := apexRule.Args["opt_flags"]
 	ensureContains(t, optFlags, "--override_apk_package_name test.overridden.package")
 	ensureContains(t, optFlags, "--pubkey testkey2.avbpubkey")
@@ -6495,18 +6410,12 @@
 	ensureContains(t, androidMk, "LOCAL_MODULE := override_app.override_myapex")
 	ensureContains(t, androidMk, "LOCAL_MODULE := overrideBpf.o.override_myapex")
 	ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.override_myapex")
-	ensureContains(t, androidMk, "LOCAL_MODULE := override_bcplib.override_myapex")
-	ensureContains(t, androidMk, "LOCAL_MODULE := override_systemserverlib.override_myapex")
-	ensureContains(t, androidMk, "LOCAL_MODULE := override_java_library.override_myapex")
 	ensureContains(t, androidMk, "LOCAL_MODULE_STEM := override_myapex.apex")
 	ensureContains(t, androidMk, "LOCAL_OVERRIDES_MODULES := unknownapex myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := app.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := bpf.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := override_app.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex")
-	ensureNotContains(t, androidMk, "LOCAL_MODULE := override_bcplib.myapex")
-	ensureNotContains(t, androidMk, "LOCAL_MODULE := override_systemserverlib.myapex")
-	ensureNotContains(t, androidMk, "LOCAL_MODULE := override_java_library.pb.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.apex")
 }
 
@@ -7122,7 +7031,10 @@
 		cc_library {
 			name: "mylib",
 			srcs: ["mylib.cpp"],
-			shared_libs: ["myotherlib"],
+			shared_libs: [
+				"myotherlib",
+				"myotherlib_ext",
+			],
 			system_shared_libs: [],
 			stl: "none",
 			apex_available: [
@@ -7146,6 +7058,20 @@
 			min_sdk_version: "current",
 		}
 
+		cc_library {
+			name: "myotherlib_ext",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			system_ext_specific: true,
+			stl: "none",
+			apex_available: [
+				"myapex",
+				"myapex.updatable",
+				"//apex_available:platform",
+			],
+			min_sdk_version: "current",
+		}
+
 		java_library {
 			name: "myjar",
 			srcs: ["foo/bar/MyClass.java"],
@@ -7186,12 +7112,15 @@
 		t.Errorf("%q is not found", file)
 	}
 
-	ensureSymlinkExists := func(t *testing.T, files []fileInApex, file string) {
+	ensureSymlinkExists := func(t *testing.T, files []fileInApex, file string, target string) {
 		for _, f := range files {
 			if f.path == file {
 				if !f.isLink {
 					t.Errorf("%q is not a symlink", file)
 				}
+				if f.src != target {
+					t.Errorf("expected symlink target to be %q, got %q", target, f.src)
+				}
 				return
 			}
 		}
@@ -7205,23 +7134,27 @@
 	ensureRealfileExists(t, files, "javalib/myjar.jar")
 	ensureRealfileExists(t, files, "lib64/mylib.so")
 	ensureRealfileExists(t, files, "lib64/myotherlib.so")
+	ensureRealfileExists(t, files, "lib64/myotherlib_ext.so")
 
 	files = getFiles(t, ctx, "myapex.updatable", "android_common_myapex.updatable_image")
 	ensureRealfileExists(t, files, "javalib/myjar.jar")
 	ensureRealfileExists(t, files, "lib64/mylib.so")
 	ensureRealfileExists(t, files, "lib64/myotherlib.so")
+	ensureRealfileExists(t, files, "lib64/myotherlib_ext.so")
 
 	// For bundled build, symlink to the system for the non-updatable APEXes only
 	ctx = testApex(t, bp)
 	files = getFiles(t, ctx, "myapex", "android_common_myapex_image")
 	ensureRealfileExists(t, files, "javalib/myjar.jar")
 	ensureRealfileExists(t, files, "lib64/mylib.so")
-	ensureSymlinkExists(t, files, "lib64/myotherlib.so") // this is symlink
+	ensureSymlinkExists(t, files, "lib64/myotherlib.so", "/system/lib64/myotherlib.so")             // this is symlink
+	ensureSymlinkExists(t, files, "lib64/myotherlib_ext.so", "/system_ext/lib64/myotherlib_ext.so") // this is symlink
 
 	files = getFiles(t, ctx, "myapex.updatable", "android_common_myapex.updatable_image")
 	ensureRealfileExists(t, files, "javalib/myjar.jar")
 	ensureRealfileExists(t, files, "lib64/mylib.so")
-	ensureRealfileExists(t, files, "lib64/myotherlib.so") // this is a real file
+	ensureRealfileExists(t, files, "lib64/myotherlib.so")     // this is a real file
+	ensureRealfileExists(t, files, "lib64/myotherlib_ext.so") // this is a real file
 }
 
 func TestSymlinksFromApexToSystemRequiredModuleNames(t *testing.T) {
diff --git a/apex/builder.go b/apex/builder.go
index 93ff80d..a62f63c 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -476,8 +476,7 @@
 		// Copy the built file to the directory. But if the symlink optimization is turned
 		// on, place a symlink to the corresponding file in /system partition instead.
 		if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() {
-			// TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
-			pathOnDevice := filepath.Join("/system", fi.path())
+			pathOnDevice := filepath.Join("/", fi.partition, fi.path())
 			copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath)
 		} else {
 			// Copy the file into APEX
@@ -941,8 +940,7 @@
 			dir := filepath.Join("apex", bundleName, fi.installDir)
 			installDir := android.PathForModuleInstall(ctx, dir)
 			if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() {
-				// TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
-				pathOnDevice := filepath.Join("/system", fi.path())
+				pathOnDevice := filepath.Join("/", fi.partition, fi.path())
 				installedSymlinks = append(installedSymlinks,
 					ctx.InstallAbsoluteSymlink(installDir, fi.stem(), pathOnDevice))
 			} else {
diff --git a/bazel/aquery.go b/bazel/aquery.go
index 6af472a..4d39e8f 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -22,6 +22,7 @@
 	"reflect"
 	"sort"
 	"strings"
+	"sync"
 
 	analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
 
@@ -105,7 +106,7 @@
 	Depfile      *string
 	OutputPaths  []string
 	SymlinkPaths []string
-	Env          []KeyValuePair
+	Env          []*analysis_v2_proto.KeyValuePair
 	Mnemonic     string
 
 	// Inputs of this build statement, either as unexpanded depsets or expanded
@@ -130,7 +131,7 @@
 
 	// depsetIdToArtifactIdsCache is a memoization of depset flattening, because flattening
 	// may be an expensive operation.
-	depsetHashToArtifactPathsCache map[string][]string
+	depsetHashToArtifactPathsCache sync.Map
 	// Maps artifact ids to fully expanded paths.
 	artifactIdToPath map[artifactId]string
 }
@@ -143,8 +144,11 @@
 	"%python_binary%": "python3",
 }
 
-// The file name of py3wrapper.sh, which is used by py_binary targets.
-const py3wrapperFileName = "/py3wrapper.sh"
+const (
+	middlemanMnemonic = "Middleman"
+	// The file name of py3wrapper.sh, which is used by py_binary targets.
+	py3wrapperFileName = "/py3wrapper.sh"
+)
 
 func indexBy[K comparable, V any](values []V, keyFn func(v V) K) map[K]V {
 	m := map[K]V{}
@@ -154,18 +158,18 @@
 	return m
 }
 
-func newAqueryHandler(aqueryResult actionGraphContainer) (*aqueryArtifactHandler, error) {
-	pathFragments := indexBy(aqueryResult.PathFragments, func(pf pathFragment) pathFragmentId {
-		return pf.Id
+func newAqueryHandler(aqueryResult *analysis_v2_proto.ActionGraphContainer) (*aqueryArtifactHandler, error) {
+	pathFragments := indexBy(aqueryResult.PathFragments, func(pf *analysis_v2_proto.PathFragment) pathFragmentId {
+		return pathFragmentId(pf.Id)
 	})
 
-	artifactIdToPath := map[artifactId]string{}
+	artifactIdToPath := make(map[artifactId]string, len(aqueryResult.Artifacts))
 	for _, artifact := range aqueryResult.Artifacts {
-		artifactPath, err := expandPathFragment(artifact.PathFragmentId, pathFragments)
+		artifactPath, err := expandPathFragment(pathFragmentId(artifact.PathFragmentId), pathFragments)
 		if err != nil {
 			return nil, err
 		}
-		artifactIdToPath[artifact.Id] = artifactPath
+		artifactIdToPath[artifactId(artifact.Id)] = artifactPath
 	}
 
 	// Map middleman artifact ContentHash to input artifact depset ID.
@@ -173,23 +177,23 @@
 	// if we find a middleman action which has inputs [foo, bar], and output [baz_middleman], then,
 	// for each other action which has input [baz_middleman], we add [foo, bar] to the inputs for
 	// that action instead.
-	middlemanIdToDepsetIds := map[artifactId][]depsetId{}
+	middlemanIdToDepsetIds := map[artifactId][]uint32{}
 	for _, actionEntry := range aqueryResult.Actions {
-		if actionEntry.Mnemonic == "Middleman" {
+		if actionEntry.Mnemonic == middlemanMnemonic {
 			for _, outputId := range actionEntry.OutputIds {
-				middlemanIdToDepsetIds[outputId] = actionEntry.InputDepSetIds
+				middlemanIdToDepsetIds[artifactId(outputId)] = actionEntry.InputDepSetIds
 			}
 		}
 	}
 
-	depsetIdToDepset := indexBy(aqueryResult.DepSetOfFiles, func(d depSetOfFiles) depsetId {
-		return d.Id
+	depsetIdToDepset := indexBy(aqueryResult.DepSetOfFiles, func(d *analysis_v2_proto.DepSetOfFiles) depsetId {
+		return depsetId(d.Id)
 	})
 
 	aqueryHandler := aqueryArtifactHandler{
 		depsetIdToAqueryDepset:         map[depsetId]AqueryDepset{},
 		depsetHashToAqueryDepset:       map[string]AqueryDepset{},
-		depsetHashToArtifactPathsCache: map[string][]string{},
+		depsetHashToArtifactPathsCache: sync.Map{},
 		emptyDepsetIds:                 make(map[depsetId]struct{}, 0),
 		artifactIdToPath:               artifactIdToPath,
 	}
@@ -207,20 +211,21 @@
 
 // Ensures that the handler's depsetIdToAqueryDepset map contains an entry for the given
 // depset.
-func (a *aqueryArtifactHandler) populateDepsetMaps(depset depSetOfFiles, middlemanIdToDepsetIds map[artifactId][]depsetId, depsetIdToDepset map[depsetId]depSetOfFiles) (*AqueryDepset, error) {
-	if aqueryDepset, containsDepset := a.depsetIdToAqueryDepset[depset.Id]; containsDepset {
+func (a *aqueryArtifactHandler) populateDepsetMaps(depset *analysis_v2_proto.DepSetOfFiles, middlemanIdToDepsetIds map[artifactId][]uint32, depsetIdToDepset map[depsetId]*analysis_v2_proto.DepSetOfFiles) (*AqueryDepset, error) {
+	if aqueryDepset, containsDepset := a.depsetIdToAqueryDepset[depsetId(depset.Id)]; containsDepset {
 		return &aqueryDepset, nil
 	}
 	transitiveDepsetIds := depset.TransitiveDepSetIds
-	var directArtifactPaths []string
-	for _, artifactId := range depset.DirectArtifactIds {
-		path, pathExists := a.artifactIdToPath[artifactId]
+	directArtifactPaths := make([]string, 0, len(depset.DirectArtifactIds))
+	for _, id := range depset.DirectArtifactIds {
+		aId := artifactId(id)
+		path, pathExists := a.artifactIdToPath[aId]
 		if !pathExists {
-			return nil, fmt.Errorf("undefined input artifactId %d", artifactId)
+			return nil, fmt.Errorf("undefined input artifactId %d", aId)
 		}
 		// Filter out any inputs which are universally dropped, and swap middleman
 		// artifacts with their corresponding depsets.
-		if depsetsToUse, isMiddleman := middlemanIdToDepsetIds[artifactId]; isMiddleman {
+		if depsetsToUse, isMiddleman := middlemanIdToDepsetIds[aId]; isMiddleman {
 			// Swap middleman artifacts with their corresponding depsets and drop the middleman artifacts.
 			transitiveDepsetIds = append(transitiveDepsetIds, depsetsToUse...)
 		} else if strings.HasSuffix(path, py3wrapperFileName) ||
@@ -237,8 +242,9 @@
 		}
 	}
 
-	var childDepsetHashes []string
-	for _, childDepsetId := range transitiveDepsetIds {
+	childDepsetHashes := make([]string, 0, len(transitiveDepsetIds))
+	for _, id := range transitiveDepsetIds {
+		childDepsetId := depsetId(id)
 		childDepset, exists := depsetIdToDepset[childDepsetId]
 		if !exists {
 			if _, empty := a.emptyDepsetIds[childDepsetId]; empty {
@@ -256,7 +262,7 @@
 		}
 	}
 	if len(directArtifactPaths) == 0 && len(childDepsetHashes) == 0 {
-		a.emptyDepsetIds[depset.Id] = struct{}{}
+		a.emptyDepsetIds[depsetId(depset.Id)] = struct{}{}
 		return nil, nil
 	}
 	aqueryDepset := AqueryDepset{
@@ -264,7 +270,7 @@
 		DirectArtifacts:        directArtifactPaths,
 		TransitiveDepSetHashes: childDepsetHashes,
 	}
-	a.depsetIdToAqueryDepset[depset.Id] = aqueryDepset
+	a.depsetIdToAqueryDepset[depsetId(depset.Id)] = aqueryDepset
 	a.depsetHashToAqueryDepset[aqueryDepset.ContentHash] = aqueryDepset
 	return &aqueryDepset, nil
 }
@@ -273,10 +279,11 @@
 // input paths contained in these depsets.
 // This is a potentially expensive operation, and should not be invoked except
 // for actions which need specialized input handling.
-func (a *aqueryArtifactHandler) getInputPaths(depsetIds []depsetId) ([]string, error) {
+func (a *aqueryArtifactHandler) getInputPaths(depsetIds []uint32) ([]string, error) {
 	var inputPaths []string
 
-	for _, inputDepSetId := range depsetIds {
+	for _, id := range depsetIds {
+		inputDepSetId := depsetId(id)
 		depset := a.depsetIdToAqueryDepset[inputDepSetId]
 		inputArtifacts, err := a.artifactPathsFromDepsetHash(depset.ContentHash)
 		if err != nil {
@@ -291,8 +298,8 @@
 }
 
 func (a *aqueryArtifactHandler) artifactPathsFromDepsetHash(depsetHash string) ([]string, error) {
-	if result, exists := a.depsetHashToArtifactPathsCache[depsetHash]; exists {
-		return result, nil
+	if result, exists := a.depsetHashToArtifactPathsCache.Load(depsetHash); exists {
+		return result.([]string), nil
 	}
 	if depset, exists := a.depsetHashToAqueryDepset[depsetHash]; exists {
 		result := depset.DirectArtifacts
@@ -303,7 +310,7 @@
 			}
 			result = append(result, childArtifactIds...)
 		}
-		a.depsetHashToArtifactPathsCache[depsetHash] = result
+		a.depsetHashToArtifactPathsCache.Store(depsetHash, result)
 		return result, nil
 	} else {
 		return nil, fmt.Errorf("undefined input depset hash %s", depsetHash)
@@ -315,124 +322,56 @@
 // action graph, as described by the given action graph json proto.
 // BuildStatements are one-to-one with actions in the given action graph, and AqueryDepsets
 // are one-to-one with Bazel's depSetOfFiles objects.
-func AqueryBuildStatements(aqueryJsonProto []byte, eventHandler *metrics.EventHandler) ([]BuildStatement, []AqueryDepset, error) {
+func AqueryBuildStatements(aqueryJsonProto []byte, eventHandler *metrics.EventHandler) ([]*BuildStatement, []AqueryDepset, error) {
 	aqueryProto := &analysis_v2_proto.ActionGraphContainer{}
 	err := proto.Unmarshal(aqueryJsonProto, aqueryProto)
 	if err != nil {
 		return nil, nil, err
 	}
-	aqueryResult := actionGraphContainer{}
-
-	for _, protoArtifact := range aqueryProto.Artifacts {
-		aqueryResult.Artifacts = append(aqueryResult.Artifacts, artifact{artifactId(protoArtifact.Id),
-			pathFragmentId(protoArtifact.PathFragmentId)})
-	}
-
-	for _, protoAction := range aqueryProto.Actions {
-		var environmentVariable []KeyValuePair
-		var inputDepSetIds []depsetId
-		var outputIds []artifactId
-		var substitutions []KeyValuePair
-
-		for _, protoEnvironmentVariable := range protoAction.EnvironmentVariables {
-			environmentVariable = append(environmentVariable, KeyValuePair{
-				protoEnvironmentVariable.Key, protoEnvironmentVariable.Value,
-			})
-		}
-		for _, protoInputDepSetIds := range protoAction.InputDepSetIds {
-			inputDepSetIds = append(inputDepSetIds, depsetId(protoInputDepSetIds))
-		}
-		for _, protoOutputIds := range protoAction.OutputIds {
-			outputIds = append(outputIds, artifactId(protoOutputIds))
-		}
-		for _, protoSubstitutions := range protoAction.Substitutions {
-			substitutions = append(substitutions, KeyValuePair{
-				protoSubstitutions.Key, protoSubstitutions.Value,
-			})
-		}
-
-		aqueryResult.Actions = append(aqueryResult.Actions,
-			action{
-				Arguments:            protoAction.Arguments,
-				EnvironmentVariables: environmentVariable,
-				InputDepSetIds:       inputDepSetIds,
-				Mnemonic:             protoAction.Mnemonic,
-				OutputIds:            outputIds,
-				TemplateContent:      protoAction.TemplateContent,
-				Substitutions:        substitutions,
-				FileContents:         protoAction.FileContents})
-	}
-
-	for _, protoDepSetOfFiles := range aqueryProto.DepSetOfFiles {
-		var directArtifactIds []artifactId
-		var transitiveDepSetIds []depsetId
-
-		for _, protoDirectArtifactIds := range protoDepSetOfFiles.DirectArtifactIds {
-			directArtifactIds = append(directArtifactIds, artifactId(protoDirectArtifactIds))
-		}
-		for _, protoTransitiveDepSetIds := range protoDepSetOfFiles.TransitiveDepSetIds {
-			transitiveDepSetIds = append(transitiveDepSetIds, depsetId(protoTransitiveDepSetIds))
-		}
-		aqueryResult.DepSetOfFiles = append(aqueryResult.DepSetOfFiles,
-			depSetOfFiles{
-				Id:                  depsetId(protoDepSetOfFiles.Id),
-				DirectArtifactIds:   directArtifactIds,
-				TransitiveDepSetIds: transitiveDepSetIds})
-
-	}
-
-	for _, protoPathFragments := range aqueryProto.PathFragments {
-		aqueryResult.PathFragments = append(aqueryResult.PathFragments,
-			pathFragment{
-				Id:       pathFragmentId(protoPathFragments.Id),
-				Label:    protoPathFragments.Label,
-				ParentId: pathFragmentId(protoPathFragments.ParentId)})
-
-	}
 
 	var aqueryHandler *aqueryArtifactHandler
 	{
 		eventHandler.Begin("init_handler")
 		defer eventHandler.End("init_handler")
-		aqueryHandler, err = newAqueryHandler(aqueryResult)
+		aqueryHandler, err = newAqueryHandler(aqueryProto)
 		if err != nil {
 			return nil, nil, err
 		}
 	}
 
-	var buildStatements []BuildStatement
+	// allocate both length and capacity so each goroutine can write to an index independently without
+	// any need for synchronization for slice access.
+	buildStatements := make([]*BuildStatement, len(aqueryProto.Actions))
 	{
 		eventHandler.Begin("build_statements")
 		defer eventHandler.End("build_statements")
-		for _, actionEntry := range aqueryResult.Actions {
-			if shouldSkipAction(actionEntry) {
-				continue
-			}
+		wg := sync.WaitGroup{}
+		var errOnce sync.Once
 
-			var buildStatement BuildStatement
-			if actionEntry.isSymlinkAction() {
-				buildStatement, err = aqueryHandler.symlinkActionBuildStatement(actionEntry)
-			} else if actionEntry.isTemplateExpandAction() && len(actionEntry.Arguments) < 1 {
-				buildStatement, err = aqueryHandler.templateExpandActionBuildStatement(actionEntry)
-			} else if actionEntry.isFileWriteAction() {
-				buildStatement, err = aqueryHandler.fileWriteActionBuildStatement(actionEntry)
-			} else if actionEntry.isSymlinkTreeAction() {
-				buildStatement, err = aqueryHandler.symlinkTreeActionBuildStatement(actionEntry)
-			} else if len(actionEntry.Arguments) < 1 {
-				err = fmt.Errorf("received action with no command: [%s]", actionEntry.Mnemonic)
-			} else {
-				buildStatement, err = aqueryHandler.normalActionBuildStatement(actionEntry)
-			}
-
-			if err != nil {
-				return nil, nil, err
-			}
-			buildStatements = append(buildStatements, buildStatement)
+		for i, actionEntry := range aqueryProto.Actions {
+			wg.Add(1)
+			go func(i int, actionEntry *analysis_v2_proto.Action) {
+				buildStatement, aErr := aqueryHandler.actionToBuildStatement(actionEntry)
+				if aErr != nil {
+					errOnce.Do(func() {
+						err = aErr
+					})
+				} else {
+					// set build statement at an index rather than appending such that each goroutine does not
+					// impact other goroutines
+					buildStatements[i] = buildStatement
+				}
+				wg.Done()
+			}(i, actionEntry)
 		}
+		wg.Wait()
+	}
+	if err != nil {
+		return nil, nil, err
 	}
 
 	depsetsByHash := map[string]AqueryDepset{}
-	var depsets []AqueryDepset
+	depsets := make([]AqueryDepset, 0, len(aqueryHandler.depsetIdToAqueryDepset))
 	{
 		eventHandler.Begin("depsets")
 		defer eventHandler.End("depsets")
@@ -455,7 +394,13 @@
 		// output). Note they are not sorted by their original IDs nor their Bazel ordering,
 		// as Bazel gives nondeterministic ordering / identifiers in aquery responses.
 		sort.Slice(buildStatements, func(i, j int) bool {
-			// For build statements, compare output lists. In Bazel, each output file
+			// Sort all nil statements to the end of the slice
+			if buildStatements[i] == nil {
+				return false
+			} else if buildStatements[j] == nil {
+				return true
+			}
+			//For build statements, compare output lists. In Bazel, each output file
 			// may only have one action which generates it, so this will provide
 			// a deterministic ordering.
 			outputs_i := buildStatements[i].OutputPaths
@@ -493,12 +438,13 @@
 	return fullHash
 }
 
-func (a *aqueryArtifactHandler) depsetContentHashes(inputDepsetIds []depsetId) ([]string, error) {
+func (a *aqueryArtifactHandler) depsetContentHashes(inputDepsetIds []uint32) ([]string, error) {
 	var hashes []string
-	for _, depsetId := range inputDepsetIds {
-		if aqueryDepset, exists := a.depsetIdToAqueryDepset[depsetId]; !exists {
-			if _, empty := a.emptyDepsetIds[depsetId]; !empty {
-				return nil, fmt.Errorf("undefined (not even empty) input depsetId %d", depsetId)
+	for _, id := range inputDepsetIds {
+		dId := depsetId(id)
+		if aqueryDepset, exists := a.depsetIdToAqueryDepset[dId]; !exists {
+			if _, empty := a.emptyDepsetIds[dId]; !empty {
+				return nil, fmt.Errorf("undefined (not even empty) input depsetId %d", dId)
 			}
 		} else {
 			hashes = append(hashes, aqueryDepset.ContentHash)
@@ -507,18 +453,18 @@
 	return hashes, nil
 }
 
-func (a *aqueryArtifactHandler) normalActionBuildStatement(actionEntry action) (BuildStatement, error) {
+func (a *aqueryArtifactHandler) normalActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
 	command := strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " ")
 	inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
 	if err != nil {
-		return BuildStatement{}, err
+		return nil, err
 	}
 	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
 	if err != nil {
-		return BuildStatement{}, err
+		return nil, err
 	}
 
-	buildStatement := BuildStatement{
+	buildStatement := &BuildStatement{
 		Command:           command,
 		Depfile:           depfile,
 		OutputPaths:       outputPaths,
@@ -529,13 +475,13 @@
 	return buildStatement, nil
 }
 
-func (a *aqueryArtifactHandler) templateExpandActionBuildStatement(actionEntry action) (BuildStatement, error) {
+func (a *aqueryArtifactHandler) templateExpandActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
 	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
 	if err != nil {
-		return BuildStatement{}, err
+		return nil, err
 	}
 	if len(outputPaths) != 1 {
-		return BuildStatement{}, fmt.Errorf("Expect 1 output to template expand action, got: output %q", outputPaths)
+		return nil, fmt.Errorf("Expect 1 output to template expand action, got: output %q", outputPaths)
 	}
 	expandedTemplateContent := expandTemplateContent(actionEntry)
 	// The expandedTemplateContent is escaped for being used in double quotes and shell unescape,
@@ -547,10 +493,10 @@
 		escapeCommandlineArgument(expandedTemplateContent), outputPaths[0])
 	inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
 	if err != nil {
-		return BuildStatement{}, err
+		return nil, err
 	}
 
-	buildStatement := BuildStatement{
+	buildStatement := &BuildStatement{
 		Command:           command,
 		Depfile:           depfile,
 		OutputPaths:       outputPaths,
@@ -561,16 +507,16 @@
 	return buildStatement, nil
 }
 
-func (a *aqueryArtifactHandler) fileWriteActionBuildStatement(actionEntry action) (BuildStatement, error) {
+func (a *aqueryArtifactHandler) fileWriteActionBuildStatement(actionEntry *analysis_v2_proto.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 nil, err
 	}
-	return BuildStatement{
+	return &BuildStatement{
 		Depfile:           nil,
 		OutputPaths:       outputPaths,
 		Env:               actionEntry.EnvironmentVariables,
@@ -580,20 +526,20 @@
 	}, nil
 }
 
-func (a *aqueryArtifactHandler) symlinkTreeActionBuildStatement(actionEntry action) (BuildStatement, error) {
+func (a *aqueryArtifactHandler) symlinkTreeActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
 	outputPaths, _, err := a.getOutputPaths(actionEntry)
 	if err != nil {
-		return BuildStatement{}, err
+		return nil, err
 	}
 	inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
 	if err != nil {
-		return BuildStatement{}, err
+		return nil, 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)
+		return nil, 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{
+	return &BuildStatement{
 		Depfile:     nil,
 		OutputPaths: outputPaths,
 		Env:         actionEntry.EnvironmentVariables,
@@ -602,18 +548,18 @@
 	}, nil
 }
 
-func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry action) (BuildStatement, error) {
+func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
 	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
 	if err != nil {
-		return BuildStatement{}, err
+		return nil, err
 	}
 
 	inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
 	if err != nil {
-		return BuildStatement{}, err
+		return nil, 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)
+		return nil, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths)
 	}
 	out := outputPaths[0]
 	outDir := proptools.ShellEscapeIncludingSpaces(filepath.Dir(out))
@@ -623,7 +569,7 @@
 	command := fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, in)
 	symlinkPaths := outputPaths[:]
 
-	buildStatement := BuildStatement{
+	buildStatement := &BuildStatement{
 		Command:      command,
 		Depfile:      depfile,
 		OutputPaths:  outputPaths,
@@ -635,9 +581,9 @@
 	return buildStatement, nil
 }
 
-func (a *aqueryArtifactHandler) getOutputPaths(actionEntry action) (outputPaths []string, depfile *string, err error) {
+func (a *aqueryArtifactHandler) getOutputPaths(actionEntry *analysis_v2_proto.Action) (outputPaths []string, depfile *string, err error) {
 	for _, outputId := range actionEntry.OutputIds {
-		outputPath, exists := a.artifactIdToPath[outputId]
+		outputPath, exists := a.artifactIdToPath[artifactId(outputId)]
 		if !exists {
 			err = fmt.Errorf("undefined outputId %d", outputId)
 			return
@@ -658,14 +604,15 @@
 }
 
 // expandTemplateContent substitutes the tokens in a template.
-func expandTemplateContent(actionEntry action) string {
-	var replacerString []string
-	for _, pair := range actionEntry.Substitutions {
+func expandTemplateContent(actionEntry *analysis_v2_proto.Action) string {
+	replacerString := make([]string, len(actionEntry.Substitutions)*2)
+	for i, pair := range actionEntry.Substitutions {
 		value := pair.Value
 		if val, ok := templateActionOverriddenTokens[pair.Key]; ok {
 			value = val
 		}
-		replacerString = append(replacerString, pair.Key, value)
+		replacerString[i*2] = pair.Key
+		replacerString[i*2+1] = value
 	}
 	replacer := strings.NewReplacer(replacerString...)
 	return replacer.Replace(actionEntry.TemplateContent)
@@ -685,44 +632,41 @@
 	return commandLineArgumentReplacer.Replace(str)
 }
 
-func (a action) isSymlinkAction() bool {
-	return a.Mnemonic == "Symlink" || a.Mnemonic == "SolibSymlink" || a.Mnemonic == "ExecutableSymlink"
-}
-
-func (a action) isTemplateExpandAction() bool {
-	return a.Mnemonic == "TemplateExpand"
-}
-
-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 {
+func (a *aqueryArtifactHandler) actionToBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
+	switch actionEntry.Mnemonic {
 	// 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
-	}
+	case middlemanMnemonic:
+		return nil, nil
 	// PythonZipper is bogus action returned by aquery, ignore it (b/236198693)
-	if a.Mnemonic == "PythonZipper" {
-		return true
-	}
+	case "PythonZipper":
+		return nil, nil
 	// Skip "Fail" actions, which are placeholder actions designed to always fail.
-	if a.Mnemonic == "Fail" {
-		return true
+	case "Fail":
+		return nil, nil
+	case "BaselineCoverage":
+		return nil, nil
+	case "Symlink", "SolibSymlink", "ExecutableSymlink":
+		return a.symlinkActionBuildStatement(actionEntry)
+	case "TemplateExpand":
+		if len(actionEntry.Arguments) < 1 {
+			return a.templateExpandActionBuildStatement(actionEntry)
+		}
+	case "FileWrite", "SourceSymlinkManifest":
+		return a.fileWriteActionBuildStatement(actionEntry)
+	case "SymlinkTree":
+		return a.symlinkTreeActionBuildStatement(actionEntry)
 	}
-	if a.Mnemonic == "BaselineCoverage" {
-		return true
+
+	if len(actionEntry.Arguments) < 1 {
+		return nil, fmt.Errorf("received action with no command: [%s]", actionEntry.Mnemonic)
 	}
-	return false
+	return a.normalActionBuildStatement(actionEntry)
+
 }
 
-func expandPathFragment(id pathFragmentId, pathFragmentsMap map[pathFragmentId]pathFragment) (string, error) {
+func expandPathFragment(id pathFragmentId, pathFragmentsMap map[pathFragmentId]*analysis_v2_proto.PathFragment) (string, error) {
 	var labels []string
 	currId := id
 	// Only positive IDs are valid for path fragments. An ID of zero indicates a terminal node.
@@ -732,10 +676,11 @@
 			return "", fmt.Errorf("undefined path fragment id %d", currId)
 		}
 		labels = append([]string{currFragment.Label}, labels...)
-		if currId == currFragment.ParentId {
+		parentId := pathFragmentId(currFragment.ParentId)
+		if currId == parentId {
 			return "", fmt.Errorf("fragment cannot refer to itself as parent %#v", currFragment)
 		}
-		currId = currFragment.ParentId
+		currId = parentId
 	}
 	return filepath.Join(labels...), nil
 }
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index c6b139e..19a584f 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -139,17 +139,17 @@
 		return
 	}
 	actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
-	var expectedBuildStatements []BuildStatement
+	var expectedBuildStatements []*BuildStatement
 	for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
 		expectedBuildStatements = append(expectedBuildStatements,
-			BuildStatement{
+			&BuildStatement{
 				Command: fmt.Sprintf(
 					"/bin/bash -c 'source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py %s ../sourceroot/bionic/libc/SYSCALLS.TXT > bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S'",
 					arch, arch),
 				OutputPaths: []string{
 					fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S", arch),
 				},
-				Env: []KeyValuePair{
+				Env: []*analysis_v2_proto.KeyValuePair{
 					{Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"},
 				},
 				Mnemonic: "Genrule",
@@ -487,11 +487,12 @@
 	}
 	actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
 
-	expectedBuildStatements := []BuildStatement{
-		{
-			Command:     "/bin/bash -c 'touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out'",
-			OutputPaths: []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"},
-			Mnemonic:    "Action",
+	expectedBuildStatements := []*BuildStatement{
+		&BuildStatement{
+			Command:      "/bin/bash -c 'touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out'",
+			OutputPaths:  []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"},
+			Mnemonic:     "Action",
+			SymlinkPaths: []string{},
 		},
 	}
 	assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
@@ -544,12 +545,13 @@
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
 	}
-	assertBuildStatements(t, []BuildStatement{
-		{
-			Command:     "",
-			OutputPaths: []string{"foo.runfiles/MANIFEST"},
-			Mnemonic:    "SymlinkTree",
-			InputPaths:  []string{"foo.manifest"},
+	assertBuildStatements(t, []*BuildStatement{
+		&BuildStatement{
+			Command:      "",
+			OutputPaths:  []string{"foo.runfiles/MANIFEST"},
+			Mnemonic:     "SymlinkTree",
+			InputPaths:   []string{"foo.manifest"},
+			SymlinkPaths: []string{},
 		},
 	}, actual)
 }
@@ -613,10 +615,11 @@
 		t.Errorf("dependency ../dep2 expected but not found")
 	}
 
-	expectedBuildStatement := BuildStatement{
-		Command:     "bogus command",
-		OutputPaths: []string{"output"},
-		Mnemonic:    "x",
+	expectedBuildStatement := &BuildStatement{
+		Command:      "bogus command",
+		OutputPaths:  []string{"output"},
+		Mnemonic:     "x",
+		SymlinkPaths: []string{},
 	}
 	buildStatementFound := false
 	for _, actualBuildStatement := range actualBuildStatements {
@@ -689,7 +692,7 @@
 		return
 	}
 
-	expectedBuildStatement := BuildStatement{
+	expectedBuildStatement := &BuildStatement{
 		Command:     "bogus command",
 		OutputPaths: []string{"output"},
 		Mnemonic:    "x",
@@ -754,8 +757,8 @@
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
 	}
-	if expected := 1; len(actualBuildStatements) != expected {
-		t.Fatalf("Expected %d build statements, got %d", expected, len(actualBuildStatements))
+	if expected := 2; len(actualBuildStatements) != expected {
+		t.Fatalf("Expected %d build statements, got %d %#v", expected, len(actualBuildStatements), actualBuildStatements)
 	}
 
 	expectedDepsetFiles := [][]string{
@@ -780,6 +783,11 @@
 	if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
 		t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
 	}
+
+	bs = actualBuildStatements[1]
+	if bs != nil {
+		t.Errorf("Expected nil action for skipped")
+	}
 }
 
 // Returns the contents of given depsets in concatenated post order.
@@ -853,8 +861,8 @@
 		t.Errorf("Unexpected error %q", err)
 	}
 
-	expectedBuildStatements := []BuildStatement{
-		{
+	expectedBuildStatements := []*BuildStatement{
+		&BuildStatement{
 			Command: "mkdir -p one/symlink_subdir && " +
 				"rm -f one/symlink_subdir/symlink && " +
 				"ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink",
@@ -901,8 +909,8 @@
 		t.Errorf("Unexpected error %q", err)
 	}
 
-	expectedBuildStatements := []BuildStatement{
-		{
+	expectedBuildStatements := []*BuildStatement{
+		&BuildStatement{
 			Command: "mkdir -p 'one/symlink subdir' && " +
 				"rm -f 'one/symlink subdir/symlink' && " +
 				"ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'",
@@ -1011,12 +1019,13 @@
 		t.Errorf("Unexpected error %q", err)
 	}
 
-	expectedBuildStatements := []BuildStatement{
-		{
+	expectedBuildStatements := []*BuildStatement{
+		&BuildStatement{
 			Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > template_file && " +
 				"chmod a+x template_file'",
-			OutputPaths: []string{"template_file"},
-			Mnemonic:    "TemplateExpand",
+			OutputPaths:  []string{"template_file"},
+			Mnemonic:     "TemplateExpand",
+			SymlinkPaths: []string{},
 		},
 	}
 	assertBuildStatements(t, expectedBuildStatements, actual)
@@ -1080,11 +1089,12 @@
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
 	}
-	assertBuildStatements(t, []BuildStatement{
-		{
+	assertBuildStatements(t, []*BuildStatement{
+		&BuildStatement{
 			OutputPaths:  []string{"foo.manifest"},
 			Mnemonic:     "FileWrite",
 			FileContents: "file data\n",
+			SymlinkPaths: []string{},
 		},
 	}, actual)
 }
@@ -1117,10 +1127,11 @@
 	if err != nil {
 		t.Errorf("Unexpected error %q", err)
 	}
-	assertBuildStatements(t, []BuildStatement{
-		{
-			OutputPaths: []string{"foo.manifest"},
-			Mnemonic:    "SourceSymlinkManifest",
+	assertBuildStatements(t, []*BuildStatement{
+		&BuildStatement{
+			OutputPaths:  []string{"foo.manifest"},
+			Mnemonic:     "SourceSymlinkManifest",
+			SymlinkPaths: []string{},
 		},
 	}, actual)
 }
@@ -1136,7 +1147,7 @@
 
 // Asserts that the given actual build statements match the given expected build statements.
 // Build statement equivalence is determined using buildStatementEquals.
-func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
+func assertBuildStatements(t *testing.T, expected []*BuildStatement, actual []*BuildStatement) {
 	t.Helper()
 	if len(expected) != len(actual) {
 		t.Errorf("expected %d build statements, but got %d,\n expected: %#v,\n actual: %#v",
@@ -1144,8 +1155,13 @@
 		return
 	}
 	type compareFn = func(i int, j int) bool
-	byCommand := func(slice []BuildStatement) compareFn {
+	byCommand := func(slice []*BuildStatement) compareFn {
 		return func(i int, j int) bool {
+			if slice[i] == nil {
+				return false
+			} else if slice[j] == nil {
+				return false
+			}
 			return slice[i].Command < slice[j].Command
 		}
 	}
@@ -1161,7 +1177,10 @@
 	}
 }
 
-func buildStatementEquals(first BuildStatement, second BuildStatement) string {
+func buildStatementEquals(first *BuildStatement, second *BuildStatement) string {
+	if (first == nil) != (second == nil) {
+		return "Nil"
+	}
 	if first.Mnemonic != second.Mnemonic {
 		return "Mnemonic"
 	}
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
index fe156df..a39ed7d 100644
--- a/bp2build/cc_binary_conversion_test.go
+++ b/bp2build/cc_binary_conversion_test.go
@@ -365,7 +365,7 @@
 		{
 			description:   "nocrt: true",
 			soongProperty: `nocrt: true,`,
-			bazelAttr:     AttrNameToString{"link_crt": `False`},
+			bazelAttr:     AttrNameToString{"features": `["-link_crt"]`},
 		},
 		{
 			description:   "nocrt: false",
@@ -408,12 +408,12 @@
 		{
 			description:   "no_libcrt: true",
 			soongProperty: `no_libcrt: true,`,
-			bazelAttr:     AttrNameToString{"use_libcrt": `False`},
+			bazelAttr:     AttrNameToString{"features": `["-use_libcrt"]`},
 		},
 		{
 			description:   "no_libcrt: false",
 			soongProperty: `no_libcrt: false,`,
-			bazelAttr:     AttrNameToString{"use_libcrt": `True`},
+			bazelAttr:     AttrNameToString{},
 		},
 		{
 			description: "no_libcrt: not set",
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index c11a50d..af14f64 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -1308,7 +1308,7 @@
 
 func TestCCLibraryNoCrtTrue(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
-		Description:                "cc_library - nocrt: true emits attribute",
+		Description:                "cc_library - nocrt: true disables feature",
 		ModuleTypeUnderTest:        "cc_library",
 		ModuleTypeUnderTestFactory: cc.LibraryFactory,
 		Filesystem: map[string]string{
@@ -1323,7 +1323,7 @@
 }
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
-			"link_crt": `False`,
+			"features": `["-link_crt"]`,
 			"srcs":     `["impl.cpp"]`,
 		}),
 	},
@@ -1375,7 +1375,13 @@
     include_build_directory: false,
 }
 `,
-		ExpectedErr: fmt.Errorf("module \"foo-lib\": nocrt is not supported for arch variants"),
+		ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
+			"features": `select({
+        "//build/bazel/platforms/arch:arm": ["-link_crt"],
+        "//conditions:default": [],
+    })`,
+			"srcs": `["impl.cpp"]`,
+		}),
 	})
 }
 
@@ -1395,8 +1401,8 @@
 }
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
-			"srcs":       `["impl.cpp"]`,
-			"use_libcrt": `False`,
+			"features": `["-use_libcrt"]`,
+			"srcs":     `["impl.cpp"]`,
 		}),
 	})
 }
@@ -1445,8 +1451,7 @@
 }
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
-			"srcs":       `["impl.cpp"]`,
-			"use_libcrt": `True`,
+			"srcs": `["impl.cpp"]`,
 		}),
 	})
 }
@@ -1475,10 +1480,10 @@
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
 			"srcs": `["impl.cpp"]`,
-			"use_libcrt": `select({
-        "//build/bazel/platforms/arch:arm": False,
-        "//build/bazel/platforms/arch:x86": False,
-        "//conditions:default": None,
+			"features": `select({
+        "//build/bazel/platforms/arch:arm": ["-use_libcrt"],
+        "//build/bazel/platforms/arch:x86": ["-use_libcrt"],
+        "//conditions:default": [],
     })`,
 		}),
 	})
@@ -1512,17 +1517,15 @@
 }
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
-			"srcs": `["impl.cpp"]`,
-			"use_libcrt": `select({
-        "//build/bazel/platforms/os_arch:android_arm": False,
-        "//build/bazel/platforms/os_arch:android_x86": False,
-        "//build/bazel/platforms/os_arch:darwin_arm64": False,
-        "//build/bazel/platforms/os_arch:darwin_x86_64": False,
-        "//build/bazel/platforms/os_arch:linux_glibc_x86": False,
-        "//build/bazel/platforms/os_arch:linux_musl_x86": False,
-        "//build/bazel/platforms/os_arch:windows_x86": False,
-        "//conditions:default": None,
+			"features": `select({
+        "//build/bazel/platforms/arch:arm": ["-use_libcrt"],
+        "//build/bazel/platforms/arch:x86": ["-use_libcrt"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms/os:darwin": ["-use_libcrt"],
+        "//conditions:default": [],
     })`,
+			"srcs": `["impl.cpp"]`,
 		}),
 	})
 }
@@ -1557,16 +1560,10 @@
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("foo-lib", AttrNameToString{
 			"srcs": `["impl.cpp"]`,
-			"use_libcrt": `select({
-        "//build/bazel/platforms/os_arch:android_arm": False,
-        "//build/bazel/platforms/os_arch:android_x86_64": False,
-        "//build/bazel/platforms/os_arch:darwin_arm64": True,
-        "//build/bazel/platforms/os_arch:darwin_x86_64": False,
-        "//build/bazel/platforms/os_arch:linux_bionic_x86_64": False,
-        "//build/bazel/platforms/os_arch:linux_glibc_x86_64": False,
-        "//build/bazel/platforms/os_arch:linux_musl_x86_64": False,
-        "//build/bazel/platforms/os_arch:windows_x86_64": False,
-        "//conditions:default": None,
+			"features": `select({
+        "//build/bazel/platforms/arch:arm": ["-use_libcrt"],
+        "//build/bazel/platforms/arch:x86_64": ["-use_libcrt"],
+        "//conditions:default": [],
     })`,
 		}),
 	})
@@ -4087,3 +4084,56 @@
 		},
 	})
 }
+
+func TestCcLibraryInApexWithStubSharedLibs(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library with in apex with stub shared_libs and export_shared_lib_headers",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+cc_library {
+	name: "barlib",
+	stubs: { symbol_file: "bar.map.txt", versions: ["28", "29", "current"] },
+	bazel_module: { bp2build_available: false },
+}
+cc_library {
+	name: "bazlib",
+	stubs: { symbol_file: "bar.map.txt", versions: ["28", "29", "current"] },
+	bazel_module: { bp2build_available: false },
+}
+cc_library {
+    name: "foo",
+	  shared_libs: ["barlib", "bazlib"],
+    export_shared_lib_headers: ["bazlib"],
+    apex_available: [
+        "apex_available:platform",
+    ],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"implementation_dynamic_deps": `select({
+        "//build/bazel/rules/apex:android-in_apex": [":barlib_stub_libs_current"],
+        "//conditions:default": [":barlib"],
+    })`,
+				"dynamic_deps": `select({
+        "//build/bazel/rules/apex:android-in_apex": [":bazlib_stub_libs_current"],
+        "//conditions:default": [":bazlib"],
+    })`,
+				"local_includes": `["."]`,
+				"tags":           `["apex_available=apex_available:platform"]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"implementation_dynamic_deps": `select({
+        "//build/bazel/rules/apex:android-in_apex": [":barlib_stub_libs_current"],
+        "//conditions:default": [":barlib"],
+    })`,
+				"dynamic_deps": `select({
+        "//build/bazel/rules/apex:android-in_apex": [":bazlib_stub_libs_current"],
+        "//conditions:default": [":bazlib"],
+    })`,
+				"local_includes": `["."]`,
+				"tags":           `["apex_available=apex_available:platform"]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index 017df6f..6207421 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -15,7 +15,6 @@
 package bp2build
 
 import (
-	"fmt"
 	"testing"
 
 	"android/soong/android"
@@ -405,7 +404,7 @@
 
 func TestCcLibrarySharedNoCrtTrue(t *testing.T) {
 	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
-		Description: "cc_library_shared - nocrt: true emits attribute",
+		Description: "cc_library_shared - nocrt: true disables feature",
 		Filesystem: map[string]string{
 			"impl.cpp": "",
 		},
@@ -419,7 +418,7 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
-				"link_crt": `False`,
+				"features": `["-link_crt"]`,
 				"srcs":     `["impl.cpp"]`,
 			}),
 		},
@@ -428,7 +427,7 @@
 
 func TestCcLibrarySharedNoCrtFalse(t *testing.T) {
 	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
-		Description: "cc_library_shared - nocrt: false doesn't emit attribute",
+		Description: "cc_library_shared - nocrt: false doesn't disable feature",
 		Filesystem: map[string]string{
 			"impl.cpp": "",
 		},
@@ -469,7 +468,15 @@
     include_build_directory: false,
 }
 `,
-		ExpectedErr: fmt.Errorf("module \"foo_shared\": nocrt is not supported for arch variants"),
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "foo_shared", AttrNameToString{
+				"features": `select({
+        "//build/bazel/platforms/arch:arm": ["-link_crt"],
+        "//conditions:default": [],
+    })`,
+				"srcs": `["impl.cpp"]`,
+			}),
+		},
 	})
 }
 
diff --git a/cc/binary.go b/cc/binary.go
index a04b174..496c610 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -151,7 +151,7 @@
 // modules common to most binaries, such as bionic libraries.
 func (binary *binaryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
 	deps = binary.baseLinker.linkerDeps(ctx, deps)
-	if !Bool(binary.baseLinker.Properties.Nocrt) {
+	if binary.baseLinker.Properties.crt() {
 		if binary.static() {
 			deps.CrtBegin = ctx.toolchain().CrtBeginStaticBinary()
 			deps.CrtEnd = ctx.toolchain().CrtEndStaticBinary()
@@ -577,12 +577,12 @@
 
 func (handler *ccBinaryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	bazelCtx.QueueBazelRequest(label, cquery.GetCcUnstrippedInfo, android.GetConfigKey(ctx))
+	bazelCtx.QueueBazelRequest(label, cquery.GetCcUnstrippedInfo, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 }
 
 func (handler *ccBinaryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	info, err := bazelCtx.GetCcUnstrippedInfo(label, android.GetConfigKey(ctx))
+	info, err := bazelCtx.GetCcUnstrippedInfo(label, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 	if err != nil {
 		ctx.ModuleErrorf(err.Error())
 		return
@@ -630,8 +630,6 @@
 		Local_includes:    baseAttrs.localIncludes,
 		Absolute_includes: baseAttrs.absoluteIncludes,
 		Linkopts:          baseAttrs.linkopts,
-		Link_crt:          baseAttrs.linkCrt,
-		Use_libcrt:        baseAttrs.useLibcrt,
 		Use_version_lib:   baseAttrs.useVersionLib,
 		Rtti:              baseAttrs.rtti,
 		Stl:               baseAttrs.stl,
@@ -695,10 +693,7 @@
 
 	Linkopts                 bazel.StringListAttribute
 	Additional_linker_inputs bazel.LabelListAttribute
-
-	Link_crt        bazel.BoolAttribute
-	Use_libcrt      bazel.BoolAttribute
-	Use_version_lib bazel.BoolAttribute
+	Use_version_lib          bazel.BoolAttribute
 
 	Rtti    bazel.BoolAttribute
 	Stl     *string
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 6c5505a..808f51c 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -965,8 +965,6 @@
 	systemDynamicDeps                bazel.LabelListAttribute
 	usedSystemDynamicDepAsDynamicDep map[string]bool
 
-	linkCrt                       bazel.BoolAttribute
-	useLibcrt                     bazel.BoolAttribute
 	useVersionLib                 bazel.BoolAttribute
 	linkopts                      bazel.StringListAttribute
 	additionalLinkerInputs        bazel.LabelListAttribute
@@ -1082,43 +1080,13 @@
 	la.resolveTargetApexProp(ctx, props)
 
 	if axis == bazel.NoConfigAxis || (axis == bazel.OsConfigurationAxis && config == bazel.OsAndroid) {
-		// If a dependency in la.implementationDynamicDeps has stubs, its stub variant should be
-		// used when the dependency is linked in a APEX. The dependencies in NoConfigAxis and
-		// OsConfigurationAxis/OsAndroid are grouped by having stubs or not, so Bazel select()
-		// statement can be used to choose source/stub variants of them.
-		depsWithStubs := []bazel.Label{}
-		for _, l := range sharedDeps.implementation.Includes {
-			dep, _ := ctx.ModuleFromName(l.OriginalModuleName)
-			if m, ok := dep.(*Module); ok && m.HasStubsVariants() {
-				depsWithStubs = append(depsWithStubs, l)
-			}
-		}
-		if len(depsWithStubs) > 0 {
-			implDynamicDeps := bazel.SubtractBazelLabelList(sharedDeps.implementation, bazel.MakeLabelList(depsWithStubs))
-			la.implementationDynamicDeps.SetSelectValue(axis, config, implDynamicDeps)
-
-			stubLibLabels := []bazel.Label{}
-			for _, l := range depsWithStubs {
-				l.Label = l.Label + stubsSuffix
-				stubLibLabels = append(stubLibLabels, l)
-			}
-			inApexSelectValue := la.implementationDynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex)
-			nonApexSelectValue := la.implementationDynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex)
-			defaultSelectValue := la.implementationDynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey)
-			if axis == bazel.NoConfigAxis {
-				(&inApexSelectValue).Append(bazel.MakeLabelList(stubLibLabels))
-				(&nonApexSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
-				(&defaultSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
-				la.implementationDynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.FirstUniqueBazelLabelList(inApexSelectValue))
-				la.implementationDynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, bazel.FirstUniqueBazelLabelList(nonApexSelectValue))
-				la.implementationDynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey, bazel.FirstUniqueBazelLabelList(defaultSelectValue))
-			} else if config == bazel.OsAndroid {
-				(&inApexSelectValue).Append(bazel.MakeLabelList(stubLibLabels))
-				(&nonApexSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
-				la.implementationDynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.FirstUniqueBazelLabelList(inApexSelectValue))
-				la.implementationDynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, bazel.FirstUniqueBazelLabelList(nonApexSelectValue))
-			}
-		}
+		// If a dependency in la.implementationDynamicDeps or la.dynamicDeps has stubs, its
+		// stub variant should be used when the dependency is linked in a APEX. The
+		// dependencies in NoConfigAxis and OsConfigurationAxis/OsAndroid are grouped by
+		// having stubs or not, so Bazel select() statement can be used to choose
+		// source/stub variants of them.
+		setStubsForDynamicDeps(ctx, axis, config, sharedDeps.export, &la.dynamicDeps, 0)
+		setStubsForDynamicDeps(ctx, axis, config, sharedDeps.implementation, &la.implementationDynamicDeps, 1)
 	}
 
 	if !BoolDefault(props.Pack_relocations, packRelocationsDefault) {
@@ -1138,6 +1106,13 @@
 		}
 	}
 
+	if !props.libCrt() {
+		axisFeatures = append(axisFeatures, "-use_libcrt")
+	}
+	if !props.crt() {
+		axisFeatures = append(axisFeatures, "-link_crt")
+	}
+
 	// This must happen before the addition of flags for Version Script and
 	// Dynamic List, as these flags must be split on spaces and those must not
 	linkerFlags = parseCommandLineFlags(linkerFlags, filterOutClangUnknownCflags)
@@ -1157,16 +1132,6 @@
 
 	la.additionalLinkerInputs.SetSelectValue(axis, config, additionalLinkerInputs)
 	la.linkopts.SetSelectValue(axis, config, linkerFlags)
-	la.useLibcrt.SetSelectValue(axis, config, props.libCrt())
-
-	// it's very unlikely for nocrt to be arch variant, so bp2build doesn't support it.
-	if props.crt() != nil {
-		if axis == bazel.NoConfigAxis {
-			la.linkCrt.SetSelectValue(axis, config, props.crt())
-		} else if axis == bazel.ArchConfigurationAxis {
-			ctx.ModuleErrorf("nocrt is not supported for arch variants")
-		}
-	}
 
 	if axisFeatures != nil {
 		la.features.SetSelectValue(axis, config, axisFeatures)
@@ -1178,6 +1143,43 @@
 	}
 }
 
+func setStubsForDynamicDeps(ctx android.BazelConversionPathContext, axis bazel.ConfigurationAxis,
+	config string, dynamicLibs bazel.LabelList, dynamicDeps *bazel.LabelListAttribute, ind int) {
+	depsWithStubs := []bazel.Label{}
+	for _, l := range dynamicLibs.Includes {
+		dep, _ := ctx.ModuleFromName(l.OriginalModuleName)
+		if m, ok := dep.(*Module); ok && m.HasStubsVariants() {
+			depsWithStubs = append(depsWithStubs, l)
+		}
+	}
+	if len(depsWithStubs) > 0 {
+		implDynamicDeps := bazel.SubtractBazelLabelList(dynamicLibs, bazel.MakeLabelList(depsWithStubs))
+		dynamicDeps.SetSelectValue(axis, config, implDynamicDeps)
+
+		stubLibLabels := []bazel.Label{}
+		for _, l := range depsWithStubs {
+			l.Label = l.Label + stubsSuffix
+			stubLibLabels = append(stubLibLabels, l)
+		}
+		inApexSelectValue := dynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex)
+		nonApexSelectValue := dynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex)
+		defaultSelectValue := dynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey)
+		if axis == bazel.NoConfigAxis {
+			(&inApexSelectValue).Append(bazel.MakeLabelList(stubLibLabels))
+			(&nonApexSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
+			(&defaultSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
+			dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.FirstUniqueBazelLabelList(inApexSelectValue))
+			dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, bazel.FirstUniqueBazelLabelList(nonApexSelectValue))
+			dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey, bazel.FirstUniqueBazelLabelList(defaultSelectValue))
+		} else if config == bazel.OsAndroid {
+			(&inApexSelectValue).Append(bazel.MakeLabelList(stubLibLabels))
+			(&nonApexSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
+			dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.FirstUniqueBazelLabelList(inApexSelectValue))
+			dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, bazel.FirstUniqueBazelLabelList(nonApexSelectValue))
+		}
+	}
+}
+
 func (la *linkerAttributes) convertStripProps(ctx android.BazelConversionPathContext, module *Module) {
 	bp2BuildPropParseHelper(ctx, module, &StripProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) {
 		if stripProperties, ok := props.(*StripProperties); ok {
diff --git a/cc/cc.go b/cc/cc.go
index c81160d..c07d836 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1905,10 +1905,32 @@
 	}
 
 	// TODO(b/261058727): Remove this (enable mixed builds for modules with UBSan)
-	ubsanEnabled := c.sanitize != nil &&
-		((c.sanitize.Properties.Sanitize.Integer_overflow != nil && *c.sanitize.Properties.Sanitize.Integer_overflow) ||
-			c.sanitize.Properties.Sanitize.Misc_undefined != nil)
-	return c.bazelHandler != nil && !ubsanEnabled
+	// Currently we can only support ubsan when minimum runtime is used.
+	return c.bazelHandler != nil && (!isUbsanEnabled(c) || c.MinimalRuntimeNeeded())
+}
+
+func isUbsanEnabled(c *Module) bool {
+	if c.sanitize == nil {
+		return false
+	}
+	sanitizeProps := &c.sanitize.Properties.SanitizeMutated
+	return Bool(sanitizeProps.Integer_overflow) || len(sanitizeProps.Misc_undefined) > 0
+}
+
+func GetApexConfigKey(ctx android.BaseModuleContext) *android.ApexConfigKey {
+	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	if !apexInfo.IsForPlatform() {
+		if !ctx.Config().BazelContext.IsModuleDclaAllowed(ctx.Module().Name()) {
+			return nil
+		}
+		apexKey := android.ApexConfigKey{
+			WithinApex:     true,
+			ApexSdkVersion: findApexSdkVersion(ctx, apexInfo).String(),
+		}
+		return &apexKey
+	}
+
+	return nil
 }
 
 func (c *Module) ProcessBazelQueryResponse(ctx android.ModuleContext) {
@@ -2841,6 +2863,23 @@
 	}
 }
 
+func findApexSdkVersion(ctx android.BaseModuleContext, apexInfo android.ApexInfo) android.ApiLevel {
+	// For the dependency from platform to apex, use the latest stubs
+	apexSdkVersion := android.FutureApiLevel
+	if !apexInfo.IsForPlatform() {
+		apexSdkVersion = apexInfo.MinSdkVersion
+	}
+
+	if android.InList("hwaddress", ctx.Config().SanitizeDevice()) {
+		// In hwasan build, we override apexSdkVersion to the FutureApiLevel(10000)
+		// so that even Q(29/Android10) apexes could use the dynamic unwinder by linking the newer stubs(e.g libc(R+)).
+		// (b/144430859)
+		apexSdkVersion = android.FutureApiLevel
+	}
+
+	return apexSdkVersion
+}
+
 // Convert dependencies to paths.  Returns a PathDeps containing paths
 func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 	var depPaths PathDeps
@@ -2856,19 +2895,8 @@
 		depPaths.ReexportedGeneratedHeaders = append(depPaths.ReexportedGeneratedHeaders, exporter.GeneratedHeaders...)
 	}
 
-	// For the dependency from platform to apex, use the latest stubs
-	c.apexSdkVersion = android.FutureApiLevel
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
-	if !apexInfo.IsForPlatform() {
-		c.apexSdkVersion = apexInfo.MinSdkVersion
-	}
-
-	if android.InList("hwaddress", ctx.Config().SanitizeDevice()) {
-		// In hwasan build, we override apexSdkVersion to the FutureApiLevel(10000)
-		// so that even Q(29/Android10) apexes could use the dynamic unwinder by linking the newer stubs(e.g libc(R+)).
-		// (b/144430859)
-		c.apexSdkVersion = android.FutureApiLevel
-	}
+	c.apexSdkVersion = findApexSdkVersion(ctx, apexInfo)
 
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		depName := ctx.OtherModuleName(dep)
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 62adfd3..0d03b73 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -28,6 +28,10 @@
 	"android/soong/bazel/cquery"
 )
 
+func init() {
+	registerTestMutators(android.InitRegistrationContext)
+}
+
 func TestMain(m *testing.M) {
 	os.Exit(m.Run())
 }
@@ -41,6 +45,36 @@
 	}),
 )
 
+var ccLibInApex = "cc_lib_in_apex"
+var apexVariationName = "apex28"
+var apexVersion = "28"
+
+func registerTestMutators(ctx android.RegistrationContext) {
+	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("apex", testApexMutator).Parallel()
+		ctx.BottomUp("mixed_builds_prep", mixedBuildsPrepareMutator).Parallel()
+	})
+}
+
+func mixedBuildsPrepareMutator(ctx android.BottomUpMutatorContext) {
+	if m := ctx.Module(); m.Enabled() {
+		if mixedBuildMod, ok := m.(android.MixedBuildBuildable); ok {
+			if mixedBuildMod.IsMixedBuildSupported(ctx) && android.MixedBuildsEnabled(ctx) {
+				mixedBuildMod.QueueBazelCall(ctx)
+			}
+		}
+	}
+}
+
+func testApexMutator(mctx android.BottomUpMutatorContext) {
+	modules := mctx.CreateVariations(apexVariationName)
+	apexInfo := android.ApexInfo{
+		ApexVariationName: apexVariationName,
+		MinSdkVersion:     android.ApiLevelForTest(apexVersion),
+	}
+	mctx.SetVariationProvider(modules[0], android.ApexInfoProvider, apexInfo)
+}
+
 // testCcWithConfig runs tests using the prepareForCcTest
 //
 // See testCc for an explanation as to how to stop using this deprecated method.
@@ -4906,3 +4940,56 @@
 		})
 	}
 }
+
+func TestDclaLibraryInApex(t *testing.T) {
+	t.Parallel()
+	bp := `
+	cc_library_shared {
+		name: "cc_lib_in_apex",
+		srcs: ["foo.cc"],
+    apex_available: ["myapex"],
+		bazel_module: { label: "//foo/bar:bar" },
+	}`
+	label := "//foo/bar:bar"
+	arch64 := "arm64_armv8-a"
+	arch32 := "arm_armv7-a-neon"
+	apexCfgKey := android.ApexConfigKey{
+		WithinApex:     true,
+		ApexSdkVersion: "28",
+	}
+
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+		android.FixtureRegisterWithContext(registerTestMutators),
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.BazelContext = android.MockBazelContext{
+				OutputBaseDir: "outputbase",
+				LabelToCcInfo: map[string]cquery.CcInfo{
+					android.BuildMockBazelContextResultKey(label, arch32, android.Android, apexCfgKey): cquery.CcInfo{
+						RootDynamicLibraries: []string{"foo.so"},
+					},
+					android.BuildMockBazelContextResultKey(label, arch64, android.Android, apexCfgKey): cquery.CcInfo{
+						RootDynamicLibraries: []string{"foo.so"},
+					},
+				},
+				BazelRequests: make(map[string]bool),
+			}
+		}),
+	).RunTestWithBp(t, bp)
+	ctx := result.TestContext
+
+	// Test if the bazel request is queued correctly
+	key := android.BuildMockBazelContextRequestKey(label, cquery.GetCcInfo, arch32, android.Android, apexCfgKey)
+	if !ctx.Config().BazelContext.(android.MockBazelContext).BazelRequests[key] {
+		t.Errorf("Bazel request was not queued: %s", key)
+	}
+
+	sharedFoo := ctx.ModuleForTests(ccLibInApex, "android_arm_armv7-a-neon_shared_"+apexVariationName).Module()
+	producer := sharedFoo.(android.OutputFileProducer)
+	outputFiles, err := producer.OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+	}
+	expectedOutputFiles := []string{"outputbase/execroot/__main__/foo.so"}
+	android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+}
diff --git a/cc/config/global.go b/cc/config/global.go
index 488af45..05dc773 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -193,7 +193,6 @@
 
 	noOverrideGlobalCflags = []string{
 		"-Werror=bool-operation",
-		"-Werror=format-insufficient-args",
 		"-Werror=implicit-int-float-conversion",
 		"-Werror=int-in-bool-context",
 		"-Werror=int-to-pointer-cast",
@@ -248,8 +247,6 @@
 	noOverride64GlobalCflags = []string{}
 
 	noOverrideExternalGlobalCflags = []string{
-		// http://b/191699019
-		"-Wno-format-insufficient-args",
 		"-Wno-sizeof-array-div",
 		"-Wno-unused-but-set-variable",
 		"-Wno-unused-but-set-parameter",
@@ -287,6 +284,9 @@
 
 		// http://b/239661264
 		"-Wno-deprecated-non-prototype",
+
+		// http://b/191699019
+		"-Wno-format-insufficient-args",
 	}
 
 	llvmNextExtraCommonGlobalCflags = []string{
diff --git a/cc/library.go b/cc/library.go
index 9421007..61e3a93 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -243,7 +243,6 @@
 	Local_includes         bazel.StringListAttribute
 	Absolute_includes      bazel.StringListAttribute
 	Linkopts               bazel.StringListAttribute
-	Use_libcrt             bazel.BoolAttribute
 	Rtti                   bazel.BoolAttribute
 
 	Stl     *string
@@ -251,7 +250,6 @@
 	C_std   *string
 
 	// This is shared only.
-	Link_crt                 bazel.BoolAttribute
 	Additional_linker_inputs bazel.LabelListAttribute
 
 	// Common properties shared between both shared and static variants.
@@ -360,7 +358,6 @@
 		Export_system_includes:   exportedIncludes.SystemIncludes,
 		Local_includes:           compilerAttrs.localIncludes,
 		Absolute_includes:        compilerAttrs.absoluteIncludes,
-		Use_libcrt:               linkerAttrs.useLibcrt,
 		Rtti:                     compilerAttrs.rtti,
 		Stl:                      compilerAttrs.stl,
 		Cpp_std:                  compilerAttrs.cppStd,
@@ -381,8 +378,6 @@
 		Local_includes:           compilerAttrs.localIncludes,
 		Absolute_includes:        compilerAttrs.absoluteIncludes,
 		Linkopts:                 linkerAttrs.linkopts,
-		Link_crt:                 linkerAttrs.linkCrt,
-		Use_libcrt:               linkerAttrs.useLibcrt,
 		Rtti:                     compilerAttrs.rtti,
 		Stl:                      compilerAttrs.stl,
 		Cpp_std:                  compilerAttrs.cppStd,
@@ -915,12 +910,12 @@
 
 func (handler *ccLibraryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKey(ctx))
+	bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 }
 
 func (handler *ccLibraryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
+	ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 	if err != nil {
 		ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
 		return
@@ -1500,7 +1495,7 @@
 		deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.StaticProperties.Static.Export_shared_lib_headers...)
 		deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.StaticProperties.Static.Export_static_lib_headers...)
 	} else if library.shared() {
-		if !Bool(library.baseLinker.Properties.Nocrt) {
+		if library.baseLinker.Properties.crt() {
 			deps.CrtBegin = append(deps.CrtBegin, ctx.toolchain().CrtBeginSharedLibrary()...)
 			deps.CrtEnd = append(deps.CrtEnd, ctx.toolchain().CrtEndSharedLibrary()...)
 		}
@@ -2884,13 +2879,10 @@
 		commonAttrs.Deps.Add(baseAttributes.protoDependency)
 		attrs = &bazelCcLibraryStaticAttributes{
 			staticOrSharedAttributes: commonAttrs,
-
-			Use_libcrt: linkerAttrs.useLibcrt,
-
-			Rtti:    compilerAttrs.rtti,
-			Stl:     compilerAttrs.stl,
-			Cpp_std: compilerAttrs.cppStd,
-			C_std:   compilerAttrs.cStd,
+			Rtti:                     compilerAttrs.rtti,
+			Stl:                      compilerAttrs.stl,
+			Cpp_std:                  compilerAttrs.cppStd,
+			C_std:                    compilerAttrs.cStd,
 
 			Export_includes:          exportedIncludes.Includes,
 			Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
@@ -2915,8 +2907,6 @@
 			Asflags:    asFlags,
 
 			Linkopts:        linkerAttrs.linkopts,
-			Link_crt:        linkerAttrs.linkCrt,
-			Use_libcrt:      linkerAttrs.useLibcrt,
 			Use_version_lib: linkerAttrs.useVersionLib,
 
 			Rtti:    compilerAttrs.rtti,
@@ -2974,13 +2964,11 @@
 type bazelCcLibraryStaticAttributes struct {
 	staticOrSharedAttributes
 
-	Use_libcrt      bazel.BoolAttribute
 	Use_version_lib bazel.BoolAttribute
-
-	Rtti    bazel.BoolAttribute
-	Stl     *string
-	Cpp_std *string
-	C_std   *string
+	Rtti            bazel.BoolAttribute
+	Stl             *string
+	Cpp_std         *string
+	C_std           *string
 
 	Export_includes          bazel.StringListAttribute
 	Export_absolute_includes bazel.StringListAttribute
@@ -3000,10 +2988,7 @@
 type bazelCcLibrarySharedAttributes struct {
 	staticOrSharedAttributes
 
-	Linkopts bazel.StringListAttribute
-	Link_crt bazel.BoolAttribute // Only for linking shared library (and cc_binary)
-
-	Use_libcrt      bazel.BoolAttribute
+	Linkopts        bazel.StringListAttribute
 	Use_version_lib bazel.BoolAttribute
 
 	Rtti    bazel.BoolAttribute
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 32ea1d4..1dee726 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -59,12 +59,12 @@
 
 func (handler *libraryHeaderBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKey(ctx))
+	bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 }
 
 func (h *libraryHeaderBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
+	ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 	if err != nil {
 		ctx.ModuleErrorf(err.Error())
 		return
diff --git a/cc/linker.go b/cc/linker.go
index 371d78d..e49b97d 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -237,29 +237,14 @@
 	Exclude_shared_libs []string `android:"arch_variant"`
 }
 
-func invertBoolPtr(value *bool) *bool {
-	if value == nil {
-		return nil
-	}
-	ret := !(*value)
-	return &ret
+func (blp *BaseLinkerProperties) crt() bool {
+	// Since crt is enabled for almost every module compiling against the Bionic runtime,
+	// we interpret `nil` as  enabled.
+	return blp.Nocrt == nil || !*blp.Nocrt
 }
 
-func (blp *BaseLinkerProperties) crt() *bool {
-	val := invertBoolPtr(blp.Nocrt)
-	if val != nil && *val {
-		// == True
-		//
-		// Since crt is enabled for almost every module compiling against the Bionic runtime,
-		// use `nil` when it's enabled, and rely on the Starlark macro to set it to True by default.
-		// This keeps the BUILD files clean.
-		return nil
-	}
-	return val // can be False or nil
-}
-
-func (blp *BaseLinkerProperties) libCrt() *bool {
-	return invertBoolPtr(blp.No_libcrt)
+func (blp *BaseLinkerProperties) libCrt() bool {
+	return blp.No_libcrt == nil || !*blp.No_libcrt
 }
 
 func NewBaseLinker(sanitize *sanitize) *baseLinker {
@@ -392,7 +377,7 @@
 
 	if ctx.toolchain().Bionic() {
 		// libclang_rt.builtins has to be last on the command line
-		if !Bool(linker.Properties.No_libcrt) && !ctx.header() {
+		if linker.Properties.libCrt() && !ctx.header() {
 			deps.UnexportedStaticLibs = append(deps.UnexportedStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
 		}
 
@@ -415,7 +400,7 @@
 			ctx.PropertyErrorf("system_shared_libs", "libdl must be after libc")
 		}
 	} else if ctx.toolchain().Musl() {
-		if !Bool(linker.Properties.No_libcrt) && !ctx.header() {
+		if linker.Properties.libCrt() && !ctx.header() {
 			deps.UnexportedStaticLibs = append(deps.UnexportedStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
 		}
 	}
diff --git a/cc/object.go b/cc/object.go
index 11ce793..ef44467 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -54,12 +54,12 @@
 
 func (handler *objectBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	bazelCtx.QueueBazelRequest(label, cquery.GetOutputFiles, android.GetConfigKey(ctx))
+	bazelCtx.QueueBazelRequest(label, cquery.GetOutputFiles, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 }
 
 func (handler *objectBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	objPaths, err := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
+	objPaths, err := bazelCtx.GetOutputFiles(label, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 	if err != nil {
 		ctx.ModuleErrorf(err.Error())
 		return
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 03a600a..bb517ea 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -761,12 +761,12 @@
 
 func (h *prebuiltBinaryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	bazelCtx.QueueBazelRequest(label, cquery.GetOutputFiles, android.GetConfigKey(ctx))
+	bazelCtx.QueueBazelRequest(label, cquery.GetOutputFiles, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 }
 
 func (h *prebuiltBinaryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
 	bazelCtx := ctx.Config().BazelContext
-	outputs, err := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
+	outputs, err := bazelCtx.GetOutputFiles(label, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
 	if err != nil {
 		ctx.ModuleErrorf(err.Error())
 		return
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 0115f4a..0212075 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -515,7 +515,8 @@
 		"TARGET_BUILD_VARIANT="+*buildVariant,
 		"TARGET_BUILD_TYPE=release",
 		"TARGET_BUILD_APPS=",
-		"TARGET_BUILD_UNBUNDLED=")
+		"TARGET_BUILD_UNBUNDLED=",
+		"USE_RBE=false") // Disabling RBE saves ~10 secs per product
 
 	if *alternateResultDir {
 		cmd.Env = append(cmd.Env,
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index 5e5769b..8175a37 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -69,42 +69,222 @@
 	Dir          string
 }
 
-type PrivilegedLevel string
+type Vector 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"
+	unknown_access_vector Vector = "unknown_access_vector"
+	// The code being fuzzed is reachable from a remote source, or using data
+	// provided by a remote source.  For example: media codecs process media files
+	// from the internet, SMS processing handles remote message data.
+	// See
+	// https://source.android.com/docs/security/overview/updates-resources#local-vs-remote
+	// for an explanation of what's considered "remote."
+	remote = "remote"
+	// The code being fuzzed can only be reached locally, such as from an
+	// installed app.  As an example, if it's fuzzing a Binder interface, it's
+	// assumed that you'd need a local app to make arbitrary Binder calls.
+	// And the app that's calling the fuzzed code does not require any privileges;
+	// any 3rd party app could make these calls.
+	local_no_privileges_required = "local_no_privileges_required"
+	// The code being fuzzed can only be called locally, and the calling process
+	// requires additional permissions that prevent arbitrary 3rd party apps from
+	// calling the code.  For instance: this requires a privileged or signature
+	// permission to reach, or SELinux restrictions prevent the untrusted_app
+	// domain from calling it.
+	local_privileges_required = "local_privileges_required"
+	// The code is only callable on a PC host, not on a production Android device.
+	// For instance, this is fuzzing code used during the build process, or
+	// tooling that does not exist on a user's actual Android device.
+	host_access = "host_access"
+	// The code being fuzzed is only reachable if the user has enabled Developer
+	// Options, or has enabled a persistent Developer Options setting.
+	local_with_developer_options = "local_with_developer_options"
 )
 
+func (vector Vector) isValidVector() bool {
+	switch vector {
+	case "",
+		unknown_access_vector,
+		remote,
+		local_no_privileges_required,
+		local_privileges_required,
+		host_access,
+		local_with_developer_options:
+		return true
+	}
+	return false
+}
+
+type ServicePrivilege string
+
+const (
+	unknown_service_privilege ServicePrivilege = "unknown_service_privilege"
+	// The code being fuzzed runs on a Secure Element.  This has access to some
+	// of the most privileged data on the device, such as authentication keys.
+	// Not all devices have a Secure Element.
+	secure_element = "secure_element"
+	// The code being fuzzed runs in the TEE.  The TEE is designed to be resistant
+	// to a compromised kernel, and stores sensitive data.
+	trusted_execution = "trusted_execution"
+	// The code being fuzzed has privileges beyond what arbitrary 3rd party apps
+	// have.  For instance, it's running as the System UID, or it's in an SELinux
+	// domain that's able to perform calls that can't be made by 3rd party apps.
+	privileged = "privileged"
+	// The code being fuzzed is equivalent to a 3rd party app.  It runs in the
+	// untrusted_app SELinux domain, or it only has privileges that are equivalent
+	// to what a 3rd party app could have.
+	unprivileged = "unprivileged"
+	// The code being fuzzed is significantly constrained, and even if it's
+	// compromised, it has significant restrictions that prevent it from
+	// performing most actions.  This is significantly more restricted than
+	// UNPRIVILEGED.  An example is the isolatedProcess=true setting in a 3rd
+	// party app.  Or a process that's very restricted by SELinux, such as
+	// anything in the mediacodec SELinux domain.
+	constrained = "constrained"
+	// The code being fuzzed always has Negligible Security Impact.  Even
+	// arbitrary out of bounds writes and full code execution would not be
+	// considered a security vulnerability.  This typically only makes sense if
+	// FuzzedCodeUsage is set to FUTURE_VERSION or EXPERIMENTAL, and if
+	// AutomaticallyRouteTo is set to ALWAYS_NSI.
+	nsi = "nsi"
+	// The code being fuzzed only runs on a PC host, not on a production Android
+	// device.  For instance, the fuzzer is fuzzing code used during the build
+	// process, or tooling that does not exist on a user's actual Android device.
+	host_only = "host_only"
+)
+
+func (service_privilege ServicePrivilege) isValidServicePrivilege() bool {
+	switch service_privilege {
+	case "",
+		unknown_service_privilege,
+		secure_element,
+		trusted_execution,
+		privileged,
+		unprivileged,
+		constrained,
+		nsi,
+		host_only:
+		return true
+	}
+	return false
+}
+
+type UserData string
+
+const (
+	unknown_user_data UserData = "unknown_user_data"
+	// The process being fuzzed only handles data from a single user, or from a
+	// single process or app.  It's possible the process shuts down before
+	// handling data from another user/process/app, or it's possible the process
+	// only ever handles one user's/process's/app's data.  As an example, some
+	// print spooler processes are started for a single document and terminate
+	// when done, so each instance only handles data from a single user/app.
+	single_user = "single_user"
+	// The process handles data from multiple users, or from multiple other apps
+	// or processes.  Media processes, for instance, can handle media requests
+	// from multiple different apps without restarting.  Wi-Fi and network
+	// processes handle data from multiple users, and processes, and apps.
+	multi_user = "multi_user"
+)
+
+func (user_data UserData) isValidUserData() bool {
+	switch user_data {
+	case "",
+		unknown_user_data,
+		single_user,
+		multi_user:
+		return true
+	}
+	return false
+}
+
+type FuzzedCodeUsage string
+
+const (
+	undefined FuzzedCodeUsage = "undefined"
+	unknown                   = "unknown"
+	// The code being fuzzed exists in a shipped version of Android and runs on
+	// devices in production.
+	shipped = "shipped"
+	// The code being fuzzed is not yet in a shipping version of Android, but it
+	// will be at some point in the future.
+	future_version = "future_version"
+	// The code being fuzzed is not in a shipping version of Android, and there
+	// are no plans to ship it in the future.
+	experimental = "experimental"
+)
+
+func (fuzzed_code_usage FuzzedCodeUsage) isValidFuzzedCodeUsage() bool {
+	switch fuzzed_code_usage {
+	case "",
+		undefined,
+		unknown,
+		shipped,
+		future_version,
+		experimental:
+		return true
+	}
+	return false
+}
+
+type AutomaticallyRouteTo string
+
+const (
+	undefined_routing AutomaticallyRouteTo = "undefined_routing"
+	// Automatically route this to the Android Automotive security team for
+	// assessment.
+	android_automotive = "android_automotive"
+	// This should not be used in fuzzer configurations.  It is used internally
+	// by Severity Assigner to flag memory leak reports.
+	memory_leak = "memory_leak"
+	// Route this vulnerability to our Ittiam vendor team for assessment.
+	ittiam = "ittiam"
+	// Reports from this fuzzer are always NSI (see the NSI ServicePrivilegeEnum
+	// value for additional context).  It is not possible for this code to ever
+	// have a security vulnerability.
+	always_nsi = "always_nsi"
+	// Route this vulnerability to AIDL team for assessment.
+	aidl = "aidl"
+)
+
+func (automatically_route_to AutomaticallyRouteTo) isValidAutomaticallyRouteTo() bool {
+	switch automatically_route_to {
+	case "",
+		undefined_routing,
+		android_automotive,
+		memory_leak,
+		ittiam,
+		always_nsi,
+		aidl:
+		return true
+	}
+	return false
+}
+
 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))
+		if !config.Vector.isValidVector() {
+			panic(fmt.Errorf("Invalid vector in fuzz config in %s", moduleName))
 		}
-		return true
-	} else {
-		return false
+
+		if !config.Service_privilege.isValidServicePrivilege() {
+			panic(fmt.Errorf("Invalid service_privilege in fuzz config in %s", moduleName))
+		}
+
+		if !config.Users.isValidUserData() {
+			panic(fmt.Errorf("Invalid users (user_data) in fuzz config in %s", moduleName))
+		}
+
+		if !config.Fuzzed_code_usage.isValidFuzzedCodeUsage() {
+			panic(fmt.Errorf("Invalid fuzzed_code_usage in fuzz config in %s", moduleName))
+		}
+
+		if !config.Automatically_route_to.isValidAutomaticallyRouteTo() {
+			panic(fmt.Errorf("Invalid automatically_route_to in fuzz config in %s", moduleName))
+		}
 	}
+	return true
 }
 
 type FuzzConfig struct {
@@ -112,19 +292,24 @@
 	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:"host_only,omitempty"`
+	// Whether the code being fuzzed is remotely accessible or requires privileges
+	// to access locally.
+	Vector Vector `json:"vector,omitempty"`
+	// How privileged the service being fuzzed is.
+	Service_privilege ServicePrivilege `json:"service_privilege,omitempty"`
+	// Whether the service being fuzzed handles data from multiple users or only
+	// a single one.
+	Users UserData `json:"users,omitempty"`
+	// Specifies the use state of the code being fuzzed. This state factors into
+	// how an issue is handled.
+	Fuzzed_code_usage FuzzedCodeUsage `json:"fuzzed_code_usage,omitempty"`
+	// Comment describing how we came to these settings for this fuzzer.
+	Config_comment string
+	// Which team to route this to, if it should be routed automatically.
+	Automatically_route_to AutomaticallyRouteTo `json:"automatically_route_to,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.
+	// When code was released 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"`
@@ -134,7 +319,7 @@
 	Fuzz_on_haiku_host *bool `json:"fuzz_on_haiku_host,omitempty"`
 	// Component in Google's bug tracking system that bugs should be filed to.
 	Componentid *int64 `json:"componentid,omitempty"`
-	// Hotlists in Google's bug tracking system that bugs should be marked with.
+	// Hotlist(s) in Google's bug tracking system that bugs should be marked with.
 	Hotlists []string `json:"hotlists,omitempty"`
 	// Specify whether this fuzz target was submitted by a researcher. Defaults
 	// to false.
diff --git a/java/base.go b/java/base.go
index cce06a4..e7e3d8f 100644
--- a/java/base.go
+++ b/java/base.go
@@ -1487,7 +1487,14 @@
 			}
 			// Dex compilation
 			var dexOutputFile android.OutputPath
-			dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), implementationAndResourcesJar, jarName)
+			params := &compileDexParams{
+				flags:         flags,
+				sdkVersion:    j.SdkVersion(ctx),
+				minSdkVersion: j.MinSdkVersion(ctx),
+				classesJar:    implementationAndResourcesJar,
+				jarName:       jarName,
+			}
+			dexOutputFile = j.dexer.compileDex(ctx, params)
 			if ctx.Failed() {
 				return
 			}
diff --git a/java/dex.go b/java/dex.go
index a8dd375..7b6a99a 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -184,7 +184,7 @@
 		"r8Flags", "zipFlags", "tmpJar", "mergeZipsFlags"}, []string{"implicits"})
 
 func (d *dexer) dexCommonFlags(ctx android.ModuleContext,
-	minSdkVersion android.SdkSpec) (flags []string, deps android.Paths) {
+	dexParams *compileDexParams) (flags []string, deps android.Paths) {
 
 	flags = d.dexProperties.Dxflags
 	// Translate all the DX flags to D8 ones until all the build files have been migrated
@@ -213,11 +213,11 @@
 	// Note: Targets with a min SDK kind of core_platform (e.g., framework.jar) or unspecified (e.g.,
 	// services.jar), are not classified as stable, which is WAI.
 	// TODO(b/232073181): Expand to additional min SDK cases after validation.
-	if !minSdkVersion.Stable() {
+	if !dexParams.sdkVersion.Stable() {
 		flags = append(flags, "--android-platform-build")
 	}
 
-	effectiveVersion, err := minSdkVersion.EffectiveVersion(ctx)
+	effectiveVersion, err := dexParams.minSdkVersion.EffectiveVersion(ctx)
 	if err != nil {
 		ctx.PropertyErrorf("min_sdk_version", "%s", err)
 	}
@@ -340,20 +340,27 @@
 	return r8Flags, r8Deps
 }
 
-func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, minSdkVersion android.SdkSpec,
-	classesJar android.Path, jarName string) android.OutputPath {
+type compileDexParams struct {
+	flags         javaBuilderFlags
+	sdkVersion    android.SdkSpec
+	minSdkVersion android.SdkSpec
+	classesJar    android.Path
+	jarName       string
+}
+
+func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParams) android.OutputPath {
 
 	// Compile classes.jar into classes.dex and then javalib.jar
-	javalibJar := android.PathForModuleOut(ctx, "dex", jarName).OutputPath
+	javalibJar := android.PathForModuleOut(ctx, "dex", dexParams.jarName).OutputPath
 	outDir := android.PathForModuleOut(ctx, "dex")
-	tmpJar := android.PathForModuleOut(ctx, "withres-withoutdex", jarName)
+	tmpJar := android.PathForModuleOut(ctx, "withres-withoutdex", dexParams.jarName)
 
 	zipFlags := "--ignore_missing_files"
 	if proptools.Bool(d.dexProperties.Uncompress_dex) {
 		zipFlags += " -L 0"
 	}
 
-	commonFlags, commonDeps := d.dexCommonFlags(ctx, minSdkVersion)
+	commonFlags, commonDeps := d.dexCommonFlags(ctx, dexParams)
 
 	// Exclude kotlinc generated files when "exclude_kotlinc_generated_files" is set to true.
 	mergeZipsFlags := ""
@@ -372,7 +379,7 @@
 			android.ModuleNameWithPossibleOverride(ctx), "unused.txt")
 		proguardUsageZip := android.PathForModuleOut(ctx, "proguard_usage.zip")
 		d.proguardUsageZip = android.OptionalPathForPath(proguardUsageZip)
-		r8Flags, r8Deps := d.r8Flags(ctx, flags)
+		r8Flags, r8Deps := d.r8Flags(ctx, dexParams.flags)
 		r8Deps = append(r8Deps, commonDeps...)
 		rule := r8
 		args := map[string]string{
@@ -396,12 +403,12 @@
 			Description:     "r8",
 			Output:          javalibJar,
 			ImplicitOutputs: android.WritablePaths{proguardDictionary, proguardUsageZip},
-			Input:           classesJar,
+			Input:           dexParams.classesJar,
 			Implicits:       r8Deps,
 			Args:            args,
 		})
 	} else {
-		d8Flags, d8Deps := d8Flags(flags)
+		d8Flags, d8Deps := d8Flags(dexParams.flags)
 		d8Deps = append(d8Deps, commonDeps...)
 		rule := d8
 		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8") {
@@ -411,7 +418,7 @@
 			Rule:        rule,
 			Description: "d8",
 			Output:      javalibJar,
-			Input:       classesJar,
+			Input:       dexParams.classesJar,
 			Implicits:   d8Deps,
 			Args: map[string]string{
 				"d8Flags":        strings.Join(append(commonFlags, d8Flags...), " "),
@@ -423,7 +430,7 @@
 		})
 	}
 	if proptools.Bool(d.dexProperties.Uncompress_dex) {
-		alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", jarName).OutputPath
+		alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", dexParams.jarName).OutputPath
 		TransformZipAlign(ctx, alignedJavalibJar, javalibJar)
 		javalibJar = alignedJavalibJar
 	}
diff --git a/java/dex_test.go b/java/dex_test.go
index dc85f9e..97fc3d0 100644
--- a/java/dex_test.go
+++ b/java/dex_test.go
@@ -43,6 +43,7 @@
 			name: "core_platform_app",
 			srcs: ["foo.java"],
 			sdk_version: "core_platform",
+			min_sdk_version: "31",
 		}
 
 		java_library {
diff --git a/java/java.go b/java/java.go
index c2fcccf..27b2a6e 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2067,7 +2067,15 @@
 			j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
 
 			var dexOutputFile android.OutputPath
-			dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), outputFile, jarName)
+			dexParams := &compileDexParams{
+				flags:         flags,
+				sdkVersion:    j.SdkVersion(ctx),
+				minSdkVersion: j.MinSdkVersion(ctx),
+				classesJar:    outputFile,
+				jarName:       jarName,
+			}
+
+			dexOutputFile = j.dexer.compileDex(ctx, dexParams)
 			if ctx.Failed() {
 				return
 			}
@@ -2807,6 +2815,9 @@
 		if sdkVersion.Kind == android.SdkPublic && sdkVersion.ApiLevel == android.FutureApiLevel {
 			// TODO(b/220869005) remove forced dependency on current public android.jar
 			deps.Add(bazel.MakeLabelAttribute("//prebuilts/sdk:public_current_android_sdk_java_import"))
+		} else if sdkVersion.Kind == android.SdkSystem && sdkVersion.ApiLevel == android.FutureApiLevel {
+			// TODO(b/215230098) remove forced dependency on current public android.jar
+			deps.Add(bazel.MakeLabelAttribute("//prebuilts/sdk:system_current_android_sdk_java_import"))
 		}
 	} else if !deps.IsEmpty() {
 		ctx.ModuleErrorf("Module has direct dependencies but no sources. Bazel will not allow this.")
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 878f896..8cec918 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -313,7 +313,7 @@
 
 func (b *bindgenDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps {
 	deps = b.BaseSourceProvider.SourceProviderDeps(ctx, deps)
-	if ctx.toolchain().Bionic() {
+	if ctx.toolchain().Bionic() && !ctx.RustModule().compiler.noStdlibs() {
 		deps = bionicDeps(ctx, deps, false)
 	} else if ctx.Os() == android.LinuxMusl {
 		deps = muslDeps(ctx, deps, false)
diff --git a/scripts/check_boot_jars/package_allowed_list.txt b/scripts/check_boot_jars/package_allowed_list.txt
index 08bd80c..ce461b1 100644
--- a/scripts/check_boot_jars/package_allowed_list.txt
+++ b/scripts/check_boot_jars/package_allowed_list.txt
@@ -8,6 +8,7 @@
 java\.io
 java\.lang
 java\.lang\.annotation
+java\.lang\.constant
 java\.lang\.invoke
 java\.lang\.ref
 java\.lang\.reflect
diff --git a/ui/build/config.go b/ui/build/config.go
index cb7fe1e..73e2c45 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -369,6 +369,7 @@
 		"CDPATH",
 		"DISPLAY",
 		"GREP_OPTIONS",
+		"JAVAC",
 		"NDK_ROOT",
 		"POSIXLY_CORRECT",
 
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 1e3e547..a9c298f 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -84,6 +84,14 @@
 	ctx.BeginTrace(metrics.RunKati, "dumpvars")
 	defer ctx.EndTrace()
 
+	tool := ctx.Status.StartTool()
+	if write_soong_vars {
+		// only print this when write_soong_vars is true so that it's not printed when using
+		// the get_build_var command.
+		tool.Status("Running product configuration...")
+	}
+	defer tool.Finish()
+
 	cmd := Command(ctx, config, "dumpvars",
 		config.PrebuiltBuildTool("ckati"),
 		"-f", "build/make/core/config.mk",
@@ -108,7 +116,7 @@
 	}
 	cmd.StartOrFatal()
 	// TODO: error out when Stderr contains any content
-	status.KatiReader(ctx.Status.StartTool(), pipe)
+	status.KatiReader(tool, pipe)
 	cmd.WaitOrFatal()
 
 	ret := make(map[string]string, len(vars))
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 3c844c1..1d17216 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -100,6 +100,8 @@
 	ctx.BeginTrace(metrics.RunSetupTool, "rbe_bootstrap")
 	defer ctx.EndTrace()
 
+	ctx.Status.Status("Starting rbe...")
+
 	if u := ulimitOrFatal(ctx, config, "-u"); u < rbeLeastNProcs {
 		ctx.Fatalf("max user processes is insufficient: %d; want >= %d.\n", u, rbeLeastNProcs)
 	}
@@ -180,6 +182,8 @@
 		return
 	}
 
+	ctx.Status.Status("Dumping rbe metrics...")
+
 	outputDir := config.rbeProxyLogsDir()
 	if outputDir == "" {
 		ctx.Fatal("RBE output dir variable not defined. Aborting metrics dumping.")