Merge changes I71a83e3a,I66101c0c,Ie387c8c4,Iea742e75

* changes:
  Strengthen metalava sandbox support using sbox
  Move metalava's output files into a subdirectory
  Fix lint warnings in droidstubs.go
  Split droidstubs out of droiddoc.go
diff --git a/android/bazel.go b/android/bazel.go
index 9a14e70..5bb3879 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -137,7 +137,6 @@
 		"libc_init_static":              true,
 		"libc_init_dynamic":             true,
 		"libc_tzcode":                   true,
-		"lib_dns":                       true,
 		"libc_freebsd":                  true,
 		"libc_freebsd_large_stack":      true,
 		"libc_netbsd":                   true,
@@ -169,6 +168,7 @@
 		"liblinker_malloc":              true,
 		"liblinker_debuggerd_stub":      true,
 		"libbionic_tests_headers_posix": true,
+		"libc_dns":                      true,
 	}
 )
 
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 04864a1..ebccaa7 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -272,11 +272,9 @@
 	}
 }
 
-// PrebuiltPostDepsMutator does two operations.  It replace dependencies on the
-// source module with dependencies on the prebuilt when both modules exist and
-// the prebuilt should be used.  When the prebuilt should not be used, disable
-// installing it.  Secondly, it also adds a sourcegroup to any filegroups found
-// in the prebuilt's 'Srcs' property.
+// PrebuiltPostDepsMutator replaces dependencies on the source module with dependencies on the
+// prebuilt when both modules exist and the prebuilt should be used.  When the prebuilt should not
+// be used, disable installing it.
 func PrebuiltPostDepsMutator(ctx BottomUpMutatorContext) {
 	if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
 		p := m.Prebuilt()
diff --git a/apex/testing.go b/apex/testing.go
index e662cad..926125f 100644
--- a/apex/testing.go
+++ b/apex/testing.go
@@ -19,4 +19,11 @@
 var PrepareForTestWithApexBuildComponents = android.GroupFixturePreparers(
 	android.FixtureRegisterWithContext(registerApexBuildComponents),
 	android.FixtureRegisterWithContext(registerApexKeyBuildComponents),
+	// Additional files needed in tests that disallow non-existent source files.
+	// This includes files that are needed by all, or at least most, instances of an apex module type.
+	android.MockFS{
+		// Needed by apex.
+		"system/core/rootdir/etc/public.libraries.android.txt": nil,
+		"build/soong/scripts/gen_ndk_backedby_apex.sh":         nil,
+	}.AddToFixture(),
 )
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index c74f902..cc616f2 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -27,6 +27,7 @@
         "build_conversion_test.go",
         "bzl_conversion_test.go",
         "cc_library_headers_conversion_test.go",
+        "cc_library_static_conversion_test.go",
         "cc_object_conversion_test.go",
         "conversion_test.go",
         "python_binary_conversion_test.go",
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index 5bf5c80..049f84a 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -123,7 +123,8 @@
     name: "foo_headers",
     export_include_dirs: ["dir-1", "dir-2"],
     header_libs: ["lib-1", "lib-2"],
-    export_header_lib_headers: ["lib-1", "lib-2"],
+
+    // TODO: Also support export_header_lib_headers
     bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{`cc_library_headers(
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
new file mode 100644
index 0000000..7bf5fd3
--- /dev/null
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -0,0 +1,307 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+	"android/soong/android"
+	"android/soong/cc"
+	"strings"
+	"testing"
+)
+
+const (
+	// See cc/testing.go for more context
+	soongCcLibraryStaticPreamble = `
+cc_defaults {
+	name: "linux_bionic_supported",
+}
+
+toolchain_library {
+	name: "libclang_rt.builtins-x86_64-android",
+	defaults: ["linux_bionic_supported"],
+	vendor_available: true,
+	vendor_ramdisk_available: true,
+	product_available: true,
+	recovery_available: true,
+	native_bridge_supported: true,
+	src: "",
+}
+
+toolchain_library {
+	name: "libatomic",
+	defaults: ["linux_bionic_supported"],
+	vendor_available: true,
+	vendor_ramdisk_available: true,
+	product_available: true,
+	recovery_available: true,
+	native_bridge_supported: true,
+	src: "",
+}`
+)
+
+func TestCcLibraryStaticLoadStatement(t *testing.T) {
+	testCases := []struct {
+		bazelTargets           BazelTargets
+		expectedLoadStatements string
+	}{
+		{
+			bazelTargets: BazelTargets{
+				BazelTarget{
+					name:      "cc_library_static_target",
+					ruleClass: "cc_library_static",
+					// NOTE: No bzlLoadLocation for native rules
+				},
+			},
+			expectedLoadStatements: ``,
+		},
+	}
+
+	for _, testCase := range testCases {
+		actual := testCase.bazelTargets.LoadStatements()
+		expected := testCase.expectedLoadStatements
+		if actual != expected {
+			t.Fatalf("Expected load statements to be %s, got %s", expected, actual)
+		}
+	}
+
+}
+
+func TestCcLibraryStaticBp2Build(t *testing.T) {
+	testCases := []struct {
+		description                        string
+		moduleTypeUnderTest                string
+		moduleTypeUnderTestFactory         android.ModuleFactory
+		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+		preArchMutators                    []android.RegisterMutatorFunc
+		depsMutators                       []android.RegisterMutatorFunc
+		bp                                 string
+		expectedBazelTargets               []string
+		filesystem                         map[string]string
+		dir                                string
+	}{
+		{
+			description:                        "cc_library_static test",
+			moduleTypeUnderTest:                "cc_library_static",
+			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+			filesystem: map[string]string{
+				// NOTE: include_dir headers *should not* appear in Bazel hdrs later (?)
+				"include_dir_1/include_dir_1_a.h": "",
+				"include_dir_1/include_dir_1_b.h": "",
+				"include_dir_2/include_dir_2_a.h": "",
+				"include_dir_2/include_dir_2_b.h": "",
+				// NOTE: local_include_dir headers *should not* appear in Bazel hdrs later (?)
+				"local_include_dir_1/local_include_dir_1_a.h": "",
+				"local_include_dir_1/local_include_dir_1_b.h": "",
+				"local_include_dir_2/local_include_dir_2_a.h": "",
+				"local_include_dir_2/local_include_dir_2_b.h": "",
+				// NOTE: export_include_dir headers *should* appear in Bazel hdrs later
+				"export_include_dir_1/export_include_dir_1_a.h": "",
+				"export_include_dir_1/export_include_dir_1_b.h": "",
+				"export_include_dir_2/export_include_dir_2_a.h": "",
+				"export_include_dir_2/export_include_dir_2_b.h": "",
+			},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_headers {
+    name: "header_lib_1",
+    export_include_dirs: ["header_lib_1"],
+}
+
+cc_library_headers {
+    name: "header_lib_2",
+    export_include_dirs: ["header_lib_2"],
+}
+
+cc_library_static {
+    name: "static_lib_1",
+    srcs: ["static_lib_1.cc"],
+    bazel_module: { bp2build_available: true },
+}
+
+cc_library_static {
+    name: "static_lib_2",
+    srcs: ["static_lib_2.cc"],
+    bazel_module: { bp2build_available: true },
+}
+
+cc_library_static {
+    name: "whole_static_lib_1",
+    srcs: ["whole_static_lib_1.cc"],
+    bazel_module: { bp2build_available: true },
+}
+
+cc_library_static {
+    name: "whole_static_lib_2",
+    srcs: ["whole_static_lib_2.cc"],
+    bazel_module: { bp2build_available: true },
+}
+
+cc_library_static {
+    name: "foo_static",
+    srcs: [
+        "foo_static1.cc",
+	"foo_static2.cc",
+    ],
+    cflags: [
+        "-Dflag1",
+	"-Dflag2"
+    ],
+    static_libs: [
+        "static_lib_1",
+	"static_lib_2"
+    ],
+    whole_static_libs: [
+        "whole_static_lib_1",
+	"whole_static_lib_2"
+    ],
+    include_dirs: [
+	"include_dir_1",
+	"include_dir_2",
+    ],
+    local_include_dirs: [
+        "local_include_dir_1",
+	"local_include_dir_2",
+    ],
+    export_include_dirs: [
+	"export_include_dir_1",
+	"export_include_dir_2"
+    ],
+    header_libs: [
+        "header_lib_1",
+	"header_lib_2"
+    ],
+
+    // TODO: Also support export_header_lib_headers
+
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = [
+        "-Dflag1",
+        "-Dflag2",
+    ],
+    deps = [
+        ":header_lib_1",
+        ":header_lib_2",
+        ":static_lib_1",
+        ":static_lib_2",
+        ":whole_static_lib_1",
+        ":whole_static_lib_2",
+    ],
+    hdrs = [
+        "export_include_dir_1/export_include_dir_1_a.h",
+        "export_include_dir_1/export_include_dir_1_b.h",
+        "export_include_dir_2/export_include_dir_2_a.h",
+        "export_include_dir_2/export_include_dir_2_b.h",
+    ],
+    includes = [
+        "export_include_dir_1",
+        "export_include_dir_2",
+        "include_dir_1",
+        "include_dir_2",
+        "local_include_dir_1",
+        "local_include_dir_2",
+    ],
+    linkstatic = True,
+    srcs = [
+        "foo_static1.cc",
+        "foo_static2.cc",
+    ],
+)`, `cc_library_static(
+    name = "static_lib_1",
+    linkstatic = True,
+    srcs = [
+        "static_lib_1.cc",
+    ],
+)`, `cc_library_static(
+    name = "static_lib_2",
+    linkstatic = True,
+    srcs = [
+        "static_lib_2.cc",
+    ],
+)`, `cc_library_static(
+    name = "whole_static_lib_1",
+    linkstatic = True,
+    srcs = [
+        "whole_static_lib_1.cc",
+    ],
+)`, `cc_library_static(
+    name = "whole_static_lib_2",
+    linkstatic = True,
+    srcs = [
+        "whole_static_lib_2.cc",
+    ],
+)`},
+		},
+	}
+
+	dir := "."
+	for _, testCase := range testCases {
+		filesystem := make(map[string][]byte)
+		toParse := []string{
+			"Android.bp",
+		}
+		for f, content := range testCase.filesystem {
+			if strings.HasSuffix(f, "Android.bp") {
+				toParse = append(toParse, f)
+			}
+			filesystem[f] = []byte(content)
+		}
+		config := android.TestConfig(buildDir, nil, testCase.bp, filesystem)
+		ctx := android.NewTestContext(config)
+
+		cc.RegisterCCBuildComponents(ctx)
+		ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
+		ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
+
+		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+		for _, m := range testCase.depsMutators {
+			ctx.DepsBp2BuildMutators(m)
+		}
+		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+		ctx.RegisterForBazelConversion()
+
+		_, errs := ctx.ParseFileList(dir, toParse)
+		if Errored(t, testCase.description, errs) {
+			continue
+		}
+		_, errs = ctx.ResolveDependencies(config)
+		if Errored(t, testCase.description, errs) {
+			continue
+		}
+
+		checkDir := dir
+		if testCase.dir != "" {
+			checkDir = testCase.dir
+		}
+		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+		bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
+		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
+		} else {
+			for i, target := range bazelTargets {
+				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+					t.Errorf(
+						"%s: Expected generated Bazel target to be '%s', got '%s'",
+						testCase.description,
+						w,
+						g,
+					)
+				}
+			}
+		}
+	}
+}
diff --git a/cc/Android.bp b/cc/Android.bp
index bdbb3c0..79e92cb 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -10,6 +10,7 @@
         "blueprint-pathtools",
         "soong",
         "soong-android",
