Merge "Fixing issue with boolean params in cc_fuzz"
diff --git a/android/Android.bp b/android/Android.bp
index cbd3459..f58a472 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -88,6 +88,7 @@
         "test_asserts.go",
         "test_suites.go",
         "testing.go",
+        "updatable_modules.go",
         "util.go",
         "variable.go",
         "visibility.go",
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index af82942..aa4363c 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -257,6 +257,7 @@
 		"packages/apps/QuickSearchBox":/* recursive = */ true,
 		"packages/apps/WallpaperPicker":/* recursive = */ false,
 
+		"prebuilts/bazel":/* recursive = */ true,
 		"prebuilts/bundletool":/* recursive = */ true,
 		"prebuilts/gcc":/* recursive = */ true,
 		"prebuilts/build-tools":/* recursive = */ true,
@@ -365,6 +366,7 @@
 	}
 
 	Bp2buildModuleTypeAlwaysConvertList = []string{
+		"linker_config",
 		"java_import",
 		"java_import_host",
 	}
@@ -439,7 +441,6 @@
 		"art-script",                                                 // depends on unconverted modules: dalvikvm, dex2oat
 		"bin2c_fastdeployagent",                                      // depends on unconverted modules: deployagent
 		"com.android.runtime",                                        // depends on unconverted modules: bionic-linker-config, linkerconfig
-		"conv_linker_config",                                         // depends on unconverted modules: linker_config_proto
 		"currysrc",                                                   // depends on unconverted modules: currysrc_org.eclipse, guavalib, jopt-simple-4.9
 		"dex2oat-script",                                             // depends on unconverted modules: dex2oat
 		"generated_android_icu4j_resources",                          // depends on unconverted modules: android_icu4j_srcgen_binary, soong_zip
diff --git a/android/androidmk.go b/android/androidmk.go
index 832c7df..006e43d 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -604,10 +604,6 @@
 		}
 	}
 
-	if len(base.noticeFiles) > 0 {
-		a.SetString("LOCAL_NOTICE_FILE", strings.Join(base.noticeFiles.Strings(), " "))
-	}
-
 	if host {
 		makeOs := base.Os().String()
 		if base.Os() == Linux || base.Os() == LinuxBionic || base.Os() == LinuxMusl {
diff --git a/android/arch.go b/android/arch.go
index e72614c..6acc8ad 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -655,7 +655,8 @@
 	prefer32 := os == Windows
 
 	// Determine the multilib selection for this module.
-	multilib, extraMultilib := decodeMultilib(base, os)
+	ignorePrefer32OnDevice := mctx.Config().IgnorePrefer32OnDevice()
+	multilib, extraMultilib := decodeMultilib(base, os, ignorePrefer32OnDevice)
 
 	// Convert the multilib selection into a list of Targets.
 	targets, err := decodeMultilibTargets(multilib, osTargets, prefer32)
@@ -730,7 +731,7 @@
 // multilib from the factory's call to InitAndroidArchModule if none was set.  For modules that
 // called InitAndroidMultiTargetsArchModule it always returns "common" for multilib, and returns
 // the actual multilib in extraMultilib.
-func decodeMultilib(base *ModuleBase, os OsType) (multilib, extraMultilib string) {
+func decodeMultilib(base *ModuleBase, os OsType, ignorePrefer32OnDevice bool) (multilib, extraMultilib string) {
 	// First check the "android.compile_multilib" or "host.compile_multilib" properties.
 	switch os.Class {
 	case Device:
@@ -749,6 +750,13 @@
 		multilib = base.commonProperties.Default_multilib
 	}
 
+	// If a device is configured with multiple targets, this option
+	// force all device targets that prefer32 to be compiled only as
+	// the first target.
+	if ignorePrefer32OnDevice && os.Class == Device && (multilib == "prefer32" || multilib == "first_prefer32") {
+		multilib = "first"
+	}
+
 	if base.commonProperties.UseTargetVariants {
 		// Darwin has the concept of "universal binaries" which is implemented in Soong by
 		// building both x86_64 and arm64 variants, and having select module types know how to
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 3be9805..5804a46 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -91,6 +91,10 @@
 	osType OsType
 }
 
+func (c configKey) String() string {
+	return fmt.Sprintf("%s::%s", c.arch, c.osType)
+}
+
 // Map key to describe bazel cquery requests.
 type cqueryKey struct {
 	label       string
@@ -98,6 +102,11 @@
 	configKey   configKey
 }
 
+func (c cqueryKey) String() string {
+	return fmt.Sprintf("cquery(%s,%s,%s)", c.label, c.requestType.Name(), c.configKey)
+
+}
+
 // BazelContext is a context object useful for interacting with Bazel during
 // the course of a build. Use of Bazel to evaluate part of the build graph
 // is referred to as a "mixed build". (Some modules are managed by Soong,
@@ -123,6 +132,9 @@
 	// TODO(b/232976601): Remove.
 	GetPythonBinary(label string, cfgKey configKey) (string, error)
 
+	// Returns the results of the GetApexInfo query (including output files)
+	GetApexInfo(label string, cfgkey configKey) (cquery.ApexCqueryInfo, error)
+
 	// ** end Cquery Results Retrieval Functions
 
 	// Issues commands to Bazel to receive results for all cquery requests
@@ -186,6 +198,7 @@
 	LabelToOutputFiles  map[string][]string
 	LabelToCcInfo       map[string]cquery.CcInfo
 	LabelToPythonBinary map[string]string
+	LabelToApexInfo     map[string]cquery.ApexCqueryInfo
 }
 
 func (m MockBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) {
@@ -207,6 +220,10 @@
 	return result, nil
 }
 
+func (n MockBazelContext) GetApexInfo(_ string, _ configKey) (cquery.ApexCqueryInfo, error) {
+	panic("unimplemented")
+}
+
 func (m MockBazelContext) InvokeBazel(_ Config) error {
 	panic("unimplemented")
 }
@@ -261,6 +278,14 @@
 	return "", fmt.Errorf("no bazel response found for %v", key)
 }
 
+func (bazelCtx *bazelContext) GetApexInfo(label string, cfgKey configKey) (cquery.ApexCqueryInfo, error) {
+	key := cqueryKey{label, cquery.GetApexInfo, cfgKey}
+	if rawString, ok := bazelCtx.results[key]; ok {
+		return cquery.GetApexInfo.ParseResult(strings.TrimSpace(rawString)), nil
+	}
+	return cquery.ApexCqueryInfo{}, fmt.Errorf("no bazel response found for %v", key)
+}
+
 func (n noopBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) {
 	panic("unimplemented")
 }
@@ -277,6 +302,10 @@
 	panic("unimplemented")
 }
 
+func (n noopBazelContext) GetApexInfo(_ string, _ configKey) (cquery.ApexCqueryInfo, error) {
+	panic("unimplemented")
+}
+
 func (n noopBazelContext) InvokeBazel(_ Config) error {
 	panic("unimplemented")
 }
@@ -399,19 +428,11 @@
 func (r *builtinBazelRunner) issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand,
 	extraFlags ...string) (string, string, error) {
 	cmdFlags := []string{
-		// --noautodetect_server_javabase has the practical consequence of preventing Bazel from
-		// attempting to download rules_java, which is incompatible with
-		// --experimental_repository_disable_download set further below.
-		// rules_java is also not needed until mixed builds start building java targets.
-		// TODO(b/197958133): Once rules_java is pulled into AOSP, remove this flag.
-		"--noautodetect_server_javabase",
 		"--output_base=" + absolutePath(paths.outputBase),
 		command.command,
-	}
-	cmdFlags = append(cmdFlags, command.expression)
-	cmdFlags = append(cmdFlags,
+		command.expression,
 		// TODO(asmundak): is it needed in every build?
-		"--profile="+shared.BazelMetricsFilename(paths, runName),
+		"--profile=" + shared.BazelMetricsFilename(paths, runName),
 
 		// Set default platforms to canonicalized values for mixed builds requests.
 		// If these are set in the bazelrc, they will have values that are
@@ -432,21 +453,23 @@
 
 		// Suppress noise
 		"--ui_event_filters=-INFO",
-		"--noshow_progress")
+		"--noshow_progress"}
 	cmdFlags = append(cmdFlags, extraFlags...)
 
 	bazelCmd := exec.Command(paths.bazelPath, cmdFlags...)
 	bazelCmd.Dir = absolutePath(paths.syntheticWorkspaceDir())
-	bazelCmd.Env = append(os.Environ(),
-		"HOME="+paths.homeDir,
+	extraEnv := []string{
+		"HOME=" + paths.homeDir,
 		pwdPrefix(),
-		"BUILD_DIR="+absolutePath(paths.soongOutDir),
+		"BUILD_DIR=" + absolutePath(paths.soongOutDir),
 		// Make OUT_DIR absolute here so tools/bazel.sh uses the correct
 		// OUT_DIR at <root>/out, instead of <root>/out/soong/workspace/out.
-		"OUT_DIR="+absolutePath(paths.outDir()),
+		"OUT_DIR=" + absolutePath(paths.outDir()),
 		// Disables local host detection of gcc; toolchain information is defined
 		// explicitly in BUILD files.
-		"BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1")
+		"BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1",
+	}
+	bazelCmd.Env = append(os.Environ(), extraEnv...)
 	stderr := &bytes.Buffer{}
 	bazelCmd.Stderr = stderr
 
@@ -657,6 +680,15 @@
     fail("expected platform name of the form 'android_<arch>' or 'linux_<arch>', but was " + str(platforms))
     return "UNKNOWN"
 
+def json_for_file(key, file):
+    return '"' + key + '":"' + file.path + '"'
+
+def json_for_files(key, files):
+    return '"' + key + '":[' + ",".join(['"' + f.path + '"' for f in files]) + ']'
+
+def json_for_labels(key, ll):
+    return '"' + key + '":[' + ",".join(['"' + str(x) + '"' for x in ll]) + ']'
+
 def format(target):
   id_string = str(target.label) + "|" + get_arch(target)
 
@@ -734,7 +766,7 @@
 	cqueryOutput, cqueryErr, err := context.issueBazelCommand(context.paths, bazel.CqueryBuildRootRunName, cqueryCmd,
 		"--output=starlark", "--starlark:file="+absolutePath(cqueryFileRelpath))
 	if err != nil {
-		err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryOutput), 0666)
+		_ = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryOutput), 0666)
 	}
 	if err != nil {
 		return err
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index 1d0a6d5..c030aa8 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -32,14 +32,14 @@
 // There is often a similar method for Bazel as there is for Soong path handling and should be used
 // in similar circumstances
 //
-// Bazel                                Soong
-//
-// BazelLabelForModuleSrc               PathForModuleSrc
-// BazelLabelForModuleSrcExcludes       PathForModuleSrcExcludes
-// BazelLabelForModuleDeps              n/a
-// tbd                                  PathForSource
-// tbd                                  ExistentPathsForSources
-// PathForBazelOut                      PathForModuleOut
+//   Bazel                                Soong
+//   ==============================================================
+//   BazelLabelForModuleSrc               PathForModuleSrc
+//   BazelLabelForModuleSrcExcludes       PathForModuleSrcExcludes
+//   BazelLabelForModuleDeps              n/a
+//   tbd                                  PathForSource
+//   tbd                                  ExistentPathsForSources
+//   PathForBazelOut                      PathForModuleOut
 //
 // Use cases:
 //  * Module contains a property (often tagged `android:"path"`) that expects paths *relative to the
@@ -68,7 +68,7 @@
 //   cannot be resolved,the function will panic. This is often due to the dependency not being added
 //   via an AddDependency* method.
 
-// A minimal context interface to check if a module should be converted by bp2build,
+// BazelConversionContext is a minimal context interface to check if a module should be converted by bp2build,
 // with functions containing information to match against allowlists and denylists.
 // If a module is deemed to be convertible by bp2build, then it should rely on a
 // BazelConversionPathContext for more functions for dep/path features.
diff --git a/android/config.go b/android/config.go
index 47346fc..8c7d789 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1446,8 +1446,8 @@
 	return Bool(c.productVariables.ForceApexSymlinkOptimization)
 }
 
-func (c *config) CompressedApex() bool {
-	return Bool(c.productVariables.CompressedApex)
+func (c *config) ApexCompressionEnabled() bool {
+	return Bool(c.productVariables.CompressedApex) && !c.UnbundledBuildApps()
 }
 
 func (c *config) EnforceSystemCertificate() bool {
@@ -1725,6 +1725,10 @@
 	return c.config.productVariables.GenerateAidlNdkPlatformBackend
 }
 
+func (c *config) IgnorePrefer32OnDevice() bool {
+	return c.productVariables.IgnorePrefer32OnDevice
+}
+
 // The ConfiguredJarList struct provides methods for handling a list of (apex, jar) pairs.
 // Such lists are used in the build system for things like bootclasspath jars or system server jars.
 // The apex part is either an apex name, or a special names "platform" or "system_ext". Jar is a
diff --git a/android/license_metadata.go b/android/license_metadata.go
index f2ab0a4..4ee5bf7 100644
--- a/android/license_metadata.go
+++ b/android/license_metadata.go
@@ -15,7 +15,6 @@
 package android
 
 import (
-	"fmt"
 	"sort"
 	"strings"
 
@@ -67,6 +66,11 @@
 			return
 		}
 
+		// Defaults add properties and dependencies that get processed on their own.
+		if ctx.OtherModuleDependencyTag(dep) == DefaultsDepTag {
+			return
+		}
+
 		if ctx.OtherModuleHasProvider(dep, LicenseMetadataProvider) {
 			info := ctx.OtherModuleProvider(dep, LicenseMetadataProvider).(*LicenseMetadataInfo)
 			allDepMetadataFiles = append(allDepMetadataFiles, info.LicenseMetadataPath)
@@ -139,8 +143,6 @@
 	if len(outputFiles) > 0 {
 		args = append(args,
 			JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(outputFiles.Strings()), "-t "))
-	} else {
-		args = append(args, fmt.Sprintf("-t //%s:%s", ctx.ModuleDir(), ctx.ModuleName()))
 	}
 
 	// Installed files
diff --git a/android/metrics.go b/android/metrics.go
index 1580f82..ecda026 100644
--- a/android/metrics.go
+++ b/android/metrics.go
@@ -32,8 +32,13 @@
 	Variants int
 }
 
-func ReadSoongMetrics(config Config) SoongMetrics {
-	return config.Get(soongMetricsOnceKey).(SoongMetrics)
+func readSoongMetrics(config Config) (SoongMetrics, bool) {
+	soongMetrics, ok := config.Peek(soongMetricsOnceKey)
+	if ok {
+		return soongMetrics.(SoongMetrics), true
+	} else {
+		return SoongMetrics{}, false
+	}
 }
 
 func init() {
@@ -60,9 +65,11 @@
 func collectMetrics(config Config, eventHandler metrics.EventHandler) *soong_metrics_proto.SoongBuildMetrics {
 	metrics := &soong_metrics_proto.SoongBuildMetrics{}
 
-	soongMetrics := ReadSoongMetrics(config)
-	metrics.Modules = proto.Uint32(uint32(soongMetrics.Modules))
-	metrics.Variants = proto.Uint32(uint32(soongMetrics.Variants))
+	soongMetrics, ok := readSoongMetrics(config)
+	if ok {
+		metrics.Modules = proto.Uint32(uint32(soongMetrics.Modules))
+		metrics.Variants = proto.Uint32(uint32(soongMetrics.Variants))
+	}
 
 	memStats := runtime.MemStats{}
 	runtime.ReadMemStats(&memStats)
diff --git a/android/module.go b/android/module.go
index 2925081..4dbfdd3 100644
--- a/android/module.go
+++ b/android/module.go
@@ -515,7 +515,6 @@
 	ExportedToMake() bool
 	InitRc() Paths
 	VintfFragments() Paths
-	NoticeFiles() Paths
 	EffectiveLicenseFiles() Paths
 
 	AddProperties(props ...interface{})
@@ -831,9 +830,6 @@
 	// names of other modules to install on target if this module is installed
 	Target_required []string `android:"arch_variant"`
 
-	// relative path to a file to include in the list of notices for the device
-	Notice *string `android:"path"`
-
 	// The OsType of artifacts that this module variant is responsible for creating.
 	//
 	// Set by osMutator
@@ -1423,7 +1419,6 @@
 	checkbuildFiles      Paths
 	packagingSpecs       []PackagingSpec
 	packagingSpecsDepSet *packagingSpecsDepSet
-	noticeFiles          Paths
 	// katiInstalls tracks the install rules that were created by Soong but are being exported
 	// to Make to convert to ninja rules so that Make can add additional dependencies.
 	katiInstalls katiInstalls
@@ -2054,10 +2049,6 @@
 	return String(m.commonProperties.Owner)
 }
 
-func (m *ModuleBase) NoticeFiles() Paths {
-	return m.noticeFiles
-}
-
 func (m *ModuleBase) setImageVariation(variant string) {
 	m.commonProperties.ImageVariation = variant
 }
@@ -2317,27 +2308,6 @@
 			}
 		})
 
