Merge "Revert "Implement mixed builds for apex modules.""
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/neverallow.go b/android/neverallow.go
index aa47bca..357cae5 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -58,6 +58,7 @@
 	AddNeverAllowRules(createMakefileGoalRules()...)
 	AddNeverAllowRules(createInitFirstStageRules()...)
 	AddNeverAllowRules(createProhibitFrameworkAccessRules()...)
+	AddNeverAllowRules(createNoticeDeprecationRules()...)
 }
 
 // Add a NeverAllow rule to the set of rules to apply.
@@ -238,6 +239,15 @@
 	}
 }
 
+func createNoticeDeprecationRules() []Rule {
+	return []Rule{
+		NeverAllow().
+			WithMatcher("notice", isSetMatcherInstance).
+			NotIn("vendor/linaro/linux-firmware/").
+			Because("notice has been replaced by licenses/default_applicable_licenses"),
+	}
+}
+
 func neverallowMutator(ctx BottomUpMutatorContext) {
 	m, ok := ctx.Module().(Module)
 	if !ok {
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/apex/apex.go b/apex/apex.go
index 7b6707c..6313d20 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -639,7 +639,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"}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index dbe9180..6abd8ff 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -949,8 +949,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 +1136,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",
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index b0c3899..5bff956 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -40,7 +40,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)
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/conversion.go b/bp2build/conversion.go
index 4b013d9..4246f7d 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -50,6 +50,7 @@
 }
 
 func CreateBazelFiles(
+	cfg android.Config,
 	ruleShims map[string]RuleShim,
 	buildToTargets map[string]BazelTargets,
 	mode CodegenMode) []BazelFile {
@@ -74,16 +75,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..b0d0740 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:      "",
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..c5075e5 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"
@@ -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
@@ -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/cc/config/global.go b/cc/config/global.go
index 6999089..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,11 +281,12 @@
 
 		// http://b/175068488
 		"-Wno-string-concatenation",
+
+		// http://b/239661264
+		"-Wno-deprecated-non-prototype",
 	}
 
 	llvmNextExtraCommonGlobalCflags = []string{
-		"-Wno-unqualified-std-cast-call",
-		"-Wno-deprecated-non-prototype",
 	}
 
 	IllegalFlags = []string{
@@ -296,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{
@@ -371,6 +375,10 @@
 			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/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/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/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/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 57c171f..7ab5285 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -1339,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 995da74..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,
 		})
 	}
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/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)
 	}
 }