+        "soong-bazel",
         "soong-cc-config",
         "soong-etc",
         "soong-genrule",
diff --git a/cc/cc.go b/cc/cc.go
index 0c46b24..f843b41 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1363,6 +1363,19 @@
 	if ver == "apex_inherit" || ver == "" {
 		ver = ctx.sdkVersion()
 	}
+	// For crt objects, the meaning of min_sdk_version is very different from other types of
+	// module. For them, min_sdk_version defines the oldest version that the build system will
+	// create versioned variants for. For example, if min_sdk_version is 16, then sdk variant of
+	// the crt object has local variants of 16, 17, ..., up to the latest version. sdk_version
+	// and min_sdk_version properties of the variants are set to the corresponding version
+	// numbers. However, the platform (non-sdk) variant of the crt object is left untouched.
+	// min_sdk_version: 16 doesn't actually mean that the platform variant has to support such
+	// an old version. Since the variant is for the platform, it's preferred to target the
+	// latest version.
+	if ctx.mod.SplitPerApiLevel() && !ctx.isSdkVariant() {
+		ver = strconv.Itoa(android.FutureApiLevelInt)
+	}
+
 	// Also make sure that minSdkVersion is not greater than sdkVersion, if they are both numbers
 	sdkVersionInt, err := strconv.Atoi(ctx.sdkVersion())
 	minSdkVersionInt, err2 := strconv.Atoi(ver)