-		m.noticeFiles = make([]Path, 0)
-		optPath := OptionalPath{}
-		notice := proptools.StringDefault(m.commonProperties.Notice, "")
-		if module := SrcIsModule(notice); module != "" {
-			optPath = ctx.ExpandOptionalSource(&notice, "notice")
-		} else if notice != "" {
-			noticePath := filepath.Join(ctx.ModuleDir(), notice)
-			optPath = ExistentPathForSource(ctx, noticePath)
-		}
-		if optPath.Valid() {
-			m.noticeFiles = append(m.noticeFiles, optPath.Path())
-		} else {
-			for _, notice = range []string{"LICENSE", "LICENCE", "NOTICE"} {
-				noticePath := filepath.Join(ctx.ModuleDir(), notice)
-				optPath = ExistentPathForSource(ctx, noticePath)
-				if optPath.Valid() {
-					m.noticeFiles = append(m.noticeFiles, optPath.Path())
-				}
-			}
-		}
-
 		licensesPropertyFlattener(ctx)
 		if ctx.Failed() {
 			return
diff --git a/android/onceper.go b/android/onceper.go
index 481cdea..fa415d1 100644
--- a/android/onceper.go
+++ b/android/onceper.go
@@ -79,6 +79,17 @@
 	return once.maybeWaitFor(key, v)
 }
 
+// Peek returns the value previously computed with Once for a given key.  If Once has not
+// been called for the given key Peek will return ok == false.
+func (once *OncePer) Peek(key OnceKey) (interface{}, bool) {
+	v, ok := once.values.Load(key)
+	if !ok {
+		return nil, false
+	}
+
+	return once.maybeWaitFor(key, v), true
+}
+
 // OnceStringSlice is the same as Once, but returns the value cast to a []string
 func (once *OncePer) OnceStringSlice(key OnceKey, value func() []string) []string {
 	return once.Once(key, func() interface{} { return value() }).([]string)
diff --git a/android/paths.go b/android/paths.go
index d5cec9a..f8e7018 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1474,7 +1474,7 @@
 // PathForVndkRefAbiDump returns an OptionalPath representing the path of the
 // reference abi dump for the given module. This is not guaranteed to be valid.
 func PathForVndkRefAbiDump(ctx ModuleInstallPathContext, version, fileName string,
-	isNdk, isLlndkOrVndk, isGzip bool) OptionalPath {
+	isNdk, isVndk, isGzip bool) OptionalPath {
 
 	currentArchType := ctx.Arch().ArchType
 	primaryArchType := ctx.Config().DevicePrimaryArchType()
@@ -1486,7 +1486,7 @@
 	var dirName string
 	if isNdk {
 		dirName = "ndk"
-	} else if isLlndkOrVndk {
+	} else if isVndk {
 		dirName = "vndk"
 	} else {
 		dirName = "platform" // opt-in libs
diff --git a/android/sdk.go b/android/sdk.go
index 07b94b2..a71f7f2 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -985,6 +985,10 @@
 
 	// RequiresTrait returns true if this member is expected to provide the specified trait.
 	RequiresTrait(trait SdkMemberTrait) bool
+
+	// IsTargetBuildBeforeTiramisu return true if the target build release for which this snapshot is
+	// being generated is before Tiramisu, i.e. S.
+	IsTargetBuildBeforeTiramisu() bool
 }
 
 // ExportedComponentsInfo contains information about the components that this module exports to an
diff --git a/apex/constants.go b/android/updatable_modules.go
similarity index 82%
rename from apex/constants.go
rename to android/updatable_modules.go
index c68edb7..71c76c5 100644
--- a/apex/constants.go
+++ b/android/updatable_modules.go
@@ -12,11 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package apex
+package android
 
-// This file contains branch specific constants. They are stored in a separate
-// file to minimise the potential of merge conflicts between branches when
-// the code from the package is changed.
+// This file contains branch specific constants for building updatable modules.
+// They are stored in a separate file to minimise the potential of merge
+// conflicts between branches when the code from the package is changed.
 
 // The default manifest version for all the modules on this branch.
 // This version code will be used only if there is no version field in the
@@ -33,4 +33,4 @@
 // * AOSP            - xx9990000
 // * x-mainline-prod - xx9990000
 // * master          - 990090000
-const defaultManifestVersion = "339990000"
+const DefaultUpdatableModuleVersion = "339990000"
diff --git a/android/variable.go b/android/variable.go
index 50fc304..874b69d 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -447,6 +447,8 @@
 	SepolicyFreezeTestExtraPrebuiltDirs []string `json:",omitempty"`
 
 	GenerateAidlNdkPlatformBackend bool `json:",omitempty"`
+
+	IgnorePrefer32OnDevice bool `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/apex/Android.bp b/apex/Android.bp
index 6533c61..018d030 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -26,7 +26,7 @@
         "apex_sdk_member.go",
         "apex_singleton.go",
         "builder.go",
-        "constants.go",
+        "bp2build.go",
         "deapexer.go",
         "key.go",
         "prebuilt.go",
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 938c8ed..3373211 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -168,10 +168,6 @@
 			if len(newDataPaths) > 0 {
 				fmt.Fprintln(w, "LOCAL_TEST_DATA :=", strings.Join(android.AndroidMkDataPaths(newDataPaths), " "))
 			}
-
-			if fi.module != nil && len(fi.module.NoticeFiles()) > 0 {
-				fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", strings.Join(fi.module.NoticeFiles().Strings(), " "))
-			}
 		} else {
 			modulePath = pathWhenActivated
 			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", pathWhenActivated)
diff --git a/apex/apex.go b/apex/apex.go
index 7b6707c..09a5784 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -17,6 +17,7 @@
 package apex
 
 import (
+	"android/soong/bazel/cquery"
 	"fmt"
 	"path/filepath"
 	"regexp"
@@ -639,7 +640,7 @@
 	fsTag           = &dependencyTag{name: "filesystem", payload: true}
 	bcpfTag         = &dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true, memberType: java.BootclasspathFragmentSdkMemberType}
 	sscpfTag        = &dependencyTag{name: "systemserverclasspathFragment", payload: true, sourceOnly: true, memberType: java.SystemServerClasspathFragmentSdkMemberType}
-	compatConfigTag = &dependencyTag{name: "compatConfig", payload: true, sourceOnly: true}
+	compatConfigTag = &dependencyTag{name: "compatConfig", payload: true, sourceOnly: true, memberType: java.CompatConfigSdkMemberType}
 	javaLibTag      = &dependencyTag{name: "javaLib", payload: true}
 	jniLibTag       = &dependencyTag{name: "jniLib", payload: true}
 	keyTag          = &dependencyTag{name: "key"}
@@ -1803,6 +1804,184 @@
 	}
 }
 
+var _ android.MixedBuildBuildable = (*apexBundle)(nil)
+
+func (a *apexBundle) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
+	return ctx.ModuleType() == "apex" && a.properties.ApexType == imageApex
+}
+
+func (a *apexBundle) QueueBazelCall(ctx android.BaseModuleContext) {
+	bazelCtx := ctx.Config().BazelContext
+	bazelCtx.QueueBazelRequest(a.GetBazelLabel(ctx, a), cquery.GetApexInfo, android.GetConfigKey(ctx))
+}
+
+func (a *apexBundle) ProcessBazelQueryResponse(ctx android.ModuleContext) {
+	if !a.commonBuildActions(ctx) {
+		return
+	}
+
+	a.setApexTypeAndSuffix(ctx)
+	a.setPayloadFsType(ctx)
+	a.setSystemLibLink(ctx)
+
+	if a.properties.ApexType != zipApex {
+		a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx, a.primaryApexType)
+	}
+
+	bazelCtx := ctx.Config().BazelContext
+	outputs, err := bazelCtx.GetApexInfo(a.GetBazelLabel(ctx, a), android.GetConfigKey(ctx))
+	if err != nil {
+		ctx.ModuleErrorf(err.Error())
+		return
+	}
+	a.installDir = android.PathForModuleInstall(ctx, "apex")
+	a.outputApexFile = android.PathForBazelOut(ctx, outputs.SignedOutput)
+	a.outputFile = a.outputApexFile
+	a.setCompression(ctx)
+
+	a.publicKeyFile = android.PathForBazelOut(ctx, outputs.BundleKeyPair[0])
+	a.privateKeyFile = android.PathForBazelOut(ctx, outputs.BundleKeyPair[1])
+	a.containerCertificateFile = android.PathForBazelOut(ctx, outputs.ContainerKeyPair[0])
+	a.containerPrivateKeyFile = android.PathForBazelOut(ctx, outputs.ContainerKeyPair[1])
+	apexType := a.properties.ApexType
+	switch apexType {
+	case imageApex:
+		// TODO(asmundak): Bazel does not create these files yet.
+		// b/190817312
+		a.htmlGzNotice = android.PathForBazelOut(ctx, "NOTICE.html.gz")
+		// b/239081457
+		a.bundleModuleFile = android.PathForBazelOut(ctx, a.Name()+apexType.suffix()+"-base.zip")
+		// b/239081455
+		a.nativeApisUsedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, a.Name()+"_using.txt"))
+		// b/239081456
+		a.nativeApisBackedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, a.Name()+"_backing.txt"))
+		// b/239084755
+		a.javaApisUsedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, a.Name()+"_using.xml"))
+		installSuffix := imageApexSuffix
+		if a.isCompressed {
+			installSuffix = imageCapexSuffix
+		}
+		a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile,
+			a.compatSymlinks.Paths()...)
+	default:
+		panic(fmt.Errorf("unexpected apex_type for the ProcessBazelQuery: %v", a.properties.ApexType))
+	}
+
+	/*
+			TODO(asmundak): compared to building an APEX with Soong, building it with Bazel does not
+			return filesInfo and requiredDeps fields (in the Soong build the latter is updated).
+			Fix this, as these fields are subsequently used in apex/androidmk.go and in apex/builder/go
+			To find out what Soong build puts there, run:
+			vctx := visitorContext{handleSpecialLibs: !android.Bool(a.properties.Ignore_system_library_special_case)}
+			ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
+		      return a.depVisitor(&vctx, ctx, child, parent)
+		    })
+			vctx.normalizeFileInfo()
+	*/
+
+}
+
+func (a *apexBundle) setCompression(ctx android.ModuleContext) {
+	if a.properties.ApexType != imageApex {
+		a.isCompressed = false
+	} else if a.testOnlyShouldForceCompression() {
+		a.isCompressed = true
+	} else {
+		a.isCompressed = ctx.Config().ApexCompressionEnabled() && a.isCompressable()
+	}
+}
+
+func (a *apexBundle) setSystemLibLink(ctx android.ModuleContext) {
+	// Optimization. If we are building bundled APEX, for the files that are gathered due to the
+	// transitive dependencies, don't place them inside the APEX, but place a symlink pointing
+	// the same library in the system partition, thus effectively sharing the same libraries
+	// across the APEX boundary. For unbundled APEX, all the gathered files are actually placed
+	// in the APEX.
+	a.linkToSystemLib = !ctx.Config().UnbundledBuild() && a.installable()
+
+	// APEXes targeting other than system/system_ext partitions use vendor/product variants.
+	// So we can't link them to /system/lib libs which are core variants.
+	if a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
+		a.linkToSystemLib = false
+	}
+
+	forced := ctx.Config().ForceApexSymlinkOptimization()
+	updatable := a.Updatable() || a.FutureUpdatable()
+
+	// We don't need the optimization for updatable APEXes, as it might give false signal
+	// to the system health when the APEXes are still bundled (b/149805758).
+	if !forced && updatable && a.properties.ApexType == imageApex {
+		a.linkToSystemLib = false
+	}
+
+	// We also don't want the optimization for host APEXes, because it doesn't make sense.
+	if ctx.Host() {
+		a.linkToSystemLib = false
+	}
+}
+
+func (a *apexBundle) setPayloadFsType(ctx android.ModuleContext) {
+	switch proptools.StringDefault(a.properties.Payload_fs_type, ext4FsType) {
+	case ext4FsType:
+		a.payloadFsType = ext4
+	case f2fsFsType:
+		a.payloadFsType = f2fs
+	case erofsFsType:
+		a.payloadFsType = erofs
+	default:
+		ctx.PropertyErrorf("payload_fs_type", "%q is not a valid filesystem for apex [ext4, f2fs, erofs]", *a.properties.Payload_fs_type)
+	}
+}
+
+func (a *apexBundle) setApexTypeAndSuffix(ctx android.ModuleContext) {
+	// Set suffix and primaryApexType depending on the ApexType
+	buildFlattenedAsDefault := ctx.Config().FlattenApex()
+	switch a.properties.ApexType {
+	case imageApex:
+		if buildFlattenedAsDefault {
+			a.suffix = imageApexSuffix
+		} else {
+			a.suffix = ""
+			a.primaryApexType = true
+
+			if ctx.Config().InstallExtraFlattenedApexes() {
+				a.requiredDeps = append(a.requiredDeps, a.Name()+flattenedSuffix)
+			}
+		}
+	case zipApex:
+		if proptools.String(a.properties.Payload_type) == "zip" {
+			a.suffix = ""
+			a.primaryApexType = true
+		} else {
+			a.suffix = zipApexSuffix
+		}
+	case flattenedApex:
+		if buildFlattenedAsDefault {
+			a.suffix = ""
+			a.primaryApexType = true
+		} else {
+			a.suffix = flattenedSuffix
+		}
+	}
+}
+
+func (a apexBundle) isCompressable() bool {
+	return proptools.BoolDefault(a.overridableProperties.Compressible, false) && !a.testApex
+}
+
+func (a *apexBundle) commonBuildActions(ctx android.ModuleContext) bool {
+	a.checkApexAvailability(ctx)
+	a.checkUpdatable(ctx)
+	a.CheckMinSdkVersion(ctx)
+	a.checkStaticLinkingToStubLibraries(ctx)
+	a.checkStaticExecutables(ctx)
+	if len(a.properties.Tests) > 0 && !a.testApex {
+		ctx.PropertyErrorf("tests", "property allowed only in apex_test module type")
+		return false
+	}
+	return true
+}
+
 type visitorContext struct {
 	// all the files that will be included in this APEX
 	filesInfo []apexFile
@@ -2188,16 +2367,9 @@
 func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 1) do some validity checks such as apex_available, min_sdk_version, etc.
-	a.checkApexAvailability(ctx)
-	a.checkUpdatable(ctx)
-	a.CheckMinSdkVersion(ctx)
-	a.checkStaticLinkingToStubLibraries(ctx)
-	a.checkStaticExecutables(ctx)
-	if len(a.properties.Tests) > 0 && !a.testApex {
-		ctx.PropertyErrorf("tests", "property allowed only in apex_test module type")
+	if !a.commonBuildActions(ctx) {
 		return
 	}
-
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 2) traverse the dependency tree to collect apexFile structs from them.
 
@@ -2219,74 +2391,9 @@
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	a.filesInfo = vctx.filesInfo
 
-	// Set suffix and primaryApexType depending on the ApexType
-	buildFlattenedAsDefault := ctx.Config().FlattenApex()
-	switch a.properties.ApexType {
-	case imageApex:
-		if buildFlattenedAsDefault {
-			a.suffix = imageApexSuffix
-		} else {
-			a.suffix = ""
-			a.primaryApexType = true
-
-			if ctx.Config().InstallExtraFlattenedApexes() {
-				a.requiredDeps = append(a.requiredDeps, a.Name()+flattenedSuffix)
-			}
-		}
-	case zipApex:
-		if proptools.String(a.properties.Payload_type) == "zip" {
-			a.suffix = ""
-			a.primaryApexType = true
-		} else {
-			a.suffix = zipApexSuffix
-		}
-	case flattenedApex:
-		if buildFlattenedAsDefault {
-			a.suffix = ""
-			a.primaryApexType = true
-		} else {
-			a.suffix = flattenedSuffix
-		}
-	}
-
-	switch proptools.StringDefault(a.properties.Payload_fs_type, ext4FsType) {
-	case ext4FsType:
-		a.payloadFsType = ext4
-	case f2fsFsType:
-		a.payloadFsType = f2fs
-	case erofsFsType:
-		a.payloadFsType = erofs
-	default:
-		ctx.PropertyErrorf("payload_fs_type", "%q is not a valid filesystem for apex [ext4, f2fs, erofs]", *a.properties.Payload_fs_type)
-	}
-
-	// Optimization. If we are building bundled APEX, for the files that are gathered due to the
-	// transitive dependencies, don't place them inside the APEX, but place a symlink pointing
-	// the same library in the system partition, thus effectively sharing the same libraries
-	// across the APEX boundary. For unbundled APEX, all the gathered files are actually placed
-	// in the APEX.
-	a.linkToSystemLib = !ctx.Config().UnbundledBuild() && a.installable()
-
-	// APEXes targeting other than system/system_ext partitions use vendor/product variants.
-	// So we can't link them to /system/lib libs which are core variants.
-	if a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
-		a.linkToSystemLib = false
-	}
-
-	forced := ctx.Config().ForceApexSymlinkOptimization()
-	updatable := a.Updatable() || a.FutureUpdatable()
-
-	// We don't need the optimization for updatable APEXes, as it might give false signal
-	// to the system health when the APEXes are still bundled (b/149805758).
-	if !forced && updatable && a.properties.ApexType == imageApex {
-		a.linkToSystemLib = false
-	}
-
-	// We also don't want the optimization for host APEXes, because it doesn't make sense.
-	if ctx.Host() {
-		a.linkToSystemLib = false
-	}
-
+	a.setApexTypeAndSuffix(ctx)
+	a.setPayloadFsType(ctx)
+	a.setSystemLibLink(ctx)
 	if a.properties.ApexType != zipApex {
 		a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx, a.primaryApexType)
 	}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index dbe9180..5282941 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -443,7 +443,6 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
-			notice: "custom_notice",
 			static_libs: ["libstatic"],
 			// TODO: remove //apex_available:platform
 			apex_available: [
@@ -467,7 +466,6 @@
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
 			stl: "none",
-			notice: "custom_notice_for_static_lib",
 			// TODO: remove //apex_available:platform
 			apex_available: [
 				"//apex_available:platform",
@@ -619,7 +617,7 @@
 			java_libs: ["myjar"],
 			apps: ["AppFoo"],
 			rros: ["rro"],
-			bpfs: ["bpf", "netd_test"],
+			bpfs: ["bpf", "netdTest"],
 			updatable: false,
 		}
 
@@ -673,8 +671,8 @@
 		}
 
 		bpf {
-			name: "netd_test",
-			srcs: ["netd_test.c"],
+			name: "netdTest",
+			srcs: ["netdTest.c"],
 			sub_dir: "netd",
 		}
 
@@ -687,7 +685,7 @@
 		"overlay/blue/rro.apk",
 		"etc/bpf/bpf.o",
 		"etc/bpf/bpf2.o",
-		"etc/bpf/netd/netd_test.o",
+		"etc/bpf/netd/netdTest.o",
 	})
 }
 
@@ -949,8 +947,10 @@
 	// mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 	// ensureNotContains(t, mylib2Cflags, "-include ")
 
-	// Ensure that genstub is invoked with --apex
-	ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"])
+	// Ensure that genstub for platform-provided lib is invoked with --systemapi
+	ensureContains(t, ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"], "--systemapi")
+	// Ensure that genstub for apex-provided lib is invoked with --apex
+	ensureContains(t, ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_shared_12").Rule("genStubSrc").Args["flags"], "--apex")
 
 	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
 		"lib64/mylib.so",
@@ -1134,8 +1134,8 @@
 	mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_29").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylib2Cflags, "-include ")
 
-	// Ensure that genstub is invoked with --apex
-	ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_29").Rule("genStubSrc").Args["flags"])
+	// Ensure that genstub is invoked with --systemapi
+	ensureContains(t, ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_29").Rule("genStubSrc").Args["flags"], "--systemapi")
 
 	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
 		"lib64/mylib.so",
@@ -6151,7 +6151,7 @@
 			name: "override_myapex",
 			base: "myapex",
 			apps: ["override_app"],
-			bpfs: ["override_bpf"],
+			bpfs: ["overrideBpf"],
 			prebuilts: ["override_myetc"],
 			bootclasspath_fragments: ["override_bootclasspath_fragment"],
 			systemserverclasspath_fragments: ["override_systemserverclasspath_fragment"],
@@ -6201,8 +6201,8 @@
 		}
 
 		bpf {
-			name: "override_bpf",
-			srcs: ["override_bpf.c"],
+			name: "overrideBpf",
+			srcs: ["overrideBpf.c"],
 		}
 
 		prebuilt_etc {
@@ -6305,7 +6305,7 @@
 	ensureContains(t, copyCmds, "image.apex/app/override_app@TEST.BUILD_ID/override_app.apk")
 
 	ensureNotContains(t, copyCmds, "image.apex/etc/bpf/bpf.o")
-	ensureContains(t, copyCmds, "image.apex/etc/bpf/override_bpf.o")
+	ensureContains(t, copyCmds, "image.apex/etc/bpf/overrideBpf.o")
 
 	ensureNotContains(t, copyCmds, "image.apex/etc/myetc")
 	ensureContains(t, copyCmds, "image.apex/etc/override_myetc")
@@ -6339,7 +6339,7 @@
 	data.Custom(&builder, name, "TARGET_", "", data)
 	androidMk := builder.String()
 	ensureContains(t, androidMk, "LOCAL_MODULE := override_app.override_myapex")
-	ensureContains(t, androidMk, "LOCAL_MODULE := override_bpf.o.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")
diff --git a/apex/bp2build.go b/apex/bp2build.go
new file mode 100644
index 0000000..221ab13
--- /dev/null
+++ b/apex/bp2build.go
@@ -0,0 +1,30 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package apex
+
+import (
+	"android/soong/android"
+	"strings"
+)
+
+// This file contains the bp2build integration for the apex package.
+
+// Export constants as Starlark using bp2build to Bazel.
+func BazelApexToolchainVars() string {
+	content := []string{
+		"# GENERATED BY SOONG. DO NOT EDIT.",
+		"default_manifest_version = " + android.DefaultUpdatableModuleVersion, // constants.go is different in every branch.
+	}
+	return strings.Join(content, "\n")
+}
diff --git a/apex/builder.go b/apex/builder.go
index 1956b44..f1b1448 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -214,7 +214,7 @@
 		Args: map[string]string{
 			"provideNativeLibs": strings.Join(provideNativeLibs, " "),
 			"requireNativeLibs": strings.Join(requireNativeLibs, " "),
-			"default_version":   defaultManifestVersion,
+			"default_version":   android.DefaultUpdatableModuleVersion,
 			"opt":               strings.Join(optCommands, " "),
 		},
 	})
@@ -549,8 +549,6 @@
 	outHostBinDir := ctx.Config().HostToolPath(ctx, "").String()
 	prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
 
-	// Figure out if we need to compress the apex.
-	compressionEnabled := ctx.Config().CompressedApex() && proptools.BoolDefault(a.overridableProperties.Compressible, false) && !a.testApex && !ctx.Config().UnbundledBuildApps()
 	if apexType == imageApex {
 
 		////////////////////////////////////////////////////////////////////////////////////
@@ -635,10 +633,15 @@
 		implicitInputs = append(implicitInputs, noticeAssetPath)
 		optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeAssetPath.String()))
 
-		if (moduleMinSdkVersion.GreaterThan(android.SdkVersion_Android10) && !a.shouldGenerateHashtree()) && !compressionEnabled {
-			// Apexes which are supposed to be installed in builtin dirs(/system, etc)
-			// don't need hashtree for activation. Therefore, by removing hashtree from
-			// apex bundle (filesystem image in it, to be specific), we can save storage.
+		// Apexes which are supposed to be installed in builtin dirs(/system, etc)
+		// don't need hashtree for activation. Therefore, by removing hashtree from
+		// apex bundle (filesystem image in it, to be specific), we can save storage.
+		needHashTree := moduleMinSdkVersion.LessThanOrEqualTo(android.SdkVersion_Android10) ||
+			a.shouldGenerateHashtree()
+		if ctx.Config().ApexCompressionEnabled() && a.isCompressable() {
+			needHashTree = true
+		}
+		if !needHashTree {
 			optFlags = append(optFlags, "--no_hashtree")
 		}
 
@@ -806,8 +809,9 @@
 		return
 	}
 
-	if apexType == imageApex && (compressionEnabled || a.testOnlyShouldForceCompression()) {
-		a.isCompressed = true
+	installSuffix := suffix
+	a.setCompression(ctx)
+	if a.isCompressed {
 		unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+imageCapexSuffix+".unsigned")
 
 		compressRule := android.NewRuleBuilder(pctx, ctx)
@@ -835,10 +839,6 @@
 			Args:        args,
 		})
 		a.outputFile = signedCompressedOutputFile
-	}
-
-	installSuffix := suffix
-	if a.isCompressed {
 		installSuffix = imageCapexSuffix
 	}
 
diff --git a/apex/vndk_test.go b/apex/vndk_test.go
index d580e5a..21526c3 100644
--- a/apex/vndk_test.go
+++ b/apex/vndk_test.go
@@ -86,7 +86,6 @@
 			},
 			system_shared_libs: [],
 			stl: "none",
-			notice: "custom_notice",
 		}
 		` + vndkLibrariesTxtFiles("current")
 
diff --git a/bazel/aquery.go b/bazel/aquery.go
index ae2b107..418b143 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -574,7 +574,7 @@
 
 // expandTemplateContent substitutes the tokens in a template.
 func expandTemplateContent(actionEntry action) string {
-	replacerString := []string{}
+	var replacerString []string
 	for _, pair := range actionEntry.Substitutions {
 		value := pair.Value
 		if val, ok := templateActionOverriddenTokens[pair.Key]; ok {
@@ -647,7 +647,7 @@
 		}
 		labels = append([]string{currFragment.Label}, labels...)
 		if currId == currFragment.ParentId {
-			return "", fmt.Errorf("Fragment cannot refer to itself as parent %#v", currFragment)
+			return "", fmt.Errorf("fragment cannot refer to itself as parent %#v", currFragment)
 		}
 		currId = currFragment.ParentId
 	}
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index 3a2bf0f..53056c7 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -499,19 +499,11 @@
 
 func TestBazelOutRemovalFromInputDepsets(t *testing.T) {
 	const inputString = `{
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 10
-  }, {
-    "id": 2,
-    "pathFragmentId": 20
-  }, {
-    "id": 3,
-    "pathFragmentId": 30
-  }, {
-    "id": 4,
-    "pathFragmentId": 40
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 10 },
+    { "id": 2, "pathFragmentId": 20 },
+    { "id": 3, "pathFragmentId": 30 },
+    { "id": 4, "pathFragmentId": 40 }],
   "depSetOfFiles": [{
     "id": 1111,
     "directArtifactIds": [3 , 4]
@@ -525,28 +517,14 @@
     "outputIds": [2],
     "primaryOutputId": 1
   }],
-  "pathFragments": [{
-    "id": 10,
-    "label": "input"
-  }, {
-    "id": 20,
-    "label": "output"
-  }, {
-    "id": 30,
-    "label": "dep1",
-    "parentId": 50
-  }, {
-    "id": 40,
-    "label": "dep2",
-    "parentId": 60
-  }, {
-    "id": 50,
-    "label": "bazel_tools",
-    "parentId": 60
-  }, {
-    "id": 60,
-    "label": ".."
-  }]
+  "pathFragments": [
+    { "id": 10, "label": "input" },
+    { "id": 20, "label": "output" },
+    { "id": 30, "label": "dep1", "parentId": 50 },
+    { "id": 40, "label": "dep2", "parentId": 60 },
+    { "id": 50, "label": "bazel_tools", "parentId": 60 },
+    { "id": 60, "label": ".."}
+  ]
 }`
 	actualBuildStatements, actualDepsets, _ := AqueryBuildStatements([]byte(inputString))
 	if len(actualDepsets) != 1 {
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index f5435f2..d32e619 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -1,6 +1,7 @@
 package cquery
 
 import (
+	"encoding/json"
 	"fmt"
 	"strings"
 )
@@ -9,6 +10,7 @@
 	GetOutputFiles  = &getOutputFilesRequestType{}
 	GetPythonBinary = &getPythonBinaryRequestType{}
 	GetCcInfo       = &getCcInfoType{}
+	GetApexInfo     = &getApexInfoType{}
 )
 
 type CcInfo struct {
@@ -179,7 +181,7 @@
 	const expectedLen = 10
 	splitString := strings.Split(rawString, "|")
 	if len(splitString) != expectedLen {
-		return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString)
+		return CcInfo{}, fmt.Errorf("expected %d items, got %q", expectedLen, splitString)
 	}
 	outputFilesString := splitString[0]
 	ccObjectsString := splitString[1]
@@ -215,6 +217,54 @@
 	}, nil
 }
 
+// Query Bazel for the artifacts generated by the apex modules.
+type getApexInfoType struct{}
+
+// Name returns a string name for this request type. Such request type names must be unique,
+// and must only consist of alphanumeric characters.
+func (g getApexInfoType) Name() string {
+	return "getApexInfo"
+}
+
+// StarlarkFunctionBody returns a starlark function body to process this request type.
+// The returned string is the body of a Starlark function which obtains
+// all request-relevant information about a target and returns a string containing
+// this information. The function should have the following properties:
+//   - `target` is the only parameter to this function (a configured target).
+//   - The return value must be a string.
+//   - The function body should not be indented outside of its own scope.
+func (g getApexInfoType) StarlarkFunctionBody() string {
+	return `info = providers(target)["//build/bazel/rules/apex:apex.bzl%ApexInfo"]
+return "{%s}" % ",".join([
+    json_for_file("signed_output", info.signed_output),
+    json_for_file("unsigned_output", info.unsigned_output),
+    json_for_labels("provides_native_libs", info.provides_native_libs),
+    json_for_labels("requires_native_libs", info.requires_native_libs),
+    json_for_files("bundle_key_pair", info.bundle_key_pair),
+    json_for_files("container_key_pair", info.container_key_pair)
+    ])`
+}
+
+type ApexCqueryInfo struct {
+	SignedOutput     string   `json:"signed_output"`
+	UnsignedOutput   string   `json:"unsigned_output"`
+	ProvidesLibs     []string `json:"provides_native_libs"`
+	RequiresLibs     []string `json:"requires_native_libs"`
+	BundleKeyPair    []string `json:"bundle_key_pair"`
+	ContainerKeyPair []string `json:"container_key_pair"`
+}
+
+// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
+// The given rawString must correspond to the string output which was created by evaluating the
+// Starlark given in StarlarkFunctionBody.
+func (g getApexInfoType) ParseResult(rawString string) ApexCqueryInfo {
+	var info ApexCqueryInfo
+	if err := json.Unmarshal([]byte(rawString), &info); err != nil {
+		panic(fmt.Errorf("cannot parse cquery result '%s': %s", rawString, err))
+	}
+	return info
+}
+
 // splitOrEmpty is a modification of strings.Split() that returns an empty list
 // if the given string is empty.
 func splitOrEmpty(s string, sep string) []string {
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 606e285..34248ce 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -148,13 +148,13 @@
 			description:          "too few result splits",
 			input:                "|",
 			expectedOutput:       CcInfo{},
-			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", expectedSplits, []string{"", ""}),
+			expectedErrorMessage: fmt.Sprintf("expected %d items, got %q", expectedSplits, []string{"", ""}),
 		},
 		{
 			description:          "too many result splits",
 			input:                strings.Repeat("|", expectedSplits+1), // 2 too many
 			expectedOutput:       CcInfo{},
-			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", expectedSplits, make([]string, expectedSplits+2)),
+			expectedErrorMessage: fmt.Sprintf("expected %d items, got %q", expectedSplits, make([]string, expectedSplits+2)),
 		},
 	}
 	for _, tc := range testCases {
@@ -167,3 +167,40 @@
 		}
 	}
 }
