Merge "Fix error handling"
diff --git a/android/bazel_test.go b/android/bazel_test.go
index 3164b23..7b38b6a 100644
--- a/android/bazel_test.go
+++ b/android/bazel_test.go
@@ -423,3 +423,21 @@
 		}
 	}
 }
+
+func TestShouldKeepExistingBuildFileForDir(t *testing.T) {
+	allowlist := NewBp2BuildAllowlist()
+	// entry "a/b2/c2" is moot because of its parent "a/b2"
+	allowlist.SetKeepExistingBuildFile(map[string]bool{"a": false, "a/b1": false, "a/b2": true, "a/b1/c1": true, "a/b2/c2": false})
+	truths := []string{"a", "a/b1", "a/b2", "a/b1/c1", "a/b2/c", "a/b2/c2", "a/b2/c2/d"}
+	falsities := []string{"a1", "a/b", "a/b1/c"}
+	for _, dir := range truths {
+		if !allowlist.ShouldKeepExistingBuildFileForDir(dir) {
+			t.Errorf("%s expected TRUE but was FALSE", dir)
+		}
+	}
+	for _, dir := range falsities {
+		if allowlist.ShouldKeepExistingBuildFileForDir(dir) {
+			t.Errorf("%s expected FALSE but was TRUE", dir)
+		}
+	}
+}
diff --git a/android/licenses.go b/android/licenses.go
index c47b3e6..c6b3243 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -340,4 +340,5 @@
 	ctx.Strict("COMPLIANCENOTICE_SHIPPEDLIBS", ctx.Config().HostToolPath(ctx, "compliancenotice_shippedlibs").String())
 	ctx.Strict("COMPLIANCE_LISTSHARE", ctx.Config().HostToolPath(ctx, "compliance_listshare").String())
 	ctx.Strict("COMPLIANCE_CHECKSHARE", ctx.Config().HostToolPath(ctx, "compliance_checkshare").String())
+	ctx.Strict("COMPLIANCE_SBOM", ctx.Config().HostToolPath(ctx, "compliance_sbom").String())
 }
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 7d21b75..1519f60 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -642,9 +642,13 @@
 // Extracts an interface from values containing the properties to apply based on config.
 // If config does not match a value with a non-nil property set, the default value will be returned.
 func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
+	configValue := config.String(s.variable)
+	if configValue != "" && !InList(configValue, s.values) {
+		return nil, fmt.Errorf("Soong config property %q must be one of %v, found %q", s.variable, s.values, configValue)
+	}
 	for j, v := range s.values {
 		f := values.Field(j)
-		if config.String(s.variable) == v && !f.Elem().IsNil() {
+		if configValue == v && !f.Elem().IsNil() {
 			return f.Interface(), nil
 		}
 	}
@@ -861,3 +865,13 @@
 }
 
 var emptyInterfaceType = reflect.TypeOf(emptyInterfaceStruct{}).Field(0).Type
+
+// InList checks if the string belongs to the list
+func InList(s string, list []string) bool {
+	for _, s2 := range list {
+		if s2 == s {
+			return true
+		}
+	}
+	return false
+}
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index a7800e8..d5d87ef 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -303,6 +303,10 @@
 	Bool_var interface{}
 }
 