@@ -1927,9 +1940,14 @@
 		return nil
 	}
 	if m.UseSdk() {
+		// Choose the CRT that best satisfies the min_sdk_version requirement of this module
+		minSdkVersion := m.MinSdkVersion()
+		if minSdkVersion == "" || minSdkVersion == "apex_inherit" {
+			minSdkVersion = m.SdkVersion()
+		}
 		return []blueprint.Variation{
 			{Mutator: "sdk", Variation: "sdk"},
-			{Mutator: "version", Variation: m.SdkVersion()},
+			{Mutator: "version", Variation: minSdkVersion},
 		}
 	}
 	return []blueprint.Variation{
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 7196615..f1efbff 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -166,6 +166,15 @@
 	recoveryVariant = "android_recovery_arm64_armv8-a_shared"
 )
 
+// Test that the PrepareForTestWithCcDefaultModules provides all the files that it uses by
+// running it in a fixture that requires all source files to exist.
+func TestPrepareForTestWithCcDefaultModules(t *testing.T) {
+	android.GroupFixturePreparers(
+		PrepareForTestWithCcDefaultModules,
+		android.PrepareForTestDisallowNonExistentPaths,
+	).RunTest(t)
+}
+
 func TestFuchsiaDeps(t *testing.T) {
 	t.Helper()
 
@@ -3628,6 +3637,71 @@
 	}
 }
 
+func TestMinSdkVersionInClangTriple(t *testing.T) {
+	ctx := testCc(t, `
+		cc_library_shared {
+			name: "libfoo",
+			srcs: ["foo.c"],
+			min_sdk_version: "29",
+		}`)
+
+	cFlags := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+	android.AssertStringDoesContain(t, "min sdk version", cFlags, "-target aarch64-linux-android29")
+}
+
+func TestMinSdkVersionsOfCrtObjects(t *testing.T) {
+	ctx := testCc(t, `
+		cc_object {
+			name: "crt_foo",
+			srcs: ["foo.c"],
+			crt: true,
+			stl: "none",
+			min_sdk_version: "28",
+
+		}`)
+
+	arch := "android_arm64_armv8-a"
+	for _, v := range []string{"", "28", "29", "30", "current"} {
+		var variant string
+		if v == "" {
+			variant = arch
+		} else {
+			variant = arch + "_sdk_" + v
+		}
+		cflags := ctx.ModuleForTests("crt_foo", variant).Rule("cc").Args["cFlags"]
+		vNum := v
+		if v == "current" || v == "" {
+			vNum = "10000"
+		}
+		expected := "-target aarch64-linux-android" + vNum + " "
+		android.AssertStringDoesContain(t, "cflag", cflags, expected)
+	}
+}
+
+func TestUseCrtObjectOfCorrectVersion(t *testing.T) {
+	ctx := testCc(t, `
+		cc_binary {
+			name: "bin",
+			srcs: ["foo.c"],
+			stl: "none",
+			min_sdk_version: "29",
+			sdk_version: "current",
+		}
+		`)
+
+	// Sdk variant uses the crt object of the matching min_sdk_version
+	variant := "android_arm64_armv8-a_sdk"
+	crt := ctx.ModuleForTests("bin", variant).Rule("ld").Args["crtBegin"]
+	android.AssertStringDoesContain(t, "crt dep of sdk variant", crt,
+		variant+"_29/crtbegin_dynamic.o")
+
+	// platform variant uses the crt object built for platform
+	variant = "android_arm64_armv8-a"
+	crt = ctx.ModuleForTests("bin", variant).Rule("ld").Args["crtBegin"]
+	android.AssertStringDoesContain(t, "crt dep of platform variant", crt,
+		variant+"/crtbegin_dynamic.o")
+}
+
 type MemtagNoteType int
 
 const (
diff --git a/cc/compiler.go b/cc/compiler.go
index b09b58e..78a5a5d 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -414,12 +414,7 @@
 
 	target := "-target " + tc.ClangTriple()
 	if ctx.Os().Class == android.Device {
-		// When built for the non-updateble part of platform, minSdkVersion doesn't matter.
-		// It matters only when building we are building for modules that can be unbundled.
-		version := "current"
-		if !ctx.isForPlatform() || ctx.isSdkVariant() {
-			version = ctx.minSdkVersion()
-		}
+		version := ctx.minSdkVersion()
 		if version == "" || version == "current" {
 			target += strconv.Itoa(android.FutureApiLevelInt)
 		} else {
diff --git a/cc/library.go b/cc/library.go
index 22a36c6..9bec974 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -27,6 +27,7 @@
 	"github.com/google/blueprint/pathtools"
 
 	"android/soong/android"
+	"android/soong/bazel"
 	"android/soong/cc/config"
 )
 
@@ -200,6 +201,8 @@
 
 func init() {
 	RegisterLibraryBuildComponents(android.InitRegistrationContext)
+
+	android.RegisterBp2BuildMutator("cc_library_static", CcLibraryStaticBp2Build)
 }
 
 func RegisterLibraryBuildComponents(ctx android.RegistrationContext) {
@@ -1914,6 +1917,7 @@
 
 	for i, module := range modules {
 		module.(*Module).Properties.Sdk_version = StringPtr(versionStrs[i])
+		module.(*Module).Properties.Min_sdk_version = StringPtr(versionStrs[i])
 	}
 }
 
@@ -2024,3 +2028,137 @@
 
 	return outputFile
 }
+
+func Bp2BuildParseHeaderLibs(ctx android.TopDownMutatorContext, module *Module) bazel.LabelList {
+	var headerLibs []string
+	for _, linkerProps := range module.linker.linkerProps() {
+		if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok {
+			headerLibs = baseLinkerProps.Header_libs
+			// FIXME: re-export include dirs from baseLinkerProps.Export_header_lib_headers?
+			break
+		}
+	}
+
+	headerLibsLabels := android.BazelLabelForModuleDeps(ctx, headerLibs)
+	return headerLibsLabels
+}
+
+func Bp2BuildParseExportedIncludes(ctx android.TopDownMutatorContext, module *Module) (bazel.LabelList, bazel.LabelList) {
+	libraryDecorator := module.linker.(*libraryDecorator)
+
+	includeDirs := libraryDecorator.flagExporter.Properties.Export_system_include_dirs
+	includeDirs = append(includeDirs, libraryDecorator.flagExporter.Properties.Export_include_dirs...)
+
+	includeDirsLabels := android.BazelLabelForModuleSrc(ctx, includeDirs)
+
+	var includeDirGlobs []string
+	for _, includeDir := range includeDirs {
+		includeDirGlobs = append(includeDirGlobs, includeDir+"/**/*.h")
+		includeDirGlobs = append(includeDirGlobs, includeDir+"/**/*.inc")
+		includeDirGlobs = append(includeDirGlobs, includeDir+"/**/*.hpp")
+	}
+
+	headersLabels := android.BazelLabelForModuleSrc(ctx, includeDirGlobs)
+
+	return includeDirsLabels, headersLabels
+}
+
+type bazelCcLibraryStaticAttributes struct {
+	Copts      []string
+	Srcs       bazel.LabelList
+	Deps       bazel.LabelList
+	Linkstatic bool
+	Includes   bazel.LabelList
+	Hdrs       bazel.LabelList
+}
+
+type bazelCcLibraryStatic struct {
+	android.BazelTargetModuleBase
+	bazelCcLibraryStaticAttributes
+}
+
+func BazelCcLibraryStaticFactory() android.Module {
+	module := &bazelCcLibraryStatic{}
+	module.AddProperties(&module.bazelCcLibraryStaticAttributes)
+	android.InitBazelTargetModule(module)
+	return module
+}
+
+func CcLibraryStaticBp2Build(ctx android.TopDownMutatorContext) {
+	module, ok := ctx.Module().(*Module)
+	if !ok {
+		// Not a cc module
+		return
+	}
+	if !module.ConvertWithBp2build(ctx) {
+		return
+	}
+	if ctx.ModuleType() != "cc_library_static" {
+		return
+	}
+
+	var copts []string
+	var srcs []string
+	var includeDirs []string
+	var localIncludeDirs []string
+	for _, props := range module.compiler.compilerProps() {
+		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
+			copts = baseCompilerProps.Cflags
+			srcs = baseCompilerProps.Srcs
+			includeDirs = baseCompilerProps.Include_dirs
+			localIncludeDirs = baseCompilerProps.Local_include_dirs
+			break
+		}
+	}
+	srcsLabels := android.BazelLabelForModuleSrc(ctx, srcs)
+
+	var staticLibs []string
+	var wholeStaticLibs []string
+	for _, props := range module.linker.linkerProps() {
+		if baseLinkerProperties, ok := props.(*BaseLinkerProperties); ok {
+			staticLibs = baseLinkerProperties.Static_libs
+			wholeStaticLibs = baseLinkerProperties.Whole_static_libs
+			break
+		}
+	}
+
+	// FIXME: Treat Static_libs and Whole_static_libs differently?
+	allDeps := staticLibs
+	allDeps = append(allDeps, wholeStaticLibs...)
+
+	depsLabels := android.BazelLabelForModuleDeps(ctx, allDeps)
+
+	// FIXME: Unify absolute vs relative paths
+	// FIXME: Use -I copts instead of setting includes= ?
+	allIncludes := includeDirs
+	allIncludes = append(allIncludes, localIncludeDirs...)
+	includesLabels := android.BazelLabelForModuleSrc(ctx, allIncludes)
+
+	exportedIncludesLabels, exportedIncludesHeadersLabels := Bp2BuildParseExportedIncludes(ctx, module)
+	includesLabels.Append(exportedIncludesLabels)
+
+	headerLibsLabels := Bp2BuildParseHeaderLibs(ctx, module)
+	depsLabels.Append(headerLibsLabels)
+
+	attrs := &bazelCcLibraryStaticAttributes{
+		Copts:      copts,
+		Srcs:       bazel.UniqueBazelLabelList(srcsLabels),
+		Deps:       bazel.UniqueBazelLabelList(depsLabels),
+		Linkstatic: true,
+		Includes:   bazel.UniqueBazelLabelList(includesLabels),
+		Hdrs:       bazel.UniqueBazelLabelList(exportedIncludesHeadersLabels),
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "cc_library_static",
+		Bzl_load_location: "//build/bazel/rules:cc_library_static.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(BazelCcLibraryStaticFactory, module.Name(), props, attrs)
+}
+
+func (m *bazelCcLibraryStatic) Name() string {
+	return m.BaseModuleName()
+}
+
+func (m *bazelCcLibraryStatic) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 0f4d8a6..719d538 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -94,35 +94,14 @@
 		return
 	}
 
