Merge "Add bp2build converter for override_apex."
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 28dc8b4..9aae6fd 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -108,6 +108,7 @@
 		"external/icu":                                       Bp2BuildDefaultTrueRecursively,
 		"external/icu/android_icu4j":                         Bp2BuildDefaultFalse, // java rules incomplete
 		"external/icu/icu4j":                                 Bp2BuildDefaultFalse, // java rules incomplete
+		"external/jarjar":                                    Bp2BuildDefaultTrueRecursively,
 		"external/javapoet":                                  Bp2BuildDefaultTrueRecursively,
 		"external/jemalloc_new":                              Bp2BuildDefaultTrueRecursively,
 		"external/jsoncpp":                                   Bp2BuildDefaultTrueRecursively,
@@ -178,6 +179,7 @@
 		"system/libprocinfo":                                 Bp2BuildDefaultTrue,
 		"system/libziparchive":                               Bp2BuildDefaultTrueRecursively,
 		"system/logging/liblog":                              Bp2BuildDefaultTrueRecursively,
+		"system/memory/libmemunreachable":                    Bp2BuildDefaultTrueRecursively,
 		"system/sepolicy/apex":                               Bp2BuildDefaultTrueRecursively,
 		"system/timezone/apex":                               Bp2BuildDefaultTrueRecursively,
 		"system/timezone/output_data":                        Bp2BuildDefaultTrueRecursively,
@@ -308,6 +310,7 @@
 		"libprotobuf-internal-python-srcs", // TODO(b/210751803), we don't handle path property for filegroups
 		"libprotobuf-java-full",            // TODO(b/210751803), we don't handle path property for filegroups
 		"libprotobuf-java-util-full",       // TODO(b/210751803), we don't handle path property for filegroups
+		"auto_value_plugin_resources",      // TODO(b/210751803), we don't handle path property for filegroups
 
 		// go deps:
 		"analyze_bcpf",                                                                               // depends on bpmodify a blueprint_go_binary.
diff --git a/android/fixture.go b/android/fixture.go
index 728f031..0690a5a 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -586,6 +586,18 @@
 	})
 }
 
+// FixtureExpectsOneErrorPattern returns an error handler that will cause the test to fail
+// if there is more than one error or the error does not match the pattern.
+//
+// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within
+// which the test is being run which means that the RunTest() method will not return.
+func FixtureExpectsOneErrorPattern(pattern string) FixtureErrorHandler {
+	return FixtureCustomErrorHandler(func(t *testing.T, result *TestResult) {
+		t.Helper()
+		CheckErrorsAgainstExpectations(t, result.Errs, []string{pattern})
+	})
+}
+
 // FixtureCustomErrorHandler creates a custom error handler
 func FixtureCustomErrorHandler(function func(t *testing.T, result *TestResult)) FixtureErrorHandler {
 	return simpleErrorHandler{
diff --git a/android/namespace_test.go b/android/namespace_test.go
index ea399da..87d1320 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -15,7 +15,6 @@
 package android
 
 import (
-	"errors"
 	"path/filepath"
 	"reflect"
 	"testing"
@@ -24,577 +23,555 @@
 )
 
 func TestDependingOnModuleInSameNamespace(t *testing.T) {
-	ctx := setupTest(t,
-		map[string]string{
+	result := GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			"dir1": `
-			soong_namespace {
-			}
-			test_module {
-				name: "a",
-			}
-			test_module {
-				name: "b",
-				deps: ["a"],
-			}
+				soong_namespace {
+				}
+				test_module {
+					name: "a",
+				}
+				test_module {
+					name: "b",
+					deps: ["a"],
+				}
 			`,
-		},
-	)
+		}),
+	).RunTest(t)
 
-	a := getModule(ctx, "a")
-	b := getModule(ctx, "b")
-	if !dependsOn(ctx, b, a) {
+	a := getModule(result, "a")
+	b := getModule(result, "b")
+	if !dependsOn(result, b, a) {
 		t.Errorf("module b does not depend on module a in the same namespace")
 	}
 }
 
 func TestDependingOnModuleInRootNamespace(t *testing.T) {
-	ctx := setupTest(t,
-		map[string]string{
+	result := GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			".": `
-			test_module {
-				name: "b",
-				deps: ["a"],
-			}
-			test_module {
-				name: "a",
-			}
+				test_module {
+					name: "b",
+					deps: ["a"],
+				}
+				test_module {
+					name: "a",
+				}
 			`,
-		},
-	)
+		}),
+	).RunTest(t)
 
-	a := getModule(ctx, "a")
-	b := getModule(ctx, "b")
-	if !dependsOn(ctx, b, a) {
+	a := getModule(result, "a")
+	b := getModule(result, "b")
+	if !dependsOn(result, b, a) {
 		t.Errorf("module b in root namespace does not depend on module a in the root namespace")
 	}
 }
 
 func TestImplicitlyImportRootNamespace(t *testing.T) {
-	_ = setupTest(t,
-		map[string]string{
+	GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			".": `
-			test_module {
-				name: "a",
-			}
+				test_module {
+					name: "a",
+				}
 			`,
 			"dir1": `
-			soong_namespace {
-			}
-			test_module {
-				name: "b",
-				deps: ["a"],
-			}
+				soong_namespace {
+				}
+				test_module {
+					name: "b",
+					deps: ["a"],
+				}
 			`,
-		},
-	)
+		}),
+	).RunTest(t)
 