+
+func TestGetApexInfoParseResults(t *testing.T) {
+	testCases := []struct {
+		description    string
+		input          string
+		expectedOutput ApexCqueryInfo
+	}{
+		{
+			description:    "no result",
+			input:          "{}",
+			expectedOutput: ApexCqueryInfo{},
+		},
+		{
+			description: "one result",
+			input: `{"signed_output":"my.apex",` +
+				`"unsigned_output":"my.apex.unsigned",` +
+				`"requires_native_libs":["//bionic/libc:libc","//bionic/libdl:libdl"],` +
+				`"bundle_key_pair":["foo.pem","foo.privkey"],` +
+				`"container_key_pair":["foo.x509.pem", "foo.pk8"],` +
+				`"provides_native_libs":[]}`,
+			expectedOutput: ApexCqueryInfo{
+				SignedOutput:     "my.apex",
+				UnsignedOutput:   "my.apex.unsigned",
+				RequiresLibs:     []string{"//bionic/libc:libc", "//bionic/libdl:libdl"},
+				ProvidesLibs:     []string{},
+				BundleKeyPair:    []string{"foo.pem", "foo.privkey"},
+				ContainerKeyPair: []string{"foo.x509.pem", "foo.pk8"},
+			},
+		},
+	}
+	for _, tc := range testCases {
+		actualOutput := GetApexInfo.ParseResult(tc.input)
+		if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+			t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
+		}
+	}
+}
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 34548ed..07557d6 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -20,15 +20,16 @@
         "soong-android",
         "soong-android-allowlists",
         "soong-android-soongconfig",
-        "soong-shared",
         "soong-apex",
         "soong-bazel",
         "soong-cc",
         "soong-cc-config",
         "soong-etc",
         "soong-genrule",
+        "soong-linkerconfig",
         "soong-python",
         "soong-sh",
+        "soong-shared",
         "soong-starlark-format",
         "soong-ui-metrics",
     ],
@@ -58,6 +59,7 @@
         "java_library_host_conversion_test.go",
         "java_plugin_conversion_test.go",
         "java_proto_conversion_test.go",
+        "linker_config_conversion_test.go",
         "performance_test.go",
         "prebuilt_etc_conversion_test.go",
         "python_binary_conversion_test.go",
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index b0c3899..0e3d2a5 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -29,7 +29,9 @@
 func Codegen(ctx *CodegenContext) CodegenMetrics {
 	// This directory stores BUILD files that could be eventually checked-in.
 	bp2buildDir := android.PathForOutput(ctx, "bp2build")
-	android.RemoveAllOutputDir(bp2buildDir)
+	if err := android.RemoveAllOutputDir(bp2buildDir); err != nil {
+		fmt.Printf("ERROR: Encountered error while cleaning %s: %s", bp2buildDir, err.Error())
+	}
 
 	res, errs := GenerateBazelTargets(ctx, true)
 	if len(errs) > 0 {
@@ -40,7 +42,7 @@
 		fmt.Printf("ERROR: Encountered %d error(s): \nERROR: %s", len(errs), strings.Join(errMsgs, "\n"))
 		os.Exit(1)
 	}
-	bp2buildFiles := CreateBazelFiles(nil, res.buildFileToTargets, ctx.mode)
+	bp2buildFiles := CreateBazelFiles(ctx.Config(), nil, res.buildFileToTargets, ctx.mode)
 	writeFiles(ctx, bp2buildDir, bp2buildFiles)
 
 	soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName)
