Merge "Check for errors in preparer.sh" into main
diff --git a/OWNERS b/OWNERS
index 58edc89..01025fb 100644
--- a/OWNERS
+++ b/OWNERS
@@ -12,4 +12,6 @@
 mrziwang@google.com
 spandandas@google.com
 weiwli@google.com
-yudiliu@google.com
\ No newline at end of file
+yudiliu@google.com
+
+per-file build/soong/ui/build/androidmk_denylist.go = joeo@google.com, weiwli@google.com
\ No newline at end of file
diff --git a/aconfig/codegen/cc_aconfig_library.go b/aconfig/codegen/cc_aconfig_library.go
index 12c2dea..8df353d 100644
--- a/aconfig/codegen/cc_aconfig_library.go
+++ b/aconfig/codegen/cc_aconfig_library.go
@@ -40,6 +40,7 @@
 	// default mode is "production", the other accepted modes are:
 	// "test": to generate test mode version of the library
 	// "exported": to generate exported mode version of the library
+	// "force-read-only": to generate force-read-only mode version of the library
 	// an error will be thrown if the mode is not supported
 	Mode *string
 }
diff --git a/aconfig/codegen/cc_aconfig_library_test.go b/aconfig/codegen/cc_aconfig_library_test.go
index 3de4626..c5ff6a1 100644
--- a/aconfig/codegen/cc_aconfig_library_test.go
+++ b/aconfig/codegen/cc_aconfig_library_test.go
@@ -29,6 +29,7 @@
 	{"mode: `production`,", "production"},
 	{"mode: `test`,", "test"},
 	{"mode: `exported`,", "exported"},
+	{"mode: `force-read-only`,", "force-read-only"},
 }
 
 func TestCCCodegenMode(t *testing.T) {
diff --git a/aconfig/codegen/java_aconfig_library.go b/aconfig/codegen/java_aconfig_library.go
index c027815..e6817e0 100644
--- a/aconfig/codegen/java_aconfig_library.go
+++ b/aconfig/codegen/java_aconfig_library.go
@@ -30,7 +30,7 @@
 
 var declarationsTag = declarationsTagType{}
 
-var aconfigSupportedModes = []string{"production", "test", "exported"}
+var aconfigSupportedModes = []string{"production", "test", "exported", "force-read-only"}
 
 type JavaAconfigDeclarationsLibraryProperties struct {
 	// name of the aconfig_declarations module to generate a library for
@@ -39,6 +39,7 @@
 	// default mode is "production", the other accepted modes are:
 	// "test": to generate test mode version of the library
 	// "exported": to generate exported mode version of the library
+	// "force-read-only": to generate force-read-only mode version of the library
 	// an error will be thrown if the mode is not supported
 	Mode *string
 }
diff --git a/aconfig/codegen/java_aconfig_library_test.go b/aconfig/codegen/java_aconfig_library_test.go
index 2523abc..8d54b5b 100644
--- a/aconfig/codegen/java_aconfig_library_test.go
+++ b/aconfig/codegen/java_aconfig_library_test.go
@@ -227,6 +227,10 @@
 	testCodegenMode(t, "mode: `exported`,", "exported")
 }
 
+func TestForceReadOnlyMode(t *testing.T) {
+	testCodegenMode(t, "mode: `force-read-only`,", "force-read-only")
+}
+
 func TestUnsupportedMode(t *testing.T) {
 	testCodegenModeWithError(t, "mode: `unsupported`,", "mode: \"unsupported\" is not a supported mode")
 }
diff --git a/aconfig/codegen/rust_aconfig_library.go b/aconfig/codegen/rust_aconfig_library.go
index 73b6fec..2ab54b6 100644
--- a/aconfig/codegen/rust_aconfig_library.go
+++ b/aconfig/codegen/rust_aconfig_library.go
@@ -23,6 +23,7 @@
 	// default mode is "production", the other accepted modes are:
 	// "test": to generate test mode version of the library
 	// "exported": to generate exported mode version of the library
+	// "force-read-only": to generate force-read-only mode version of the library
 	// an error will be thrown if the mode is not supported
 	Mode *string
 }
diff --git a/aconfig/codegen/rust_aconfig_library_test.go b/aconfig/codegen/rust_aconfig_library_test.go
index c09f701..60bc9f7 100644
--- a/aconfig/codegen/rust_aconfig_library_test.go
+++ b/aconfig/codegen/rust_aconfig_library_test.go
@@ -72,6 +72,7 @@
 	{"mode: `production`,", "production"},
 	{"mode: `test`,", "test"},
 	{"mode: `exported`,", "exported"},