+type stringSoongConfigVars struct {
+	String_var interface{}
+}
+
 func Test_PropertiesToApply(t *testing.T) {
 	mt, _ := newModuleType(&ModuleTypeProperties{
 		Module_type:      "foo",
@@ -365,6 +369,51 @@
 	}
 }
 
+func Test_PropertiesToApply_String_Error(t *testing.T) {
+	mt, _ := newModuleType(&ModuleTypeProperties{
+		Module_type:      "foo",
+		Config_namespace: "bar",
+		Variables:        []string{"string_var"},
+		Properties:       []string{"a", "b"},
+	})
+	mt.Variables = append(mt.Variables, &stringVariable{
+		baseVariable: baseVariable{
+			variable: "string_var",
+		},
+		values: []string{"a", "b", "c"},
+	})
+	stringVarPositive := &properties{
+		A: proptools.StringPtr("A"),
+		B: true,
+	}
+	conditionsDefault := &properties{
+		A: proptools.StringPtr("default"),
+		B: false,
+	}
+	actualProps := &struct {
+		Soong_config_variables stringSoongConfigVars
+	}{
+		Soong_config_variables: stringSoongConfigVars{
+			String_var: &boolVarProps{
+				A:                  stringVarPositive.A,
+				B:                  stringVarPositive.B,
+				Conditions_default: conditionsDefault,
+			},
+		},
+	}
+	props := reflect.ValueOf(actualProps)
+
+	_, err := PropertiesToApply(mt, props, Config(map[string]string{
+		"string_var": "x",
+	}))
+	expected := `Soong config property "string_var" must be one of [a b c], found "x"`
+	if err == nil {
+		t.Fatalf("Expected an error, got nil")
+	} else if err.Error() != expected {
+		t.Fatalf("Error message was not correct, expected %q, got %q", expected, err.Error())
+	}
+}
+
 func Test_Bp2BuildSoongConfigDefinitions(t *testing.T) {
 	testCases := []struct {
 		desc     string
diff --git a/apex/apex.go b/apex/apex.go
index b039d0d..a3872d3 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -225,7 +225,7 @@
 	// List of JNI libraries that are embedded inside this APEX.
 	Jni_libs []string
 
-	// List of rust dyn libraries
+	// List of rust dyn libraries that are embedded inside this APEX.
 	Rust_dyn_libs []string
 
 	// List of native executables that are embedded inside this APEX.
@@ -236,6 +236,41 @@
 
 	// List of filesystem images that are embedded inside this APEX bundle.
 	Filesystems []string
+
+	// List of native libraries to exclude from this APEX.
+	Exclude_native_shared_libs []string
+
+	// List of JNI libraries to exclude from this APEX.
+	Exclude_jni_libs []string
+
+	// List of rust dyn libraries to exclude from this APEX.
+	Exclude_rust_dyn_libs []string
+
+	// List of native executables to exclude from this APEX.
+	Exclude_binaries []string
+
+	// List of native tests to exclude from this APEX.
+	Exclude_tests []string
+
+	// List of filesystem images to exclude from this APEX bundle.
+	Exclude_filesystems []string
+}
+
+// Merge combines another ApexNativeDependencies into this one
+func (a *ApexNativeDependencies) Merge(b ApexNativeDependencies) {
+	a.Native_shared_libs = append(a.Native_shared_libs, b.Native_shared_libs...)
+	a.Jni_libs = append(a.Jni_libs, b.Jni_libs...)
+	a.Rust_dyn_libs = append(a.Rust_dyn_libs, b.Rust_dyn_libs...)
+	a.Binaries = append(a.Binaries, b.Binaries...)
+	a.Tests = append(a.Tests, b.Tests...)
+	a.Filesystems = append(a.Filesystems, b.Filesystems...)
+
+	a.Exclude_native_shared_libs = append(a.Exclude_native_shared_libs, b.Exclude_native_shared_libs...)
+	a.Exclude_jni_libs = append(a.Exclude_jni_libs, b.Exclude_jni_libs...)
+	a.Exclude_rust_dyn_libs = append(a.Exclude_rust_dyn_libs, b.Exclude_rust_dyn_libs...)
+	a.Exclude_binaries = append(a.Exclude_binaries, b.Exclude_binaries...)
+	a.Exclude_tests = append(a.Exclude_tests, b.Exclude_tests...)
+	a.Exclude_filesystems = append(a.Exclude_filesystems, b.Exclude_filesystems...)
 }
 
 type apexMultilibProperties struct {
@@ -675,12 +710,18 @@
 	// Use *FarVariation* to be able to depend on modules having conflicting variations with
 	// this module. This is required since arch variant of an APEX bundle is 'common' but it is
 	// 'arm' or 'arm64' for native shared libs.
-	ctx.AddFarVariationDependencies(binVariations, executableTag, nativeModules.Binaries...)
-	ctx.AddFarVariationDependencies(binVariations, testTag, nativeModules.Tests...)
-	ctx.AddFarVariationDependencies(libVariations, jniLibTag, nativeModules.Jni_libs...)
-	ctx.AddFarVariationDependencies(libVariations, sharedLibTag, nativeModules.Native_shared_libs...)
-	ctx.AddFarVariationDependencies(rustLibVariations, sharedLibTag, nativeModules.Rust_dyn_libs...)
-	ctx.AddFarVariationDependencies(target.Variations(), fsTag, nativeModules.Filesystems...)
+	ctx.AddFarVariationDependencies(binVariations, executableTag,
+		android.RemoveListFromList(nativeModules.Binaries, nativeModules.Exclude_binaries)...)
+	ctx.AddFarVariationDependencies(binVariations, testTag,
+		android.RemoveListFromList(nativeModules.Tests, nativeModules.Exclude_tests)...)
+	ctx.AddFarVariationDependencies(libVariations, jniLibTag,
+		android.RemoveListFromList(nativeModules.Jni_libs, nativeModules.Exclude_jni_libs)...)
+	ctx.AddFarVariationDependencies(libVariations, sharedLibTag,
+		android.RemoveListFromList(nativeModules.Native_shared_libs, nativeModules.Exclude_native_shared_libs)...)
+	ctx.AddFarVariationDependencies(rustLibVariations, sharedLibTag,
+		android.RemoveListFromList(nativeModules.Rust_dyn_libs, nativeModules.Exclude_rust_dyn_libs)...)
+	ctx.AddFarVariationDependencies(target.Variations(), fsTag,
+		android.RemoveListFromList(nativeModules.Filesystems, nativeModules.Exclude_filesystems)...)
 }
 
 func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) {
@@ -748,12 +789,12 @@
 			continue
 		}
 
-		var depsList []ApexNativeDependencies
+		var deps ApexNativeDependencies
 
 		// Add native modules targeting both ABIs. When multilib.* is omitted for
 		// native_shared_libs/jni_libs/tests, it implies multilib.both
-		depsList = append(depsList, a.properties.Multilib.Both)
-		depsList = append(depsList, ApexNativeDependencies{
+		deps.Merge(a.properties.Multilib.Both)
+		deps.Merge(ApexNativeDependencies{
 			Native_shared_libs: a.properties.Native_shared_libs,
 			Tests:              a.properties.Tests,
 			Jni_libs:           a.properties.Jni_libs,
@@ -764,8 +805,8 @@
 		// binaries, it implies multilib.first
 		isPrimaryAbi := i == 0
 		if isPrimaryAbi {
-			depsList = append(depsList, a.properties.Multilib.First)
-			depsList = append(depsList, ApexNativeDependencies{
+			deps.Merge(a.properties.Multilib.First)
+			deps.Merge(ApexNativeDependencies{
 				Native_shared_libs: nil,
 				Tests:              nil,
 				Jni_libs:           nil,
@@ -776,34 +817,32 @@
 		// Add native modules targeting either 32-bit or 64-bit ABI
 		switch target.Arch.ArchType.Multilib {
 		case "lib32":
-			depsList = append(depsList, a.properties.Multilib.Lib32)
-			depsList = append(depsList, a.properties.Multilib.Prefer32)
+			deps.Merge(a.properties.Multilib.Lib32)
+			deps.Merge(a.properties.Multilib.Prefer32)
 		case "lib64":
-			depsList = append(depsList, a.properties.Multilib.Lib64)
+			deps.Merge(a.properties.Multilib.Lib64)
 			if !has32BitTarget {
-				depsList = append(depsList, a.properties.Multilib.Prefer32)
+				deps.Merge(a.properties.Multilib.Prefer32)
 			}
 		}
 
 		// Add native modules targeting a specific arch variant
 		switch target.Arch.ArchType {
 		case android.Arm:
-			depsList = append(depsList, a.archProperties.Arch.Arm.ApexNativeDependencies)
+			deps.Merge(a.archProperties.Arch.Arm.ApexNativeDependencies)
 		case android.Arm64:
-			depsList = append(depsList, a.archProperties.Arch.Arm64.ApexNativeDependencies)
+			deps.Merge(a.archProperties.Arch.Arm64.ApexNativeDependencies)
 		case android.Riscv64:
-			depsList = append(depsList, a.archProperties.Arch.Riscv64.ApexNativeDependencies)
+			deps.Merge(a.archProperties.Arch.Riscv64.ApexNativeDependencies)
 		case android.X86:
-			depsList = append(depsList, a.archProperties.Arch.X86.ApexNativeDependencies)
+			deps.Merge(a.archProperties.Arch.X86.ApexNativeDependencies)
 		case android.X86_64:
-			depsList = append(depsList, a.archProperties.Arch.X86_64.ApexNativeDependencies)
+			deps.Merge(a.archProperties.Arch.X86_64.ApexNativeDependencies)
 		default:
 			panic(fmt.Errorf("unsupported arch %v\n", ctx.Arch().ArchType))
 		}
 
-		for _, d := range depsList {
-			addDependenciesForNativeModules(ctx, d, target, imageVariation)
-		}
+		addDependenciesForNativeModules(ctx, deps, target, imageVariation)
 		ctx.AddFarVariationDependencies([]blueprint.Variation{
 			{Mutator: "os", Variation: target.OsVariation()},
 			{Mutator: "arch", Variation: target.ArchVariation()},
@@ -2593,6 +2632,7 @@
 	module.AddProperties(
 		&apexBundleProperties{},
 		&apexTargetBundleProperties{},
+		&apexArchBundleProperties{},
 		&overridableProperties{},
 	)
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 2e116c7..21a71ce 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4399,12 +4399,15 @@
 			name: "myapex",
 			key: "myapex.key",
 			updatable: false,
+			native_shared_libs: ["mylib.generic"],
 			arch: {
 				arm64: {
 					native_shared_libs: ["mylib.arm64"],
+					exclude_native_shared_libs: ["mylib.generic"],
 				},
 				x86_64: {
 					native_shared_libs: ["mylib.x64"],
+					exclude_native_shared_libs: ["mylib.generic"],
 				},
 			}
 		}
@@ -4416,6 +4419,18 @@
 		}
 
 		cc_library {
+			name: "mylib.generic",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+		}
+
+		cc_library {
 			name: "mylib.arm64",
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
@@ -4445,6 +4460,7 @@
 
 	// Ensure that apex variant is created for the direct dep
 	ensureListContains(t, ctx.ModuleVariantsForTests("mylib.arm64"), "android_arm64_armv8-a_shared_apex10000")
+	ensureListNotContains(t, ctx.ModuleVariantsForTests("mylib.generic"), "android_arm64_armv8-a_shared_apex10000")
 	ensureListNotContains(t, ctx.ModuleVariantsForTests("mylib.x64"), "android_arm64_armv8-a_shared_apex10000")
 
 	// Ensure that both direct and indirect deps are copied into apex
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 70308c8..39446a1 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -532,6 +532,10 @@
 		src = String(p.Arch.Arm64.Src)
 	case android.Riscv64:
 		src = String(p.Arch.Riscv64.Src)
+		// HACK: fall back to arm64 prebuilts, the riscv64 ones don't exist yet.
+		if src == "" {
+			src = String(p.Arch.Arm64.Src)
+		}
 	case android.X86:
 		src = String(p.Arch.X86.Src)
 	case android.X86_64:
diff --git a/bp2build/java_binary_host_conversion_test.go b/bp2build/java_binary_host_conversion_test.go
index c860844..e8551e5 100644
--- a/bp2build/java_binary_host_conversion_test.go
+++ b/bp2build/java_binary_host_conversion_test.go
@@ -33,7 +33,7 @@
 	}, tc)
 }
 
-var fs = map[string]string{
+var testFs = map[string]string{
 	"test.mf": "Main-Class: com.android.test.MainClass",
 	"other/Android.bp": `cc_library_host_shared {
     name: "jni-lib-1",
@@ -44,7 +44,7 @@
 func TestJavaBinaryHost(t *testing.T) {
 	runJavaBinaryHostTestCase(t, Bp2buildTestCase{
 		Description: "java_binary_host with srcs, exclude_srcs, jni_libs, javacflags, and manifest.",
-		Filesystem:  fs,
+		Filesystem:  testFs,
 		Blueprint: `java_binary_host {
     name: "java-binary-host-1",
     srcs: ["a.java", "b.java"],
@@ -77,7 +77,7 @@
 func TestJavaBinaryHostRuntimeDeps(t *testing.T) {
 	runJavaBinaryHostTestCase(t, Bp2buildTestCase{
 		Description: "java_binary_host with srcs, exclude_srcs, jni_libs, javacflags, and manifest.",
-		Filesystem:  fs,
+		Filesystem:  testFs,
 		Blueprint: `java_binary_host {
     name: "java-binary-host-1",
     static_libs: ["java-dep-1"],
@@ -107,7 +107,7 @@
 func TestJavaBinaryHostLibs(t *testing.T) {
 	runJavaBinaryHostTestCase(t, Bp2buildTestCase{
 		Description: "java_binary_host with srcs, libs.",
-		Filesystem:  fs,
+		Filesystem:  testFs,
 		Blueprint: `java_binary_host {
     name: "java-binary-host-libs",
     libs: ["java-lib-dep-1"],
diff --git a/bp2build/symlink_forest.go b/bp2build/symlink_forest.go
index 45817e3..81ec7ee 100644
--- a/bp2build/symlink_forest.go
+++ b/bp2build/symlink_forest.go
@@ -15,7 +15,9 @@
 package bp2build
 
 import (
+	"errors"
 	"fmt"
+	"io/fs"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -49,6 +51,59 @@
 	okay  atomic.Bool // Whether the forest was successfully constructed
 }
 
+// A simple thread pool to limit concurrency on system calls.
+// Necessary because Go spawns a new OS-level thread for each blocking system
+// call. This means that if syscalls are too slow and there are too many of
+// them, the hard limit on OS-level threads can be exhausted.
+type syscallPool struct {
+	shutdownCh []chan<- struct{}
+	workCh     chan syscall
+}
+
+type syscall struct {
+	work func()
+	done chan<- struct{}
+}
+
+func createSyscallPool(count int) *syscallPool {
+	result := &syscallPool{
+		shutdownCh: make([]chan<- struct{}, count),
+		workCh:     make(chan syscall),
+	}
+
+	for i := 0; i < count; i++ {
+		shutdownCh := make(chan struct{})
+		result.shutdownCh[i] = shutdownCh
+		go result.worker(shutdownCh)
+	}
+
+	return result
+}
+
+func (p *syscallPool) do(work func()) {
+	doneCh := make(chan struct{})
+	p.workCh <- syscall{work, doneCh}
+	<-doneCh
+}
+
+func (p *syscallPool) shutdown() {
+	for _, ch := range p.shutdownCh {
+		ch <- struct{}{} // Blocks until the value is received
+	}
+}
+
+func (p *syscallPool) worker(shutdownCh <-chan struct{}) {
+	for {
+		select {
+		case <-shutdownCh:
+			return
+		case work := <-p.workCh:
+			work.work()
+			work.done <- struct{}{}
+		}
+	}
+}
+
 // Ensures that the node for the given path exists in the tree and returns it.
 func ensureNodeExists(root *instructionsNode, path string) *instructionsNode {
 	if path == "" {
@@ -317,6 +372,51 @@
 	}
 }
 
+func removeParallelRecursive(pool *syscallPool, path string, fi os.FileInfo, wg *sync.WaitGroup) {
+	defer wg.Done()
+
+	if fi.IsDir() {
+		children := readdirToMap(path)
+		childrenWg := &sync.WaitGroup{}
+		childrenWg.Add(len(children))
+
+		for child, childFi := range children {
+			go removeParallelRecursive(pool, shared.JoinPath(path, child), childFi, childrenWg)
+		}
+
+		childrenWg.Wait()
+	}
+
+	pool.do(func() {
+		if err := os.Remove(path); err != nil {
+			fmt.Fprintf(os.Stderr, "Cannot unlink '%s': %s\n", path, err)
+			os.Exit(1)
+		}
+	})
+}
+
+func removeParallel(path string) {
+	fi, err := os.Lstat(path)
+	if err != nil {
+		if errors.Is(err, fs.ErrNotExist) {
+			return
+		}
+
+		fmt.Fprintf(os.Stderr, "Cannot lstat '%s': %s\n", path, err)
+		os.Exit(1)
+	}
+
+	wg := &sync.WaitGroup{}
+	wg.Add(1)
+
+	// Random guess as to the best number of syscalls to run in parallel
+	pool := createSyscallPool(100)
+	removeParallelRecursive(pool, path, fi, wg)
+	pool.shutdown()
+
+	wg.Wait()
+}
+
 // Creates a symlink forest by merging the directory tree at "buildFiles" and
 // "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
@@ -330,7 +430,7 @@
 
 	context.okay.Store(true)
 
-	os.RemoveAll(shared.JoinPath(topdir, forest))
+	removeParallel(shared.JoinPath(topdir, forest))
 
 	instructions := instructionsFromExcludePathList(exclude)
 	go func() {
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index eb71aa1..052832d 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -244,4 +244,8 @@
 	return LibclangRuntimeLibrary(t, "fuzzer")
 }
 
+func LibFuzzerRuntimeInterceptors(t Toolchain) string {
+	return LibclangRuntimeLibrary(t, "fuzzer_interceptors")
+}
+
 var inList = android.InList
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 0fbe45c..64bb7dd 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -126,6 +126,14 @@
 		deps.HeaderLibs = append(deps.HeaderLibs, "libafl_headers")
 	} else {
 		deps.StaticLibs = append(deps.StaticLibs, config.LibFuzzerRuntimeLibrary(ctx.toolchain()))
+		// Fuzzers built with HWASAN should use the interceptors for better
+		// mutation based on signals in strcmp, memcpy, etc. This is only needed for
+		// fuzz targets, not generic HWASAN-ified binaries or libraries.
+		if module, ok := ctx.Module().(*Module); ok {
+			if module.IsSanitizerEnabled(Hwasan) {
+				deps.StaticLibs = append(deps.StaticLibs, config.LibFuzzerRuntimeInterceptors(ctx.toolchain()))
+			}
+		}
 	}
 
 	deps = fuzzBin.binaryDecorator.linkerDeps(ctx, deps)
@@ -137,9 +145,18 @@
 	// 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/lib`)
 
+	// 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`)
+	} else {
+		flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
+	}
+
 	return flags
 }
 
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 2cea5a5..0669f65 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -244,7 +244,7 @@
 	ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
 
 	// Create soong_injection repository
-	soongInjectionFiles := bp2build.CreateSoongInjectionFiles(configuration, bp2build.CodegenMetrics{})
+	soongInjectionFiles := bp2build.CreateSoongInjectionFiles(configuration, bp2build.CreateCodegenMetrics())
 	absoluteSoongInjectionDir := shared.JoinPath(topDir, configuration.SoongOutDir(), bazel.SoongInjectionDirName)
 	for _, file := range soongInjectionFiles {
 		writeReadOnlyFile(absoluteSoongInjectionDir, file)
diff --git a/java/app_set.go b/java/app_set.go
index d99fadb..d8c2a8d 100644
--- a/java/app_set.go
+++ b/java/app_set.go
@@ -90,9 +90,10 @@
 }
 
 var TargetCpuAbi = map[string]string{
-	"arm":     "ARMEABI_V7A",
-	"arm64":   "ARM64_V8A",
-	"riscv64": "RISCV64",
+	"arm":   "ARMEABI_V7A",
+	"arm64": "ARM64_V8A",
+	// TODO: use "RISCV64" when that is supported in bundles
+	"riscv64": "ARM64_V8A",
 	"x86":     "X86",
 	"x86_64":  "X86_64",
 }
diff --git a/java/base.go b/java/base.go
index 96f36e8..602e8d8 100644
--- a/java/base.go
+++ b/java/base.go
@@ -593,6 +593,8 @@
 		return android.Paths{j.outputFile}, nil
 	case ".jar":
 		return android.Paths{j.implementationAndResourcesJar}, nil
+	case ".hjar":
+		return android.Paths{j.headerJarFile}, nil
 	case ".proguard_map":
 		if j.dexer.proguardDictionary.Valid() {
 			return android.Paths{j.dexer.proguardDictionary.Path()}, nil
diff --git a/symbol_inject/macho.go b/symbol_inject/macho.go
index 9946d34..ca3d50e 100644
--- a/symbol_inject/macho.go
+++ b/symbol_inject/macho.go
@@ -16,9 +16,12 @@
 
 import (
 	"debug/macho"
+	"encoding/binary"
 	"fmt"
 	"io"
+	"os"
 	"os/exec"
+	"path/filepath"
 	"sort"
 	"strings"
 )
@@ -98,6 +101,80 @@
 }
 
 func CodeSignMachoFile(path string) error {
-	cmd := exec.Command("/usr/bin/codesign", "--force", "-s", "-", path)
-	return cmd.Run()
+	filename := filepath.Base(path)
+	cmd := exec.Command("/usr/bin/codesign", "--force", "-s", "-", "-i", filename, path)
+	if err := cmd.Run(); err != nil {
+		return err
+	}
+	return modifyCodeSignFlags(path)
+}
+
+const LC_CODE_SIGNATURE = 0x1d
+const CSSLOT_CODEDIRECTORY = 0
+
+// To make codesign not invalidated by stripping, modify codesign flags to 0x20002
+// (adhoc | linkerSigned).
+func modifyCodeSignFlags(path string) error {
+	f, err := os.OpenFile(path, os.O_RDWR, 0)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+
+	// Step 1: find code signature section.
+	machoFile, err := macho.NewFile(f)
+	if err != nil {
+		return err
+	}
+	var codeSignSectionOffset uint32 = 0
+	var codeSignSectionSize uint32 = 0
+	for _, l := range machoFile.Loads {
+		data := l.Raw()
+		cmd := machoFile.ByteOrder.Uint32(data)
+		if cmd == LC_CODE_SIGNATURE {
+			codeSignSectionOffset = machoFile.ByteOrder.Uint32(data[8:])
+			codeSignSectionSize = machoFile.ByteOrder.Uint32(data[12:])
+		}
+	}
+	if codeSignSectionOffset == 0 {
+		return fmt.Errorf("code signature section not found")
+	}
+
+	data := make([]byte, codeSignSectionSize)
+	_, err = f.ReadAt(data, int64(codeSignSectionOffset))
+	if err != nil {
+		return err
+	}
+
+	// Step 2: get flags offset.
+	blobCount := binary.BigEndian.Uint32(data[8:])
+	off := 12
+	var codeDirectoryOff uint32 = 0
+	for blobCount > 0 {
+		blobType := binary.BigEndian.Uint32(data[off:])
+		if blobType == CSSLOT_CODEDIRECTORY {
+			codeDirectoryOff = binary.BigEndian.Uint32(data[off+4:])
+			break
+		}
+		blobCount--
+		off += 8
+	}
+	if codeDirectoryOff == 0 {
+		return fmt.Errorf("no code directory in code signature section")
+	}
+	flagsOff := codeSignSectionOffset + codeDirectoryOff + 12
+
+	// Step 3: modify flags.
+	flagsData := make([]byte, 4)
+	_, err = f.ReadAt(flagsData, int64(flagsOff))
+	if err != nil {
+		return err
+	}
+	oldFlags := binary.BigEndian.Uint32(flagsData)
+	if oldFlags != 0x2 {
+		return fmt.Errorf("unexpected flags in code signature section: 0x%x", oldFlags)
+	}
+	binary.BigEndian.PutUint32(flagsData, 0x20002)
+	_, err = f.WriteAt(flagsData, int64(flagsOff))
+	return err
 }
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 2331eb1..ba5ba8b 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -547,7 +547,6 @@
 
 function test_bp2build_generates_marker_file {
   setup
-  create_mock_bazel
 
   run_soong bp2build
 
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index ad21d7d..679ac55 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -50,7 +50,6 @@
 
 function test_different_relative_outdir {
   setup
-  create_mock_bazel
 
   mkdir -p a
   touch a/g.txt
@@ -73,7 +72,6 @@
 
 function test_different_absolute_outdir {
   setup
-  create_mock_bazel
 
   mkdir -p a
   touch a/g.txt
@@ -96,7 +94,6 @@
 
 function test_bp2build_generates_all_buildfiles {
   setup
-  create_mock_bazel
 
   mkdir -p foo/convertible_soong_module
   cat > foo/convertible_soong_module/Android.bp <<'EOF'
@@ -167,7 +164,6 @@
 
 function test_cc_correctness {
   setup
-  create_mock_bazel
 
   mkdir -p a
   cat > a/Android.bp <<EOF
diff --git a/tests/lib.sh b/tests/lib.sh
index 006186a..e40f0ad 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -82,10 +82,10 @@
 }
 
 function create_mock_soong {
+  create_mock_bazel
   copy_directory build/blueprint
   copy_directory build/soong
   copy_directory build/make/tools/rbcrun
-  copy_directory prebuilts/bazel/common/proto
 
   symlink_directory prebuilts/sdk
   symlink_directory prebuilts/go
diff --git a/tests/mixed_mode_test.sh b/tests/mixed_mode_test.sh
index 076ec4b..8949b42 100755
--- a/tests/mixed_mode_test.sh
+++ b/tests/mixed_mode_test.sh
@@ -12,7 +12,6 @@
 
 function test_bazel_smoke {
   setup
-  create_mock_bazel
 
   run_soong bp2build
 
@@ -21,7 +20,6 @@
 
 function test_add_irrelevant_file {
   setup
-  create_mock_bazel
 
   mkdir -p soong_tests/a/b
   touch soong_tests/a/b/c.txt
@@ -33,7 +31,7 @@
 }
 EOF
 
-  run_soong --bazel-mode nothing
+  run_soong --bazel-mode-staging nothing
 
   if [[ ! -e out/soong/bp2build/soong_tests/a/b/BUILD.bazel ]]; then
     fail "BUILD.bazel not created"
@@ -48,7 +46,7 @@
 
   touch soong_tests/a/irrelevant.txt
 
-  run_soong --bazel-mode nothing
+  run_soong --bazel-mode-staging nothing
   local mtime_build2=$(stat -c "%y" out/soong/bp2build/soong_tests/a/b/BUILD.bazel)
   local mtime_ninja2=$(stat -c "%y" out/soong/build.ninja)
 
diff --git a/ui/build/config.go b/ui/build/config.go
index 36119f0..de10112 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -236,6 +236,19 @@
 	return nil
 }
 
+func defaultBazelProdMode(cfg *configImpl) bool {
+	// Envirnoment flag to disable Bazel for users which experience
+	// broken bazel-handled builds, or significant performance regressions.
+	if cfg.IsBazelMixedBuildForceDisabled() {
+		return false
+	}
+	// Darwin-host builds are currently untested with Bazel.
+	if runtime.GOOS == "darwin" {
+		return false
+	}
+	return true
+}
+
 func NewConfig(ctx Context, args ...string) Config {
 	ret := &configImpl{
 		environ:       OsEnvironment(),
@@ -774,6 +787,9 @@
 			c.arguments = append(c.arguments, arg)
 		}
 	}
+	if (!c.bazelProdMode) && (!c.bazelDevMode) && (!c.bazelStagingMode) {
+		c.bazelProdMode = defaultBazelProdMode(c)
+	}
 }
 
 func (c *configImpl) configureLocale(ctx Context) {
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index 968544b..940d85c 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -90,7 +90,9 @@
 				t.Fatal(err)
 			})
 
+			env := Environment([]string{})
 			c := &configImpl{
+				environ:   &env,
 				parallel:  -1,
 				keepGoing: -1,
 			}