@@ -52,7 +54,9 @@
 // Get the output directory and create it if it doesn't exist.
 func getOrCreateOutputDir(outputDir android.OutputPath, ctx android.PathContext, dir string) android.OutputPath {
 	dirPath := outputDir.Join(ctx, dir)
-	android.CreateOutputDirIfNonexistent(dirPath, os.ModePerm)
+	if err := android.CreateOutputDirIfNonexistent(dirPath, os.ModePerm); err != nil {
+		fmt.Printf("ERROR: path %s: %s", dirPath, err.Error())
+	}
 	return dirPath
 }
 
@@ -60,13 +64,13 @@
 func writeFiles(ctx android.PathContext, outputDir android.OutputPath, files []BazelFile) {
 	for _, f := range files {
 		p := getOrCreateOutputDir(outputDir, ctx, f.Dir).Join(ctx, f.Basename)
-		if err := writeFile(ctx, p, f.Contents); err != nil {
+		if err := writeFile(p, f.Contents); err != nil {
 			panic(fmt.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err))
 		}
 	}
 }
 
-func writeFile(ctx android.PathContext, pathToFile android.OutputPath, content string) error {
+func writeFile(pathToFile android.OutputPath, content string) error {
 	// These files are made editable to allow users to modify and iterate on them
 	// in the source tree.
 	return android.WriteFileToOutputDir(pathToFile, []byte(content), 0644)
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index a96a3fc..415becb 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -161,22 +161,22 @@
 	unconvertedDepMode unconvertedDepsMode
 }
 
-func (c *CodegenContext) Mode() CodegenMode {
-	return c.mode
+func (ctx *CodegenContext) Mode() CodegenMode {
+	return ctx.mode
 }
 
 // CodegenMode is an enum to differentiate code-generation modes.
 type CodegenMode int
 
 const (
-	// Bp2Build: generate BUILD files with targets buildable by Bazel directly.
+	// Bp2Build - generate BUILD files with targets buildable by Bazel directly.
 	//
 	// This mode is used for the Soong->Bazel build definition conversion.
 	Bp2Build CodegenMode = iota
 
-	// QueryView: generate BUILD files with targets representing fully mutated
+	// QueryView - generate BUILD files with targets representing fully mutated
 	// Soong modules, representing the fully configured Soong module graph with
-	// variants and dependency endges.
+	// variants and dependency edges.
 	//
 	// This mode is used for discovering and introspecting the existing Soong
 	// module graph.
@@ -470,13 +470,13 @@
 		})
 	}
 
-	for p, _ := range ignoredPropNames {
+	for p := range ignoredPropNames {
 		delete(props.Attrs, p)
 	}
 	attributes := propsToAttributes(props.Attrs)
 
 	depLabelList := "[\n"
-	for depLabel, _ := range depLabels {
+	for depLabel := range depLabels {
 		depLabelList += fmt.Sprintf("        %q,\n", depLabel)
 	}
 	depLabelList += "    ]"
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index f3345a6..6cb9509 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -189,7 +189,7 @@
 			content: "irrelevant",
 		},
 	}
-	files := CreateBazelFiles(ruleShims, make(map[string]BazelTargets), QueryView)
+	files := CreateBazelFiles(android.NullConfig("out", "out/soong"), ruleShims, make(map[string]BazelTargets), QueryView)
 
 	var actualSoongModuleBzl BazelFile
 	for _, f := range files {
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index be09616..74729e4 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -569,3 +569,58 @@
 		},
 	})
 }