+	{"mode: `force-read-only`,", "force-read-only"},
 }
 
 func TestRustCodegenMode(t *testing.T) {
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 183849a..2436f33 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -144,20 +144,25 @@
 }
 
 func (fuzz *fuzzBinary) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+	subdir := "lib"
+	if ctx.inVendor() {
+		subdir = "lib/vendor"
+	}
+
 	flags = fuzz.binaryDecorator.linkerFlags(ctx, flags)
 	// RunPaths on devices isn't instantiated by the base linker. `../lib` for
 	// installed fuzz targets (both host and device), and `./lib` for fuzz
 	// target packages.
-	flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/lib`)
+	flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/`+subdir)
 
 	// When running on device, fuzz targets with vendor: true set will be in
 	// fuzzer_name/vendor/fuzzer_name (note the extra 'vendor' and thus need to
 	// link with libraries in ../../lib/. Non-vendor binaries only need to look
 	// one level up, in ../lib/.
 	if ctx.inVendor() {
-		flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../../lib`)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../../`+subdir)
 	} else {
-		flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../`+subdir)
 	}
 
 	return flags
@@ -221,19 +226,27 @@
 }
 
 func SharedLibraryInstallLocation(
-	libraryBase string, isHost bool, fuzzDir string, archString string) string {
+	libraryBase string, isHost bool, isVendor bool, fuzzDir string, archString string) string {
 	installLocation := "$(PRODUCT_OUT)/data"
 	if isHost {
 		installLocation = "$(HOST_OUT)"
 	}
+	subdir := "lib"
+	if isVendor {
+		subdir = "lib/vendor"
+	}
 	installLocation = filepath.Join(
-		installLocation, fuzzDir, archString, "lib", libraryBase)
+		installLocation, fuzzDir, archString, subdir, libraryBase)
 	return installLocation
 }
 
 // Get the device-only shared library symbols install directory.
-func SharedLibrarySymbolsInstallLocation(libraryBase string, fuzzDir string, archString string) string {
-	return filepath.Join("$(PRODUCT_OUT)/symbols/data/", fuzzDir, archString, "/lib/", libraryBase)
+func SharedLibrarySymbolsInstallLocation(libraryBase string, isVendor bool, fuzzDir string, archString string) string {
+	subdir := "lib"
+	if isVendor {
+		subdir = "lib/vendor"
+	}
+	return filepath.Join("$(PRODUCT_OUT)/symbols/data/", fuzzDir, archString, subdir, libraryBase)
 }
 
 func (fuzzBin *fuzzBinary) install(ctx ModuleContext, file android.Path) {
@@ -244,16 +257,29 @@
 	// Grab the list of required shared libraries.
 	fuzzBin.sharedLibraries, _ = CollectAllSharedDependencies(ctx)
 
+	// TODO: does not mirror Android linkernamespaces
+	// the logic here has special cases for vendor, but it would need more work to
+	// work in arbitrary partitions, so just surface errors early for a few cases
+	//
+	// Even without these, there are certain situations across linkernamespaces
+	// that this won't support. For instance, you might have:
+	//
+	//     my_fuzzer (vendor) -> libbinder_ndk (core) -> libbinder (vendor)
+	//
+	// This dependency chain wouldn't be possible to express in the current
+	// logic because all the deps currently match the variant of the source
+	// module.
+
 	for _, ruleBuilderInstall := range fuzzBin.sharedLibraries {
 		install := ruleBuilderInstall.To
 		fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
 			SharedLibraryInstallLocation(
-				install, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
+				install, ctx.Host(), ctx.inVendor(), installBase, ctx.Arch().ArchType.String()))
 
 		// Also add the dependency on the shared library symbols dir.
 		if !ctx.Host() {
 			fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
-				SharedLibrarySymbolsInstallLocation(install, installBase, ctx.Arch().ArchType.String()))
+				SharedLibrarySymbolsInstallLocation(install, ctx.inVendor(), installBase, ctx.Arch().ArchType.String()))
 		}
 	}
 
@@ -412,6 +438,10 @@
 		}
 
 		sharedLibsInstallDirPrefix := "lib"
+		if ccModule.InVendor() {
+			sharedLibsInstallDirPrefix = "lib/vendor"
+		}
+
 		if !ccModule.IsFuzzModule() {
 			return
 		}
@@ -504,7 +534,7 @@
 		// install it to the output directory. Setup the install destination here,
 		// which will be used by $(copy-many-files) in the Make backend.
 		installDestination := SharedLibraryInstallLocation(
-			install, module.Host(), fuzzDir, archString)
+			install, module.Host(), module.InVendor(), fuzzDir, archString)
 		if (*sharedLibraryInstalled)[installDestination] {
 			continue
 		}
@@ -522,7 +552,7 @@
 		// we want symbolization tools (like `stack`) to be able to find the symbols
 		// in $ANDROID_PRODUCT_OUT/symbols automagically.
 		if !module.Host() {
-			symbolsInstallDestination := SharedLibrarySymbolsInstallLocation(install, fuzzDir, archString)
+			symbolsInstallDestination := SharedLibrarySymbolsInstallLocation(install, module.InVendor(), fuzzDir, archString)
 			symbolsInstallDestination = strings.ReplaceAll(symbolsInstallDestination, "$", "$$")
 			s.SharedLibInstallStrings = append(s.SharedLibInstallStrings,
 				library.String()+":"+symbolsInstallDestination)
diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py
index 94c8567..345e9f9 100644
--- a/cc/symbolfile/__init__.py
+++ b/cc/symbolfile/__init__.py
@@ -46,6 +46,15 @@
     Arch('x86_64'),
 )
 
+# TODO: it would be nice to dedupe with 'has_*_tag' property methods
+SUPPORTED_TAGS = ALL_ARCHITECTURES + (
+    Tag('apex'),
+    Tag('llndk'),
+    Tag('platform-only'),
+    Tag('systemapi'),
+    Tag('var'),
+    Tag('weak'),
+)
 
 # Arbitrary magic number. We use the same one in api-level.h for this purpose.
 FUTURE_API_LEVEL = 10000
@@ -136,6 +145,8 @@
 
 def is_api_level_tag(tag: Tag) -> bool:
     """Returns true if this tag has an API level that may need decoding."""
+    if tag.startswith('llndk-deprecated='):
+        return True
     if tag.startswith('introduced='):
         return True
     if tag.startswith('introduced-'):
@@ -170,6 +181,9 @@
         ParseError: An unknown version name was found in a tag.
     """
     if not is_api_level_tag(tag):
+        if tag not in SUPPORTED_TAGS:
+            raise ParseError(f'Unsupported tag: {tag}')
+
         return tag
 
     name, value = split_tag(tag)
diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py
index 856b9d7..83becc2 100644
--- a/cc/symbolfile/test_symbolfile.py
+++ b/cc/symbolfile/test_symbolfile.py
@@ -40,10 +40,20 @@
         self.assertEqual(Tags(), symbolfile.get_tags('foo bar baz', {}))
 
     def test_get_tags(self) -> None:
-        self.assertEqual(Tags.from_strs(['foo', 'bar']),
-                         symbolfile.get_tags('# foo bar', {}))
-        self.assertEqual(Tags.from_strs(['bar', 'baz']),
-                         symbolfile.get_tags('foo # bar baz', {}))
+        self.assertEqual(Tags.from_strs(['llndk', 'apex']),
+                         symbolfile.get_tags('# llndk apex', {}))
+        self.assertEqual(Tags.from_strs(['llndk', 'apex']),
+                         symbolfile.get_tags('foo # llndk apex', {}))
+
+    def test_get_unrecognized_tags(self) -> None:
+        with self.assertRaises(symbolfile.ParseError):
+            symbolfile.get_tags('# bar', {})
+        with self.assertRaises(symbolfile.ParseError):
+            symbolfile.get_tags('foo # bar', {})
+        with self.assertRaises(symbolfile.ParseError):
+            symbolfile.get_tags('# #', {})
+        with self.assertRaises(symbolfile.ParseError):
+            symbolfile.get_tags('# apex # llndk', {})
 
     def test_split_tag(self) -> None:
         self.assertTupleEqual(('foo', 'bar'),
@@ -425,13 +435,13 @@
 
     def test_parse_version(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
-            VERSION_1 { # foo bar
+            VERSION_1 { # weak introduced=35
                 baz;
-                qux; # woodly doodly
+                qux; # apex llndk
             };
 
             VERSION_2 {
-            } VERSION_1; # asdf
+            } VERSION_1; # not-a-tag
         """))
         parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
 
@@ -439,11 +449,11 @@
         version = parser.parse_version()
         self.assertEqual('VERSION_1', version.name)
         self.assertIsNone(version.base)
-        self.assertEqual(Tags.from_strs(['foo', 'bar']), version.tags)
+        self.assertEqual(Tags.from_strs(['weak', 'introduced=35']), version.tags)
 
         expected_symbols = [
             Symbol('baz', Tags()),
-            Symbol('qux', Tags.from_strs(['woodly', 'doodly'])),
+            Symbol('qux', Tags.from_strs(['apex', 'llndk'])),
         ]
         self.assertEqual(expected_symbols, version.symbols)
 
@@ -476,7 +486,7 @@
     def test_parse_symbol(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             foo;
-            bar; # baz qux
+            bar; # llndk apex
         """))
         parser = symbolfile.SymbolFileParser(input_file, {}, self.filter)
 
@@ -488,7 +498,7 @@
         parser.next_line()
         symbol = parser.parse_symbol()
         self.assertEqual('bar', symbol.name)
-        self.assertEqual(Tags.from_strs(['baz', 'qux']), symbol.tags)
+        self.assertEqual(Tags.from_strs(['llndk', 'apex']), symbol.tags)
 
     def test_wildcard_symbol_global(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
@@ -537,13 +547,13 @@
                     hidden1;
                 global:
                     foo;
-                    bar; # baz
+                    bar; # llndk
             };
 
-            VERSION_2 { # wasd
+            VERSION_2 { # weak
                 # Implicit global scope.
                     woodly;
-                    doodly; # asdf
+                    doodly; # llndk
                 local:
                     qwerty;
             } VERSION_1;
@@ -554,12 +564,12 @@
         expected = [
             symbolfile.Version('VERSION_1', None, Tags(), [
                 Symbol('foo', Tags()),
-                Symbol('bar', Tags.from_strs(['baz'])),
+                Symbol('bar', Tags.from_strs(['llndk'])),
             ]),
             symbolfile.Version(
-                'VERSION_2', 'VERSION_1', Tags.from_strs(['wasd']), [
+                'VERSION_2', 'VERSION_1', Tags.from_strs(['weak']), [
                     Symbol('woodly', Tags()),
-                    Symbol('doodly', Tags.from_strs(['asdf'])),
+                    Symbol('doodly', Tags.from_strs(['llndk'])),
                 ]),
         ]
 
diff --git a/rust/fuzz.go b/rust/fuzz.go
index bacfa2d..1770d2e 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -142,12 +142,12 @@
 
 		fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
 			cc.SharedLibraryInstallLocation(
-				install, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
+				install, ctx.Host(), ctx.InstallInVendor(), installBase, ctx.Arch().ArchType.String()))
 
 		// Also add the dependency on the shared library symbols dir.
 		if !ctx.Host() {
 			fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
-				cc.SharedLibrarySymbolsInstallLocation(install, installBase, ctx.Arch().ArchType.String()))
+				cc.SharedLibrarySymbolsInstallLocation(install, ctx.InstallInVendor(), installBase, ctx.Arch().ArchType.String()))
 		}
 	}
 
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index 21453ba..ee286f6 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -35,6 +35,7 @@
         "blueprint",
         "blueprint-bootstrap",
         "blueprint-microfactory",
+        "soong-android",
         "soong-finder",
         "soong-remoteexec",
         "soong-shared",
@@ -46,6 +47,7 @@
         "soong-ui-tracer",
     ],
     srcs: [
+        "androidmk_denylist.go",
         "build.go",
         "cleanbuild.go",
         "config.go",
diff --git a/ui/build/androidmk_denylist.go b/ui/build/androidmk_denylist.go
new file mode 100644
index 0000000..b2266b2
--- /dev/null
+++ b/ui/build/androidmk_denylist.go
@@ -0,0 +1,44 @@
+// Copyright 2024 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 build
+
+import (
+	"strings"
+
+	"android/soong/android"
+)
+
+var androidmk_denylist []string = []string{
+	"chained_build_config/",
+	"cts/",
+	"dalvik/",
+	"developers/",
+	"kernel/",
+	"libcore/",
+	"libnativehelper/",
+	"pdk/",
+	"toolchain/",
+}
+
+func blockAndroidMks(androidMks []string) []string {
+	return android.FilterListPred(androidMks, func(s string) bool {
+		for _, d := range androidmk_denylist {
+			if strings.HasPrefix(s, d) {
+				return false
+			}
+		}
+		return true
+	})
+}
diff --git a/ui/build/finder.go b/ui/build/finder.go
index d0bcf40..a114079 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -128,6 +128,7 @@
 
 	// Stop searching a subdirectory recursively after finding an Android.mk.
 	androidMks := f.FindFirstNamedAt(".", "Android.mk")
+	androidMks = blockAndroidMks(androidMks)
 	err := dumpListToFile(ctx, config, androidMks, filepath.Join(dumpDir, "Android.mk.list"))
 	if err != nil {
 		ctx.Fatalf("Could not export module list: %v", err)