-	lib, _ := module.linker.(*libraryDecorator)
+	exportedIncludesLabels, exportedIncludesHeadersLabels := Bp2BuildParseExportedIncludes(ctx, module)
 
-	// list of directories that will be added to the include path (using -I) for this
-	// module and any module that links against this module.
-	includeDirs := lib.flagExporter.Properties.Export_system_include_dirs
-	includeDirs = append(includeDirs, lib.flagExporter.Properties.Export_include_dirs...)
-	includeDirLabels := android.BazelLabelForModuleSrc(ctx, includeDirs)
-
-	var includeDirGlobs []string
-	for _, includeDir := range includeDirs {
-		includeDirGlobs = append(includeDirGlobs, includeDir+"/**/*.h")
-	}
-
-	headerLabels := android.BazelLabelForModuleSrc(ctx, includeDirGlobs)
-
-	// list of modules that should only provide headers for this module.
-	var headerLibs []string
-	for _, linkerProps := range lib.linkerProps() {
-		if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok {
-			headerLibs = baseLinkerProps.Export_header_lib_headers
-			break
-		}
-	}
-	headerLibLabels := android.BazelLabelForModuleDeps(ctx, headerLibs)
+	headerLibsLabels := Bp2BuildParseHeaderLibs(ctx, module)
 
 	attrs := &bazelCcLibraryHeadersAttributes{
-		Includes: includeDirLabels,
-		Hdrs:     headerLabels,
-		Deps:     headerLibLabels,
+		Includes: exportedIncludesLabels,
+		Hdrs:     exportedIncludesHeadersLabels,
+		Deps:     headerLibsLabels,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/cc/linkable.go b/cc/linkable.go
index 58919a0..6aa238b 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -111,6 +111,7 @@
 	InProduct() bool
 
 	SdkVersion() string
+	MinSdkVersion() string
 	AlwaysSdk() bool
 	IsSdkVariant() bool
 
@@ -157,8 +158,16 @@
 }
 
 // StaticDepTag returns the dependency tag for any C++ static libraries.
-func StaticDepTag() blueprint.DependencyTag {
-	return libraryDependencyTag{Kind: staticLibraryDependency}
+func StaticDepTag(wholeStatic bool) blueprint.DependencyTag {
+	return libraryDependencyTag{Kind: staticLibraryDependency, wholeStatic: wholeStatic}
+}
+
+// IsWholeStaticLib whether a dependency tag is a whole static library dependency.
+func IsWholeStaticLib(depTag blueprint.DependencyTag) bool {
+	if tag, ok := depTag.(libraryDependencyTag); ok {
+		return tag.wholeStatic
+	}
+	return false
 }
 
 // HeaderDepTag returns the dependency tag for any C++ "header-only" libraries.
diff --git a/cc/testing.go b/cc/testing.go
index d8adc61..6e35655 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -619,11 +619,26 @@
 
 		RegisterVndkLibraryTxtTypes(ctx)
 	}),