-	// setupTest will report any errors
+	// RunTest will report any errors
 }
 
 func TestDependingOnBlueprintModuleInRootNamespace(t *testing.T) {
-	_ = setupTest(t,
-		map[string]string{
+	GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			".": `
-			blueprint_test_module {
-				name: "a",
-			}
+				blueprint_test_module {
+					name: "a",
+				}
 			`,
 			"dir1": `
-			soong_namespace {
-			}
-			blueprint_test_module {
-				name: "b",
-				deps: ["a"],
-			}
+				soong_namespace {
+				}
+				blueprint_test_module {
+					name: "b",
+					deps: ["a"],
+				}
 			`,
-		},
-	)
+		}),
+	).RunTest(t)
 
-	// setupTest will report any errors
+	// RunTest will report any errors
 }
 
 func TestDependingOnModuleInImportedNamespace(t *testing.T) {
-	ctx := setupTest(t,
-		map[string]string{
+	result := GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			"dir1": `
-			soong_namespace {
-			}
-			test_module {
-				name: "a",
-			}
+				soong_namespace {
+				}
+				test_module {
+					name: "a",
+				}
 			`,
 			"dir2": `
-			soong_namespace {
-				imports: ["dir1"],
-			}
-			test_module {
-				name: "b",
-				deps: ["a"],
-			}
+				soong_namespace {
+					imports: ["dir1"],
+				}
+				test_module {
+					name: "b",
+					deps: ["a"],
+				}
 			`,
-		},
-	)
+		}),
+	).RunTest(t)
 
-	a := getModule(ctx, "a")
-	b := getModule(ctx, "b")
-	if !dependsOn(ctx, b, a) {
+	a := getModule(result, "a")
+	b := getModule(result, "b")
+	if !dependsOn(result, b, a) {
 		t.Errorf("module b does not depend on module a in the same namespace")
 	}
 }
 
 func TestDependingOnModuleInNonImportedNamespace(t *testing.T) {
-	_, errs := setupTestExpectErrs(t,
-		map[string]string{
+	GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			"dir1": `
-			soong_namespace {
-			}
-			test_module {
-				name: "a",
-			}
+				soong_namespace {
+				}
+				test_module {
+					name: "a",
+				}
 			`,
 			"dir2": `
-			soong_namespace {
-			}
-			test_module {
-				name: "a",
-			}
+				soong_namespace {
+				}
+				test_module {
+					name: "a",
+				}
 			`,
 			"dir3": `
-			soong_namespace {
-			}
-			test_module {
-				name: "b",
-				deps: ["a"],
-			}
+				soong_namespace {
+				}
+				test_module {
+					name: "b",
+					deps: ["a"],
+				}
 			`,
-		},
-	)
-
-	expectedErrors := []error{
-		errors.New(
-			`dir3/Android.bp:4:4: "b" depends on undefined module "a"
+		}),
+	).
+		ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`\Qdir3/Android.bp:4:5: "b" depends on undefined module "a"
 Module "b" is defined in namespace "dir3" which can read these 2 namespaces: ["dir3" "."]
-Module "a" can be found in these namespaces: ["dir1" "dir2"]`),
-	}
-
-	if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
-		t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
-	}
+Module "a" can be found in these namespaces: ["dir1" "dir2"]\E`)).
+		RunTest(t)
 }
 
 func TestDependingOnModuleByFullyQualifiedReference(t *testing.T) {
-	ctx := setupTest(t,
-		map[string]string{
+	result := GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			"dir1": `
-			soong_namespace {
-			}
-			test_module {
-				name: "a",
-			}
+				soong_namespace {
+				}
+				test_module {
+					name: "a",
+				}
 			`,
 			"dir2": `
-			soong_namespace {
-			}
-			test_module {
-				name: "b",
-				deps: ["//dir1:a"],
-			}
+				soong_namespace {
+				}
+				test_module {
+					name: "b",
+					deps: ["//dir1:a"],
+				}
 			`,
-		},
-	)
-	a := getModule(ctx, "a")
-	b := getModule(ctx, "b")
-	if !dependsOn(ctx, b, a) {
+		}),
+	).RunTest(t)
+
+	a := getModule(result, "a")
+	b := getModule(result, "b")
+	if !dependsOn(result, b, a) {
 		t.Errorf("module b does not depend on module a")
 	}
 }
 
 func TestSameNameInTwoNamespaces(t *testing.T) {
-	ctx := setupTest(t,
-		map[string]string{
+	result := GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			"dir1": `
-			soong_namespace {
-			}
-			test_module {
-				name: "a",
-				id: "1",
-			}
-			test_module {
-				name: "b",
-				deps: ["a"],
-				id: "2",
-			}
+				soong_namespace {
+				}
+				test_module {
+					name: "a",
+					id: "1",
+				}
+				test_module {
+					name: "b",
+					deps: ["a"],
+					id: "2",
+				}
 			`,
 			"dir2": `
-			soong_namespace {
-			}
-			test_module {
-				name: "a",
-				id:"3",
-			}
-			test_module {
-				name: "b",
-				deps: ["a"],
-				id:"4",
-			}
+				soong_namespace {
+				}
+				test_module {
+					name: "a",
+					id:"3",
+				}
+				test_module {
+					name: "b",
+					deps: ["a"],
+					id:"4",
+				}
 			`,
-		},
-	)
+		}),
+	).RunTest(t)
 