+
+func TestCcLibrarySharedClangUnknownFlags(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		blueprint: soongCcProtoPreamble + `cc_library_shared {
+	name: "foo",
+	conlyflags: ["-a", "-finline-functions"],
+	cflags: ["-b","-finline-functions"],
+	cppflags: ["-c", "-finline-functions"],
+	ldflags: ["-d","-finline-functions", "-e"],
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+				"conlyflags": `["-a"]`,
+				"copts":      `["-b"]`,
+				"cppflags":   `["-c"]`,
+				"linkopts": `[
+        "-d",
+        "-e",
+    ]`,
+			}),
+		},
+	})
+}
+
+func TestCCLibraryFlagSpaceSplitting(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		blueprint: soongCcProtoPreamble + `cc_library_shared {
+	name: "foo",
+	conlyflags: [ "-include header.h"],
+	cflags: ["-include header.h"],
+	cppflags: ["-include header.h"],
+	version_script: "version_script",
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+				"additional_linker_inputs": `["version_script"]`,
+				"conlyflags": `[
+        "-include",
+        "header.h",
+    ]`,
+				"copts": `[
+        "-include",
+        "header.h",
+    ]`,
+				"cppflags": `[
+        "-include",
+        "header.h",
+    ]`,
+				"linkopts": `["-Wl,--version-script,$(location version_script)"]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 4b013d9..8cf9bea 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -10,6 +10,8 @@
 	cc_config "android/soong/cc/config"
 	java_config "android/soong/java/config"
 
+	"android/soong/apex"
+
 	"github.com/google/blueprint/proptools"
 )
 
@@ -28,6 +30,9 @@
 	files = append(files, newFile("java_toolchain", GeneratedBuildFileName, "")) // Creates a //java_toolchain package.
 	files = append(files, newFile("java_toolchain", "constants.bzl", java_config.BazelJavaToolchainVars(cfg)))
 
+	files = append(files, newFile("apex_toolchain", GeneratedBuildFileName, "")) // Creates a //apex_toolchain package.
+	files = append(files, newFile("apex_toolchain", "constants.bzl", apex.BazelApexToolchainVars()))
+
 	files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.convertedModules, "\n")))
 
 	files = append(files, newFile("product_config", "soong_config_variables.bzl", cfg.Bp2buildSoongConfigDefinitions.String()))
@@ -50,6 +55,7 @@
 }
 
 func CreateBazelFiles(
+	cfg android.Config,
 	ruleShims map[string]RuleShim,
 	buildToTargets map[string]BazelTargets,
 	mode CodegenMode) []BazelFile {
@@ -74,16 +80,19 @@
 		files = append(files, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims)))
 	}
 
-	files = append(files, createBuildFiles(buildToTargets, mode)...)
+	files = append(files, createBuildFiles(cfg, buildToTargets, mode)...)
 
 	return files
 }
 
-func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
+func createBuildFiles(cfg android.Config, buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
 	files := make([]BazelFile, 0, len(buildToTargets))
+	warnNotWriting := cfg.IsEnvTrue("BP2BUILD_VERBOSE")
 	for _, dir := range android.SortedStringKeys(buildToTargets) {
 		if mode == Bp2Build && android.ShouldKeepExistingBuildFileForDir(dir) {
-			fmt.Printf("[bp2build] Not writing generated BUILD file for dir: '%s'\n", dir)
+			if warnNotWriting {
+				fmt.Printf("[bp2build] Not writing generated BUILD file for dir: '%s'\n", dir)
+			}
 			continue
 		}
 		targets := buildToTargets[dir]
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index e49d855..0cb711c 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -27,7 +27,8 @@
 }
 
 func TestCreateBazelFiles_QueryView_AddsTopLevelFiles(t *testing.T) {
-	files := CreateBazelFiles(map[string]RuleShim{}, map[string]BazelTargets{}, QueryView)
+	files := CreateBazelFiles(android.NullConfig("out", "out/soong"),
+		map[string]RuleShim{}, map[string]BazelTargets{}, QueryView)
 	expectedFilePaths := []bazelFilepath{
 		{
 			dir:      "",
@@ -103,6 +104,14 @@
 			basename: "constants.bzl",
 		},
 		{
+			dir:      "apex_toolchain",
+			basename: GeneratedBuildFileName,
+		},
+		{
+			dir:      "apex_toolchain",
+			basename: "constants.bzl",
+		},
+		{
 			dir:      "metrics",
 			basename: "converted_modules.txt",
 		},
diff --git a/bp2build/linker_config_conversion_test.go b/bp2build/linker_config_conversion_test.go
new file mode 100644
index 0000000..4662af4
--- /dev/null
+++ b/bp2build/linker_config_conversion_test.go
@@ -0,0 +1,59 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+	"fmt"
+	"testing"
+
+	"android/soong/linkerconfig"
+)
+
+func runLinkerConfigTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	(&tc).moduleTypeUnderTest = "linker_config"
+	(&tc).moduleTypeUnderTestFactory = linkerconfig.LinkerConfigFactory
+	runBp2BuildTestCaseSimple(t, tc)
+}
+
+func TestLinkerConfigConvertsSrc(t *testing.T) {
+	runLinkerConfigTestCase(t,
+		bp2buildTestCase{
+			blueprint: `
+linker_config {
+	name: "foo",
+	src: "a.json",
+}
+`,
+			expectedBazelTargets: []string{makeBazelTarget("linker_config", "foo", attrNameToString{
+				"src": `"a.json"`,
+			})},
+		})
+
+}
+
+func TestLinkerConfigNoSrc(t *testing.T) {
+	runLinkerConfigTestCase(t,
+		bp2buildTestCase{
+			blueprint: `
+linker_config {
+	name: "foo",
+}
+`,
+			expectedBazelTargets: []string{},
+			expectedErr:          fmt.Errorf("Android.bp:2:1: module \"foo\": src: empty src is not supported"),
+		})
+
+}
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
index 04fac44..3a21c34 100644
--- a/bp2build/metrics.go
+++ b/bp2build/metrics.go
@@ -116,8 +116,6 @@
 	}
 	if _, err := os.Stat(metricsFile); err != nil {
 		fail(err, "MISSING BP2BUILD METRICS OUTPUT: Failed to `stat` %s", metricsFile)
-	} else {
-		fmt.Printf("\nWrote bp2build metrics to: %s\n", metricsFile)
 	}
 }
 
diff --git a/bp2build/symlink_forest.go b/bp2build/symlink_forest.go
index 818d7ae..78e7b0e 100644
--- a/bp2build/symlink_forest.go
+++ b/bp2build/symlink_forest.go
@@ -1,6 +1,7 @@
 package bp2build
 
 import (
+	"android/soong/android"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -21,7 +22,7 @@
 	children map[string]*node
 }
 
-// Ensures that the a node for the given path exists in the tree and returns it.
+// Ensures that the node for the given path exists in the tree and returns it.
 func ensureNodeExists(root *node, path string) *node {
 	if path == "" {
 		return root
@@ -114,7 +115,7 @@
 // contain every file in buildFilesDir and srcDir excluding the files in
 // exclude. Collects every directory encountered during the traversal of srcDir
 // into acc.
-func plantSymlinkForestRecursive(topdir string, forestDir string, buildFilesDir string, srcDir string, exclude *node, acc *[]string, okay *bool) {
+func plantSymlinkForestRecursive(cfg android.Config, topdir string, forestDir string, buildFilesDir string, srcDir string, exclude *node, acc *[]string, okay *bool) {
 	if exclude != nil && exclude.excluded {
 		// This directory is not needed, bail out
 		return
@@ -125,11 +126,11 @@
 	buildFilesMap := readdirToMap(shared.JoinPath(topdir, buildFilesDir))
 
 	allEntries := make(map[string]bool)
-	for n, _ := range srcDirMap {
+	for n := range srcDirMap {
 		allEntries[n] = true
 	}
 
-	for n, _ := range buildFilesMap {
+	for n := range buildFilesMap {
 		allEntries[n] = true
 	}
 
@@ -139,7 +140,7 @@
 		os.Exit(1)
 	}
 
-	for f, _ := range allEntries {
+	for f := range allEntries {
 		if f[0] == '.' {
 			continue // Ignore dotfiles
 		}
@@ -179,7 +180,7 @@
 			if bDir && excludeChild != nil {
 				// Not in the source tree, but we have to exclude something from under
 				// this subtree, so descend
-				plantSymlinkForestRecursive(topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
+				plantSymlinkForestRecursive(cfg, topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
 			} else {
 				// Not in the source tree, symlink BUILD file
 				symlinkIntoForest(topdir, forestChild, buildFilesChild)
@@ -188,19 +189,21 @@
 			if sDir && excludeChild != nil {
 				// Not in the build file tree, but we have to exclude something from
 				// under this subtree, so descend
-				plantSymlinkForestRecursive(topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
+				plantSymlinkForestRecursive(cfg, topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
 			} else {
 				// Not in the build file tree, symlink source tree, carry on
 				symlinkIntoForest(topdir, forestChild, srcChild)
 			}
 		} else if sDir && bDir {
 			// Both are directories. Descend.
-			plantSymlinkForestRecursive(topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
+			plantSymlinkForestRecursive(cfg, topdir, forestChild, buildFilesChild, srcChild, excludeChild, acc, okay)
 		} else if !sDir && !bDir {
 			// Neither is a directory. Prioritize BUILD files generated by bp2build
 			// over any BUILD file imported into external/.
-			fmt.Fprintf(os.Stderr, "Both '%s' and '%s' exist, symlinking the former to '%s'\n",
-				buildFilesChild, srcChild, forestChild)
+			if cfg.IsEnvTrue("BP2BUILD_VERBOSE") {
+				fmt.Fprintf(os.Stderr, "Both '%s' and '%s' exist, symlinking the former to '%s'\n",
+					buildFilesChild, srcChild, forestChild)
+			}
 			symlinkIntoForest(topdir, forestChild, buildFilesChild)
 		} else {
 			// Both exist and one is a file. This is an error.
@@ -216,12 +219,12 @@
 // "srcDir" while excluding paths listed in "exclude". Returns the set of paths
 // under srcDir on which readdir() had to be called to produce the symlink
 // forest.
-func PlantSymlinkForest(topdir string, forest string, buildFiles string, srcDir string, exclude []string) []string {
+func PlantSymlinkForest(cfg android.Config, topdir string, forest string, buildFiles string, srcDir string, exclude []string) []string {
 	deps := make([]string, 0)
 	os.RemoveAll(shared.JoinPath(topdir, forest))
 	excludeTree := treeFromExcludePathList(exclude)
 	okay := true
-	plantSymlinkForestRecursive(topdir, forest, buildFiles, srcDir, excludeTree, &deps, &okay)
+	plantSymlinkForestRecursive(cfg, topdir, forest, buildFiles, srcDir, excludeTree, &deps, &okay)
 	if !okay {
 		os.Exit(1)
 	}
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 14b2d84..5d2533f 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"io"
+	"path/filepath"
 	"strings"
 
 	"android/soong/android"
@@ -154,6 +155,9 @@
 	srcs := android.PathsForModuleSrc(ctx, bpf.properties.Srcs)
 
 	for _, src := range srcs {
+		if strings.ContainsRune(filepath.Base(src.String()), '_') {
+			ctx.ModuleErrorf("invalid character '_' in source name")
+		}
 		obj := android.ObjPathWithExt(ctx, "unstripped", src, "o")
 
 		ctx.Build(pctx, android.BuildParams{
diff --git a/bpf/bpf_test.go b/bpf/bpf_test.go
index 51fbc15..6e39096 100644
--- a/bpf/bpf_test.go
+++ b/bpf/bpf_test.go
@@ -30,8 +30,9 @@
 	cc.PrepareForTestWithCcDefaultModules,
 	android.FixtureMergeMockFs(
 		map[string][]byte{
-			"bpf.c":       nil,
-			"BpfTest.cpp": nil,
+			"bpf.c":              nil,
+			"bpf_invalid_name.c": nil,
+			"BpfTest.cpp":        nil,
 		},
 	),
 	PrepareForTestWithBpf,
@@ -58,3 +59,15 @@
 	// value is not available for testing from this package.
 	// TODO(jungjw): Add a check for data or move this test to the cc package.
 }
+
+func TestBpfSourceName(t *testing.T) {
+	bp := `
+		bpf {
+			name: "bpf_invalid_name.o",
+			srcs: ["bpf_invalid_name.c"],
+		}
+	`
+	prepareForBpfTest.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+		`\QAndroid.bp:2:3: module "bpf_invalid_name.o" variant "android_common": invalid character '_' in source name\E`)).
+		RunTestWithBp(t, bp)
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index fa30d09..6cd6733 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -20,6 +20,7 @@
 
 	"android/soong/android"
 	"android/soong/bazel"
+	"android/soong/cc/config"
 
 	"github.com/google/blueprint"
 
@@ -156,7 +157,7 @@
 	attrs := staticOrSharedAttributes{}
 
 	setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
-		attrs.Copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag))
+		attrs.Copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, true, filterOutStdFlag))
 		attrs.Srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
 		attrs.System_dynamic_deps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, props.System_shared_libs))
 
@@ -326,16 +327,39 @@
 	return strings.HasPrefix(flag, "-std=")
 }
 
-func parseCommandLineFlags(soongFlags []string, filterOut filterOutFn) []string {
+func filterOutClangUnknownCflags(flag string) bool {
+	for _, f := range config.ClangUnknownCflags {
+		if f == flag {
+			return true
+		}
+	}
+	return false
+}
+
+func parseCommandLineFlags(soongFlags []string, noCoptsTokenization bool, filterOut ...filterOutFn) []string {
 	var result []string
 	for _, flag := range soongFlags {
-		if filterOut != nil && filterOut(flag) {
+		skipFlag := false
+		for _, filter := range filterOut {
+			if filter != nil && filter(flag) {
+				skipFlag = true
+			}
+		}
+		if skipFlag {
 			continue
 		}
 		// Soong's cflags can contain spaces, like `-include header.h`. For
 		// Bazel's copts, split them up to be compatible with the
 		// no_copts_tokenization feature.
-		result = append(result, strings.Split(flag, " ")...)
+		if noCoptsTokenization {
+			result = append(result, strings.Split(flag, " ")...)
+		} else {
+			// Soong's Version Script and Dynamic List Properties are added as flags
+			// to Bazel's linkopts using "($location label)" syntax.
+			// Splitting on spaces would separate this into two different flags
+			// "($ location" and "label)"
+			result = append(result, flag)
+		}
 	}
 	return result
 }
@@ -362,10 +386,10 @@
 	// overridden. In Bazel we always allow overriding, via flags; however, this can cause
 	// incompatibilities, so we remove "-std=" flags from Cflag properties while leaving it in other
 	// cases.
-	ca.copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag))
-	ca.asFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Asflags, nil))
-	ca.conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Conlyflags, nil))
-	ca.cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Cppflags, nil))
+	ca.copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, true, filterOutStdFlag, filterOutClangUnknownCflags))
+	ca.asFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Asflags, true, nil))
+	ca.conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Conlyflags, true, filterOutClangUnknownCflags))
+	ca.cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Cppflags, true, filterOutClangUnknownCflags))
 	ca.rtti.SetSelectValue(axis, config, props.Rtti)
 }
 
@@ -721,7 +745,7 @@
 		linkerFlags = append(linkerFlags, fmt.Sprintf("-Wl,--dynamic-list,$(location %s)", label.Label))
 	}
 
-	la.linkopts.SetSelectValue(axis, config, linkerFlags)
+	la.linkopts.SetSelectValue(axis, config, parseCommandLineFlags(linkerFlags, false, filterOutClangUnknownCflags))
 	la.useLibcrt.SetSelectValue(axis, config, props.libCrt())
 
 	if axis == bazel.NoConfigAxis {
diff --git a/cc/builder.go b/cc/builder.go
index 70bbd6a..ab2b80a 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -63,10 +63,10 @@
 	ld, ldRE = pctx.RemoteStaticRules("ld",
 		blueprint.RuleParams{
 			Command: "$reTemplate$ldCmd ${crtBegin} @${out}.rsp " +
-				"${libFlags} ${crtEnd} -o ${out} ${ldFlags} ${extraLibFlags}",
+				"${crtEnd} -o ${out} ${ldFlags} ${extraLibFlags}",
 			CommandDeps:    []string{"$ldCmd"},
 			Rspfile:        "${out}.rsp",
-			RspfileContent: "${in}",
+			RspfileContent: "${in} ${libFlags}",
 			// clang -Wl,--out-implib doesn't update its output file if it hasn't changed.
 			Restat: true,
 		},
diff --git a/cc/config/global.go b/cc/config/global.go
index c5fde55..26d93ab 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -228,7 +228,6 @@
 		// New warnings to be fixed after clang-r383902.
 		"-Wno-deprecated-copy",                      // http://b/153746672
 		"-Wno-range-loop-construct",                 // http://b/153747076
-		"-Wno-misleading-indentation",               // http://b/153746954
 		"-Wno-zero-as-null-pointer-constant",        // http://b/68236239
 		"-Wno-deprecated-anon-enum-enum-conversion", // http://b/153746485
 		"-Wno-pessimizing-move",                     // http://b/154270751
@@ -239,6 +238,8 @@
 		// New warnings to be fixed after clang-r433403
 		"-Wno-error=unused-but-set-variable",  // http://b/197240255
 		"-Wno-error=unused-but-set-parameter", // http://b/197240255
+		// New warnings to be fixed after clang-r458507
+		"-Wno-error=unqualified-std-cast-call", // http://b/239662094
 	}
 
 	noOverrideExternalGlobalCflags = []string{
@@ -247,6 +248,8 @@
 		"-Wno-unused-but-set-parameter",
 		// http://b/215753485
 		"-Wno-bitwise-instead-of-logical",
+		// http://b/232926688
+		"-Wno-misleading-indentation",
 	}
 
 	// Extra cflags for external third-party projects to disable warnings that
@@ -278,6 +281,12 @@
 
 		// http://b/175068488
 		"-Wno-string-concatenation",
+
+		// http://b/239661264
+		"-Wno-deprecated-non-prototype",
+	}
+
+	llvmNextExtraCommonGlobalCflags = []string{
 	}
 
 	IllegalFlags = []string{
@@ -291,8 +300,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r450784e"
-	ClangDefaultShortVersion = "14.0.7"
+	ClangDefaultVersion      = "clang-r458507"
+	ClangDefaultShortVersion = "15.0.1"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
@@ -361,6 +370,15 @@
 		if ctx.Config().IsEnvTrue("USE_CCACHE") {
 			flags = append(flags, "-Wno-unused-command-line-argument")
 		}
+
+		if ctx.Config().IsEnvTrue("LLVM_NEXT") {
+			flags = append(flags, llvmNextExtraCommonGlobalCflags...)
+		}
+
+		if ctx.Config().IsEnvTrue("ALLOW_UNKNOWN_WARNING_OPTION") {
+			flags = append(flags, "-Wno-error=unknown-warning-option")
+		}
+
 		return strings.Join(flags, " ")
 	})
 
diff --git a/cc/image.go b/cc/image.go
index 3a0857b..921b2bb 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -533,7 +533,7 @@
 		}
 	} else {
 		// This is either in /system (or similar: /data), or is a
-		// modules built with the NDK. Modules built with the NDK
+		// module built with the NDK. Modules built with the NDK
 		// will be restricted using the existing link type checks.
 		coreVariantNeeded = true
 	}
diff --git a/cc/library.go b/cc/library.go
index c445a42..bd6ccb5 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -1032,9 +1032,19 @@
 			ctx.PropertyErrorf("symbol_file", "%q doesn't have .map.txt suffix", symbolFile)
 			return Objects{}
 		}
+		// b/239274367 --apex and --systemapi filters symbols tagged with # apex and #
+		// systemapi, respectively. The former is for symbols defined in platform libraries
+		// and the latter is for symbols defined in APEXes.
+		var flag string
+		if ctx.Module().(android.ApexModule).NotInPlatform() {
+			flag = "--apex"
+		} else {
+			// TODO(b/239274367) drop --apex when #apex is replaced with #systemapi
+			// in the map.txt files of platform libraries
+			flag = "--systemapi --apex"
+		}
 		nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile,
-			android.ApiLevelOrPanic(ctx, library.MutatedProperties.StubsVersion),
-			"--apex")
+			android.ApiLevelOrPanic(ctx, library.MutatedProperties.StubsVersion), flag)
 		objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
 		library.versionScriptPath = android.OptionalPathForPath(
 			nativeAbiResult.versionScript)
@@ -1584,10 +1594,10 @@
 func getRefAbiDumpFile(ctx ModuleContext, vndkVersion, fileName string) android.Path {
 	// The logic must be consistent with classifySourceAbiDump.
 	isNdk := ctx.isNdk(ctx.Config())
-	isLlndkOrVndk := ctx.IsLlndkPublic() || (ctx.useVndk() && ctx.isVndk())
+	isVndk := ctx.useVndk() && ctx.isVndk()
 
-	refAbiDumpTextFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isLlndkOrVndk, false)
-	refAbiDumpGzipFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isLlndkOrVndk, true)
+	refAbiDumpTextFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isVndk, false)
+	refAbiDumpGzipFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isVndk, true)
 
 	if refAbiDumpTextFile.Valid() {
 		if refAbiDumpGzipFile.Valid() {
diff --git a/cc/ndk_api_coverage_parser/__init__.py b/cc/ndk_api_coverage_parser/__init__.py
index 8b9cd66..752f7d4 100755
--- a/cc/ndk_api_coverage_parser/__init__.py
+++ b/cc/ndk_api_coverage_parser/__init__.py
@@ -23,6 +23,7 @@
 from xml.etree.ElementTree import Element, SubElement, tostring
 from symbolfile import (
     ALL_ARCHITECTURES,
+    Filter,
     FUTURE_API_LEVEL,
     MultiplyDefinedSymbolError,
     SymbolFileParser,
@@ -139,9 +140,8 @@
 
     with open(args.symbol_file) as symbol_file:
         try:
-            versions = SymbolFileParser(
-                symbol_file, api_map, "", FUTURE_API_LEVEL, True, True
-            ).parse()
+            filt = Filter("", FUTURE_API_LEVEL, True, True, True)
+            versions = SymbolFileParser(symbol_file, api_map, filt).parse()
         except MultiplyDefinedSymbolError as ex:
             sys.exit('{}: error: {}'.format(args.symbol_file, ex))
 
diff --git a/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py b/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py
index 141059c..7c6ef68 100644
--- a/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py
+++ b/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py
@@ -20,7 +20,7 @@
 import unittest
 
 from xml.etree.ElementTree import fromstring
-from symbolfile import FUTURE_API_LEVEL, SymbolFileParser
+from symbolfile import Filter, FUTURE_API_LEVEL, SymbolFileParser
 import ndk_api_coverage_parser as nparser
 
 
@@ -78,9 +78,8 @@
         """
             )
         )
-        parser = SymbolFileParser(
-            input_file, {}, "", FUTURE_API_LEVEL, True, True
-        )
+        filt = Filter("", FUTURE_API_LEVEL, True, True, True)
+        parser = SymbolFileParser(input_file, {}, filt)
         generator = nparser.XmlGenerator(io.StringIO())
         result = generator.convertToXml(parser.parse())
         expected = fromstring(
diff --git a/cc/ndkstubgen/__init__.py b/cc/ndkstubgen/__init__.py
index 5e6b8f5..f893d41 100755
--- a/cc/ndkstubgen/__init__.py
+++ b/cc/ndkstubgen/__init__.py
@@ -29,15 +29,12 @@
 class Generator:
     """Output generator that writes stub source files and version scripts."""
     def __init__(self, src_file: TextIO, version_script: TextIO,
-                 symbol_list: TextIO, arch: Arch, api: int, llndk: bool,
-                 apex: bool) -> None:
+                 symbol_list: TextIO, filt: symbolfile.Filter) -> None:
         self.src_file = src_file
         self.version_script = version_script
         self.symbol_list = symbol_list
-        self.arch = arch
-        self.api = api
-        self.llndk = llndk
-        self.apex = apex
+        self.filter = filt
+        self.api = filt.api
 
     def write(self, versions: Iterable[Version]) -> None:
         """Writes all symbol data to the output files."""
@@ -47,8 +44,7 @@
 
     def write_version(self, version: Version) -> None:
         """Writes a single version block's data to the output files."""
-        if symbolfile.should_omit_version(version, self.arch, self.api,
-                                          self.llndk, self.apex):
+        if self.filter.should_omit_version(version):
             return
 
         section_versioned = symbolfile.symbol_versioned_in_api(
@@ -56,8 +52,7 @@
         version_empty = True
         pruned_symbols = []
         for symbol in version.symbols:
-            if symbolfile.should_omit_symbol(symbol, self.arch, self.api,
-                                             self.llndk, self.apex):
+            if self.filter.should_omit_symbol(symbol):
                 continue
 
             if symbolfile.symbol_versioned_in_api(symbol.tags, self.api):
@@ -110,12 +105,12 @@
     parser.add_argument(
         '--apex',
         action='store_true',
-        help='Use the APEX variant. Note: equivalent to --system-api.')
+        help='Use the APEX variant.')
     parser.add_argument(
-        '--system-api',
+        '--systemapi',
         action='store_true',
-        dest='apex',
-        help='Use the SystemAPI variant. Note: equivalent to --apex.')
+        dest='systemapi',
+        help='Use the SystemAPI variant.')
 
     parser.add_argument('--api-map',
                         type=resolved_path,
@@ -152,11 +147,10 @@
         verbosity = 2
     logging.basicConfig(level=verbose_map[verbosity])
 
+    filt = symbolfile.Filter(args.arch, api, args.llndk, args.apex, args.systemapi)
     with args.symbol_file.open() as symbol_file:
         try:
-            versions = symbolfile.SymbolFileParser(symbol_file, api_map,
-                                                   args.arch, api, args.llndk,
-                                                   args.apex).parse()
+          versions = symbolfile.SymbolFileParser(symbol_file, api_map, filt).parse()
         except symbolfile.MultiplyDefinedSymbolError as ex:
             sys.exit(f'{args.symbol_file}: error: {ex}')
 
@@ -164,7 +158,7 @@
         with args.version_script.open('w') as version_script:
             with args.symbol_list.open('w') as symbol_list:
                 generator = Generator(src_file, version_script, symbol_list,
-                                      args.arch, api, args.llndk, args.apex)
+                                      filt)
                 generator.write(versions)
 
 
diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py
index c8cd056..450719b 100755
--- a/cc/ndkstubgen/test_ndkstubgen.py
+++ b/cc/ndkstubgen/test_ndkstubgen.py
@@ -18,6 +18,7 @@
 import io
 import textwrap
 import unittest
+from copy import copy
 
 import symbolfile
 from symbolfile import Arch, Tags
@@ -29,6 +30,9 @@
 
 
 class GeneratorTest(unittest.TestCase):
+    def setUp(self) -> None:
+        self.filter = symbolfile.Filter(Arch('arm'), 9, False, False)
+
     def test_omit_version(self) -> None:
         # Thorough testing of the cases involved here is handled by
         # OmitVersionTest, PrivateVersionTest, and SymbolPresenceTest.
@@ -37,7 +41,7 @@
         symbol_list_file = io.StringIO()
         generator = ndkstubgen.Generator(src_file,
                                          version_file, symbol_list_file,
-                                         Arch('arm'), 9, False, False)
+                                         self.filter)
 
         version = symbolfile.Version('VERSION_PRIVATE', None, Tags(), [
             symbolfile.Symbol('foo', Tags()),
@@ -70,7 +74,7 @@
         symbol_list_file = io.StringIO()
         generator = ndkstubgen.Generator(src_file,
                                          version_file, symbol_list_file,
-                                         Arch('arm'), 9, False, False)
+                                         self.filter)
 
         version = symbolfile.Version('VERSION_1', None, Tags(), [
             symbolfile.Symbol('foo', Tags.from_strs(['x86'])),
@@ -106,7 +110,7 @@
         symbol_list_file = io.StringIO()
         generator = ndkstubgen.Generator(src_file,
                                          version_file, symbol_list_file,
-                                         Arch('arm'), 9, False, False)
+                                         self.filter)
 
         versions = [
             symbolfile.Version('VERSION_1', None, Tags(), [
@@ -162,6 +166,9 @@
 
 
 class IntegrationTest(unittest.TestCase):
+    def setUp(self) -> None:
+        self.filter = symbolfile.Filter(Arch('arm'), 9, False, False)
+
     def test_integration(self) -> None:
         api_map = {
             'O': 9000,
@@ -199,8 +206,7 @@
                 wobble;
             } VERSION_4;
         """))
-        parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
-                                             9, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, api_map, self.filter)
         versions = parser.parse()
 
         src_file = io.StringIO()
@@ -208,7 +214,7 @@
         symbol_list_file = io.StringIO()
         generator = ndkstubgen.Generator(src_file,
                                          version_file, symbol_list_file,
-                                         Arch('arm'), 9, False, False)
+                                         self.filter)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
@@ -263,16 +269,18 @@
                     *;
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
-                                             9001, False, False)
+        f = copy(self.filter)
+        f.api = 9001
+        parser = symbolfile.SymbolFileParser(input_file, api_map, f)
         versions = parser.parse()
 
         src_file = io.StringIO()
         version_file = io.StringIO()
         symbol_list_file = io.StringIO()
+        f = copy(self.filter)
+        f.api = 9001
         generator = ndkstubgen.Generator(src_file,
-                                         version_file, symbol_list_file,
-                                         Arch('arm'), 9001, False, False)
+                                         version_file, symbol_list_file, f)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
@@ -322,8 +330,9 @@
             } VERSION_2;
 
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        f = copy(self.filter)
+        f.api = 16
+        parser = symbolfile.SymbolFileParser(input_file, {}, f)
 
         with self.assertRaises(
                 symbolfile.MultiplyDefinedSymbolError) as ex_context:
@@ -370,16 +379,18 @@
                 wobble;
             } VERSION_4;
         """))
-        parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
-                                             9, False, True)
+        f = copy(self.filter)
+        f.apex = True
+        parser = symbolfile.SymbolFileParser(input_file, api_map, f)
         versions = parser.parse()
 
         src_file = io.StringIO()
         version_file = io.StringIO()
         symbol_list_file = io.StringIO()
+        f = copy(self.filter)
+        f.apex = True
         generator = ndkstubgen.Generator(src_file,
-                                         version_file, symbol_list_file,
-                                         Arch('arm'), 9, False, True)
+                                         version_file, symbol_list_file, f)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
@@ -428,20 +439,19 @@
                     *;
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'),
-                                             9, llndk=False, apex=True)
+        f = copy(self.filter)
+        f.apex = True
+        parser = symbolfile.SymbolFileParser(input_file, {}, f)
         versions = parser.parse()
 
         src_file = io.StringIO()
         version_file = io.StringIO()
         symbol_list_file = io.StringIO()
+        f = copy(self.filter)
+        f.apex = True
         generator = ndkstubgen.Generator(src_file,
                                          version_file,
-                                         symbol_list_file,
-                                         Arch('arm'),
-                                         9,
-                                         llndk=False,
-                                         apex=True)
+                                         symbol_list_file, f)
         generator.write(versions)
 
         self.assertEqual('', src_file.getvalue())
diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py
index f8d1841..471a12f 100644
--- a/cc/symbolfile/__init__.py
+++ b/cc/symbolfile/__init__.py
@@ -78,12 +78,17 @@
     @property
     def has_mode_tags(self) -> bool:
         """Returns True if any mode tags (apex, llndk, etc) are set."""
-        return self.has_apex_tags or self.has_llndk_tags
+        return self.has_apex_tags or self.has_llndk_tags or self.has_systemapi_tags
 
     @property
     def has_apex_tags(self) -> bool:
         """Returns True if any APEX tags are set."""
-        return 'apex' in self.tags or 'systemapi' in self.tags
+        return 'apex' in self.tags
+
+    @property
+    def has_systemapi_tags(self) -> bool:
+        """Returns True if any APEX tags are set."""
+        return 'systemapi' in self.tags
 
     @property
     def has_llndk_tags(self) -> bool:
@@ -198,50 +203,57 @@
     """
     return split_tag(tag)[1]
 
-
-def _should_omit_tags(tags: Tags, arch: Arch, api: int, llndk: bool,
-                      apex: bool) -> bool:
-    """Returns True if the tagged object should be omitted.
-
-    This defines the rules shared between version tagging and symbol tagging.
+class Filter:
+    """A filter encapsulates a condition that tells whether a version or a
+    symbol should be omitted or not
     """
-    # The apex and llndk tags will only exclude APIs from other modes. If in
-    # APEX or LLNDK mode and neither tag is provided, we fall back to the
-    # default behavior because all NDK symbols are implicitly available to APEX
-    # and LLNDK.
-    if tags.has_mode_tags:
-        if not apex and not llndk:
+
+    def __init__(self, arch: Arch, api: int, llndk: bool = False, apex: bool = False, systemapi: bool = False):
+        self.arch = arch
+        self.api = api
+        self.llndk = llndk
+        self.apex = apex
+        self.systemapi = systemapi
+
+    def _should_omit_tags(self, tags: Tags) -> bool:
+        """Returns True if the tagged object should be omitted.
+
+        This defines the rules shared between version tagging and symbol tagging.
+        """
+        # The apex and llndk tags will only exclude APIs from other modes. If in
+        # APEX or LLNDK mode and neither tag is provided, we fall back to the
+        # default behavior because all NDK symbols are implicitly available to
+        # APEX and LLNDK.
+        if tags.has_mode_tags:
+            if self.apex and tags.has_apex_tags:
+                return False
+            if self.llndk and tags.has_llndk_tags:
+                return False
+            if self.systemapi and tags.has_systemapi_tags:
+                return False
             return True
-        if apex and not tags.has_apex_tags:
+        if not symbol_in_arch(tags, self.arch):
             return True
-        if llndk and not tags.has_llndk_tags:
+        if not symbol_in_api(tags, self.arch, self.api):
             return True
-    if not symbol_in_arch(tags, arch):
-        return True
-    if not symbol_in_api(tags, arch, api):
-        return True
-    return False
+        return False
 
+    def should_omit_version(self, version: Version) -> bool:
+        """Returns True if the version section should be omitted.
 
-def should_omit_version(version: Version, arch: Arch, api: int, llndk: bool,
-                        apex: bool) -> bool:
-    """Returns True if the version section should be omitted.
+        We want to omit any sections that do not have any symbols we'll have in
+        the stub library. Sections that contain entirely future symbols or only
+        symbols for certain architectures.
+        """
+        if version.is_private:
+            return True
+        if version.tags.has_platform_only_tags:
+            return True
+        return self._should_omit_tags(version.tags)
 
-    We want to omit any sections that do not have any symbols we'll have in the
-    stub library. Sections that contain entirely future symbols or only symbols
-    for certain architectures.
-    """
-    if version.is_private:
-        return True
-    if version.tags.has_platform_only_tags:
-        return True
-    return _should_omit_tags(version.tags, arch, api, llndk, apex)
-
-
-def should_omit_symbol(symbol: Symbol, arch: Arch, api: int, llndk: bool,
-                       apex: bool) -> bool:
-    """Returns True if the symbol should be omitted."""
-    return _should_omit_tags(symbol.tags, arch, api, llndk, apex)
+    def should_omit_symbol(self, symbol: Symbol) -> bool:
+        """Returns True if the symbol should be omitted."""
+        return self._should_omit_tags(symbol.tags)
 
 
 def symbol_in_arch(tags: Tags, arch: Arch) -> bool:
@@ -316,14 +328,10 @@
 
 class SymbolFileParser:
     """Parses NDK symbol files."""
-    def __init__(self, input_file: TextIO, api_map: ApiMap, arch: Arch,
-                 api: int, llndk: bool, apex: bool) -> None:
+    def __init__(self, input_file: TextIO, api_map: ApiMap, filt: Filter) -> None:
         self.input_file = input_file
         self.api_map = api_map
-        self.arch = arch
-        self.api = api
-        self.llndk = llndk
-        self.apex = apex
+        self.filter = filt
         self.current_line: Optional[str] = None
 
     def parse(self) -> List[Version]:
@@ -352,13 +360,11 @@
         symbol_names = set()
         multiply_defined_symbols = set()
         for version in versions:
-            if should_omit_version(version, self.arch, self.api, self.llndk,
-                                   self.apex):
+            if self.filter.should_omit_version(version):
                 continue
 
             for symbol in version.symbols:
-                if should_omit_symbol(symbol, self.arch, self.api, self.llndk,
-                                      self.apex):
+                if self.filter.should_omit_symbol(symbol):
                     continue
 
                 if symbol.name in symbol_names:
diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py
index c1e8219..e17a8d0 100644
--- a/cc/symbolfile/test_symbolfile.py
+++ b/cc/symbolfile/test_symbolfile.py
@@ -19,7 +19,8 @@
 import unittest
 
 import symbolfile
-from symbolfile import Arch, Tag, Tags, Version
+from symbolfile import Arch, Tag, Tags, Version, Symbol, Filter
+from copy import copy
 
 # pylint: disable=missing-docstring
 
@@ -202,178 +203,188 @@
 
 
 class OmitVersionTest(unittest.TestCase):
+    def setUp(self) -> None:
+        self.filter = Filter(arch = Arch('arm'), api = 9)
+        self.version = Version('foo', None, Tags(), [])
+
+    def assertOmit(self, f: Filter, v: Version) -> None:
+        self.assertTrue(f.should_omit_version(v))
+
+    def assertInclude(self, f: Filter, v: Version) -> None:
+        self.assertFalse(f.should_omit_version(v))
+
     def test_omit_private(self) -> None:
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
-                False, False))
+        f = self.filter
+        v = self.version
 
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo_PRIVATE', None, Tags(), []),
-                Arch('arm'), 9, False, False))
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo_PLATFORM', None, Tags(), []),
-                Arch('arm'), 9, False, False))
+        self.assertInclude(f, v)
 
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None,
-                                   Tags.from_strs(['platform-only']), []),
-                Arch('arm'), 9, False, False))
+        v.name = 'foo_PRIVATE'
+        self.assertOmit(f, v)
+
+        v.name = 'foo_PLATFORM'
+        self.assertOmit(f, v)
+
+        v.name = 'foo'
+        v.tags = Tags.from_strs(['platform-only'])
+        self.assertOmit(f, v)
 
     def test_omit_llndk(self) -> None:
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags.from_strs(['llndk']), []),
-                Arch('arm'), 9, False, False))
+        f = self.filter
+        v = self.version
+        v_llndk = copy(v)
+        v_llndk.tags = Tags.from_strs(['llndk'])
 
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
-                True, False))
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags.from_strs(['llndk']), []),
-                Arch('arm'), 9, True, False))
+        self.assertOmit(f, v_llndk)
+
+        f.llndk = True
+        self.assertInclude(f, v)
+        self.assertInclude(f, v_llndk)
 
     def test_omit_apex(self) -> None:
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags.from_strs(['apex']), []),
-                Arch('arm'), 9, False, False))
+        f = self.filter
+        v = self.version
+        v_apex = copy(v)
+        v_apex.tags = Tags.from_strs(['apex'])
+        v_systemapi = copy(v)
+        v_systemapi.tags = Tags.from_strs(['systemapi'])
 
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
-                False, True))
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags.from_strs(['apex']), []),
-                Arch('arm'), 9, False, True))
+        self.assertOmit(f, v_apex)
+
+        f.apex = True
+        self.assertInclude(f, v)
+        self.assertInclude(f, v_apex)
+        self.assertOmit(f, v_systemapi)
 
     def test_omit_systemapi(self) -> None:
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags.from_strs(['systemapi']),
-                                   []), Arch('arm'), 9, False, False))
+        f = self.filter
+        v = self.version
+        v_apex = copy(v)
+        v_apex.tags = Tags.from_strs(['apex'])
+        v_systemapi = copy(v)
+        v_systemapi.tags = Tags.from_strs(['systemapi'])
 
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
-                False, True))
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags.from_strs(['systemapi']),
-                                   []), Arch('arm'), 9, False, True))
+        self.assertOmit(f, v_systemapi)
+
+        f.systemapi = True
+        self.assertInclude(f, v)
+        self.assertInclude(f, v_systemapi)
+        self.assertOmit(f, v_apex)
 
     def test_omit_arch(self) -> None:
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
-                False, False))
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags.from_strs(['arm']), []),
-                Arch('arm'), 9, False, False))
+        f_arm = self.filter
+        v_none = self.version
+        self.assertInclude(f_arm, v_none)
 
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags.from_strs(['x86']), []),
-                Arch('arm'), 9, False, False))
+        v_arm = copy(v_none)
+        v_arm.tags = Tags.from_strs(['arm'])
+        self.assertInclude(f_arm, v_arm)
+
+        v_x86 = copy(v_none)
+        v_x86.tags = Tags.from_strs(['x86'])
+        self.assertOmit(f_arm, v_x86)
 
     def test_omit_api(self) -> None:
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
-                False, False))
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None,
-                                   Tags.from_strs(['introduced=9']), []),
-                Arch('arm'), 9, False, False))
+        f_api9 = self.filter
+        v_none = self.version
+        self.assertInclude(f_api9, v_none)
 
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None,
-                                   Tags.from_strs(['introduced=14']), []),
-                Arch('arm'), 9, False, False))
+        v_api9 = copy(v_none)
+        v_api9.tags = Tags.from_strs(['introduced=9'])
+        self.assertInclude(f_api9, v_api9)
+
+        v_api14 = copy(v_none)
+        v_api14.tags = Tags.from_strs(['introduced=14'])
+        self.assertOmit(f_api9, v_api14)
 
 
 class OmitSymbolTest(unittest.TestCase):
-    def test_omit_llndk(self) -> None:
-        self.assertTrue(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
-                Arch('arm'), 9, False, False))
+    def setUp(self) -> None:
+        self.filter = Filter(arch = Arch('arm'), api = 9)
 
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
-                                          Arch('arm'), 9, True, False))
-        self.assertFalse(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
-                Arch('arm'), 9, True, False))
+    def assertOmit(self, f: Filter, s: Symbol) -> None:
+        self.assertTrue(f.should_omit_symbol(s))
+
+    def assertInclude(self, f: Filter, s: Symbol) -> None:
+        self.assertFalse(f.should_omit_symbol(s))
+
+    def test_omit_llndk(self) -> None:
+        f_none = self.filter
+        f_llndk = copy(f_none)
+        f_llndk.llndk = True
+
+        s_none = Symbol('foo', Tags())
+        s_llndk = Symbol('foo', Tags.from_strs(['llndk']))
+
+        self.assertOmit(f_none, s_llndk)
+        self.assertInclude(f_llndk, s_none)
+        self.assertInclude(f_llndk, s_llndk)
 
     def test_omit_apex(self) -> None:
-        self.assertTrue(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
-                Arch('arm'), 9, False, False))
+        f_none = self.filter
+        f_apex = copy(f_none)
+        f_apex.apex = True
 
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
-                                          Arch('arm'), 9, False, True))
-        self.assertFalse(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
-                Arch('arm'), 9, False, True))
+        s_none = Symbol('foo', Tags())
+        s_apex = Symbol('foo', Tags.from_strs(['apex']))
+        s_systemapi = Symbol('foo', Tags.from_strs(['systemapi']))
+
+        self.assertOmit(f_none, s_apex)
+        self.assertInclude(f_apex, s_none)
+        self.assertInclude(f_apex, s_apex)
+        self.assertOmit(f_apex, s_systemapi)
 
     def test_omit_systemapi(self) -> None:
-        self.assertTrue(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['systemapi'])),
-                Arch('arm'), 9, False, False))
+        f_none = self.filter
+        f_systemapi = copy(f_none)
+        f_systemapi.systemapi = True
 
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
-                                          Arch('arm'), 9, False, True))
-        self.assertFalse(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['systemapi'])),
-                Arch('arm'), 9, False, True))
+        s_none = Symbol('foo', Tags())
+        s_apex = Symbol('foo', Tags.from_strs(['apex']))
+        s_systemapi = Symbol('foo', Tags.from_strs(['systemapi']))
+
+        self.assertOmit(f_none, s_systemapi)
+        self.assertInclude(f_systemapi, s_none)
+        self.assertInclude(f_systemapi, s_systemapi)
+        self.assertOmit(f_systemapi, s_apex)
+
+    def test_omit_apex_and_systemapi(self) -> None:
+        f = self.filter
+        f.systemapi = True
+        f.apex = True
+
+        s_none = Symbol('foo', Tags())
+        s_apex = Symbol('foo', Tags.from_strs(['apex']))
+        s_systemapi = Symbol('foo', Tags.from_strs(['systemapi']))
+        self.assertInclude(f, s_none)
+        self.assertInclude(f, s_apex)
+        self.assertInclude(f, s_systemapi)
 
     def test_omit_arch(self) -> None:
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
-                                          Arch('arm'), 9, False, False))
-        self.assertFalse(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['arm'])), Arch('arm'),
-                9, False, False))
+        f_arm = self.filter
+        s_none = Symbol('foo', Tags())
+        s_arm = Symbol('foo', Tags.from_strs(['arm']))
+        s_x86 = Symbol('foo', Tags.from_strs(['x86']))
 
-        self.assertTrue(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['x86'])), Arch('arm'),
-                9, False, False))
+        self.assertInclude(f_arm, s_none)
+        self.assertInclude(f_arm, s_arm)
+        self.assertOmit(f_arm, s_x86)
 
     def test_omit_api(self) -> None:
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
-                                          Arch('arm'), 9, False, False))
-        self.assertFalse(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['introduced=9'])),
-                Arch('arm'), 9, False, False))
+        f_api9 = self.filter
+        s_none = Symbol('foo', Tags())
+        s_api9 = Symbol('foo', Tags.from_strs(['introduced=9']))
+        s_api14 = Symbol('foo', Tags.from_strs(['introduced=14']))
 
-        self.assertTrue(
-            symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', Tags.from_strs(['introduced=14'])),
-                Arch('arm'), 9, False, False))
+        self.assertInclude(f_api9, s_none)
+        self.assertInclude(f_api9, s_api9)
+        self.assertOmit(f_api9, s_api14)
 
 
 class SymbolFileParseTest(unittest.TestCase):
+    def setUp(self) -> None:
+        self.filter = Filter(arch = Arch('arm'), api = 16)
+
     def test_next_line(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             foo
@@ -382,8 +393,7 @@
             # baz
             qux
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
         self.assertIsNone(parser.current_line)
 
         self.assertEqual('foo', parser.next_line().strip())
@@ -409,8 +419,7 @@
             VERSION_2 {
             } VERSION_1; # asdf
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
 
         parser.next_line()
         version = parser.parse_version()
@@ -419,8 +428,8 @@
         self.assertEqual(Tags.from_strs(['foo', 'bar']), version.tags)
 
         expected_symbols = [
-            symbolfile.Symbol('baz', Tags()),
-            symbolfile.Symbol('qux', Tags.from_strs(['woodly', 'doodly'])),
+            Symbol('baz', Tags()),
+            Symbol('qux', Tags.from_strs(['woodly', 'doodly'])),
         ]
         self.assertEqual(expected_symbols, version.symbols)
 
@@ -434,8 +443,7 @@
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
         parser.next_line()
         with self.assertRaises(symbolfile.ParseError):
             parser.parse_version()
@@ -446,8 +454,7 @@
                 foo:
             }
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
         parser.next_line()
         with self.assertRaises(symbolfile.ParseError):
             parser.parse_version()
@@ -457,8 +464,7 @@
             foo;
             bar; # baz qux
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
 
         parser.next_line()
         symbol = parser.parse_symbol()
@@ -476,8 +482,7 @@
                 *;
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
         parser.next_line()
         with self.assertRaises(symbolfile.ParseError):
             parser.parse_version()
@@ -489,8 +494,7 @@
                     *;
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
         parser.next_line()
         version = parser.parse_version()
         self.assertEqual([], version.symbols)
@@ -501,8 +505,7 @@
                 foo
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
         parser.next_line()
         with self.assertRaises(symbolfile.ParseError):
             parser.parse_version()
@@ -510,8 +513,7 @@
     def test_parse_fails_invalid_input(self) -> None:
         with self.assertRaises(symbolfile.ParseError):
             input_file = io.StringIO('foo')
-            parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'),
-                                                 16, False, False)
+            parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
             parser.parse()
 
     def test_parse(self) -> None:
@@ -532,19 +534,18 @@
                     qwerty;
             } VERSION_1;
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
         versions = parser.parse()
 
         expected = [
             symbolfile.Version('VERSION_1', None, Tags(), [
-                symbolfile.Symbol('foo', Tags()),
-                symbolfile.Symbol('bar', Tags.from_strs(['baz'])),
+                Symbol('foo', Tags()),
+                Symbol('bar', Tags.from_strs(['baz'])),
             ]),
             symbolfile.Version(
                 'VERSION_2', 'VERSION_1', Tags.from_strs(['wasd']), [
-                    symbolfile.Symbol('woodly', Tags()),
-                    symbolfile.Symbol('doodly', Tags.from_strs(['asdf'])),
+                    Symbol('woodly', Tags()),
+                    Symbol('doodly', Tags.from_strs(['asdf'])),
                 ]),
         ]
 
@@ -559,8 +560,9 @@
                 qux; # apex
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
-                                             False, True)
+        f = copy(self.filter)
+        f.llndk = True
+        parser = symbolfile.SymbolFileParser(input_file, {}, f)
 
         parser.next_line()
         version = parser.parse_version()
@@ -568,10 +570,10 @@
         self.assertIsNone(version.base)
 
         expected_symbols = [
-            symbolfile.Symbol('foo', Tags()),
-            symbolfile.Symbol('bar', Tags.from_strs(['llndk'])),
-            symbolfile.Symbol('baz', Tags.from_strs(['llndk', 'apex'])),
-            symbolfile.Symbol('qux', Tags.from_strs(['apex'])),
+            Symbol('foo', Tags()),
+            Symbol('bar', Tags.from_strs(['llndk'])),
+            Symbol('baz', Tags.from_strs(['llndk', 'apex'])),
+            Symbol('qux', Tags.from_strs(['apex'])),
         ]
         self.assertEqual(expected_symbols, version.symbols)
 
diff --git a/cc/testing.go b/cc/testing.go
index 6b858d5..d70ec9b 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -194,7 +194,6 @@
 			native_coverage: false,
 			system_shared_libs: [],
 			stl: "none",
-			notice: "custom_notice",
 		}
 		cc_library {
 			name: "libprofile-clang-extras",
@@ -205,7 +204,6 @@
 			native_coverage: false,
 			system_shared_libs: [],
 			stl: "none",
-			notice: "custom_notice",
 		}
 		cc_library {
 			name: "libprofile-extras_ndk",
@@ -214,7 +212,6 @@
 			native_coverage: false,
 			system_shared_libs: [],
 			stl: "none",
-			notice: "custom_notice",
 			sdk_version: "current",
 		}
 		cc_library {
@@ -224,7 +221,6 @@
 			native_coverage: false,
 			system_shared_libs: [],
 			stl: "none",
-			notice: "custom_notice",
 			sdk_version: "current",
 		}
 
diff --git a/cc/vndk.go b/cc/vndk.go
index bf6148b..4cd4d42 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -671,12 +671,8 @@
 	snapshotArchDir := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
 
 	configsDir := filepath.Join(snapshotArchDir, "configs")
-	noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES")
 	includeDir := filepath.Join(snapshotArchDir, "include")
 
-	// set of notice files copied.
-	noticeBuilt := make(map[string]bool)
-
 	// paths of VNDK modules for GPL license checking
 	modulePaths := make(map[string]string)
 
@@ -762,16 +758,6 @@
 		moduleNames[stem] = ctx.ModuleName(m)
 		modulePaths[stem] = ctx.ModuleDir(m)
 
-		if len(m.NoticeFiles()) > 0 {
-			noticeName := stem + ".txt"
-			// skip already copied notice file
-			if _, ok := noticeBuilt[noticeName]; !ok {
-				noticeBuilt[noticeName] = true
-				snapshotOutputs = append(snapshotOutputs, combineNoticesRule(
-					ctx, m.NoticeFiles(), filepath.Join(noticeDir, noticeName)))
-			}
-		}
-
 		if ctx.Config().VndkSnapshotBuildArtifacts() {
 			headers = append(headers, m.SnapshotHeaders()...)
 		}
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 8a3d6e0..53422cd 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -220,7 +220,7 @@
 // doChosenActivity runs Soong for a specific activity, like bp2build, queryview
 // or the actual Soong build for the build.ninja file. Returns the top level
 // output file of the specific activity.
-func doChosenActivity(configuration android.Config, extraNinjaDeps []string, logDir string) string {
+func doChosenActivity(ctx *android.Context, configuration android.Config, extraNinjaDeps []string, logDir string) string {
 	mixedModeBuild := configuration.BazelContext.BazelEnabled()
 	generateBazelWorkspace := bp2buildMarker != ""
 	generateQueryView := bazelQueryViewDir != ""
@@ -236,7 +236,6 @@
 
 	blueprintArgs := cmdlineArgs
 
-	ctx := newContext(configuration)
 	if mixedModeBuild {
 		runMixedModeBuild(configuration, ctx, extraNinjaDeps)
 	} else {
@@ -284,7 +283,6 @@
 		}
 	}
 
-	writeMetrics(configuration, *ctx.EventHandler, logDir)
 	return cmdlineArgs.OutFile
 }
 
@@ -344,7 +342,13 @@
 	// change between every CI build, so tracking it would require re-running Soong for every build.
 	logDir := availableEnv["LOG_DIR"]
 
-	finalOutputFile := doChosenActivity(configuration, extraNinjaDeps, logDir)
+	ctx := newContext(configuration)
+	ctx.EventHandler.Begin("soong_build")
+
+	finalOutputFile := doChosenActivity(ctx, configuration, extraNinjaDeps, logDir)
+
+	ctx.EventHandler.End("soong_build")
+	writeMetrics(configuration, *ctx.EventHandler, logDir)
 
 	writeUsedEnvironmentFile(configuration, finalOutputFile)
 }
@@ -552,7 +556,7 @@
 		excludes = append(excludes, getTemporaryExcludes()...)
 
 		symlinkForestDeps := bp2build.PlantSymlinkForest(
-			topDir, workspaceRoot, generatedRoot, ".", excludes)
+			configuration, topDir, workspaceRoot, generatedRoot, ".", excludes)
 
 		ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
 		ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
@@ -566,7 +570,9 @@
 	// Only report metrics when in bp2build mode. The metrics aren't relevant
 	// for queryview, since that's a total repo-wide conversion and there's a
 	// 1:1 mapping for each module.
-	metrics.Print()
+	if configuration.IsEnvTrue("BP2BUILD_VERBOSE") {
+		metrics.Print()
+	}
 	writeBp2BuildMetrics(&metrics, configuration, eventHandler)
 }
 
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index d63ded5..983dbf0 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -32,7 +32,8 @@
 		panic(err)
 	}
 
-	filesToWrite := bp2build.CreateBazelFiles(ruleShims, res.BuildDirToTargets(), bp2build.QueryView)
+	filesToWrite := bp2build.CreateBazelFiles(ctx.Config(), ruleShims, res.BuildDirToTargets(),
+		bp2build.QueryView)
 	for _, f := range filesToWrite {
 		if err := writeReadOnlyFile(bazelQueryViewDir, f); err != nil {
 			return err
diff --git a/java/aar.go b/java/aar.go
index cf84309..dadfc6a 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -893,7 +893,7 @@
 	return nil
 }
 
-var _ android.PrebuiltInterface = (*Import)(nil)
+var _ android.PrebuiltInterface = (*AARImport)(nil)
 
 // android_library_import imports an `.aar` file into the build graph as if it was built with android_library.
 //
diff --git a/java/java.go b/java/java.go
index eaf82ad..dae69dc 100644
--- a/java/java.go
+++ b/java/java.go
@@ -118,6 +118,16 @@
 		copyEverythingToSnapshot,
 	}
 
+	snapshotRequiresImplementationJar = func(ctx android.SdkMemberContext) bool {
+		// In the S build the build will break if updatable-media does not provide a full implementation
+		// jar. That issue was fixed in Tiramisu by b/229932396.
+		if ctx.IsTargetBuildBeforeTiramisu() && ctx.Name() == "updatable-media" {
+			return true
+		}
+
+		return false
+	}
+
 	// Supports adding java boot libraries to module_exports and sdk.
 	//
 	// The build has some implicit dependencies (via the boot jars configuration) on a number of
@@ -135,13 +145,21 @@
 			SupportsSdk:  true,
 		},
 		func(ctx android.SdkMemberContext, j *Library) android.Path {
+			if snapshotRequiresImplementationJar(ctx) {
+				return exportImplementationClassesJar(ctx, j)
+			}
+
 			// Java boot libs are only provided in the SDK to provide access to their dex implementation
 			// jar for use by dexpreopting and boot jars package check. They do not need to provide an
 			// actual implementation jar but the java_import will need a file that exists so just copy an
 			// empty file. Any attempt to use that file as a jar will cause a build error.
 			return ctx.SnapshotBuilder().EmptyFile()
 		},
-		func(osPrefix, name string) string {
+		func(ctx android.SdkMemberContext, osPrefix, name string) string {
+			if snapshotRequiresImplementationJar(ctx) {
+				return sdkSnapshotFilePathForJar(ctx, osPrefix, name)
+			}
+
 			// Create a special name for the implementation jar to try and provide some useful information
 			// to a developer that attempts to compile against this.
 			// TODO(b/175714559): Provide a proper error message in Soong not ninja.
@@ -175,7 +193,7 @@
 			// file. Any attempt to use that file as a jar will cause a build error.
 			return ctx.SnapshotBuilder().EmptyFile()
 		},
-		func(osPrefix, name string) string {
+		func(_ android.SdkMemberContext, osPrefix, name string) string {
 			// Create a special name for the implementation jar to try and provide some useful information
 			// to a developer that attempts to compile against this.
 			// TODO(b/175714559): Provide a proper error message in Soong not ninja.
@@ -672,7 +690,7 @@
 )
 
 // path to the jar file of a java library. Relative to <sdk_root>/<api_dir>
-func sdkSnapshotFilePathForJar(osPrefix, name string) string {
+func sdkSnapshotFilePathForJar(_ android.SdkMemberContext, osPrefix, name string) string {
 	return sdkSnapshotFilePathForMember(osPrefix, name, jarFileSuffix)
 }
 
@@ -689,7 +707,7 @@
 
 	// Function to compute the snapshot relative path to which the named library's
 	// jar should be copied.
-	snapshotPathGetter func(osPrefix, name string) string
+	snapshotPathGetter func(ctx android.SdkMemberContext, osPrefix, name string) string
 
 	// True if only the jar should be copied to the snapshot, false if the jar plus any additional
 	// files like aidl files should also be copied.
@@ -747,7 +765,7 @@
 	exportedJar := p.JarToExport
 	if exportedJar != nil {
 		// Delegate the creation of the snapshot relative path to the member type.
-		snapshotRelativeJavaLibPath := memberType.snapshotPathGetter(p.OsPrefix(), ctx.Name())
+		snapshotRelativeJavaLibPath := memberType.snapshotPathGetter(ctx, p.OsPrefix(), ctx.Name())
 
 		// Copy the exported jar to the snapshot.
 		builder.CopyToSnapshot(exportedJar, snapshotRelativeJavaLibPath)
@@ -1213,7 +1231,7 @@
 
 	exportedJar := p.JarToExport
 	if exportedJar != nil {
-		snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(p.OsPrefix(), ctx.Name())
+		snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(ctx, p.OsPrefix(), ctx.Name())
 		builder.CopyToSnapshot(exportedJar, snapshotRelativeJavaLibPath)
 
 		propertySet.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
diff --git a/java/lint.go b/java/lint.go
index e276345..c27ca98 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -338,6 +338,14 @@
 			ctx.PropertyErrorf("lint.disabled_checks",
 				"Can't disable %v checks if min_sdk_version is different from sdk_version.", filtered)
 		}
+
+		// TODO(b/238784089): Remove this workaround when the NewApi issues have been addressed in PermissionController
+		if ctx.ModuleName() == "PermissionController" {
+			l.extraMainlineLintErrors = android.FilterListPred(l.extraMainlineLintErrors, func(s string) bool {
+				return s != "NewApi"
+			})
+			l.properties.Lint.Warning_checks = append(l.properties.Lint.Warning_checks, "NewApi")
+		}
 	}
 
 	extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag)
diff --git a/java/lint_defaults.txt b/java/lint_defaults.txt
index 1eee354..e99cb05 100644
--- a/java/lint_defaults.txt
+++ b/java/lint_defaults.txt
@@ -6,6 +6,7 @@
 
 --disable_check AnimatorKeep
 --disable_check AppBundleLocaleChanges
+--disable_check AppCompatCustomView
 --disable_check BlockedPrivateApi
 --disable_check CustomSplashScreen
 --disable_check CustomX509TrustManager
@@ -22,6 +23,7 @@
 --disable_check PrivateApi
 --disable_check ProtectedPermissions
 --disable_check QueryPermissionsNeeded
+--disable_check ReservedSystemPermission
 --disable_check ScopedStorage
 --disable_check ServiceCast
 --disable_check SoonBlockedPrivateApi
@@ -99,7 +101,10 @@
 --warning_check WrongViewCast                      # 1 occurences in 1 modules
 
 --warning_check CoarseFineLocation
+--warning_check ExtraText
 --warning_check IntentFilterExportedReceiver
+--warning_check MissingInflatedId
+--warning_check NotificationPermission
 --warning_check QueryAllPackagesPermission
 --warning_check RemoteViewLayout
 --warning_check SupportAnnotationUsage
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index f442ddf..1c42495 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -26,12 +26,14 @@
 func init() {
 	registerPlatformCompatConfigBuildComponents(android.InitRegistrationContext)
 
-	android.RegisterSdkMemberType(&compatConfigMemberType{
-		SdkMemberTypeBase: android.SdkMemberTypeBase{
-			PropertyName: "compat_configs",
-			SupportsSdk:  true,
-		},
-	})
+	android.RegisterSdkMemberType(CompatConfigSdkMemberType)
+}
+
+var CompatConfigSdkMemberType = &compatConfigMemberType{
+	SdkMemberTypeBase: android.SdkMemberTypeBase{
+		PropertyName: "compat_configs",
+		SupportsSdk:  true,
+	},
 }
 
 func registerPlatformCompatConfigBuildComponents(ctx android.RegistrationContext) {
diff --git a/licenses/Android.bp b/licenses/Android.bp
index 8db001f..133f7f7 100644
--- a/licenses/Android.bp
+++ b/licenses/Android.bp
@@ -32,6 +32,15 @@
 }
 
 license_kind {
+    name: "BSD-Like-Binary-Only",
+    conditions: [
+        "notice",
+        "by_exception_only",
+        "proprietary",
+    ],
+}
+
+license_kind {
     name: "SPDX-license-identifier-0BSD",
     conditions: ["unencumbered"],
     url: "https://spdx.org/licenses/0BSD",
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index 003b275..412a23b 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -22,6 +22,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel"
 	"android/soong/cc"
 	"android/soong/etc"
 )
@@ -36,7 +37,7 @@
 }
 
 func registerLinkerConfigBuildComponent(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("linker_config", linkerConfigFactory)
+	ctx.RegisterModuleType("linker_config", LinkerConfigFactory)
 }
 
 type linkerConfigProperties struct {
@@ -52,6 +53,7 @@
 
 type linkerConfig struct {
 	android.ModuleBase
+	android.BazelModuleBase
 	properties linkerConfigProperties
 
 	outputFilePath android.OutputPath
@@ -100,6 +102,28 @@
 	ctx.InstallFile(l.installDirPath, l.outputFilePath.Base(), l.outputFilePath)
 }
 
+type linkerConfigAttributes struct {
+	Src bazel.LabelAttribute
+}
+
+func (l *linkerConfig) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	if l.properties.Src == nil {
+		ctx.PropertyErrorf("src", "empty src is not supported")
+		return
+	}
+	src := android.BazelLabelForModuleSrcSingle(ctx, *l.properties.Src)
+	targetModuleProperties := bazel.BazelTargetModuleProperties{
+		Rule_class:        "linker_config",
+		Bzl_load_location: "//build/bazel/rules:linker_config.bzl",
+	}
+	ctx.CreateBazelTargetModule(
+		targetModuleProperties,
+		android.CommonAttributes{Name: l.Name()},
+		&linkerConfigAttributes{
+			Src: bazel.LabelAttribute{Value: &src},
+		})
+}
+
 func BuildLinkerConfig(ctx android.ModuleContext, builder *android.RuleBuilder,
 	input android.Path, otherModules []android.Module, output android.OutputPath) {
 
@@ -141,10 +165,11 @@
 // linker_config generates protobuf file from json file. This protobuf file will be used from
 // linkerconfig while generating ld.config.txt. Format of this file can be found from
 // https://android.googlesource.com/platform/system/linkerconfig/+/master/README.md
-func linkerConfigFactory() android.Module {
+func LinkerConfigFactory() android.Module {
 	m := &linkerConfig{}
 	m.AddProperties(&m.properties)
 	android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibFirst)
+	android.InitBazelModule(m)
 	return m
 }
 
diff --git a/rust/OWNERS b/rust/OWNERS
index ddaebc5..b595511 100644
--- a/rust/OWNERS
+++ b/rust/OWNERS
@@ -1,5 +1,2 @@
 # Additional owner/reviewers for rust rules, including parent directory owners.
 per-file * = chiw@google.com, chriswailes@google.com, ivanlozano@google.com, jeffv@google.com, mmaurer@google.com, srhines@google.com
-
-# Limited owners/reviewers of the allowed list.
-per-file allowed_list.go = chiw@google.com, chriswailes@google.com, ivanlozano@google.com, jeffv@google.com, mmaurer@google.com, srhines@google.com
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 4d723d6..72cc894 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -239,6 +239,11 @@
 		cflags = append(cflags, "-x c")
 	}
 
+	// LLVM_NEXT may contain flags that bindgen doesn't recognise. Turn off unknown flags warning.
+	if ctx.Config().IsEnvTrue("LLVM_NEXT") {
+		cflags = append(cflags, "-Wno-unknown-warning-option")
+	}
+
 	outputFile := android.PathForModuleOut(ctx, b.BaseSourceProvider.getStem(ctx)+".rs")
 
 	var cmd, cmdDesc string
diff --git a/rust/config/Android.bp b/rust/config/Android.bp
index ba40cb0..be73d69 100644
--- a/rust/config/Android.bp
+++ b/rust/config/Android.bp
@@ -16,7 +16,6 @@
         "global.go",
         "lints.go",
         "toolchain.go",
-        "allowed_list.go",
         "darwin_host.go",
         "x86_linux_bionic_host.go",
         "x86_linux_host.go",
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
deleted file mode 100644
index 9129b0e..0000000
--- a/rust/config/allowed_list.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package config
-
-var (
-	// When adding a new path below, add a rustfmt.toml file at the root of
-	// the repository and enable the rustfmt repo hook. See aosp/1458238
-	// for an example.
-	// TODO(b/160223496): enable rustfmt globally.
-	RustAllowedPaths = []string{
-		"device/google/cuttlefish",
-		"external/adhd",
-		"external/boringssl",
-		"external/crosvm",
-		"external/libchromeos-rs",
-		"external/minijail",
-		"external/open-dice",
-		"external/rust",
-		"external/selinux/libselinux",
-		"external/uwb",
-		"external/vm_tools/p9",
-		"frameworks/native/libs/binder/rust",
-		"frameworks/proto_logging/stats",
-		"hardware/interfaces/security",
-		"hardware/interfaces/uwb",
-		"packages/modules/Bluetooth",
-		"packages/modules/DnsResolver",
-		"packages/modules/Uwb",
-		"packages/modules/Virtualization",
-		"platform_testing/tests/codecoverage/native/rust",
-		"prebuilts/rust",
-		"system/core/debuggerd/rust",
-		"system/core/libstats/pull_rust",
-		"system/core/trusty/libtrusty-rs",
-		"system/core/trusty/keymint",
-		"system/extras/profcollectd",
-		"system/extras/simpleperf",
-		"system/hardware/interfaces/keystore2",
-		"system/keymint",
-		"system/librustutils",
-		"system/logging/liblog",
-		"system/logging/rust",
-		"system/nfc",
-		"system/security",
-		"system/tools/aidl",
-		"tools/security/fuzzing/example_rust_fuzzer",
-		"tools/security/fuzzing/orphans",
-		"tools/security/remote_provisioning/cert_validator",
-		"tools/vendor",
-		"vendor/",
-	}
-
-	DownstreamRustAllowedPaths = []string{
-		// Add downstream allowed Rust paths here.
-	}
-
-	RustModuleTypes = []string{
-		// Don't add rust_bindgen or rust_protobuf as these are code generation modules
-		// and can be expected to be in paths without Rust code.
-		"rust_benchmark",
-		"rust_benchmark_host",
-		"rust_binary",
-		"rust_binary_host",
-		"rust_library",
-		"rust_library_dylib",
-		"rust_library_rlib",
-		"rust_ffi",
-		"rust_ffi_shared",
-		"rust_ffi_static",
-		"rust_fuzz",
-		"rust_library_host",
-		"rust_library_host_dylib",
-		"rust_library_host_rlib",
-		"rust_ffi_host",
-		"rust_ffi_host_shared",
-		"rust_ffi_host_static",
-		"rust_proc_macro",
-		"rust_test",
-		"rust_test_host",
-	}
-)
diff --git a/rust/config/global.go b/rust/config/global.go
index 9e48344..9acbfb3 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.62.0"
+	RustDefaultVersion = "1.62.0.p1"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
diff --git a/rust/rust.go b/rust/rust.go
index d5d4929..4d9fe4c 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -33,13 +33,6 @@
 var pctx = android.NewPackageContext("android/soong/rust")
 
 func init() {
-	// Only allow rust modules to be defined for certain projects
-
-	android.AddNeverAllowRules(
-		android.NeverAllow().
-			NotIn(append(config.RustAllowedPaths, config.DownstreamRustAllowedPaths...)...).
-			ModuleType(config.RustModuleTypes...))
-
 	android.RegisterModuleType("rust_defaults", defaultsFactory)
 	android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
diff --git a/sdk/build_release.go b/sdk/build_release.go
index 4c2277e..0494a28 100644
--- a/sdk/build_release.go
+++ b/sdk/build_release.go
@@ -24,18 +24,22 @@
 
 // buildRelease represents the version of a build system used to create a specific release.
 //
-// The name of the release, is the same as the code for the dessert release, e.g. S, T, etc.
+// The name of the release, is the same as the code for the dessert release, e.g. S, Tiramisu, etc.
 type buildRelease struct {
-	// The name of the release, e.g. S, T, etc.
+	// The name of the release, e.g. S, Tiramisu, etc.
 	name string
 
 	// The index of this structure within the buildReleases list.
 	ordinal int
 }
 
+func (br *buildRelease) EarlierThan(other *buildRelease) bool {
+	return br.ordinal < other.ordinal
+}
+
 // String returns the name of the build release.
-func (s *buildRelease) String() string {
-	return s.name
+func (br *buildRelease) String() string {
+	return br.name
 }
 
 // buildReleaseSet represents a set of buildRelease objects.
diff --git a/sdk/compat_config_sdk_test.go b/sdk/compat_config_sdk_test.go
index d166add..45e8e0e 100644
--- a/sdk/compat_config_sdk_test.go
+++ b/sdk/compat_config_sdk_test.go
@@ -21,16 +21,12 @@
 	"android/soong/java"
 )
 
-func TestSnapshotWithCompatConfig(t *testing.T) {
+func testSnapshotWithCompatConfig(t *testing.T, sdk string) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		java.PrepareForTestWithPlatformCompatConfig,
-	).RunTestWithBp(t, `
-		sdk {
-			name: "mysdk",
-			compat_configs: ["myconfig"],
-		}
-
+		prepareForSdkTestWithApex,
+	).RunTestWithBp(t, sdk+`
 		platform_compat_config {
 			name: "myconfig",
 		}
@@ -73,3 +69,28 @@
 			}),
 	)
 }
+
+func TestSnapshotWithCompatConfig(t *testing.T) {
+	testSnapshotWithCompatConfig(t, `
+		sdk {
+			name: "mysdk",
+			compat_configs: ["myconfig"],
+		}
+`)
+}
+
+func TestSnapshotWithCompatConfig_Apex(t *testing.T) {
+	testSnapshotWithCompatConfig(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			min_sdk_version: "2",
+			compat_configs: ["myconfig"],
+		}
+
+		sdk {
+			name: "mysdk",
+			apexes: ["myapex"],
+		}
+`)
+}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index d25138f..7ab5285 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -15,6 +15,7 @@
 package sdk
 
 import (
+	"fmt"
 	"testing"
 
 	"android/soong/android"
@@ -257,8 +258,8 @@
 		android.FixtureAddFile("aidl", nil),
 		android.FixtureAddFile("resource.txt", nil),
 	).RunTestWithBp(t, `
-		module_exports {
-			name: "myexports",
+		sdk {
+			name: "mysdk",
 			java_boot_libs: ["myjavalib"],
 		}
 
@@ -278,7 +279,7 @@
 		}
 	`)
 