+
+	// Additional files needed in tests that disallow non-existent source files.
+	// This includes files that are needed by all, or at least most, instances of a cc module type.
+	android.MockFS{
+		// Needed for ndk_prebuilt_(shared|static)_stl.
+		"prebuilts/ndk/current/sources/cxx-stl/llvm-libc++/libs": nil,
+	}.AddToFixture(),
 )
 
 // Preparer that will define default cc modules, e.g. standard prebuilt modules.
 var PrepareForTestWithCcDefaultModules = android.GroupFixturePreparers(
 	PrepareForTestWithCcBuildComponents,
+
+	// Additional files needed in tests that disallow non-existent source.
+	android.MockFS{
+		"defaults/cc/common/libc.map.txt":  nil,
+		"defaults/cc/common/libdl.map.txt": nil,
+		"defaults/cc/common/libm.map.txt":  nil,
+	}.AddToFixture(),
+
 	// Place the default cc test modules that are common to all platforms in a location that will not
 	// conflict with default test modules defined by other packages.
 	android.FixtureAddTextFile(DefaultCcCommonTestModulesDir+"Android.bp", commonDefaultModules()),
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 03e82c2..c3d13ae 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -221,12 +221,40 @@
 	metadata android.Path
 }
 
+// isModulePreferredByCompatConfig checks to see whether the module is preferred for use by
+// platform compat config.
+func isModulePreferredByCompatConfig(module android.Module) bool {
+	// A versioned prebuilt_platform_compat_config, i.e. foo-platform-compat-config@current should be
+	// ignored.
+	if s, ok := module.(android.SdkAware); ok {
+		if !s.ContainingSdk().Unversioned() {
+			return false
+		}
+	}
+
+	// A prebuilt module should only be used when it is preferred.
+	if pi, ok := module.(android.PrebuiltInterface); ok {
+		if p := pi.Prebuilt(); p != nil {
+			return p.UsePrebuilt()
+		}
+	}
+
+	// Otherwise, a module should only be used if it has not been replaced by a prebuilt.
+	return !module.IsReplacedByPrebuilt()
+}
+
 func (p *platformCompatConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 
 	var compatConfigMetadata android.Paths
 
 	ctx.VisitAllModules(func(module android.Module) {
+		if !module.Enabled() {
+			return
+		}
 		if c, ok := module.(platformCompatConfigMetadataProvider); ok {
+			if !isModulePreferredByCompatConfig(module) {
+				return
+			}
 			metadata := c.compatConfigMetadata()
 			compatConfigMetadata = append(compatConfigMetadata, metadata)
 		}
diff --git a/rust/compiler.go b/rust/compiler.go
index 200af90..41b7371 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -97,13 +97,25 @@
 	// list of C shared library dependencies
 	Shared_libs []string `android:"arch_variant"`
 
-	// list of C static library dependencies. Note, static libraries prefixed by "lib" will be passed to rustc
-	// along with "-lstatic=<name>". This will bundle the static library into rlib/static libraries so dependents do
-	// not need to also declare the static library as a dependency. Static libraries which are not prefixed by "lib"
-	// cannot be passed to rustc with this flag and will not be bundled into rlib/static libraries, and thus must
-	// be redeclared in dependents.
+	// list of C static library dependencies. These dependencies do not normally propagate to dependents
+	// and may need to be redeclared. See whole_static_libs for bundling static dependencies into a library.
 	Static_libs []string `android:"arch_variant"`
 
+	// Similar to static_libs, but will bundle the static library dependency into a library. This is helpful
+	// to avoid having to redeclare the dependency for dependents of this library, but in some cases may also
+	// result in bloat if multiple dependencies all include the same static library whole.
+	//
+	// The common use case for this is when the static library is unlikely to be a dependency of other modules to avoid
+	// having to redeclare the static library dependency for every dependent module.
+	// If you are not sure what to, for rust_library modules most static dependencies should go in static_libraries,
+	// and for rust_ffi modules most static dependencies should go into whole_static_libraries.
+	//
+	// For rust_ffi static variants, these libraries will be included in the resulting static library archive.
+	//
+	// For rust_library rlib variants, these libraries will be bundled into the resulting rlib library. This will
+	// include all of the static libraries symbols in any dylibs or binaries which use this rlib as well.
+	Whole_static_libs []string `android:"arch_variant"`
+
 	// crate name, required for modules which produce Rust libraries: rust_library, rust_ffi and SourceProvider
 	// modules which create library variants (rust_bindgen). This must be the expected extern crate name used in
 	// source, and is required to conform to an enforced format matching library output files (if the output file is
@@ -266,6 +278,7 @@
 	deps.Rustlibs = append(deps.Rustlibs, compiler.Properties.Rustlibs...)
 	deps.ProcMacros = append(deps.ProcMacros, compiler.Properties.Proc_macros...)
 	deps.StaticLibs = append(deps.StaticLibs, compiler.Properties.Static_libs...)
+	deps.WholeStaticLibs = append(deps.WholeStaticLibs, compiler.Properties.Whole_static_libs...)
 	deps.SharedLibs = append(deps.SharedLibs, compiler.Properties.Shared_libs...)
 
 	if !Bool(compiler.Properties.No_stdlibs) {
diff --git a/rust/rust.go b/rust/rust.go
index 8ebdb72..f0d0e36 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -256,6 +256,10 @@
 	return ""
 }
 
+func (mod *Module) MinSdkVersion() string {
+	return ""
+}
+
 func (mod *Module) AlwaysSdk() bool {
 	return false
 }
@@ -269,14 +273,15 @@
 }
 
 type Deps struct {
-	Dylibs     []string
-	Rlibs      []string
-	Rustlibs   []string
-	Stdlibs    []string
-	ProcMacros []string
-	SharedLibs []string
-	StaticLibs []string
-	HeaderLibs []string
+	Dylibs          []string
+	Rlibs           []string
+	Rustlibs        []string
+	Stdlibs         []string
+	ProcMacros      []string
+	SharedLibs      []string
+	StaticLibs      []string
+	WholeStaticLibs []string
+	HeaderLibs      []string
 
 	CrtBegin, CrtEnd string
 }
@@ -751,7 +756,7 @@
 	deps.ProcMacros = android.LastUniqueStrings(deps.ProcMacros)
 	deps.SharedLibs = android.LastUniqueStrings(deps.SharedLibs)
 	deps.StaticLibs = android.LastUniqueStrings(deps.StaticLibs)
-
+	deps.WholeStaticLibs = android.LastUniqueStrings(deps.WholeStaticLibs)
 	return deps
 
 }
@@ -911,16 +916,13 @@
 			exportDep := false
 			switch {
 			case cc.IsStaticDepTag(depTag):
-				// Only pass -lstatic for rlibs as it results in dylib bloat.
-				if lib, ok := ctx.Module().(*Module).compiler.(libraryInterface); ok && lib.rlib() {
-					// Link cc static libraries using "-lstatic" so rustc can reason about how to handle these
-					// (for example, bundling them into rlibs).
-					//
-					// rustc does not support linking libraries with the "-l" flag unless they are prefixed by "lib".
-					// If we need to link a library that isn't prefixed by "lib", we'll just link to it directly through
-					// linkObjects; such a library may need to be redeclared by static dependents.
+				if cc.IsWholeStaticLib(depTag) {
+					// rustc will bundle static libraries when they're passed with "-lstatic=<lib>". This will fail
+					// if the library is not prefixed by "lib".
 					if libName, ok := libNameFromFilePath(linkObject.Path()); ok {
 						depPaths.depFlags = append(depPaths.depFlags, "-lstatic="+libName)
+					} else {
+						ctx.ModuleErrorf("'%q' cannot be listed as a whole_static_library in Rust modules unless the output is prefixed by 'lib'", depName, ctx.ModuleName())
 					}
 				}
 
@@ -1099,7 +1101,10 @@
 		cc.SharedDepTag(), deps.SharedLibs...)
 	actx.AddVariationDependencies(append(commonDepVariations,
 		blueprint.Variation{Mutator: "link", Variation: "static"}),
-		cc.StaticDepTag(), deps.StaticLibs...)
+		cc.StaticDepTag(false), deps.StaticLibs...)
+	actx.AddVariationDependencies(append(commonDepVariations,
+		blueprint.Variation{Mutator: "link", Variation: "static"}),
+		cc.StaticDepTag(true), deps.WholeStaticLibs...)
 
 	actx.AddVariationDependencies(nil, cc.HeaderDepTag(), deps.HeaderLibs...)
 
diff --git a/rust/rust_test.go b/rust/rust_test.go
index bed28ec..418bd93 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -149,6 +149,11 @@
 			srcs: ["foo.rs"],
 			crate_name: "static",
 		}