-	one := findModuleById(ctx, "1")
-	two := findModuleById(ctx, "2")
-	three := findModuleById(ctx, "3")
-	four := findModuleById(ctx, "4")
-	if !dependsOn(ctx, two, one) {
+	one := findModuleById(result, "1")
+	two := findModuleById(result, "2")
+	three := findModuleById(result, "3")
+	four := findModuleById(result, "4")
+	if !dependsOn(result, two, one) {
 		t.Fatalf("Module 2 does not depend on module 1 in its namespace")
 	}
-	if dependsOn(ctx, two, three) {
+	if dependsOn(result, two, three) {
 		t.Fatalf("Module 2 depends on module 3 in another namespace")
 	}
-	if !dependsOn(ctx, four, three) {
+	if !dependsOn(result, four, three) {
 		t.Fatalf("Module 4 does not depend on module 3 in its namespace")
 	}
-	if dependsOn(ctx, four, one) {
+	if dependsOn(result, four, one) {
 		t.Fatalf("Module 4 depends on module 1 in another namespace")
 	}
 }
 
 func TestSearchOrder(t *testing.T) {
-	ctx := setupTest(t,
-		map[string]string{
+	result := GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			"dir1": `
-			soong_namespace {
-			}
-			test_module {
-				name: "a",
-				id: "1",
-			}
+				soong_namespace {
+				}
+				test_module {
+					name: "a",
+					id: "1",
+				}
 			`,
 			"dir2": `
-			soong_namespace {
-			}
-			test_module {
-				name: "a",
-				id:"2",
-			}
-			test_module {
-				name: "b",
-				id:"3",
-			}
+				soong_namespace {
+				}
+				test_module {
+					name: "a",
+					id:"2",
+				}
+				test_module {
+					name: "b",
+					id:"3",
+				}
 			`,
 			"dir3": `
-			soong_namespace {
-			}
-			test_module {
-				name: "a",
-				id:"4",
-			}
-			test_module {
-				name: "b",
-				id:"5",
-			}
-			test_module {
-				name: "c",
-				id:"6",
-			}
+				soong_namespace {
+				}
+				test_module {
+					name: "a",
+					id:"4",
+				}
+				test_module {
+					name: "b",
+					id:"5",
+				}
+				test_module {
+					name: "c",
+					id:"6",
+				}
 			`,
 			".": `
-			test_module {
-				name: "a",
-				id: "7",
-			}
-			test_module {
-				name: "b",
-				id: "8",
-			}
-			test_module {
-				name: "c",
-				id: "9",
-			}
-			test_module {
-				name: "d",
-				id: "10",
-			}
+				test_module {
+					name: "a",
+					id: "7",
+				}
+				test_module {
+					name: "b",
+					id: "8",
+				}
+				test_module {
+					name: "c",
+					id: "9",
+				}
+				test_module {
+					name: "d",
+					id: "10",
+				}
 			`,
 			"dir4": `
-			soong_namespace {
-				imports: ["dir1", "dir2", "dir3"]
-			}
-			test_module {
-				name: "test_me",
-				id:"0",
-				deps: ["a", "b", "c", "d"],
-			}
+				soong_namespace {
+					imports: ["dir1", "dir2", "dir3"]
+				}
+				test_module {
+					name: "test_me",
+					id:"0",
+					deps: ["a", "b", "c", "d"],
+				}
 			`,
-		},
-	)
+		}),
+	).RunTest(t)
 
-	testMe := findModuleById(ctx, "0")
-	if !dependsOn(ctx, testMe, findModuleById(ctx, "1")) {
+	testMe := findModuleById(result, "0")
+	if !dependsOn(result, testMe, findModuleById(result, "1")) {
 		t.Errorf("test_me doesn't depend on id 1")
 	}
-	if !dependsOn(ctx, testMe, findModuleById(ctx, "3")) {
+	if !dependsOn(result, testMe, findModuleById(result, "3")) {
 		t.Errorf("test_me doesn't depend on id 3")
 	}
-	if !dependsOn(ctx, testMe, findModuleById(ctx, "6")) {
+	if !dependsOn(result, testMe, findModuleById(result, "6")) {
 		t.Errorf("test_me doesn't depend on id 6")
 	}
-	if !dependsOn(ctx, testMe, findModuleById(ctx, "10")) {
+	if !dependsOn(result, testMe, findModuleById(result, "10")) {
 		t.Errorf("test_me doesn't depend on id 10")
 	}
-	if numDeps(ctx, testMe) != 4 {
-		t.Errorf("num dependencies of test_me = %v, not 4\n", numDeps(ctx, testMe))
+	if numDeps(result, testMe) != 4 {
+		t.Errorf("num dependencies of test_me = %v, not 4\n", numDeps(result, testMe))
 	}
 }
 
 func TestTwoNamespacesCanImportEachOther(t *testing.T) {
-	_ = setupTest(t,
-		map[string]string{
+	GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			"dir1": `
-			soong_namespace {
-				imports: ["dir2"]
-			}
-			test_module {
-				name: "a",
-			}
-			test_module {
-				name: "c",
-				deps: ["b"],
-			}
+				soong_namespace {
+					imports: ["dir2"]
+				}
+				test_module {
+					name: "a",
+				}
+				test_module {
+					name: "c",
+					deps: ["b"],
+				}
 			`,
 			"dir2": `
-			soong_namespace {
-				imports: ["dir1"],
-			}
-			test_module {
-				name: "b",
-				deps: ["a"],
-			}
+				soong_namespace {
+					imports: ["dir1"],
+				}
+				test_module {
+					name: "b",
+					deps: ["a"],
+				}
 			`,
-		},
-	)
+		}),
+	).RunTest(t)
 
-	// setupTest will report any errors
+	// RunTest will report any errors
 }
 
 func TestImportingNonexistentNamespace(t *testing.T) {
-	_, errs := setupTestExpectErrs(t,
-		map[string]string{
+	GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			"dir1": `
-			soong_namespace {
-				imports: ["a_nonexistent_namespace"]
-			}
-			test_module {
-				name: "a",
-				deps: ["a_nonexistent_module"]
-			}
+				soong_namespace {
+					imports: ["a_nonexistent_namespace"]
+				}
+				test_module {
+					name: "a",
+					deps: ["a_nonexistent_module"]
+				}
 			`,
-		},
-	)
-
-	// should complain about the missing namespace and not complain about the unresolvable dependency
-	expectedErrors := []error{
-		errors.New(`dir1/Android.bp:2:4: module "soong_namespace": namespace a_nonexistent_namespace does not exist`),
-	}
-	if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
-		t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
-	}
+		}),
+	).
+		// should complain about the missing namespace and not complain about the unresolvable dependency
+		ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`\Qdir1/Android.bp:2:5: module "soong_namespace": namespace a_nonexistent_namespace does not exist\E`)).
+		RunTest(t)
 }
 
 func TestNamespacesDontInheritParentNamespaces(t *testing.T) {
-	_, errs := setupTestExpectErrs(t,
-		map[string]string{
+	GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			"dir1": `
-			soong_namespace {
-			}
-			test_module {
-				name: "a",
-			}
+				soong_namespace {
+				}
+				test_module {
+					name: "a",
+				}
 			`,
 			"dir1/subdir1": `
-			soong_namespace {
-			}
-			test_module {
-				name: "b",
-				deps: ["a"],
-			}
+				soong_namespace {
+				}
+				test_module {
+					name: "b",
+					deps: ["a"],
+				}
 			`,
-		},
-	)
-
-	expectedErrors := []error{
-		errors.New(`dir1/subdir1/Android.bp:4:4: "b" depends on undefined module "a"
+		}),
+	).
+		ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`\Qdir1/subdir1/Android.bp:4:5: "b" depends on undefined module "a"
 Module "b" is defined in namespace "dir1/subdir1" which can read these 2 namespaces: ["dir1/subdir1" "."]
-Module "a" can be found in these namespaces: ["dir1"]`),
-	}
-	if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
-		t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
-	}
+Module "a" can be found in these namespaces: ["dir1"]\E`)).
+		RunTest(t)
 }
 
 func TestModulesDoReceiveParentNamespace(t *testing.T) {
-	_ = setupTest(t,
-		map[string]string{
+	GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			"dir1": `
-			soong_namespace {
-			}
-			test_module {
-				name: "a",
-			}
+				soong_namespace {
+				}
+				test_module {
+					name: "a",
+				}
 			`,
 			"dir1/subdir": `
-			test_module {
-				name: "b",
-				deps: ["a"],
-			}
+				test_module {
+					name: "b",
+					deps: ["a"],
+				}
 			`,
-		},
-	)
+		}),
+	).RunTest(t)
 