-	CheckSnapshot(t, result, "myexports", "",
+	CheckSnapshot(t, result, "mysdk", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
@@ -292,11 +293,65 @@
 }
 `),
 		checkAllCopyRules(`
-.intermediates/myexports/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar
+.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar
 `),
 	)
 }
 
+func TestSnapshotWithJavaBootLibrary_UpdatableMedia(t *testing.T) {
+	runTest := func(t *testing.T, targetBuildRelease, expectedJarPath, expectedCopyRule string) {
+		result := android.GroupFixturePreparers(
+			prepareForSdkTestWithJava,
+			android.FixtureMergeEnv(map[string]string{
+				"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": targetBuildRelease,
+			}),
+		).RunTestWithBp(t, `
+		sdk {
+			name: "mysdk",
+			java_boot_libs: ["updatable-media"],
+		}
+
+		java_library {
+			name: "updatable-media",
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+			permitted_packages: ["pkg.media"],
+			apex_available: ["com.android.media"],
+		}
+	`)
+
+		CheckSnapshot(t, result, "mysdk", "",
+			checkAndroidBpContents(fmt.Sprintf(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "updatable-media",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["com.android.media"],
+    jars: ["%s"],
+    permitted_packages: ["pkg.media"],
+}
+`, expectedJarPath)),
+			checkAllCopyRules(expectedCopyRule),
+		)
+	}
+
+	t.Run("updatable-media in S", func(t *testing.T) {
+		runTest(t, "S", "java/updatable-media.jar", `
+.intermediates/updatable-media/android_common/package-check/updatable-media.jar -> java/updatable-media.jar
+`)
+	})
+
+	t.Run("updatable-media in T", func(t *testing.T) {
+		runTest(t, "Tiramisu", "java_boot_libs/snapshot/jars/are/invalid/updatable-media.jar", `
+.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/updatable-media.jar
+`)
+	})
+}
+
 func TestSnapshotWithJavaSystemserverLibrary(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
@@ -1284,9 +1339,9 @@
 .intermediates/myjavalib.stubs.source.module_lib/android_common/metalava/myjavalib.stubs.source.module_lib_removed.txt -> sdk_library/module-lib/myjavalib-removed.txt
 `),
 		checkMergeZips(
+			".intermediates/mysdk/common_os/tmp/sdk_library/module-lib/myjavalib_stub_sources.zip",
 			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
 			".intermediates/mysdk/common_os/tmp/sdk_library/system/myjavalib_stub_sources.zip",
-			".intermediates/mysdk/common_os/tmp/sdk_library/module-lib/myjavalib_stub_sources.zip",
 		),
 	)
 }
diff --git a/sdk/update.go b/sdk/update.go
index b9ef3d0..c555ddc 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -108,7 +108,7 @@
 
 	mergeZips = pctx.AndroidStaticRule("SnapshotMergeZips",
 		blueprint.RuleParams{
-			Command: `${config.MergeZipsCmd} $out $in`,
+			Command: `${config.MergeZipsCmd} -s $out $in`,
 			CommandDeps: []string{
 				"${config.MergeZipsCmd}",
 			},
@@ -481,7 +481,7 @@
 	// Copy the build number file into the snapshot.
 	builder.CopyToSnapshot(ctx.Config().BuildNumberFile(ctx), BUILD_NUMBER_FILE)
 
-	filesToZip := builder.filesToZip
+	filesToZip := android.SortedUniquePaths(builder.filesToZip)
 
 	// zip them all
 	zipPath := fmt.Sprintf("%s%s.zip", ctx.ModuleName(), snapshotFileSuffix)
@@ -517,7 +517,7 @@
 			Description: outputDesc,
 			Rule:        mergeZips,
 			Input:       zipFile,
-			Inputs:      builder.zipsToMerge,
+			Inputs:      android.SortedUniquePaths(builder.zipsToMerge),
 			Output:      outputZipFile,
 		})
 	}
@@ -1972,6 +1972,12 @@
 	return m.requiredTraits.Contains(trait)
 }
 
+func (m *memberContext) IsTargetBuildBeforeTiramisu() bool {
+	return m.builder.targetBuildRelease.EarlierThan(buildReleaseT)
+}
+
+var _ android.SdkMemberContext = (*memberContext)(nil)
+
 func (s *sdk) createMemberSnapshot(ctx *memberContext, member *sdkMember, bpModule *bpModule) {
 
 	memberType := member.memberType
diff --git a/ui/build/build.go b/ui/build/build.go
index ec42b70..5b80b4d 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -183,8 +183,8 @@
 	}
 }
 
-// Build the tree. The 'what' argument can be used to chose which components of
-// the build to run, via checking various bitmasks.
+// Build the tree. Various flags in `config` govern which components of
+// the build to run.
 func Build(ctx Context, config Config) {
 	ctx.Verboseln("Starting build with args:", config.Arguments())
 	ctx.Verboseln("Environment:", config.Environment().Environ())
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 1c80cff..fd60177 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -167,6 +167,7 @@
 		productOut("debug_ramdisk"),
 		productOut("vendor_ramdisk"),
 		productOut("vendor_debug_ramdisk"),
+		productOut("vendor_kernel_ramdisk"),
 		productOut("test_harness_ramdisk"),
 		productOut("recovery"),
 		productOut("root"),
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 8992b4f..cfcf990 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -544,7 +544,7 @@
 	buf, err := os.ReadFile(soongBuildMetricsFile)
 	if errors.Is(err, fs.ErrNotExist) {
 		// Soong may not have run during this invocation
-          ctx.Verbosef("Failed to read metrics file, %s: %s", soongBuildMetricsFile, err)
+		ctx.Verbosef("Failed to read metrics file, %s: %s", soongBuildMetricsFile, err)
 		return nil
 	} else if err != nil {
 		ctx.Fatalf("Failed to load %s: %s", soongBuildMetricsFile, err)
diff --git a/ui/terminal/simple_status.go b/ui/terminal/simple_status.go
index cef3b5d..9e9ffc0 100644
--- a/ui/terminal/simple_status.go
+++ b/ui/terminal/simple_status.go
@@ -22,30 +22,24 @@
 )
 
 type simpleStatusOutput struct {
-	writer      io.Writer
-	formatter   formatter
-	keepANSI    bool
-	outputLevel status.MsgLevel
+	writer    io.Writer
+	formatter formatter
+	keepANSI  bool
 }
 
 // NewSimpleStatusOutput returns a StatusOutput that represents the
 // current build status similarly to Ninja's built-in terminal
 // output.
-func NewSimpleStatusOutput(w io.Writer, formatter formatter, keepANSI bool, quietBuild bool) status.StatusOutput {
-	level := status.StatusLvl
-	if quietBuild {
-		level = status.PrintLvl
-	}
+func NewSimpleStatusOutput(w io.Writer, formatter formatter, keepANSI bool) status.StatusOutput {
 	return &simpleStatusOutput{
-		writer:      w,
-		formatter:   formatter,
-		keepANSI:    keepANSI,
-		outputLevel: level,
+		writer:    w,
+		formatter: formatter,
+		keepANSI:  keepANSI,
 	}
 }
 
 func (s *simpleStatusOutput) Message(level status.MsgLevel, message string) {
-	if level >= s.outputLevel {
+	if level >= status.StatusLvl {
 		output := s.formatter.message(level, message)
 		if !s.keepANSI {
 			output = string(stripAnsiEscapes([]byte(output)))
@@ -54,13 +48,10 @@
 	}
 }
 
-func (s *simpleStatusOutput) StartAction(_ *status.Action, _ status.Counts) {
+func (s *simpleStatusOutput) StartAction(action *status.Action, counts status.Counts) {
 }
 
 func (s *simpleStatusOutput) FinishAction(result status.ActionResult, counts status.Counts) {
-	if s.outputLevel > status.StatusLvl {
-		return
-	}
 	str := result.Description
 	if str == "" {
 		str = result.Command
diff --git a/ui/terminal/status.go b/ui/terminal/status.go
index ff0af47..2ad174f 100644
--- a/ui/terminal/status.go
+++ b/ui/terminal/status.go
@@ -29,9 +29,9 @@
 func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild, forceKeepANSI bool) status.StatusOutput {
 	formatter := newFormatter(statusFormat, quietBuild)
 
-	if forceSimpleOutput || quietBuild || !isSmartTerminal(w) {
-		return NewSimpleStatusOutput(w, formatter, forceKeepANSI, quietBuild)
-	} else {
+	if !forceSimpleOutput && isSmartTerminal(w) {
 		return NewSmartStatusOutput(w, formatter)
+	} else {
+		return NewSimpleStatusOutput(w, formatter, forceKeepANSI)
 	}
 }