+		rust_ffi_host_static {
+			name: "libwholestatic",
+			srcs: ["foo.rs"],
+			crate_name: "wholestatic",
+		}
 		rust_ffi_host_shared {
 			name: "libshared",
 			srcs: ["foo.rs"],
@@ -164,6 +169,7 @@
 			srcs: ["foo.rs"],
 			crate_name: "rlib",
 			static_libs: ["libstatic"],
+			whole_static_libs: ["libwholestatic"],
 		}
 		rust_proc_macro {
 			name: "libpm",
@@ -204,8 +210,8 @@
 		t.Errorf("Static library dependency not detected (dependency missing from AndroidMkStaticLibs)")
 	}
 
-	if !strings.Contains(rustc.Args["rustcFlags"], "-lstatic=static") {
-		t.Errorf("-lstatic flag not being passed to rustc for static library")
+	if !strings.Contains(rustc.Args["rustcFlags"], "-lstatic=wholestatic") {
+		t.Errorf("-lstatic flag not being passed to rustc for static library %#v", rustc.Args["rustcFlags"])
 	}
 
 }
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 9626a04..a886a18 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -486,6 +486,9 @@
 		}
 	`)
 
+	// TODO(b/183322862): Remove this and fix the issue.
+	errorHandler := android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module source path "snapshot/include_gen/generated_foo/gen/protos" does not exist`)
+
 	CheckSnapshot(t, result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
@@ -518,6 +521,9 @@
 .intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
 .intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
 `),
+		snapshotTestErrorHandler(checkSnapshotWithoutSource, errorHandler),
+		snapshotTestErrorHandler(checkSnapshotWithSourcePreferred, errorHandler),
+		snapshotTestErrorHandler(checkSnapshotPreferredWithSource, errorHandler),
 	)
 }
 
@@ -1816,7 +1822,10 @@
 .intermediates/mynativelib/android_arm64_armv8-a_static/mynativelib.a -> arm64/lib/mynativelib.a
 .intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
 .intermediates/mynativelib/android_arm_armv7-a-neon_static/mynativelib.a -> arm/lib/mynativelib.a
-.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`),
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
+`),
+		// TODO(b/183315522): Remove this and fix the issue.
+		snapshotTestErrorHandler(checkSnapshotPreferredWithSource, android.FixtureExpectsAtLeastOneErrorMatchingPattern(`\Qunrecognized property "arch.arm.shared.export_include_dirs"\E`)),
 	)
 }
 
@@ -2664,6 +2673,11 @@
 		}
 	`)
 
+	// Mixing the snapshot with the source (irrespective of which one is preferred) causes a problem
+	// due to missing variants.
+	// TODO(b/183204176): Remove this and fix the cause.
+	snapshotWithSourceErrorHandler := android.FixtureExpectsAtLeastOneErrorMatchingPattern(`\QReplaceDependencies could not find identical variant {os:android,image:,arch:arm64_armv8-a,sdk:,link:shared,version:} for module mynativelib\E`)
+
 	CheckSnapshot(t, result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
@@ -2688,6 +2702,9 @@
 		checkAllCopyRules(`
 myinclude/Test.h -> include/myinclude/Test.h
 arm64/include/Arm64Test.h -> arm64/include/arm64/include/Arm64Test.h
-.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`),
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
+`),
+		snapshotTestErrorHandler(checkSnapshotWithSourcePreferred, snapshotWithSourceErrorHandler),
+		snapshotTestErrorHandler(checkSnapshotPreferredWithSource, snapshotWithSourceErrorHandler),
 	)
 }
diff --git a/sdk/compat_config_sdk_test.go b/sdk/compat_config_sdk_test.go
index dffc02a..00073c2 100644
--- a/sdk/compat_config_sdk_test.go
+++ b/sdk/compat_config_sdk_test.go
@@ -66,5 +66,26 @@
 		checkAllCopyRules(`
 .intermediates/myconfig/android_common/myconfig_meta.xml -> compat_configs/myconfig/myconfig_meta.xml
 `),
+		snapshotTestChecker(checkSnapshotWithoutSource,
+			func(t *testing.T, result *android.TestResult) {
+				// Make sure that the snapshot metadata is collated by the platform compat config singleton.
+				java.CheckMergedCompatConfigInputs(t, result, "snapshot module", "snapshot/compat_configs/myconfig/myconfig_meta.xml")
+			}),
+
+		snapshotTestChecker(checkSnapshotWithSourcePreferred,
+			func(t *testing.T, result *android.TestResult) {
+				// Make sure that the snapshot metadata is collated by the platform compat config singleton.
+				java.CheckMergedCompatConfigInputs(t, result, "snapshot module",
+					"out/soong/.intermediates/myconfig/android_common/myconfig_meta.xml",
+				)
+			}),
+
+		snapshotTestChecker(checkSnapshotPreferredWithSource,
+			func(t *testing.T, result *android.TestResult) {
+				// Make sure that the snapshot metadata is collated by the platform compat config singleton.
+				java.CheckMergedCompatConfigInputs(t, result, "snapshot module",
+					"snapshot/compat_configs/myconfig/myconfig_meta.xml",
+				)
+			}),
 	)
 }
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index ad45131..208cd58 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -24,6 +24,16 @@
 var prepareForSdkTestWithJava = android.GroupFixturePreparers(
 	java.PrepareForTestWithJavaBuildComponents,
 	PrepareForTestWithSdkBuildComponents,
+
+	// Ensure that all source paths are provided. This helps ensure that the snapshot generation is
+	// consistent and all files referenced from the snapshot's Android.bp file have actually been
+	// copied into the snapshot.
+	android.PrepareForTestDisallowNonExistentPaths,
+
+	// Files needs by most of the tests.
+	android.MockFS{
+		"Test.java": nil,
+	}.AddToFixture(),
 )
 
 var prepareForSdkTestWithJavaSdkLibrary = android.GroupFixturePreparers(
@@ -48,42 +58,17 @@
 			system_modules: "none",
 			sdk_version: "none",
 		}
-
-		java_import {
-			name: "sdkmember",
-			prefer: true,
-			jars: ["prebuilt.jar"],
-		}
 	`)
 
 	// Make sure that the mysdk module depends on "sdkmember" and not "prebuilt_sdkmember".