-	// setupTest will report any errors
+	// RunTest will report any errors
 }
 
 func TestNamespaceImportsNotTransitive(t *testing.T) {
-	_, errs := setupTestExpectErrs(t,
-		map[string]string{
+	GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			"dir1": `
-			soong_namespace {
-			}
-			test_module {
-				name: "a",
-			}
+				soong_namespace {
+				}
+				test_module {
+					name: "a",
+				}
 			`,
 			"dir2": `
-			soong_namespace {
-				imports: ["dir1"],
-			}
-			test_module {
-				name: "b",
-				deps: ["a"],
-			}
+				soong_namespace {
+					imports: ["dir1"],
+				}
+				test_module {
+					name: "b",
+					deps: ["a"],
+				}
 			`,
 			"dir3": `
-			soong_namespace {
-				imports: ["dir2"],
-			}
-			test_module {
-				name: "c",
-				deps: ["a"],
-			}
+				soong_namespace {
+					imports: ["dir2"],
+				}
+				test_module {
+					name: "c",
+					deps: ["a"],
+				}
 			`,
-		},
-	)
-
-	expectedErrors := []error{
-		errors.New(`dir3/Android.bp:5:4: "c" depends on undefined module "a"
+		}),
+	).
+		ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`\Qdir3/Android.bp:5:5: "c" depends on undefined module "a"
 Module "c" is defined in namespace "dir3" which can read these 3 namespaces: ["dir3" "dir2" "."]
-Module "a" can be found in these namespaces: ["dir1"]`),
-	}
-	if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
-		t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
-	}
+Module "a" can be found in these namespaces: ["dir1"]\E`)).
+		RunTest(t)
 }
 
 func TestTwoNamepacesInSameDir(t *testing.T) {
-	_, errs := setupTestExpectErrs(t,
-		map[string]string{
+	GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			"dir1": `
-			soong_namespace {
-			}
-			soong_namespace {
-			}
+				soong_namespace {
+				}
+				soong_namespace {
+				}
 			`,
-		},
-	)
-
-	expectedErrors := []error{
-		errors.New(`dir1/Android.bp:4:4: namespace dir1 already exists`),
-	}
-	if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
-		t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
-	}
+		}),
+	).
+		ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`\Qdir1/Android.bp:4:5: namespace dir1 already exists\E`)).
+		RunTest(t)
 }
 
 func TestNamespaceNotAtTopOfFile(t *testing.T) {
-	_, errs := setupTestExpectErrs(t,
-		map[string]string{
+	GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			"dir1": `
-			test_module {
-				name: "a"
-			}
-			soong_namespace {
-			}
+				test_module {
+					name: "a"
+				}
+				soong_namespace {
+				}
 			`,
-		},
-	)
-
-	expectedErrors := []error{
-		errors.New(`dir1/Android.bp:5:4: a namespace must be the first module in the file`),
-	}
-	if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
-		t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
-	}
+		}),
+	).
+		ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`\Qdir1/Android.bp:5:5: a namespace must be the first module in the file\E`)).
+		RunTest(t)
 }
 
 func TestTwoModulesWithSameNameInSameNamespace(t *testing.T) {
-	_, errs := setupTestExpectErrs(t,
-		map[string]string{
+	GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			"dir1": `
-			soong_namespace {
-			}
-			test_module {
-				name: "a"
-			}
-			test_module {
-				name: "a"
-			}
+				soong_namespace {
+				}
+				test_module {
+					name: "a"
+				}
+				test_module {
+					name: "a"
+				}
 			`,
-		},
-	)
-
-	expectedErrors := []error{
-		errors.New(`dir1/Android.bp:7:4: module "a" already defined
-       dir1/Android.bp:4:4 <-- previous definition here`),
-	}
-	if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
-		t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
-	}
+		}),
+	).
+		ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`\Qdir1/Android.bp:7:5: module "a" already defined
+       dir1/Android.bp:4:5 <-- previous definition here\E`)).
+		RunTest(t)
 }
 
 func TestDeclaringNamespaceInNonAndroidBpFile(t *testing.T) {
-	_, errs := setupTestFromFiles(t,
-		map[string][]byte{
-			"Android.bp": []byte(`
+	GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		FixtureWithRootAndroidBp(`
 				build = ["include.bp"]
-			`),
-			"include.bp": []byte(`
+		`),
+		FixtureAddTextFile("include.bp", `
 				soong_namespace {
 				}
-			`),
-		},
-	)
-
-	expectedErrors := []error{
-		errors.New(`include.bp:2:5: A namespace may only be declared in a file named Android.bp`),
-	}
-
-	if len(errs) != 1 || errs[0].Error() != expectedErrors[0].Error() {
-		t.Errorf("Incorrect errors. Expected:\n%v\n, got:\n%v\n", expectedErrors, errs)
-	}
+		`),
+	).
+		ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(
+			`\Qinclude.bp:2:5: A namespace may only be declared in a file named Android.bp\E`,
+		)).
+		RunTest(t)
 }
 
 // so that the generated .ninja file will have consistent names
 func TestConsistentNamespaceNames(t *testing.T) {
-	ctx := setupTest(t,
-		map[string]string{
+	result := GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			"dir1": "soong_namespace{}",
 			"dir2": "soong_namespace{}",
 			"dir3": "soong_namespace{}",
-		})
+		}),
+	).RunTest(t)
 