-	java.CheckModuleDependencies(t, result.TestContext, "mysdk", "android_common", []string{"sdkmember"})
+	sdkChecker := func(t *testing.T, result *android.TestResult) {
+		java.CheckModuleDependencies(t, result.TestContext, "mysdk", "android_common", []string{"sdkmember"})
+	}
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkAndroidBpContents(`// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "mysdk_sdkmember@current",
-    sdk_member_name: "sdkmember",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/sdkmember.jar"],
-}
-
-java_import {
-    name: "sdkmember",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/sdkmember.jar"],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_header_libs: ["mysdk_sdkmember@current"],
-}
-`))
+		snapshotTestChecker(checkSnapshotWithSourcePreferred, sdkChecker),
+		snapshotTestChecker(checkSnapshotPreferredWithSource, sdkChecker),
+	)
 }
 
 func TestBasicSdkWithJavaLibrary(t *testing.T) {
@@ -364,6 +349,7 @@
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		android.FixtureAddFile("aidl/foo/bar/Test.aidl", nil),
+		android.FixtureAddFile("resource.txt", nil),
 	).RunTestWithBp(t, `
 		module_exports {
 			name: "myexports",
@@ -419,7 +405,11 @@
 }
 
 func TestSnapshotWithJavaBootLibrary(t *testing.T) {
-	result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		android.FixtureAddFile("aidl", nil),
+		android.FixtureAddFile("resource.txt", nil),
+	).RunTestWithBp(t, `
 		module_exports {
 			name: "myexports",
 			java_boot_libs: ["myjavalib"],
@@ -1564,7 +1554,10 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_DoctagFiles(t *testing.T) {
-	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJavaSdkLibrary,
+		android.FixtureAddFile("docs/known_doctags", nil),
+	).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			java_sdk_libs: ["myjavalib"],
diff --git a/sdk/sdk.go b/sdk/sdk.go
index e561529..b60fb18 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -486,6 +486,15 @@
 			// sdk containing sdkmember.
 			memberName := versionedSdkMember.MemberName()
 
+			// Convert a panic into a normal error to allow it to be more easily tested for. This is a
+			// temporary workaround, once http://b/183204176 has been fixed this can be removed.
+			// TODO(b/183204176): Remove this after fixing.
+			defer func() {
+				if r := recover(); r != nil {
+					mctx.ModuleErrorf("%s", r)
+				}
+			}()
+
 			// Replace dependencies on sdkmember with a dependency on the current module which
 			// is a versioned prebuilt of the sdkmember if required.
 			mctx.ReplaceDependenciesIf(memberName, func(from blueprint.Module, tag blueprint.DependencyTag, to blueprint.Module) bool {
diff --git a/sdk/testing.go b/sdk/testing.go
index ba40f67..44970f7 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -48,10 +48,10 @@
 		"system/sepolicy/apex/myapex-file_contexts":    nil,
 		"system/sepolicy/apex/myapex2-file_contexts":   nil,
 		"system/sepolicy/apex/mysdkapex-file_contexts": nil,
-		"myapex.avbpubkey":                             nil,
-		"myapex.pem":                                   nil,
-		"myapex.x509.pem":                              nil,
-		"myapex.pk8":                                   nil,
+		"sdk/tests/myapex.avbpubkey":                   nil,
+		"sdk/tests/myapex.pem":                         nil,
+		"sdk/tests/myapex.x509.pem":                    nil,
+		"sdk/tests/myapex.pk8":                         nil,
 	}),
 )
 
@@ -80,6 +80,12 @@
 			{android.Windows, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", "", true},
 		}
 	}),
+
+	// Make sure that every test provides all the source files.
+	android.PrepareForTestDisallowNonExistentPaths,
+	android.MockFS{
+		"Test.java": nil,
+	}.AddToFixture(),
 )
 
 var PrepareForTestWithSdkBuildComponents = android.GroupFixturePreparers(
@@ -125,6 +131,7 @@
 		androidBpContents:            sdk.GetAndroidBpContentsForTests(),
 		androidUnversionedBpContents: sdk.GetUnversionedAndroidBpContentsForTests(),
 		androidVersionedBpContents:   sdk.GetVersionedAndroidBpContentsForTests(),
+		snapshotTestCustomizations:   map[snapshotTest]*snapshotTestCustomization{},
 	}
 
 	buildParams := sdk.BuildParamsForTests()
@@ -183,6 +190,24 @@
 	return info
 }
 
+// The enum of different sdk snapshot tests performed by CheckSnapshot.
+type snapshotTest int
+
+const (
+	// The enumeration of the different test configurations.
+	// A test with the snapshot/Android.bp file but without the original Android.bp file.
+	checkSnapshotWithoutSource snapshotTest = iota
+
+	// A test with both the original source and the snapshot, with the source preferred.
+	checkSnapshotWithSourcePreferred
+
+	// A test with both the original source and the snapshot, with the snapshot preferred.
+	checkSnapshotPreferredWithSource
+
+	// The directory into which the snapshot will be 'unpacked'.
+	snapshotSubDir = "snapshot"
+)
+
 // Check the snapshot build rules.
 //
 // Takes a list of functions which check different facets of the snapshot build rules.