-	ns1, _ := ctx.NameResolver.namespaceAt("dir1")
-	ns2, _ := ctx.NameResolver.namespaceAt("dir2")
-	ns3, _ := ctx.NameResolver.namespaceAt("dir3")
+	ns1, _ := result.NameResolver.namespaceAt("dir1")
+	ns2, _ := result.NameResolver.namespaceAt("dir2")
+	ns3, _ := result.NameResolver.namespaceAt("dir3")
 	actualIds := []string{ns1.id, ns2.id, ns3.id}
 	expectedIds := []string{"1", "2", "3"}
 	if !reflect.DeepEqual(actualIds, expectedIds) {
@@ -604,103 +581,88 @@
 
 // so that the generated .ninja file will have consistent names
 func TestRename(t *testing.T) {
-	_ = setupTest(t,
-		map[string]string{
+	GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		dirBpToPreparer(map[string]string{
 			"dir1": `
-			soong_namespace {
-			}
-			test_module {
-				name: "a",
-				deps: ["c"],
-			}
-			test_module {
-				name: "b",
-				rename: "c",
-			}
-		`})
-	// setupTest will report any errors
+				soong_namespace {
+				}
+				test_module {
+					name: "a",
+					deps: ["c"],
+				}
+				test_module {
+					name: "b",
+					rename: "c",
+				}
+			`,
+		}),
+	).RunTest(t)
+
+	// RunTest will report any errors
 }
 
 // some utils to support the tests
 
-func mockFiles(bps map[string]string) (files map[string][]byte) {
-	files = make(map[string][]byte, len(bps))
+var prepareForTestWithNamespace = GroupFixturePreparers(
+	FixtureRegisterWithContext(registerNamespaceBuildComponents),
+	FixtureRegisterWithContext(func(ctx RegistrationContext) {
+		ctx.PreArchMutators(RegisterNamespaceMutator)
+	}),
+	FixtureModifyContext(func(ctx *TestContext) {
+		ctx.RegisterModuleType("test_module", newTestModule)
+		ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
+		ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+			ctx.BottomUp("rename", renameMutator)
+		})
+	}),
+)
+
+// dirBpToPreparer takes a map from directory to the contents of the Android.bp file and produces a
+// FixturePreparer.
+func dirBpToPreparer(bps map[string]string) FixturePreparer {
+	files := make(MockFS, len(bps))
 	files["Android.bp"] = []byte("")
 	for dir, text := range bps {
 		files[filepath.Join(dir, "Android.bp")] = []byte(text)
 	}
-	return files
+	return files.AddToFixture()
 }
 
-func setupTestFromFiles(t *testing.T, bps MockFS) (ctx *TestContext, errs []error) {
-	result := GroupFixturePreparers(
-		FixtureModifyContext(func(ctx *TestContext) {
-			ctx.RegisterModuleType("test_module", newTestModule)
-			ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
-			ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-				ctx.BottomUp("rename", renameMutator)
-			})
-		}),
-		PrepareForTestWithNamespace,
-		bps.AddToFixture(),
-	).
-		// Ignore errors for now so tests can check them later.
-		ExtendWithErrorHandler(FixtureIgnoreErrors).
-		RunTest(t)
-
-	return result.TestContext, result.Errs
-}
-
-func setupTestExpectErrs(t *testing.T, bps map[string]string) (ctx *TestContext, errs []error) {
-	files := make(map[string][]byte, len(bps))
-	files["Android.bp"] = []byte("")
-	for dir, text := range bps {
-		files[filepath.Join(dir, "Android.bp")] = []byte(text)
-	}
-	return setupTestFromFiles(t, files)
-}
-
-func setupTest(t *testing.T, bps map[string]string) (ctx *TestContext) {
-	t.Helper()
-	ctx, errs := setupTestExpectErrs(t, bps)
-	FailIfErrored(t, errs)
-	return ctx
-}
-
-func dependsOn(ctx *TestContext, module TestingModule, possibleDependency TestingModule) bool {
+func dependsOn(result *TestResult, module TestingModule, possibleDependency TestingModule) bool {
 	depends := false
 	visit := func(dependency blueprint.Module) {
 		if dependency == possibleDependency.module {
 			depends = true
 		}
 	}
-	ctx.VisitDirectDeps(module.module, visit)
+	result.VisitDirectDeps(module.module, visit)
 	return depends
 }
 
-func numDeps(ctx *TestContext, module TestingModule) int {
+func numDeps(result *TestResult, module TestingModule) int {
 	count := 0
 	visit := func(dependency blueprint.Module) {
 		count++
 	}
-	ctx.VisitDirectDeps(module.module, visit)
+	result.VisitDirectDeps(module.module, visit)
 	return count
 }
 
-func getModule(ctx *TestContext, moduleName string) TestingModule {
-	return ctx.ModuleForTests(moduleName, "")
+func getModule(result *TestResult, moduleName string) TestingModule {
+	return result.ModuleForTests(moduleName, "")
 }
 
-func findModuleById(ctx *TestContext, id string) (module TestingModule) {
+func findModuleById(result *TestResult, id string) (module TestingModule) {
 	visit := func(candidate blueprint.Module) {
 		testModule, ok := candidate.(*testModule)
 		if ok {
 			if testModule.properties.Id == id {
-				module = newTestingModule(ctx.config, testModule)
+				module = newTestingModule(result.config, testModule)
 			}
 		}
 	}
-	ctx.VisitAllModules(visit)
+	result.VisitAllModules(visit)
 	return module
 }
 
@@ -747,7 +709,7 @@
 	}
 }
 
-func (b *blueprintTestModule) DynamicDependencies(ctx blueprint.DynamicDependerModuleContext) []string {
+func (b *blueprintTestModule) DynamicDependencies(_ blueprint.DynamicDependerModuleContext) []string {
 	return b.properties.Deps
 }
 
diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
index 3b66369..e4d9cbc 100644
--- a/bp2build/java_library_conversion_test.go
+++ b/bp2build/java_library_conversion_test.go
@@ -267,3 +267,108 @@
 			}),
 		}})
 }
+
+func TestJavaLibraryResources(t *testing.T) {
+	runJavaLibraryTestCase(t, bp2buildTestCase{
+		filesystem: map[string]string{
+			"res/a.res":      "",
+			"res/b.res":      "",
+			"res/dir1/b.res": "",
+		},
+		blueprint: `java_library {
+    name: "java-lib-1",
+	java_resources: ["res/a.res", "res/b.res"],
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+				"resources": `[
+        "res/a.res",
+        "res/b.res",
+    ]`,
+			}),
+		},
+	})
+}
+
+func TestJavaLibraryResourceDirs(t *testing.T) {
+	runJavaLibraryTestCase(t, bp2buildTestCase{
+		filesystem: map[string]string{
+			"res/a.res":      "",
+			"res/b.res":      "",
+			"res/dir1/b.res": "",
+		},
+		blueprint: `java_library {
+    name: "java-lib-1",
+	java_resource_dirs: ["res"],
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+				"resource_strip_prefix": `"res"`,
+				"resources": `[
+        "res/a.res",
+        "res/b.res",
+        "res/dir1/b.res",
+    ]`,
+			}),
+		},
+	})
+}
+
+func TestJavaLibraryResourcesExcludeDir(t *testing.T) {
+	runJavaLibraryTestCase(t, bp2buildTestCase{
+		filesystem: map[string]string{
+			"res/a.res":         "",
+			"res/exclude/b.res": "",
+		},
+		blueprint: `java_library {
+    name: "java-lib-1",
+	java_resource_dirs: ["res"],
+	exclude_java_resource_dirs: ["res/exclude"],
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+				"resource_strip_prefix": `"res"`,
+				"resources":             `["res/a.res"]`,
+			}),
+		},
+	})
+}
+
+func TestJavaLibraryResourcesExcludeFile(t *testing.T) {
+	runJavaLibraryTestCase(t, bp2buildTestCase{
+		filesystem: map[string]string{
+			"res/a.res":            "",
+			"res/dir1/b.res":       "",
+			"res/dir1/exclude.res": "",
+		},
+		blueprint: `java_library {
+    name: "java-lib-1",
+	java_resource_dirs: ["res"],
+	exclude_java_resources: ["res/dir1/exclude.res"],
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+				"resource_strip_prefix": `"res"`,
+				"resources": `[
+        "res/a.res",
+        "res/dir1/b.res",
+    ]`,
+			}),
+		},
+	})
+}
+
+func TestJavaLibraryResourcesFailsWithMultipleDirs(t *testing.T) {
+	runJavaLibraryTestCase(t, bp2buildTestCase{
+		filesystem: map[string]string{
+			"res/a.res":  "",
+			"res1/a.res": "",
+		},
+		blueprint: `java_library {
+    name: "java-lib-1",
+	java_resource_dirs: ["res", "res1"],
+}`,
+		expectedErr:          fmt.Errorf("bp2build does not support more than one directory in java_resource_dirs (b/226423379)"),
+		expectedBazelTargets: []string{},
+	})
+}
diff --git a/java/base.go b/java/base.go
index 1e8095b..c6f859a 100644
--- a/java/base.go
+++ b/java/base.go
@@ -1020,6 +1020,7 @@
 		ctx.PropertyErrorf("common_srcs", "common_srcs must be .kt files")
 	}
 
+	nonGeneratedSrcJars := srcFiles.FilterByExt(".srcjar")
 	srcFiles = j.genSources(ctx, srcFiles, flags)
 
 	// Collect javac flags only after computing the full set of srcFiles to
@@ -1492,8 +1493,8 @@
 		}
 
 		j.linter.name = ctx.ModuleName()
-		j.linter.srcs = srcFiles
-		j.linter.srcJars = srcJars
+		j.linter.srcs = append(srcFiles, nonGeneratedSrcJars...)
+		j.linter.srcJars, _ = android.FilterPathList(srcJars, nonGeneratedSrcJars)
 		j.linter.classpath = append(append(android.Paths(nil), flags.bootClasspath...), flags.classpath...)
 		j.linter.classes = j.implementationJarFile
 		j.linter.minSdkVersion = lintSDKVersion(j.MinSdkVersion(ctx))
diff --git a/java/bootclasspath.go b/java/bootclasspath.go
index 52ce77d..f4cef7f 100644
--- a/java/bootclasspath.go
+++ b/java/bootclasspath.go
@@ -84,6 +84,9 @@
 		}
 	}
 
+	target := ctx.Module().Target()
+	variations = append(variations, target.Variations()...)
+
 	addedDep := false
 	if ctx.OtherModuleDependencyVariantExists(variations, name) {
 		ctx.AddFarVariationDependencies(variations, tag, name)
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index c3a5d5f..0345aad 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -273,7 +273,7 @@
 	android.InitApexModule(m)
 	android.InitSdkAwareModule(m)
 	initClasspathFragment(m, BOOTCLASSPATH)
-	android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
+	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
 
 	android.AddLoadHook(m, func(ctx android.LoadHookContext) {
 		// If code coverage has been enabled for the framework then append the properties with
diff --git a/java/java.go b/java/java.go
index 13f4c80..079d4b9 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2018,7 +2018,49 @@
 	}
 }
 
+type javaResourcesAttributes struct {
+	Resources             bazel.LabelListAttribute
+	Resource_strip_prefix *string
+}
+
+func (m *Library) convertJavaResourcesAttributes(ctx android.TopDownMutatorContext) *javaResourcesAttributes {
+	var resources bazel.LabelList
+	var resourceStripPrefix *string
+
+	if m.properties.Java_resources != nil {
+		resources.Append(android.BazelLabelForModuleSrc(ctx, m.properties.Java_resources))
+	}
+
+	//TODO(b/179889880) handle case where glob includes files outside package
+	resDeps := ResourceDirsToFiles(
+		ctx,
+		m.properties.Java_resource_dirs,
+		m.properties.Exclude_java_resource_dirs,
+		m.properties.Exclude_java_resources,
+	)
+
+	for i, resDep := range resDeps {
+		dir, files := resDep.dir, resDep.files
+
+		resources.Append(bazel.MakeLabelList(android.RootToModuleRelativePaths(ctx, files)))
+
+		// Bazel includes the relative path from the WORKSPACE root when placing the resource
+		// inside the JAR file, so we need to remove that prefix
+		resourceStripPrefix = proptools.StringPtr(dir.String())
+		if i > 0 {
+			// TODO(b/226423379) allow multiple resource prefixes
+			ctx.ModuleErrorf("bp2build does not support more than one directory in java_resource_dirs (b/226423379)")
+		}
+	}
+
+	return &javaResourcesAttributes{
+		Resources:             bazel.MakeLabelListAttribute(resources),
+		Resource_strip_prefix: resourceStripPrefix,
+	}
+}
+
 type javaCommonAttributes struct {
+	*javaResourcesAttributes
 	Srcs      bazel.LabelListAttribute
 	Plugins   bazel.LabelListAttribute
 	Javacopts bazel.StringListAttribute
@@ -2095,7 +2137,8 @@
 	}
 
 	commonAttrs := &javaCommonAttributes{
-		Srcs: javaSrcs,
+		Srcs:                    javaSrcs,
+		javaResourcesAttributes: m.convertJavaResourcesAttributes(ctx),
 		Plugins: bazel.MakeLabelListAttribute(
 			android.BazelLabelForModuleDeps(ctx, m.properties.Plugins),
 		),
diff --git a/java/java_resources.go b/java/java_resources.go
index 787d74a..b0dc5a1 100644
--- a/java/java_resources.go
+++ b/java/java_resources.go
@@ -33,8 +33,13 @@
 	"**/*~",
 }
 
-func ResourceDirsToJarArgs(ctx android.ModuleContext,
-	resourceDirs, excludeResourceDirs, excludeResourceFiles []string) (args []string, deps android.Paths) {
+type resourceDeps struct {
+	dir   android.Path
+	files android.Paths
+}
+
+func ResourceDirsToFiles(ctx android.BaseModuleContext,
+	resourceDirs, excludeResourceDirs, excludeResourceFiles []string) (deps []resourceDeps) {
 	var excludeDirs []string
 	var excludeFiles []string
 
@@ -55,21 +60,36 @@
 		dirs := ctx.Glob(android.PathForSource(ctx, ctx.ModuleDir()).Join(ctx, resourceDir).String(), excludeDirs)
 		for _, dir := range dirs {
 			files := ctx.GlobFiles(filepath.Join(dir.String(), "**/*"), excludeFiles)
+			deps = append(deps, resourceDeps{
+				dir:   dir,
+				files: files,
+			})
+		}
+	}
 
+	return deps
+}
+
+func ResourceDirsToJarArgs(ctx android.ModuleContext,
+	resourceDirs, excludeResourceDirs, excludeResourceFiles []string) (args []string, deps android.Paths) {
+	resDeps := ResourceDirsToFiles(ctx, resourceDirs, excludeResourceDirs, excludeResourceFiles)
+
+	for _, resDep := range resDeps {
+		dir, files := resDep.dir, resDep.files
+
+		if len(files) > 0 {
+			args = append(args, "-C", dir.String())
 			deps = append(deps, files...)
 
-			if len(files) > 0 {
-				args = append(args, "-C", dir.String())
-
-				for _, f := range files {
-					path := f.String()
-					if !strings.HasPrefix(path, dir.String()) {
-						panic(fmt.Errorf("path %q does not start with %q", path, dir))
-					}
-					args = append(args, "-f", pathtools.MatchEscape(path))
+			for _, f := range files {
+				path := f.String()
+				if !strings.HasPrefix(path, dir.String()) {
+					panic(fmt.Errorf("path %q does not start with %q", path, dir))
 				}
+				args = append(args, "-f", pathtools.MatchEscape(path))
 			}
 		}
+
 	}
 
 	return args, deps
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index 1c2a3ae..6257e49 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -51,6 +51,7 @@
 	var addSourceBootclassPathModule = android.FixtureAddTextFile("source/Android.bp", `
 		java_library {
 			name: "foo",
+			host_supported: true, // verify that b/232106778 is fixed
 			srcs: ["a.java"],
 			system_modules: "none",
 			sdk_version: "none",
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index 9f9b863..4bc713b 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -1303,7 +1303,7 @@
 
 	// Modules that are enabled for Mixed Builds.
 	MixedBuildEnabledModules []string `protobuf:"bytes,1,rep,name=mixed_build_enabled_modules,json=mixedBuildEnabledModules" json:"mixed_build_enabled_modules,omitempty"`
-	// Modules that are not currently eligible for MixedBuilds
+	// Modules that are not enabled for MixedBuilds
 	MixedBuildDisabledModules []string `protobuf:"bytes,2,rep,name=mixed_build_disabled_modules,json=mixedBuildDisabledModules" json:"mixed_build_disabled_modules,omitempty"`
 }