@@ -214,31 +239,58 @@
 	// Populate a mock filesystem with the files that would have been copied by
 	// the rules.
 	fs := android.MockFS{}
-	snapshotSubDir := "snapshot"
 	for _, dest := range snapshotBuildInfo.snapshotContents {
 		fs[filepath.Join(snapshotSubDir, dest)] = nil
 	}
 	fs[filepath.Join(snapshotSubDir, "Android.bp")] = []byte(snapshotBuildInfo.androidBpContents)
 
-	preparer := result.Preparer()
+	// The preparers from the original source fixture.
+	sourcePreparers := result.Preparer()
 
-	// Process the generated bp file to make sure it is valid. Use the same preparer as was used to
-	// produce this result.
+	// Preparer to combine the snapshot and the source.
+	snapshotPreparer := android.GroupFixturePreparers(sourcePreparers, fs.AddToFixture())
+
+	var runSnapshotTestWithCheckers = func(t *testing.T, testConfig snapshotTest, extraPreparer android.FixturePreparer) {
+		customization := snapshotBuildInfo.snapshotTestCustomization(testConfig)
+
+		// TODO(b/183184375): Set Config.TestAllowNonExistentPaths = false to verify that all the
+		//  files the snapshot needs are actually copied into the snapshot.
+
+		// Run the snapshot with the snapshot preparer and the extra preparer, which must come after as
+		// it may need to modify parts of the MockFS populated by the snapshot preparer.
+		result := android.GroupFixturePreparers(snapshotPreparer, extraPreparer).
+			ExtendWithErrorHandler(customization.errorHandler).
+			RunTest(t)
+
+		// Perform any additional checks the test need on the result of processing the snapshot.
+		for _, checker := range customization.checkers {
+			checker(t, result)
+		}
+	}
+
 	t.Run("snapshot without source", func(t *testing.T) {
-		android.GroupFixturePreparers(
-			preparer,
-			// TODO(b/183184375): Set Config.TestAllowNonExistentPaths = false to verify that all the
-			//  files the snapshot needs are actually copied into the snapshot.
+		// Remove the source Android.bp file to make sure it works without.
+		removeSourceAndroidBp := android.FixtureModifyMockFS(func(fs android.MockFS) {
+			delete(fs, "Android.bp")
+		})
 
-			// Add the files (including bp) created for this snapshot to the test fixture.
-			fs.AddToFixture(),
+		runSnapshotTestWithCheckers(t, checkSnapshotWithoutSource, removeSourceAndroidBp)
+	})
 
-			// Remove the source Android.bp file to make sure it works without.
-			// TODO(b/183184375): Add a test with the source.
-			android.FixtureModifyMockFS(func(fs android.MockFS) {
-				delete(fs, "Android.bp")
-			}),
-		).RunTest(t)
+	t.Run("snapshot with source preferred", func(t *testing.T) {
+		runSnapshotTestWithCheckers(t, checkSnapshotWithSourcePreferred, android.NullFixturePreparer)
+	})
+
+	t.Run("snapshot preferred with source", func(t *testing.T) {
+		// Replace the snapshot/Android.bp file with one where "prefer: false," has been replaced with
+		// "prefer: true,"
+		preferPrebuilts := android.FixtureModifyMockFS(func(fs android.MockFS) {
+			snapshotBpFile := filepath.Join(snapshotSubDir, "Android.bp")
+			unpreferred := string(fs[snapshotBpFile])
+			fs[snapshotBpFile] = []byte(strings.ReplaceAll(unpreferred, "prefer: false,", "prefer: true,"))
+		})
+
+		runSnapshotTestWithCheckers(t, checkSnapshotPreferredWithSource, preferPrebuilts)
 	})
 }
 
@@ -312,6 +364,46 @@
 	}
 }
 
+type resultChecker func(t *testing.T, result *android.TestResult)
+
+// snapshotTestChecker registers a checker that will be run against the result of processing the
+// generated snapshot for the specified snapshotTest.
+func snapshotTestChecker(snapshotTest snapshotTest, checker resultChecker) snapshotBuildInfoChecker {
+	return func(info *snapshotBuildInfo) {
+		customization := info.snapshotTestCustomization(snapshotTest)
+		customization.checkers = append(customization.checkers, checker)
+	}
+}
+
+// snapshotTestErrorHandler registers an error handler to use when processing the snapshot
+// in the specific test case.
+//
+// Generally, the snapshot should work with all the test cases but some do not and just in case
+// there are a lot of issues to resolve, or it will take a lot of time this is a
+// get-out-of-jail-free card that allows progress to be made.
+//
+// deprecated: should only be used as a temporary workaround with an attached to do and bug.
+func snapshotTestErrorHandler(snapshotTest snapshotTest, handler android.FixtureErrorHandler) snapshotBuildInfoChecker {
+	return func(info *snapshotBuildInfo) {
+		customization := info.snapshotTestCustomization(snapshotTest)
+		customization.errorHandler = handler
+	}
+}
+
+// Encapsulates information provided by each test to customize a specific snapshotTest.
+type snapshotTestCustomization struct {
+	// Checkers that are run on the result of processing the preferred snapshot in a specific test
+	// case.
+	checkers []resultChecker
+
+	// Specify an error handler for when processing a specific test case.
+	//
+	// In some cases the generated snapshot cannot be used in a test configuration. Those cases are
+	// invariably bugs that need to be resolved but sometimes that can take a while. This provides a
+	// mechanism to temporarily ignore that error.
+	errorHandler android.FixtureErrorHandler
+}
+
 // Encapsulates information about the snapshot build structure in order to insulate tests from
 // knowing too much about internal structures.
 //
@@ -355,4 +447,21 @@
 
 	// The final output zip.
 	outputZip string
+
+	// The test specific customizations for each snapshot test.
+	snapshotTestCustomizations map[snapshotTest]*snapshotTestCustomization
+}
+
+// snapshotTestCustomization gets the test specific customization for the specified snapshotTest.
+//
+// If no customization was created previously then it creates a default customization.
+func (i *snapshotBuildInfo) snapshotTestCustomization(snapshotTest snapshotTest) *snapshotTestCustomization {
+	customization := i.snapshotTestCustomizations[snapshotTest]
+	if customization == nil {
+		customization = &snapshotTestCustomization{
+			errorHandler: android.FixtureExpectsNoErrors,
+		}
+		i.snapshotTestCustomizations[snapshotTest] = customization
+	}
+	return customization
 }