Merge "Revert^2 "Add debug ramdisk variant""
diff --git a/android/Android.bp b/android/Android.bp
index 62d5e20..6124654 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -85,9 +85,11 @@
         "androidmk_test.go",
         "apex_test.go",
         "arch_test.go",
+        "bazel_handler_test.go",
         "bazel_test.go",
         "config_test.go",
         "csuite_config_test.go",
+        "defaults_test.go",
         "depset_test.go",
         "deptag_test.go",
         "expand_test.go",
diff --git a/android/bazel.go b/android/bazel.go
index 6e87d57..3e57e22 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -180,8 +180,6 @@
 		"libc_init_dynamic",             // ruperts@, cc_library_static, 'private/bionic_defs.h' file not found
 		"libc_tzcode",                   // ruperts@, cc_library_static, error: expected expression
 		"libc_netbsd",                   // ruperts@, cc_library_static, 'engine.c' file not found
-		"libc_openbsd_large_stack",      // ruperts@, cc_library_static, 'android/log.h' file not found
-		"libc_openbsd",                  // ruperts@, cc_library_static, 'android/log.h' file not found
 		"libc_fortify",                  // ruperts@, cc_library_static, 'private/bionic_fortify.h' file not found
 		"libc_bionic",                   // ruperts@, cc_library_static, 'private/bionic_asm.h' file not found
 		"libc_bionic_ndk",               // ruperts@, cc_library_static, depends on //bionic/libc/system_properties
@@ -203,7 +201,7 @@
 		"liblinker_malloc",              // ruperts@, cc_library_static, depends on //system/logging/liblog:liblog
 		"liblinker_debuggerd_stub",      // ruperts@, cc_library_static, depends on //system/libbase
 		"libbionic_tests_headers_posix", // ruperts@, cc_library_static, 'complex.h' file not found
-		"libc_dns",                      // ruperts@, cc_library_static, 'android/log.h' file not found
+		"libc_dns",                      // ruperts@, cc_library_static, 'private/android_filesystem_config.h' file not found
 		"libc_static_dispatch",          // eakammer@, cc_library_static, 'private/bionic_asm.h' file not found
 		"libc_dynamic_dispatch",         // eakammer@, cc_library_static, 'private/bionic_ifuncs.h' file not found
 		"note_memtag_heap_async",        // jingwen@, cc_library_static, 'private/bionic_asm.h' file not found (arm64)
diff --git a/android/test_asserts.go b/android/test_asserts.go
index bfb88ab..edeb408 100644
--- a/android/test_asserts.go
+++ b/android/test_asserts.go
@@ -126,13 +126,44 @@
 	}
 }
 
+// AssertStringContainsEquals checks if the string contains or does not contain the substring, given
+// the value of the expected bool. If the expectation does not hold it reports an error prefixed with
+// the supplied message and including a reason for why it failed.
+func AssertStringContainsEquals(t *testing.T, message string, s string, substring string, expected bool) {
+	if expected {
+		AssertStringDoesContain(t, message, s, substring)
+	} else {
+		AssertStringDoesNotContain(t, message, s, substring)
+	}
+}
+
 // AssertStringListContains checks if the list of strings contains the expected string. If it does
 // not then it reports an error prefixed with the supplied message and including a reason for why it
 // failed.
-func AssertStringListContains(t *testing.T, message string, list []string, expected string) {
+func AssertStringListContains(t *testing.T, message string, list []string, s string) {
 	t.Helper()
-	if !InList(expected, list) {
-		t.Errorf("%s: could not find %q within %q", message, expected, list)
+	if !InList(s, list) {
+		t.Errorf("%s: could not find %q within %q", message, s, list)
+	}
+}
+
+// AssertStringListDoesNotContain checks if the list of strings contains the expected string. If it does
+// then it reports an error prefixed with the supplied message and including a reason for why it failed.
+func AssertStringListDoesNotContain(t *testing.T, message string, list []string, s string) {
+	t.Helper()
+	if InList(s, list) {
+		t.Errorf("%s: unexpectedly found %q within %q", message, s, list)
+	}
+}
+
+// AssertStringContainsEquals checks if the string contains or does not contain the substring, given
+// the value of the expected bool. If the expectation does not hold it reports an error prefixed with
+// the supplied message and including a reason for why it failed.
+func AssertStringListContainsEquals(t *testing.T, message string, list []string, s string, expected bool) {
+	if expected {
+		AssertStringListContains(t, message, list, s)
+	} else {
+		AssertStringListDoesNotContain(t, message, list, s)
 	}
 }
 
diff --git a/apex/apex.go b/apex/apex.go
index 6f02c47..088a462 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -110,16 +110,6 @@
 	// List of filesystem images that are embedded inside this APEX bundle.
 	Filesystems []string
 
-	// Name of the apex_key module that provides the private key to sign this APEX bundle.
-	Key *string
-
-	// Specifies the certificate and the private key to sign the zip container of this APEX. If
-	// this is "foo", foo.x509.pem and foo.pk8 under PRODUCT_DEFAULT_DEV_CERTIFICATE are used
-	// as the certificate and the private key, respectively. If this is ":module", then the
-	// certificate and the private key are provided from the android_app_certificate module
-	// named "module".
-	Certificate *string
-
 	// The minimum SDK version that this APEX must support at minimum. This is usually set to
 	// the SDK version that the APEX was first introduced.
 	Min_sdk_version *string
@@ -299,6 +289,16 @@
 
 	// A txt file containing list of files that are allowed to be included in this APEX.
 	Allowed_files *string `android:"path"`
+
+	// Name of the apex_key module that provides the private key to sign this APEX bundle.
+	Key *string
+
+	// Specifies the certificate and the private key to sign the zip container of this APEX. If
+	// this is "foo", foo.x509.pem and foo.pk8 under PRODUCT_DEFAULT_DEV_CERTIFICATE are used
+	// as the certificate and the private key, respectively. If this is ":module", then the
+	// certificate and the private key are provided from the android_app_certificate module
+	// named "module".
+	Certificate *string
 }
 
 type apexBundle struct {
@@ -760,20 +760,6 @@
 		}
 	}
 
-	// Dependencies for signing
-	if String(a.properties.Key) == "" {
-		ctx.PropertyErrorf("key", "missing")
-		return
-	}
-	ctx.AddDependency(ctx.Module(), keyTag, String(a.properties.Key))
-
-	cert := android.SrcIsModule(a.getCertString(ctx))
-	if cert != "" {
-		ctx.AddDependency(ctx.Module(), certificateTag, cert)
-		// empty cert is not an error. Cert and private keys will be directly found under
-		// PRODUCT_DEFAULT_DEV_CERTIFICATE
-	}
-
 	// Marks that this APEX (in fact all the modules in it) has to be built with the given SDKs.
 	// This field currently isn't used.
 	// TODO(jiyong): consider dropping this feature
@@ -797,6 +783,20 @@
 	commonVariation := ctx.Config().AndroidCommonTarget.Variations()
 	ctx.AddFarVariationDependencies(commonVariation, androidAppTag, a.overridableProperties.Apps...)
 	ctx.AddFarVariationDependencies(commonVariation, rroTag, a.overridableProperties.Rros...)
+
+	// Dependencies for signing
+	if String(a.overridableProperties.Key) == "" {
+		ctx.PropertyErrorf("key", "missing")
+		return
+	}
+	ctx.AddDependency(ctx.Module(), keyTag, String(a.overridableProperties.Key))
+
+	cert := android.SrcIsModule(a.getCertString(ctx))
+	if cert != "" {
+		ctx.AddDependency(ctx.Module(), certificateTag, cert)
+		// empty cert is not an error. Cert and private keys will be directly found under
+		// PRODUCT_DEFAULT_DEV_CERTIFICATE
+	}
 }
 
 type ApexBundleInfo struct {
@@ -1292,7 +1292,7 @@
 	if overridden {
 		return ":" + certificate
 	}
-	return String(a.properties.Certificate)
+	return String(a.overridableProperties.Certificate)
 }
 
 // See the installable property
@@ -1924,7 +1924,7 @@
 					// Rlib is statically linked, but it might have shared lib
 					// dependencies. Track them.
 					return true
-				} else if java.IsbootImageContentDepTag(depTag) {
+				} else if java.IsBootclasspathFragmentContentDepTag(depTag) {
 					// Add the contents of the bootclasspath fragment to the apex.
 					switch child.(type) {
 					case *java.Library, *java.SdkLibrary:
@@ -1949,7 +1949,7 @@
 		return false
 	})
 	if a.privateKeyFile == nil {
-		ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.properties.Key))
+		ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.overridableProperties.Key))
 		return
 	}
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 977a954..a7ae6f0 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -5599,6 +5599,8 @@
 			overrides: ["unknownapex"],
 			logging_parent: "com.foo.bar",
 			package_name: "test.overridden.package",
+			key: "mynewapex.key",
+			certificate: ":myapex.certificate",
 		}
 
 		apex_key {
@@ -5607,6 +5609,17 @@
 			private_key: "testkey.pem",
 		}
 
+		apex_key {
+			name: "mynewapex.key",
+			public_key: "testkey2.avbpubkey",
+			private_key: "testkey2.pem",
+		}
+
+		android_app_certificate {
+			name: "myapex.certificate",
+			certificate: "testkey",
+		}
+
 		android_app {
 			name: "app",
 			srcs: ["foo/bar/MyClass.java"],
@@ -5651,6 +5664,10 @@
 
 	optFlags := apexRule.Args["opt_flags"]
 	ensureContains(t, optFlags, "--override_apk_package_name test.overridden.package")
+	ensureContains(t, optFlags, "--pubkey testkey2.avbpubkey")
+
+	signApkRule := module.Rule("signapk")
+	ensureEquals(t, signApkRule.Args["certificates"], "testkey.x509.pem testkey.pk8")
 
 	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
 	var builder strings.Builder
diff --git a/apex/boot_image_test.go b/apex/boot_image_test.go
index e18c2ea..dab72f7 100644
--- a/apex/boot_image_test.go
+++ b/apex/boot_image_test.go
@@ -216,6 +216,22 @@
 			],
 		}
 
+		java_import {
+			name: "foo",
+			jars: ["foo.jar"],
+			apex_available: [
+				"com.android.art",
+			],
+		}
+
+		java_import {
+			name: "bar",
+			jars: ["bar.jar"],
+			apex_available: [
+				"com.android.art",
+			],
+		}
+
 		// Make sure that a preferred prebuilt doesn't affect the apex.
 		prebuilt_boot_image {
 			name: "mybootclasspathfragment",
diff --git a/apex/builder.go b/apex/builder.go
index b382a53..da8841c 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -871,7 +871,7 @@
 		return a.containerCertificateFile, a.containerPrivateKeyFile
 	}
 
-	cert := String(a.properties.Certificate)
+	cert := String(a.overridableProperties.Certificate)
 	if cert == "" {
 		return ctx.Config().DefaultAppCertificate(ctx)
 	}
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 74830d3..52b1689 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -137,9 +137,12 @@
 	)
 
 	java.CheckPlatformBootclasspathModules(t, result, "myplatform-bootclasspath", []string{
+		// The configured contents of BootJars.
 		"com.android.art:baz",
 		"com.android.art:quuz",
 		"platform:foo",
+
+		// The configured contents of UpdatableBootJars.
 		"myapex:bar",
 	})
 
@@ -149,11 +152,24 @@
 
 	// Make sure that the myplatform-bootclasspath has the correct dependencies.
 	CheckModuleDependencies(t, result.TestContext, "myplatform-bootclasspath", "android_common", []string{
+		// The following are stubs.
+		`platform:android_stubs_current`,
+		`platform:android_system_stubs_current`,
+		`platform:android_test_stubs_current`,
+		`platform:legacy.core.platform.api.stubs`,
+
+		// Needed for generating the boot image.
 		`platform:dex2oatd`,
+
+		// The configured contents of BootJars.
 		`com.android.art:baz`,
 		`com.android.art:quuz`,
 		`platform:foo`,
+
+		// The configured contents of UpdatableBootJars.
 		`myapex:bar`,
+
+		// The fragments.
 		`com.android.art:art-bootclasspath-fragment`,
 	})
 }
diff --git a/bazel/cquery/Android.bp b/bazel/cquery/Android.bp
index 3a71e9c..74f7721 100644
--- a/bazel/cquery/Android.bp
+++ b/bazel/cquery/Android.bp
@@ -11,4 +11,7 @@
     pluginFor: [
         "soong_build",
     ],
+    testSrcs: [
+        "request_type_test.go",
+    ],
 }
diff --git a/bazel/properties.go b/bazel/properties.go
index 5d3299b..48d9589 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -125,7 +125,9 @@
 	}
 }
 
-func UniqueBazelLabels(originalLabels []Label) []Label {
+// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
+// the slice in a sorted order.
+func UniqueSortedBazelLabels(originalLabels []Label) []Label {
 	uniqueLabelsSet := make(map[Label]bool)
 	for _, l := range originalLabels {
 		uniqueLabelsSet[l] = true
@@ -142,8 +144,8 @@
 
 func UniqueBazelLabelList(originalLabelList LabelList) LabelList {
 	var uniqueLabelList LabelList
-	uniqueLabelList.Includes = UniqueBazelLabels(originalLabelList.Includes)
-	uniqueLabelList.Excludes = UniqueBazelLabels(originalLabelList.Excludes)
+	uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
+	uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
 	return uniqueLabelList
 }
 
@@ -292,7 +294,7 @@
 	return LabelListAttribute{Value: UniqueBazelLabelList(value)}
 }
 
-// Append appends all values, including os and arch specific ones, from another
+// Append all values, including os and arch specific ones, from another
 // LabelListAttribute to this LabelListAttribute.
 func (attrs *LabelListAttribute) Append(other LabelListAttribute) {
 	for arch := range PlatformArchMap {
@@ -500,6 +502,26 @@
 	*v = value
 }
 
+// Append appends all values, including os and arch specific ones, from another
+// StringListAttribute to this StringListAttribute
+func (attrs *StringListAttribute) Append(other StringListAttribute) {
+	for arch := range PlatformArchMap {
+		this := attrs.GetValueForArch(arch)
+		that := other.GetValueForArch(arch)
+		this = append(this, that...)
+		attrs.SetValueForArch(arch, this)
+	}
+
+	for os := range PlatformOsMap {
+		this := attrs.GetValueForOS(os)
+		that := other.GetValueForOS(os)
+		this = append(this, that...)
+		attrs.SetValueForOS(os, this)
+	}
+
+	attrs.Value = append(attrs.Value, other.Value...)
+}
+
 // TryVariableSubstitution, replace string substitution formatting within each string in slice with
 // Starlark string.format compatible tag for productVariable.
 func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index 56840ef..229a4aa 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -39,7 +39,7 @@
 		},
 	}
 	for _, tc := range testCases {
-		actualUniqueLabels := UniqueBazelLabels(tc.originalLabels)
+		actualUniqueLabels := UniqueSortedBazelLabels(tc.originalLabels)
 		if !reflect.DeepEqual(tc.expectedUniqueLabels, actualUniqueLabels) {
 			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabels, actualUniqueLabels)
 		}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 783af2e..4b6e888 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -109,10 +109,22 @@
 `,
 			expectedBazelTargets: []string{`cc_library(
     name = "foo-lib",
-    copts = ["-Wall"],
+    copts = [
+        "-Wall",
+        "-I.",
+    ],
     deps = [":some-headers"],
-    hdrs = [
+    hdrs = ["foo-dir/a.h"],
+    includes = ["foo-dir"],
+    linkopts = ["-Wl,--exclude-libs=bar.a"] + select({
+        "//build/bazel/platforms/arch:x86": ["-Wl,--exclude-libs=baz.a"],
+        "//build/bazel/platforms/arch:x86_64": ["-Wl,--exclude-libs=qux.a"],
+        "//conditions:default": [],
+    }),
+    srcs = [
+        "impl.cpp",
         "header.h",
+        "foo-dir/a.h",
         "header.hh",
         "header.hpp",
         "header.hxx",
@@ -121,15 +133,7 @@
         "header.inc",
         "header.ipp",
         "header.h.generic",
-        "foo-dir/a.h",
-    ],
-    includes = ["foo-dir"],
-    linkopts = ["-Wl,--exclude-libs=bar.a"] + select({
-        "//build/bazel/platforms/arch:x86": ["-Wl,--exclude-libs=baz.a"],
-        "//build/bazel/platforms/arch:x86_64": ["-Wl,--exclude-libs=qux.a"],
-        "//conditions:default": [],
-    }),
-    srcs = ["impl.cpp"] + select({
+    ] + select({
         "//build/bazel/platforms/arch:x86": ["x86.cpp"],
         "//build/bazel/platforms/arch:x86_64": ["x86_64.cpp"],
         "//conditions:default": [],
@@ -190,14 +194,9 @@
         "-Wextra",
         "-Wunused",
         "-Werror",
+        "-I.",
     ],
     deps = [":libc_headers"],
-    hdrs = [
-        "linked_list.h",
-        "linker.h",
-        "linker_block_allocator.h",
-        "linker_cfi.h",
-    ],
     linkopts = [
         "-Wl,--exclude-libs=libgcc.a",
         "-Wl,--exclude-libs=libgcc_stripped.a",
@@ -210,7 +209,13 @@
         "//build/bazel/platforms/arch:x86_64": ["-Wl,--exclude-libs=libgcc_eh.a"],
         "//conditions:default": [],
     }),
-    srcs = ["ld_android.cpp"],
+    srcs = [
+        "ld_android.cpp",
+        "linked_list.h",
+        "linker.h",
+        "linker_block_allocator.h",
+        "linker_cfi.h",
+    ],
 )`},
 		},
 	}
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index c59241f..3180267 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -131,6 +131,7 @@
 }`,
 			expectedBazelTargets: []string{`cc_library_headers(
     name = "foo_headers",
+    copts = ["-I."],
     deps = [
         ":lib-1",
         ":lib-2",
@@ -157,6 +158,7 @@
     }),
 )`, `cc_library_headers(
     name = "lib-1",
+    copts = ["-I."],
     hdrs = [
         "lib-1/lib1a.h",
         "lib-1/lib1b.h",
@@ -164,6 +166,7 @@
     includes = ["lib-1"],
 )`, `cc_library_headers(
     name = "lib-2",
+    copts = ["-I."],
     hdrs = [
         "lib-2/lib2a.h",
         "lib-2/lib2b.h",
@@ -201,12 +204,16 @@
 }`,
 			expectedBazelTargets: []string{`cc_library_headers(
     name = "android-lib",
+    copts = ["-I."],
 )`, `cc_library_headers(
     name = "base-lib",
+    copts = ["-I."],
 )`, `cc_library_headers(
     name = "darwin-lib",
+    copts = ["-I."],
 )`, `cc_library_headers(
     name = "foo_headers",
+    copts = ["-I."],
     deps = [":base-lib"] + select({
         "//build/bazel/platforms/os:android": [":android-lib"],
         "//build/bazel/platforms/os:darwin": [":darwin-lib"],
@@ -218,12 +225,16 @@
     }),
 )`, `cc_library_headers(
     name = "fuchsia-lib",
+    copts = ["-I."],
 )`, `cc_library_headers(
     name = "linux-lib",
+    copts = ["-I."],
 )`, `cc_library_headers(
     name = "linux_bionic-lib",
+    copts = ["-I."],
 )`, `cc_library_headers(
     name = "windows-lib",
+    copts = ["-I."],
 )`},
 		},
 		{
@@ -244,10 +255,13 @@
 }`,
 			expectedBazelTargets: []string{`cc_library_headers(
     name = "android-lib",
+    copts = ["-I."],
 )`, `cc_library_headers(
     name = "exported-lib",
+    copts = ["-I."],
 )`, `cc_library_headers(
     name = "foo_headers",
+    copts = ["-I."],
     deps = select({
         "//build/bazel/platforms/os:android": [
             ":android-lib",
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index 7e72a8b..00325fb 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -119,71 +119,70 @@
 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",
+        "foo_static2.cc",
     ],
     cflags: [
         "-Dflag1",
-	"-Dflag2"
+        "-Dflag2"
     ],
     static_libs: [
         "static_lib_1",
-	"static_lib_2"
+        "static_lib_2"
     ],
     whole_static_libs: [
         "whole_static_lib_1",
-	"whole_static_lib_2"
+        "whole_static_lib_2"
     ],
     include_dirs: [
-	"include_dir_1",
-	"include_dir_2",
+        "include_dir_1",
+        "include_dir_2",
     ],
     local_include_dirs: [
         "local_include_dir_1",
-	"local_include_dir_2",
+        "local_include_dir_2",
     ],
     export_include_dirs: [
-	"export_include_dir_1",
-	"export_include_dir_2"
+    "export_include_dir_1",
+    "export_include_dir_2"
     ],
     header_libs: [
         "header_lib_1",
-	"header_lib_2"
+        "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",
+        "-Iinclude_dir_1",
+        "-Iinclude_dir_2",
+        "-Ilocal_include_dir_1",
+        "-Ilocal_include_dir_2",
+        "-I.",
     ],
     deps = [
         ":header_lib_1",
@@ -194,8 +193,6 @@
         ":whole_static_lib_2",
     ],
     hdrs = [
-        "implicit_include_1.h",
-        "implicit_include_2.h",
         "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",
@@ -204,16 +201,17 @@
     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",
+        "implicit_include_1.h",
+        "implicit_include_2.h",
+        "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",
         "include_dir_1/include_dir_1_a.h",
         "include_dir_1/include_dir_1_b.h",
         "include_dir_2/include_dir_2_a.h",
@@ -222,60 +220,90 @@
         "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",
-        "implicit_include_1.h",
-        "implicit_include_2.h",
     ],
 )`, `cc_library_static(
     name = "static_lib_1",
-    hdrs = [
-        "implicit_include_1.h",
-        "implicit_include_2.h",
-    ],
-    includes = ["."],
+    copts = ["-I."],
     linkstatic = True,
     srcs = [
         "static_lib_1.cc",
         "implicit_include_1.h",
         "implicit_include_2.h",
+        "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",
+        "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",
+        "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",
     ],
 )`, `cc_library_static(
     name = "static_lib_2",
-    hdrs = [
-        "implicit_include_1.h",
-        "implicit_include_2.h",
-    ],
-    includes = ["."],
+    copts = ["-I."],
     linkstatic = True,
     srcs = [
         "static_lib_2.cc",
         "implicit_include_1.h",
         "implicit_include_2.h",
+        "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",
+        "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",
+        "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",
     ],
 )`, `cc_library_static(
     name = "whole_static_lib_1",
-    hdrs = [
-        "implicit_include_1.h",
-        "implicit_include_2.h",
-    ],
-    includes = ["."],
+    copts = ["-I."],
     linkstatic = True,
     srcs = [
         "whole_static_lib_1.cc",
         "implicit_include_1.h",
         "implicit_include_2.h",
+        "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",
+        "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",
+        "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",
     ],
 )`, `cc_library_static(
     name = "whole_static_lib_2",
-    hdrs = [
-        "implicit_include_1.h",
-        "implicit_include_2.h",
-    ],
-    includes = ["."],
+    copts = ["-I."],
     linkstatic = True,
     srcs = [
         "whole_static_lib_2.cc",
         "implicit_include_1.h",
         "implicit_include_2.h",
+        "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",
+        "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",
+        "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",
     ],
 )`},
 		},
@@ -306,14 +334,12 @@
     include_dirs: [
 	"subpackage",
     ],
-
-    bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    includes = [
-        "subpackage",
-        ".",
+    copts = [
+        "-Isubpackage",
+        "-I.",
     ],
     linkstatic = True,
     srcs = [
@@ -326,6 +352,299 @@
     ],
 )`},
 		},
+		{
+			description:                        "cc_library_static export include dir",
+			moduleTypeUnderTest:                "cc_library_static",
+			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+			filesystem: map[string]string{
+				// subpackage with subdirectory
+				"subpackage/Android.bp":                         "",
+				"subpackage/subpackage_header.h":                "",
+				"subpackage/subdirectory/subdirectory_header.h": "",
+			},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    export_include_dirs: ["subpackage"],
+}`,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = ["-I."],
+    hdrs = [
+        "//subpackage:subdirectory/subdirectory_header.h",
+        "//subpackage:subpackage_header.h",
+    ],
+    includes = ["subpackage"],
+    linkstatic = True,
+    srcs = [
+        "//subpackage:subpackage_header.h",
+        "//subpackage:subdirectory/subdirectory_header.h",
+    ],
+)`},
+		},
+		{
+			description:                        "cc_library_static export system include dir",
+			moduleTypeUnderTest:                "cc_library_static",
+			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+			filesystem: map[string]string{
+				// subpackage with subdirectory
+				"subpackage/Android.bp":                         "",
+				"subpackage/subpackage_header.h":                "",
+				"subpackage/subdirectory/subdirectory_header.h": "",
+			},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    export_system_include_dirs: ["subpackage"],
+}`,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = ["-I."],
+    hdrs = [
+        "//subpackage:subdirectory/subdirectory_header.h",
+        "//subpackage:subpackage_header.h",
+    ],
+    includes = ["subpackage"],
+    linkstatic = True,
+    srcs = [
+        "//subpackage:subpackage_header.h",
+        "//subpackage:subdirectory/subdirectory_header.h",
+    ],
+)`},
+		},
+		{
+			description:                        "cc_library_static include_dirs, local_include_dirs, export_include_dirs (b/183742505)",
+			moduleTypeUnderTest:                "cc_library_static",
+			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+			dir:                                "subpackage",
+			filesystem: map[string]string{
+				// subpackage with subdirectory
+				"subpackage/Android.bp": `
+cc_library_static {
+    name: "foo_static",
+    // include_dirs are workspace/root relative
+    include_dirs: [
+        "subpackage/subsubpackage",
+        "subpackage2",
+        "subpackage3/subsubpackage"
+    ],
+    local_include_dirs: ["subsubpackage2"], // module dir relative
+    export_include_dirs: ["./exported_subsubpackage"], // module dir relative
+    include_build_directory: true,
+    bazel_module: { bp2build_available: true },
+}`,
+				"subpackage/subsubpackage/header.h":          "",
+				"subpackage/subsubpackage2/header.h":         "",
+				"subpackage/exported_subsubpackage/header.h": "",
+				"subpackage2/header.h":                       "",
+				"subpackage3/subsubpackage/header.h":         "",
+			},
+			bp: soongCcLibraryStaticPreamble,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = [
+        "-Isubpackage/subsubpackage",
+        "-Isubpackage2",
+        "-Isubpackage3/subsubpackage",
+        "-Isubpackage/subsubpackage2",
+        "-Isubpackage",
+    ],
+    hdrs = ["exported_subsubpackage/header.h"],
+    includes = ["./exported_subsubpackage"],
+    linkstatic = True,
+    srcs = [
+        "exported_subsubpackage/header.h",
+        "subsubpackage/header.h",
+        "subsubpackage2/header.h",
+    ],
+)`},
+		},
+		{
+			description:                        "cc_library_static include_build_directory disabled",
+			moduleTypeUnderTest:                "cc_library_static",
+			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+			filesystem: map[string]string{
+				// subpackage with subdirectory
+				"subpackage/Android.bp":                         "",
+				"subpackage/subpackage_header.h":                "",
+				"subpackage/subdirectory/subdirectory_header.h": "",
+			},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    include_dirs: ["subpackage"], // still used, but local_include_dirs is recommended
+    local_include_dirs: ["subpackage2"],
+    include_build_directory: false,
+}`,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = [
+        "-Isubpackage",
+        "-Isubpackage2",
+    ],
+    linkstatic = True,
+)`},
+		},
+		{
+			description:                        "cc_library_static include_build_directory enabled",
+			moduleTypeUnderTest:                "cc_library_static",
+			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+			filesystem: map[string]string{
+				// subpackage with subdirectory
+				"subpackage/Android.bp":                         "",
+				"subpackage/subpackage_header.h":                "",
+				"subpackage2/Android.bp":                        "",
+				"subpackage2/subpackage2_header.h":              "",
+				"subpackage/subdirectory/subdirectory_header.h": "",
+			},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    include_dirs: ["subpackage"], // still used, but local_include_dirs is recommended
+    local_include_dirs: ["subpackage2"],
+    include_build_directory: true,
+}`,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = [
+        "-Isubpackage",
+        "-Isubpackage2",
+        "-I.",
+    ],
+    linkstatic = True,
+    srcs = [
+        "//subpackage:subpackage_header.h",
+        "//subpackage:subdirectory/subdirectory_header.h",
+        "//subpackage2:subpackage2_header.h",
+    ],
+)`},
+		},
+		{
+			description:                        "cc_library_static arch-specific static_libs",
+			moduleTypeUnderTest:                "cc_library_static",
+			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+			filesystem:                         map[string]string{},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_static { name: "static_dep" }
+cc_library_static { name: "static_dep2" }
+cc_library_static {
+    name: "foo_static",
+    arch: { arm64: { static_libs: ["static_dep"], whole_static_libs: ["static_dep2"] } },
+}`,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = ["-I."],
+    deps = select({
+        "//build/bazel/platforms/arch:arm64": [
+            ":static_dep",
+            ":static_dep2",
+        ],
+        "//conditions:default": [],
+    }),
+    linkstatic = True,
+)`, `cc_library_static(
+    name = "static_dep",
+    copts = ["-I."],
+    linkstatic = True,
+)`, `cc_library_static(
+    name = "static_dep2",
+    copts = ["-I."],
+    linkstatic = True,
+)`},
+		},
+		{
+			description:                        "cc_library_static os-specific static_libs",
+			moduleTypeUnderTest:                "cc_library_static",
+			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+			filesystem:                         map[string]string{},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_static { name: "static_dep" }
+cc_library_static { name: "static_dep2" }
+cc_library_static {
+    name: "foo_static",
+    target: { android: { static_libs: ["static_dep"], whole_static_libs: ["static_dep2"] } },
+}`,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = ["-I."],
+    deps = select({
+        "//build/bazel/platforms/os:android": [
+            ":static_dep",
+            ":static_dep2",
+        ],
+        "//conditions:default": [],
+    }),
+    linkstatic = True,
+)`, `cc_library_static(
+    name = "static_dep",
+    copts = ["-I."],
+    linkstatic = True,
+)`, `cc_library_static(
+    name = "static_dep2",
+    copts = ["-I."],
+    linkstatic = True,
+)`},
+		},
+		{
+			description:                        "cc_library_static base, arch and os-specific static_libs",
+			moduleTypeUnderTest:                "cc_library_static",
+			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+			filesystem:                         map[string]string{},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_static { name: "static_dep" }
+cc_library_static { name: "static_dep2" }
+cc_library_static { name: "static_dep3" }
+cc_library_static { name: "static_dep4" }
+cc_library_static {
+    name: "foo_static",
+    static_libs: ["static_dep"],
+    whole_static_libs: ["static_dep2"],
+    target: { android: { static_libs: ["static_dep3"] } },
+    arch: { arm64: { static_libs: ["static_dep4"] } },
+}`,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = ["-I."],
+    deps = [
+        ":static_dep",
+        ":static_dep2",
+    ] + select({
+        "//build/bazel/platforms/arch:arm64": [":static_dep4"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms/os:android": [":static_dep3"],
+        "//conditions:default": [],
+    }),
+    linkstatic = True,
+)`, `cc_library_static(
+    name = "static_dep",
+    copts = ["-I."],
+    linkstatic = True,
+)`, `cc_library_static(
+    name = "static_dep2",
+    copts = ["-I."],
+    linkstatic = True,
+)`, `cc_library_static(
+    name = "static_dep3",
+    copts = ["-I."],
+    linkstatic = True,
+)`, `cc_library_static(
+    name = "static_dep4",
+    copts = ["-I."],
+    linkstatic = True,
+)`},
+		},
 	}
 
 	dir := "."
@@ -352,6 +671,7 @@
 			ctx.DepsBp2BuildMutators(m)
 		}
 		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+		ctx.RegisterBp2BuildConfig(bp2buildConfig)
 		ctx.RegisterForBazelConversion()
 
 		_, errs := ctx.ParseFileList(dir, toParse)
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index a9d24ac..d00a1cb 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -55,8 +55,6 @@
         "a/b/*.c"
     ],
     exclude_srcs: ["a/b/exclude.c"],
-
-    bazel_module: { bp2build_available: true },
 }
 `,
 			expectedBazelTargets: []string{`cc_object(
@@ -66,16 +64,14 @@
         "-Wno-gcc-compat",
         "-Wall",
         "-Werror",
+        "-Iinclude",
+        "-I.",
     ],
-    hdrs = [
+    srcs = [
+        "a/b/c.c",
         "a/b/bar.h",
         "a/b/foo.h",
     ],
-    local_include_dirs = [
-        "include",
-        ".",
-    ],
-    srcs = ["a/b/c.c"],
 )`,
 			},
 		},
@@ -93,7 +89,6 @@
     ],
 
     defaults: ["foo_defaults"],
-    bazel_module: { bp2build_available: true },
 }
 
 cc_defaults {
@@ -117,10 +112,8 @@
         "-Wall",
         "-Werror",
         "-fno-addrsig",
-    ],
-    local_include_dirs = [
-        "include",
-        ".",
+        "-Iinclude",
+        "-I.",
     ],
     srcs = ["a/b/c.c"],
 )`,
@@ -139,27 +132,27 @@
     name: "foo",
     srcs: ["a/b/c.c"],
     objs: ["bar"],
-
-    bazel_module: { bp2build_available: true },
 }
 
 cc_object {
     name: "bar",
     srcs: ["x/y/z.c"],
-
-    bazel_module: { bp2build_available: true },
 }
 `,
 			expectedBazelTargets: []string{`cc_object(
     name = "bar",
-    copts = ["-fno-addrsig"],
-    local_include_dirs = ["."],
+    copts = [
+        "-fno-addrsig",
+        "-I.",
+    ],
     srcs = ["x/y/z.c"],
 )`, `cc_object(
     name = "foo",
-    copts = ["-fno-addrsig"],
+    copts = [
+        "-fno-addrsig",
+        "-I.",
+    ],
     deps = [":bar"],
-    local_include_dirs = ["."],
     srcs = ["a/b/c.c"],
 )`,
 			},
@@ -177,8 +170,6 @@
     name: "foo",
     srcs: ["a/b/c.c"],
     include_build_directory: false,
-
-    bazel_module: { bp2build_available: true },
 }
 `,
 			expectedBazelTargets: []string{`cc_object(
@@ -201,8 +192,6 @@
             asflags: ["-DPLATFORM_SDK_VERSION=%d"],
         },
     },
-
-    bazel_module: { bp2build_available: true },
 }
 `,
 			expectedBazelTargets: []string{`cc_object(
@@ -233,6 +222,7 @@
 
 		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
 		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+		ctx.RegisterBp2BuildConfig(bp2buildConfig)
 		ctx.RegisterForBazelConversion()
 
 		_, errs := ctx.ParseFileList(dir, toParse)
@@ -290,17 +280,18 @@
             srcs: ["arch/arm/file.S"], // label list
         },
     },
-    bazel_module: { bp2build_available: true },
 }
 `,
 			expectedBazelTargets: []string{
 				`cc_object(
     name = "foo",
-    copts = ["-fno-addrsig"] + select({
+    copts = [
+        "-fno-addrsig",
+        "-I.",
+    ] + select({
         "//build/bazel/platforms/arch:x86": ["-fPIC"],
         "//conditions:default": [],
     }),
-    local_include_dirs = ["."],
     srcs = ["a.cpp"] + select({
         "//build/bazel/platforms/arch:arm": ["arch/arm/file.S"],
         "//conditions:default": [],
@@ -334,20 +325,21 @@
             cflags: ["-Wall"],
         },
     },
-    bazel_module: { bp2build_available: true },
 }
 `,
 			expectedBazelTargets: []string{
 				`cc_object(
     name = "foo",
-    copts = ["-fno-addrsig"] + select({
+    copts = [
+        "-fno-addrsig",
+        "-I.",
+    ] + select({
         "//build/bazel/platforms/arch:arm": ["-Wall"],
         "//build/bazel/platforms/arch:arm64": ["-Wall"],
         "//build/bazel/platforms/arch:x86": ["-fPIC"],
         "//build/bazel/platforms/arch:x86_64": ["-fPIC"],
         "//conditions:default": [],
     }),
-    local_include_dirs = ["."],
     srcs = ["base.cpp"] + select({
         "//build/bazel/platforms/arch:arm": ["arm.cpp"],
         "//build/bazel/platforms/arch:arm64": ["arm64.cpp"],
@@ -377,19 +369,20 @@
             cflags: ["-Wall"],
         },
     },
-    bazel_module: { bp2build_available: true },
 }
 `,
 			expectedBazelTargets: []string{
 				`cc_object(
     name = "foo",
-    copts = ["-fno-addrsig"] + select({
+    copts = [
+        "-fno-addrsig",
+        "-I.",
+    ] + select({
         "//build/bazel/platforms/os:android": ["-fPIC"],
         "//build/bazel/platforms/os:darwin": ["-Wall"],
         "//build/bazel/platforms/os:windows": ["-fPIC"],
         "//conditions:default": [],
     }),
-    local_include_dirs = ["."],
     srcs = ["base.cpp"],
 )`,
 			},
@@ -409,6 +402,7 @@
 
 		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
 		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+		ctx.RegisterBp2BuildConfig(bp2buildConfig)
 		ctx.RegisterForBazelConversion()
 
 		_, errs := ctx.ParseFileList(dir, toParse)
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index 97729df..b9ffc04 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -110,7 +110,11 @@
 		if err != nil {
 			return "", err
 		}
-		selects += s + ",\n"
+		// s could still be an empty string, e.g. unset slices of structs with
+		// length of 0.
+		if s != "" {
+			selects += s + ",\n"
+		}
 	}
 
 	if len(selects) == 0 {
@@ -137,6 +141,9 @@
 	if err != nil {
 		return "", err
 	}
+	if v == "" {
+		return "", nil
+	}
 	s += fmt.Sprintf("\"%s\": %s", key, v)
 	return s, nil
 }
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 0bca30a..79304a5 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -16,7 +16,7 @@
 import (
 	"android/soong/android"
 	"android/soong/bazel"
-	"strings"
+	"path/filepath"
 )
 
 // bp2build functions and helpers for converting cc_* modules to Bazel.
@@ -53,6 +53,18 @@
 		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
 			allDeps = append(allDeps, baseLinkerProps.Header_libs...)
 			allDeps = append(allDeps, baseLinkerProps.Export_header_lib_headers...)
+			allDeps = append(allDeps, baseLinkerProps.Static_libs...)
+			allDeps = append(allDeps, baseLinkerProps.Whole_static_libs...)
+		}
+	}
+
+	for _, p := range module.GetArchProperties(&BaseLinkerProperties{}) {
+		// arch specific linker props
+		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
+			allDeps = append(allDeps, baseLinkerProps.Header_libs...)
+			allDeps = append(allDeps, baseLinkerProps.Export_header_lib_headers...)
+			allDeps = append(allDeps, baseLinkerProps.Static_libs...)
+			allDeps = append(allDeps, baseLinkerProps.Whole_static_libs...)
 		}
 	}
 
@@ -61,51 +73,80 @@
 
 // Convenience struct to hold all attributes parsed from compiler properties.
 type compilerAttributes struct {
-	copts bazel.StringListAttribute
-	srcs  bazel.LabelListAttribute
-	hdrs  bazel.LabelListAttribute
+	copts    bazel.StringListAttribute
+	srcs     bazel.LabelListAttribute
+	includes bazel.StringListAttribute
 }
 
 // bp2BuildParseCompilerProps returns copts, srcs and hdrs and other attributes.
 func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Module) compilerAttributes {
-	var hdrs, srcs bazel.LabelListAttribute
+	var localHdrs, srcs bazel.LabelListAttribute
 	var copts bazel.StringListAttribute
 
-	hdrsAndSrcs := func(baseCompilerProps *BaseCompilerProperties) (bazel.LabelList, bazel.LabelList) {
-		srcsList := android.BazelLabelForModuleSrcExcludes(
-			ctx, baseCompilerProps.Srcs, baseCompilerProps.Exclude_srcs)
-		hdrsList := android.BazelLabelForModuleSrc(ctx, srcsList.LooseHdrsGlobs(headerExts))
-		return hdrsList, srcsList
+	// Creates the -I flag for a directory, while making the directory relative
+	// to the exec root for Bazel to work.
+	includeFlag := func(dir string) string {
+		// filepath.Join canonicalizes the path, i.e. it takes care of . or .. elements.
+		return "-I" + filepath.Join(ctx.ModuleDir(), dir)
+	}
+
+	// Parse the list of srcs, excluding files from exclude_srcs.
+	parseSrcs := func(baseCompilerProps *BaseCompilerProperties) bazel.LabelList {
+		return android.BazelLabelForModuleSrcExcludes(ctx, baseCompilerProps.Srcs, baseCompilerProps.Exclude_srcs)
+	}
+
+	// Parse the list of module-relative include directories (-I).
+	parseLocalIncludeDirs := func(baseCompilerProps *BaseCompilerProperties) []string {
+		// include_dirs are root-relative, not module-relative.
+		includeDirs := bp2BuildMakePathsRelativeToModule(ctx, baseCompilerProps.Include_dirs)
+		return append(includeDirs, baseCompilerProps.Local_include_dirs...)
+	}
+
+	// Parse the list of copts.
+	parseCopts := func(baseCompilerProps *BaseCompilerProperties) []string {
+		copts := append([]string{}, baseCompilerProps.Cflags...)
+		for _, dir := range parseLocalIncludeDirs(baseCompilerProps) {
+			copts = append(copts, includeFlag(dir))
+		}
+		return copts
 	}
 
 	for _, props := range module.compiler.compilerProps() {
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			hdrs.Value, srcs.Value = hdrsAndSrcs(baseCompilerProps)
-			copts.Value = baseCompilerProps.Cflags
+			srcs.Value = parseSrcs(baseCompilerProps)
+			copts.Value = parseCopts(baseCompilerProps)
 			break
 		}
 	}
 
+	if c, ok := module.compiler.(*baseCompiler); ok && c.includeBuildDirectory() {
+		copts.Value = append(copts.Value, includeFlag("."))
+		localHdrs.Value = bp2BuildListHeadersInDir(ctx, ".")
+	} else if c, ok := module.compiler.(*libraryDecorator); ok && c.includeBuildDirectory() {
+		copts.Value = append(copts.Value, includeFlag("."))
+		localHdrs.Value = bp2BuildListHeadersInDir(ctx, ".")
+	}
+
 	for arch, props := range module.GetArchProperties(&BaseCompilerProperties{}) {
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			hdrsList, srcsList := hdrsAndSrcs(baseCompilerProps)
-			hdrs.SetValueForArch(arch.Name, bazel.SubtractBazelLabelList(hdrsList, hdrs.Value))
-			srcs.SetValueForArch(arch.Name, srcsList)
-			copts.SetValueForArch(arch.Name, baseCompilerProps.Cflags)
+			srcsList := parseSrcs(baseCompilerProps)
+			srcs.SetValueForArch(arch.Name, bazel.SubtractBazelLabelList(srcsList, srcs.Value))
+			copts.SetValueForArch(arch.Name, parseCopts(baseCompilerProps))
 		}
 	}
 
 	for os, props := range module.GetTargetProperties(&BaseCompilerProperties{}) {
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			hdrsList, srcsList := hdrsAndSrcs(baseCompilerProps)
-			hdrs.SetValueForOS(os.Name, bazel.SubtractBazelLabelList(hdrsList, hdrs.Value))
-			srcs.SetValueForOS(os.Name, srcsList)
-			copts.SetValueForOS(os.Name, baseCompilerProps.Cflags)
+			srcsList := parseSrcs(baseCompilerProps)
+			srcs.SetValueForOS(os.Name, bazel.SubtractBazelLabelList(srcsList, srcs.Value))
+			copts.SetValueForOS(os.Name, parseCopts(baseCompilerProps))
 		}
 	}
 
+	// Combine local, non-exported hdrs into srcs
+	srcs.Append(localHdrs)
+
 	return compilerAttributes{
-		hdrs:  hdrs,
 		srcs:  srcs,
 		copts: copts,
 	}
@@ -120,7 +161,6 @@
 // bp2BuildParseLinkerProps creates a label list attribute containing the header library deps of a module, including
 // configurable attribute values.
 func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) linkerAttributes {
-
 	var deps bazel.LabelListAttribute
 	var linkopts bazel.StringListAttribute
 
@@ -128,8 +168,10 @@
 		if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok {
 			libs := baseLinkerProps.Header_libs
 			libs = append(libs, baseLinkerProps.Export_header_lib_headers...)
-			deps = bazel.MakeLabelListAttribute(
-				android.BazelLabelForModuleDeps(ctx, android.SortedUniqueStrings(libs)))
+			libs = append(libs, baseLinkerProps.Static_libs...)
+			libs = append(libs, baseLinkerProps.Whole_static_libs...)
+			libs = android.SortedUniqueStrings(libs)
+			deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, libs))
 			linkopts.Value = baseLinkerProps.Ldflags
 			break
 		}
@@ -139,6 +181,8 @@
 		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
 			libs := baseLinkerProps.Header_libs
 			libs = append(libs, baseLinkerProps.Export_header_lib_headers...)
+			libs = append(libs, baseLinkerProps.Static_libs...)
+			libs = append(libs, baseLinkerProps.Whole_static_libs...)
 			libs = android.SortedUniqueStrings(libs)
 			deps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, libs))
 			linkopts.SetValueForArch(arch.Name, baseLinkerProps.Ldflags)
@@ -149,6 +193,8 @@
 		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
 			libs := baseLinkerProps.Header_libs
 			libs = append(libs, baseLinkerProps.Export_header_lib_headers...)
+			libs = append(libs, baseLinkerProps.Static_libs...)
+			libs = append(libs, baseLinkerProps.Whole_static_libs...)
 			libs = android.SortedUniqueStrings(libs)
 			deps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, libs))
 			linkopts.SetValueForOS(os.Name, baseLinkerProps.Ldflags)
@@ -162,28 +208,44 @@
 }
 
 func bp2BuildListHeadersInDir(ctx android.TopDownMutatorContext, includeDir string) bazel.LabelList {
-	globs := bazel.GlobsInDir(includeDir, includeDir != ".", headerExts)
+	globs := bazel.GlobsInDir(includeDir, true, headerExts)
 	return android.BazelLabelForModuleSrc(ctx, globs)
 }
 
-// Bazel wants include paths to be relative to the module
-func bp2BuildMakePathsRelativeToModule(ctx android.TopDownMutatorContext, paths []string) []string {
+// Relativize a list of root-relative paths with respect to the module's
+// directory.
+//
+// include_dirs Soong prop are root-relative (b/183742505), but
+// local_include_dirs, export_include_dirs and export_system_include_dirs are
+// module dir relative. This function makes a list of paths entirely module dir
+// relative.
+//
+// For the `include` attribute, Bazel wants the paths to be relative to the
+// module.
+func bp2BuildMakePathsRelativeToModule(ctx android.BazelConversionPathContext, paths []string) []string {
 	var relativePaths []string
 	for _, path := range paths {
-		relativePath := strings.TrimPrefix(path, ctx.ModuleDir()+"/")
+		// Semantics of filepath.Rel: join(ModuleDir, rel(ModuleDir, path)) == path
+		relativePath, err := filepath.Rel(ctx.ModuleDir(), path)
+		if err != nil {
+			panic(err)
+		}
 		relativePaths = append(relativePaths, relativePath)
 	}
 	return relativePaths
 }
 
-// bp2BuildParseExportedIncludes creates a label list attribute contains the
-// exported included directories of a module.
+// bp2BuildParseExportedIncludes creates a string list attribute contains the
+// exported included directories of a module, and a label list attribute
+// containing the exported headers of a module.
 func bp2BuildParseExportedIncludes(ctx android.TopDownMutatorContext, module *Module) (bazel.StringListAttribute, bazel.LabelListAttribute) {
 	libraryDecorator := module.linker.(*libraryDecorator)
 
+	// Export_system_include_dirs and export_include_dirs are already module dir
+	// relative, so they don't need to be relativized like include_dirs, which
+	// are root-relative.
 	includeDirs := libraryDecorator.flagExporter.Properties.Export_system_include_dirs
 	includeDirs = append(includeDirs, libraryDecorator.flagExporter.Properties.Export_include_dirs...)
-	includeDirs = bp2BuildMakePathsRelativeToModule(ctx, includeDirs)
 	includeDirsAttribute := bazel.MakeStringListAttribute(includeDirs)
 
 	var headersAttribute bazel.LabelListAttribute
@@ -198,7 +260,6 @@
 		if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
 			archIncludeDirs := flagExporterProperties.Export_system_include_dirs
 			archIncludeDirs = append(archIncludeDirs, flagExporterProperties.Export_include_dirs...)
-			archIncludeDirs = bp2BuildMakePathsRelativeToModule(ctx, archIncludeDirs)
 
 			// To avoid duplicate includes when base includes + arch includes are combined
 			archIncludeDirs = bazel.SubtractStrings(archIncludeDirs, includeDirs)
diff --git a/cc/library.go b/cc/library.go
index 2b0ee46..9a2b02e 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -260,11 +260,10 @@
 	compilerAttrs := bp2BuildParseCompilerProps(ctx, m)
 	linkerAttrs := bp2BuildParseLinkerProps(ctx, m)
 	exportedIncludes, exportedIncludesHeaders := bp2BuildParseExportedIncludes(ctx, m)
-	compilerAttrs.hdrs.Append(exportedIncludesHeaders)
 
 	attrs := &bazelCcLibraryAttributes{
 		Srcs:     compilerAttrs.srcs,
-		Hdrs:     compilerAttrs.hdrs,
+		Hdrs:     exportedIncludesHeaders,
 		Copts:    compilerAttrs.copts,
 		Linkopts: linkerAttrs.linkopts,
 		Deps:     linkerAttrs.deps,
@@ -2163,69 +2162,16 @@
 	}
 
 	compilerAttrs := bp2BuildParseCompilerProps(ctx, module)
-
-	var includeDirs []string
-	var localIncludeDirs []string
-	for _, props := range module.compiler.compilerProps() {
-		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			// TODO: these should be arch and os specific.
-			includeDirs = bp2BuildMakePathsRelativeToModule(ctx, baseCompilerProps.Include_dirs)
-			localIncludeDirs = bp2BuildMakePathsRelativeToModule(ctx, baseCompilerProps.Local_include_dirs)
-			break
-		}
-	}
-
-	// Soong implicitly includes headers from the module's directory.
-	// For Bazel builds to work we have to make these header includes explicit.
-	if module.compiler.(*libraryDecorator).includeBuildDirectory() {
-		localIncludeDirs = append(localIncludeDirs, ".")
-	}
-
-	// For Bazel, be more explicit about headers - list all header files in include dirs as srcs
-	for _, includeDir := range includeDirs {
-		compilerAttrs.srcs.Value.Append(bp2BuildListHeadersInDir(ctx, includeDir))
-	}
-	for _, localIncludeDir := range localIncludeDirs {
-		compilerAttrs.srcs.Value.Append(bp2BuildListHeadersInDir(ctx, localIncludeDir))
-	}
-
-	var staticLibs []string
-	var wholeStaticLibs []string
-	for _, props := range module.linker.linkerProps() {
-		// TODO: move this into bp2buildParseLinkerProps
-		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)
-
-	exportedIncludes, exportedIncludesHeaders := bp2BuildParseExportedIncludes(ctx, module)
-
-	// FIXME: Unify absolute vs relative paths
-	// FIXME: Use -I copts instead of setting includes= ?
-	allIncludes := exportedIncludes
-	allIncludes.Value = append(allIncludes.Value, includeDirs...)
-	allIncludes.Value = append(allIncludes.Value, localIncludeDirs...)
-
-	compilerAttrs.hdrs.Append(exportedIncludesHeaders)
-
 	linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
-	depsLabels.Append(linkerAttrs.deps.Value)
+	exportedIncludes, exportedIncludesHeaders := bp2BuildParseExportedIncludes(ctx, module)
 
 	attrs := &bazelCcLibraryStaticAttributes{
 		Copts:      compilerAttrs.copts,
 		Srcs:       compilerAttrs.srcs,
-		Deps:       bazel.MakeLabelListAttribute(depsLabels),
+		Deps:       linkerAttrs.deps,
 		Linkstatic: true,
-		Includes:   allIncludes,
-		Hdrs:       compilerAttrs.hdrs,
+		Includes:   exportedIncludes,
+		Hdrs:       exportedIncludesHeaders,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 076ce80..078242f 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -95,14 +95,14 @@
 		return
 	}
 
-	exportedIncludes, exportedIncludesHeaders := bp2BuildParseExportedIncludes(ctx, module)
+	exportedIncludes, exportedHdrs := bp2BuildParseExportedIncludes(ctx, module)
 	compilerAttrs := bp2BuildParseCompilerProps(ctx, module)
 	linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
 
 	attrs := &bazelCcLibraryHeadersAttributes{
 		Copts:    compilerAttrs.copts,
 		Includes: exportedIncludes,
-		Hdrs:     exportedIncludesHeaders,
+		Hdrs:     exportedHdrs,
 		Deps:     linkerAttrs.deps,
 	}
 
diff --git a/cc/object.go b/cc/object.go
index 9bb279a..d8f1aba 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -112,12 +112,11 @@
 
 // For bp2build conversion.
 type bazelObjectAttributes struct {
-	Srcs               bazel.LabelListAttribute
-	Hdrs               bazel.LabelListAttribute
-	Deps               bazel.LabelListAttribute
-	Copts              bazel.StringListAttribute
-	Asflags            []string
-	Local_include_dirs []string
+	Srcs    bazel.LabelListAttribute
+	Hdrs    bazel.LabelListAttribute
+	Deps    bazel.LabelListAttribute
+	Copts   bazel.StringListAttribute
+	Asflags []string
 }
 
 type bazelObject struct {
@@ -158,18 +157,7 @@
 
 	// Set arch-specific configurable attributes
 	compilerAttrs := bp2BuildParseCompilerProps(ctx, m)
-	var localIncludeDirs []string
 	var asFlags []string
-	for _, props := range m.compiler.compilerProps() {
-		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			localIncludeDirs = baseCompilerProps.Local_include_dirs
-			break
-		}
-	}
-
-	if c, ok := m.compiler.(*baseCompiler); ok && c.includeBuildDirectory() {
-		localIncludeDirs = append(localIncludeDirs, ".")
-	}
 
 	var deps bazel.LabelListAttribute
 	for _, props := range m.linker.linkerProps() {
@@ -197,12 +185,10 @@
 	// TODO(b/183595872) warn/error if we're not handling product variables
 
 	attrs := &bazelObjectAttributes{
-		Srcs:               compilerAttrs.srcs,
-		Hdrs:               compilerAttrs.hdrs,
-		Deps:               deps,
-		Copts:              compilerAttrs.copts,
-		Asflags:            asFlags,
-		Local_include_dirs: localIncludeDirs,
+		Srcs:    compilerAttrs.srcs,
+		Deps:    deps,
+		Copts:   compilerAttrs.copts,
+		Asflags: asFlags,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/java/Android.bp b/java/Android.bp
index 2a4b596..8e3e10d 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -79,9 +79,11 @@
         "droiddoc_test.go",
         "droidstubs_test.go",
         "hiddenapi_singleton_test.go",
+        "jacoco_test.go",
         "java_test.go",
         "jdeps_test.go",
         "kotlin_test.go",
+        "lint_test.go",
         "platform_bootclasspath_test.go",
         "platform_compat_config_test.go",
         "plugin_test.go",
diff --git a/java/boot_image.go b/java/boot_image.go
index 78215f0..192b16b 100644
--- a/java/boot_image.go
+++ b/java/boot_image.go
@@ -53,7 +53,7 @@
 	ctx.RegisterModuleType("prebuilt_bootclasspath_fragment", prebuiltBootImageFactory)
 }
 
-type bootImageContentDependencyTag struct {
+type bootclasspathFragmentContentDependencyTag struct {
 	blueprint.BaseDependencyTag
 }
 
@@ -62,16 +62,22 @@
 // This is a temporary workaround to make it easier to migrate to boot image modules with proper
 // dependencies.
 // TODO(b/177892522): Remove this and add needed visibility.
-func (b bootImageContentDependencyTag) ExcludeFromVisibilityEnforcement() {
+func (b bootclasspathFragmentContentDependencyTag) ExcludeFromVisibilityEnforcement() {
+}
+
+// The bootclasspath_fragment contents must never depend on prebuilts.
+func (b bootclasspathFragmentContentDependencyTag) ReplaceSourceWithPrebuilt() bool {
+	return false
 }
 
 // The tag used for the dependency between the boot image module and its contents.
-var bootImageContentDepTag = bootImageContentDependencyTag{}
+var bootclasspathFragmentContentDepTag = bootclasspathFragmentContentDependencyTag{}
 
-var _ android.ExcludeFromVisibilityEnforcementTag = bootImageContentDepTag
+var _ android.ExcludeFromVisibilityEnforcementTag = bootclasspathFragmentContentDepTag
+var _ android.ReplaceSourceWithPrebuilt = bootclasspathFragmentContentDepTag
 
-func IsbootImageContentDepTag(tag blueprint.DependencyTag) bool {
-	return tag == bootImageContentDepTag
+func IsBootclasspathFragmentContentDepTag(tag blueprint.DependencyTag) bool {
+	return tag == bootclasspathFragmentContentDepTag
 }
 
 type bootImageProperties struct {
@@ -187,7 +193,7 @@
 
 func (b *BootImageModule) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
 	tag := ctx.OtherModuleDependencyTag(dep)
-	if tag == bootImageContentDepTag {
+	if IsBootclasspathFragmentContentDepTag(tag) {
 		// Boot image contents are automatically added to apex.
 		return true
 	}
@@ -202,8 +208,26 @@
 	return nil
 }
 
+// ComponentDepsMutator adds dependencies onto modules before any prebuilt modules without a
+// corresponding source module are renamed. This means that adding a dependency using a name without
+// a prebuilt_ prefix will always resolve to a source module and when using a name with that prefix
+// it will always resolve to a prebuilt module.
+func (b *BootImageModule) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
+	module := ctx.Module()
+	_, isSourceModule := module.(*BootImageModule)
+
+	for _, name := range b.properties.Contents {
+		// A bootclasspath_fragment must depend only on other source modules, while the
+		// prebuilt_bootclasspath_fragment must only depend on other prebuilt modules.
+		if !isSourceModule {
+			name = android.PrebuiltNameFromSource(name)
+		}
+		ctx.AddDependency(module, bootclasspathFragmentContentDepTag, name)
+	}
+
+}
+
 func (b *BootImageModule) DepsMutator(ctx android.BottomUpMutatorContext) {
-	ctx.AddDependency(ctx.Module(), bootImageContentDepTag, b.properties.Contents...)
 
 	if SkipDexpreoptBootJars(ctx) {
 		return
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index ebf541d..8cc6f8f 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -21,6 +21,158 @@
 
 // Contains support for processing hiddenAPI in a modular fashion.
 
+type hiddenAPIStubsDependencyTag struct {
+	blueprint.BaseDependencyTag
+	sdkKind android.SdkKind
+}
+
+func (b hiddenAPIStubsDependencyTag) ExcludeFromApexContents() {
+}
+
+func (b hiddenAPIStubsDependencyTag) ReplaceSourceWithPrebuilt() bool {
+	return false
+}
+
+// Avoid having to make stubs content explicitly visible to dependent modules.
+//
+// This is a temporary workaround to make it easier to migrate to bootclasspath_fragment modules
+// with proper dependencies.
+// TODO(b/177892522): Remove this and add needed visibility.
+func (b hiddenAPIStubsDependencyTag) ExcludeFromVisibilityEnforcement() {
+}
+
+var _ android.ExcludeFromVisibilityEnforcementTag = hiddenAPIStubsDependencyTag{}
+var _ android.ReplaceSourceWithPrebuilt = hiddenAPIStubsDependencyTag{}
+var _ android.ExcludeFromApexContentsTag = hiddenAPIStubsDependencyTag{}
+
+// hiddenAPIRelevantSdkKinds lists all the android.SdkKind instances that are needed by the hidden
+// API processing.
+var hiddenAPIRelevantSdkKinds = []android.SdkKind{
+	android.SdkPublic,
+	android.SdkSystem,
+	android.SdkTest,
+	android.SdkCorePlatform,
+}
+
+// hiddenAPIComputeMonolithicStubLibModules computes the set of module names that provide stubs
+// needed to produce the hidden API monolithic stub flags file.
+func hiddenAPIComputeMonolithicStubLibModules(config android.Config) map[android.SdkKind][]string {
+	var publicStubModules []string
+	var systemStubModules []string
+	var testStubModules []string
+	var corePlatformStubModules []string
+
+	if config.AlwaysUsePrebuiltSdks() {
+		// Build configuration mandates using prebuilt stub modules
+		publicStubModules = append(publicStubModules, "sdk_public_current_android")
+		systemStubModules = append(systemStubModules, "sdk_system_current_android")
+		testStubModules = append(testStubModules, "sdk_test_current_android")
+	} else {
+		// Use stub modules built from source
+		publicStubModules = append(publicStubModules, "android_stubs_current")
+		systemStubModules = append(systemStubModules, "android_system_stubs_current")
+		testStubModules = append(testStubModules, "android_test_stubs_current")
+	}
+	// We do not have prebuilts of the core platform api yet
+	corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs")
+
+	// Allow products to define their own stubs for custom product jars that apps can use.
+	publicStubModules = append(publicStubModules, config.ProductHiddenAPIStubs()...)
+	systemStubModules = append(systemStubModules, config.ProductHiddenAPIStubsSystem()...)
+	testStubModules = append(testStubModules, config.ProductHiddenAPIStubsTest()...)
+	if config.IsEnvTrue("EMMA_INSTRUMENT") {
+		publicStubModules = append(publicStubModules, "jacoco-stubs")
+	}
+
+	m := map[android.SdkKind][]string{}
+	m[android.SdkPublic] = publicStubModules
+	m[android.SdkSystem] = systemStubModules
+	m[android.SdkTest] = testStubModules
+	m[android.SdkCorePlatform] = corePlatformStubModules
+	return m
+}
+
+// hiddenAPIAddStubLibDependencies adds dependencies onto the modules specified in
+// sdkKindToStubLibModules. It adds them in a well known order and uses an SdkKind specific tag to
+// identify the source of the dependency.
+func hiddenAPIAddStubLibDependencies(ctx android.BottomUpMutatorContext, sdkKindToStubLibModules map[android.SdkKind][]string) {
+	module := ctx.Module()
+	for _, sdkKind := range hiddenAPIRelevantSdkKinds {
+		modules := sdkKindToStubLibModules[sdkKind]
+		ctx.AddDependency(module, hiddenAPIStubsDependencyTag{sdkKind: sdkKind}, modules...)
+	}
+}
+
+// hiddenAPIGatherStubLibDexJarPaths gathers the paths to the dex jars from the dependencies added
+// in hiddenAPIAddStubLibDependencies.
+func hiddenAPIGatherStubLibDexJarPaths(ctx android.ModuleContext) map[android.SdkKind]android.Paths {
+	m := map[android.SdkKind]android.Paths{}
+	ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) {
+		tag := ctx.OtherModuleDependencyTag(module)
+		if hiddenAPIStubsTag, ok := tag.(hiddenAPIStubsDependencyTag); ok {
+			kind := hiddenAPIStubsTag.sdkKind
+			dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module)
+			if dexJar != nil {
+				m[kind] = append(m[kind], dexJar)
+			}
+		}
+	})
+	return m
+}
+
+// hiddenAPIRetrieveDexJarBuildPath retrieves the DexJarBuildPath from the specified module, if
+// available, or reports an error.
+func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android.Module) android.Path {
+	if j, ok := module.(UsesLibraryDependency); ok {
+		dexJar := j.DexJarBuildPath()
+		if dexJar != nil {
+			return dexJar
+		}
+		ctx.ModuleErrorf("dependency %s does not provide a dex jar, consider setting compile_dex: true", module)
+	} else {
+		ctx.ModuleErrorf("dependency %s of module type %s does not support providing a dex jar", module, ctx.OtherModuleType(module))
+	}
+	return nil
+}
+
+var sdkKindToHiddenapiListOption = map[android.SdkKind]string{
+	android.SdkPublic:       "public-stub-classpath",
+	android.SdkSystem:       "system-stub-classpath",
+	android.SdkTest:         "test-stub-classpath",
+	android.SdkCorePlatform: "core-platform-stub-classpath",
+}
+
+// ruleToGenerateHiddenAPIStubFlagsFile creates a rule to create a hidden API stub flags file.
+//
+// The rule is initialized but not built so that the caller can modify it and select an appropriate
+// name.
+func ruleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, outputPath android.OutputPath, bootDexJars android.Paths, sdkKindToPathList map[android.SdkKind]android.Paths) *android.RuleBuilder {
+	// Singleton rule which applies hiddenapi on all boot class path dex files.
+	rule := android.NewRuleBuilder(pctx, ctx)
+
+	tempPath := tempPathForRestat(ctx, outputPath)
+
+	command := rule.Command().
+		Tool(ctx.Config().HostToolPath(ctx, "hiddenapi")).
+		Text("list").
+		FlagForEachInput("--boot-dex=", bootDexJars)
+
+	// Iterate over the sdk kinds in a fixed order.
+	for _, sdkKind := range hiddenAPIRelevantSdkKinds {
+		paths := sdkKindToPathList[sdkKind]
+		if len(paths) > 0 {
+			option := sdkKindToHiddenapiListOption[sdkKind]
+			command.FlagWithInputList("--"+option+"=", paths, ":")
+		}
+	}
+
+	// Add the output path.
+	command.FlagWithOutput("--out-api-flags=", tempPath)
+
+	commitChangeForRestat(rule, tempPath, outputPath)
+	return rule
+}
+
 // HiddenAPIFlagFileProperties contains paths to the flag files that can be used to augment the
 // information obtained from annotations within the source code in order to create the complete set
 // of flags that should be applied to the dex implementation jars on the bootclasspath.
@@ -206,7 +358,7 @@
 // augmentationInfo is a struct containing paths to files that augment the information provided by
 // the moduleSpecificFlagsPaths.
 func ruleToGenerateHiddenApiFlags(ctx android.BuilderContext, outputPath android.WritablePath, baseFlagsPath android.Path, moduleSpecificFlagsPaths android.Paths, augmentationInfo hiddenAPIFlagFileInfo) {
-	tempPath := android.PathForOutput(ctx, outputPath.Rel()+".tmp")
+	tempPath := tempPathForRestat(ctx, outputPath)
 	rule := android.NewRuleBuilder(pctx, ctx)
 	command := rule.Command().
 		BuiltTool("generate_hiddenapi_lists").
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index ed0b722..3cc88e6 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -15,6 +15,8 @@
 package java
 
 import (
+	"strings"
+
 	"android/soong/android"
 )
 
@@ -125,8 +127,6 @@
 		return
 	}
 
-	stubFlagsRule(ctx)
-
 	// If there is a prebuilt hiddenapi dir, generate rules to use the
 	// files within. Generally, we build the hiddenapi files from source
 	// during the build, ensuring consistency. It's possible, in a split
@@ -158,109 +158,6 @@
 	ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_FLAGS", h.flags.String())
 }
 
-// stubFlagsRule creates the rule to build hiddenapi-stub-flags.txt out of dex jars from stub modules and boot image
-// modules.
-func stubFlagsRule(ctx android.SingletonContext) {
-	var publicStubModules []string
-	var systemStubModules []string
-	var testStubModules []string
-	var corePlatformStubModules []string
-
-	if ctx.Config().AlwaysUsePrebuiltSdks() {
-		// Build configuration mandates using prebuilt stub modules
-		publicStubModules = append(publicStubModules, "sdk_public_current_android")
-		systemStubModules = append(systemStubModules, "sdk_system_current_android")
-		testStubModules = append(testStubModules, "sdk_test_current_android")
-	} else {
-		// Use stub modules built from source
-		publicStubModules = append(publicStubModules, "android_stubs_current")
-		systemStubModules = append(systemStubModules, "android_system_stubs_current")
-		testStubModules = append(testStubModules, "android_test_stubs_current")
-	}
-	// We do not have prebuilts of the core platform api yet
-	corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs")
-
-	// Allow products to define their own stubs for custom product jars that apps can use.
-	publicStubModules = append(publicStubModules, ctx.Config().ProductHiddenAPIStubs()...)
-	systemStubModules = append(systemStubModules, ctx.Config().ProductHiddenAPIStubsSystem()...)
-	testStubModules = append(testStubModules, ctx.Config().ProductHiddenAPIStubsTest()...)
-	if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT") {
-		publicStubModules = append(publicStubModules, "jacoco-stubs")
-	}
-
-	publicStubPaths := make(android.Paths, len(publicStubModules))
-	systemStubPaths := make(android.Paths, len(systemStubModules))
-	testStubPaths := make(android.Paths, len(testStubModules))
-	corePlatformStubPaths := make(android.Paths, len(corePlatformStubModules))
-
-	moduleListToPathList := map[*[]string]android.Paths{
-		&publicStubModules:       publicStubPaths,
-		&systemStubModules:       systemStubPaths,
-		&testStubModules:         testStubPaths,
-		&corePlatformStubModules: corePlatformStubPaths,
-	}
-
-	var bootDexJars android.Paths
-
-	ctx.VisitAllModules(func(module android.Module) {
-		// Collect dex jar paths for the modules listed above.
-		if j, ok := module.(UsesLibraryDependency); ok {
-			name := ctx.ModuleName(module)
-			for moduleList, pathList := range moduleListToPathList {
-				if i := android.IndexList(name, *moduleList); i != -1 {
-					pathList[i] = j.DexJarBuildPath()
-				}
-			}
-		}
-
-		// Collect dex jar paths for modules that had hiddenapi encode called on them.
-		if h, ok := module.(hiddenAPIIntf); ok {
-			if jar := h.bootDexJar(); jar != nil {
-				bootDexJars = append(bootDexJars, jar)
-			}
-		}
-	})
-
-	var missingDeps []string
-	// Ensure all modules were converted to paths
-	for moduleList, pathList := range moduleListToPathList {
-		for i := range pathList {
-			if pathList[i] == nil {
-				moduleName := (*moduleList)[i]
-				pathList[i] = android.PathForOutput(ctx, "missing/module", moduleName)
-				if ctx.Config().AllowMissingDependencies() {
-					missingDeps = append(missingDeps, moduleName)
-				} else {
-					ctx.Errorf("failed to find dex jar path for module %q",
-						moduleName)
-				}
-			}
-		}
-	}
-
-	// Singleton rule which applies hiddenapi on all boot class path dex files.
-	rule := android.NewRuleBuilder(pctx, ctx)
-
-	outputPath := hiddenAPISingletonPaths(ctx).stubFlags
-	tempPath := android.PathForOutput(ctx, outputPath.Rel()+".tmp")
-
-	rule.MissingDeps(missingDeps)
-
-	rule.Command().
-		Tool(ctx.Config().HostToolPath(ctx, "hiddenapi")).
-		Text("list").
-		FlagForEachInput("--boot-dex=", bootDexJars).
-		FlagWithInputList("--public-stub-classpath=", publicStubPaths, ":").
-		FlagWithInputList("--system-stub-classpath=", systemStubPaths, ":").
-		FlagWithInputList("--test-stub-classpath=", testStubPaths, ":").
-		FlagWithInputList("--core-platform-stub-classpath=", corePlatformStubPaths, ":").
-		FlagWithOutput("--out-api-flags=", tempPath)
-
-	commitChangeForRestat(rule, tempPath, outputPath)
-
-	rule.Build("hiddenAPIStubFlagsFile", "hiddenapi stub flags")
-}
-
 // Checks to see whether the supplied module variant is in the list of boot jars.
 //
 // This is similar to logic in getBootImageJar() so any changes needed here are likely to be needed
@@ -348,6 +245,16 @@
 	return outputPath
 }
 
+// tempPathForRestat creates a path of the same type as the supplied type but with a name of
+// <path>.tmp.
+//
+// e.g. If path is an OutputPath for out/soong/hiddenapi/hiddenapi-flags.csv then this will return
+// an OutputPath for out/soong/hiddenapi/hiddenapi-flags.csv.tmp
+func tempPathForRestat(ctx android.PathContext, path android.WritablePath) android.WritablePath {
+	extWithoutLeadingDot := strings.TrimPrefix(path.Ext(), ".")
+	return path.ReplaceExtension(ctx, extWithoutLeadingDot+".tmp")
+}
+
 // commitChangeForRestat adds a command to a rule that updates outputPath from tempPath if they are different.  It
 // also marks the rule as restat and marks the tempPath as a temporary file that should not be considered an output of
 // the rule.
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index 5ea9a5b..3ab2277 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -23,12 +23,20 @@
 	"github.com/google/blueprint/proptools"
 )
 
+// TODO(b/177892522): Move these tests into a more appropriate place.
+
 func fixtureSetPrebuiltHiddenApiDirProductVariable(prebuiltHiddenApiDir *string) android.FixturePreparer {
 	return android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 		variables.PrebuiltHiddenApiDir = prebuiltHiddenApiDir
 	})
 }
 
+var prepareForTestWithDefaultPlatformBootclasspath = android.FixtureAddTextFile("frameworks/base/boot/Android.bp", `
+	platform_bootclasspath {
+		name: "platform-bootclasspath",
+	}
+`)
+
 var hiddenApiFixtureFactory = android.GroupFixturePreparers(
 	prepareForJavaTest, PrepareForTestWithHiddenApiBuildComponents)
 
@@ -36,6 +44,7 @@
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
+		prepareForTestWithDefaultPlatformBootclasspath,
 	).RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -44,8 +53,8 @@
 		}
 	`)
 
-	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 	want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
 }
@@ -59,6 +68,7 @@
 	android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
+		prepareForTestWithDefaultPlatformBootclasspath,
 	).ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorMessage)).
 		RunTestWithBp(t, `
 		java_library {
@@ -79,6 +89,7 @@
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
+		prepareForTestWithDefaultPlatformBootclasspath,
 	).RunTestWithBp(t, `
 		java_import {
 			name: "foo",
@@ -87,8 +98,8 @@
 	}
 	`)
 
-	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 	want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
 }
@@ -97,6 +108,7 @@
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
+		prepareForTestWithDefaultPlatformBootclasspath,
 	).RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -112,8 +124,8 @@
 		}
 	`)
 
-	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 	fromSourceJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, fromSourceJarArg)
 
@@ -125,6 +137,7 @@
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
+		prepareForTestWithDefaultPlatformBootclasspath,
 	).RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -140,8 +153,8 @@
 		}
 	`)
 
-	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 	prebuiltJarArg := "--boot-dex=out/soong/.intermediates/prebuilt_foo/android_common/dex/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, prebuiltJarArg)
 
@@ -184,13 +197,14 @@
 			result := android.GroupFixturePreparers(
 				hiddenApiFixtureFactory,
 				tc.preparer,
+				prepareForTestWithDefaultPlatformBootclasspath,
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 					variables.Always_use_prebuilt_sdks = proptools.BoolPtr(tc.unbundledBuild)
 				}),
 			).RunTest(t)
 
-			hiddenAPI := result.SingletonForTests("hiddenapi")
-			hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+			hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+			hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 			wantPublicStubs := "--public-stub-classpath=" + generateSdkDexPath(tc.publicStub, tc.unbundledBuild)
 			android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantPublicStubs)
 
diff --git a/java/java_test.go b/java/java_test.go
index e7ea4ef..1b8aec2 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1203,107 +1203,6 @@
 	}
 }
 
-func TestJavaLint(t *testing.T) {
-	ctx, _ := testJavaWithFS(t, `
-		java_library {
-			name: "foo",
-			srcs: [
-				"a.java",
-				"b.java",
-				"c.java",
-			],
-			min_sdk_version: "29",
-			sdk_version: "system_current",
-		}
-       `, map[string][]byte{
-		"lint-baseline.xml": nil,
-	})
-
-	foo := ctx.ModuleForTests("foo", "android_common")
-
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
-	if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml") {
-		t.Error("did not pass --baseline flag")
-	}
-}
-
-func TestJavaLintWithoutBaseline(t *testing.T) {
-	ctx, _ := testJavaWithFS(t, `
-		java_library {
-			name: "foo",
-			srcs: [
-				"a.java",
-				"b.java",
-				"c.java",
-			],
-			min_sdk_version: "29",
-			sdk_version: "system_current",
-		}
-       `, map[string][]byte{})
-
-	foo := ctx.ModuleForTests("foo", "android_common")
-
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
-	if strings.Contains(*sboxProto.Commands[0].Command, "--baseline") {
-		t.Error("passed --baseline flag for non existent file")
-	}
-}
-
-func TestJavaLintRequiresCustomLintFileToExist(t *testing.T) {
-	android.GroupFixturePreparers(
-		PrepareForTestWithJavaDefaultModules,
-		android.PrepareForTestDisallowNonExistentPaths,
-	).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern([]string{`source path "mybaseline.xml" does not exist`})).
-		RunTestWithBp(t, `
-			java_library {
-				name: "foo",
-				srcs: [
-				],
-				min_sdk_version: "29",
-				sdk_version: "system_current",
-				lint: {
-					baseline_filename: "mybaseline.xml",
-				},
-			}
-	 `)
-}
-
-func TestJavaLintUsesCorrectBpConfig(t *testing.T) {
-	ctx, _ := testJavaWithFS(t, `
-		java_library {
-			name: "foo",
-			srcs: [
-				"a.java",
-				"b.java",
-				"c.java",
-			],
-			min_sdk_version: "29",
-			sdk_version: "system_current",
-			lint: {
-				error_checks: ["SomeCheck"],
-				baseline_filename: "mybaseline.xml",
-			},
-		}
-       `, map[string][]byte{
-		"mybaseline.xml": nil,
-	})
-
-	foo := ctx.ModuleForTests("foo", "android_common")
-
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
-	if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline mybaseline.xml") {
-		t.Error("did not use the correct file for baseline")
-	}
-
-	if !strings.Contains(*sboxProto.Commands[0].Command, "--error_check NewApi") {
-		t.Error("should check NewApi errors")
-	}
-
-	if !strings.Contains(*sboxProto.Commands[0].Command, "--error_check SomeCheck") {
-		t.Error("should combine NewApi errors with SomeCheck errors")
-	}
-}
-
 func TestGeneratedSources(t *testing.T) {
 	ctx, _ := testJavaWithFS(t, `
 		java_library {
@@ -1647,31 +1546,51 @@
 		java_sdk_library {
 			name: "sdklib",
 			srcs: ["a.java"],
-			impl_only_libs: ["foo"],
-			stub_only_libs: ["bar"],
+			libs: ["lib"],
+			static_libs: ["static-lib"],
+			impl_only_libs: ["impl-only-lib"],
+			stub_only_libs: ["stub-only-lib"],
+			stub_only_static_libs: ["stub-only-static-lib"],
 		}
-		java_library {
-			name: "foo",
+		java_defaults {
+			name: "defaults",
 			srcs: ["a.java"],
 			sdk_version: "current",
 		}
-		java_library {
-			name: "bar",
-			srcs: ["a.java"],
-			sdk_version: "current",
-		}
+		java_library { name: "lib", defaults: ["defaults"] }
+		java_library { name: "static-lib", defaults: ["defaults"] }
+		java_library { name: "impl-only-lib", defaults: ["defaults"] }
+		java_library { name: "stub-only-lib", defaults: ["defaults"] }
+		java_library { name: "stub-only-static-lib", defaults: ["defaults"] }
 		`)
-
-	for _, implName := range []string{"sdklib", "sdklib.impl"} {
-		implJavacCp := result.ModuleForTests(implName, "android_common").Rule("javac").Args["classpath"]
-		if !strings.Contains(implJavacCp, "/foo.jar") || strings.Contains(implJavacCp, "/bar.jar") {
-			t.Errorf("%v javac classpath %v does not contain foo and not bar", implName, implJavacCp)
-		}
+	var expectations = []struct {
+		lib               string
+		on_impl_classpath bool
+		on_stub_classpath bool
+		in_impl_combined  bool
+		in_stub_combined  bool
+	}{
+		{lib: "lib", on_impl_classpath: true},
+		{lib: "static-lib", in_impl_combined: true},
+		{lib: "impl-only-lib", on_impl_classpath: true},
+		{lib: "stub-only-lib", on_stub_classpath: true},
+		{lib: "stub-only-static-lib", in_stub_combined: true},
 	}
-	stubName := apiScopePublic.stubsLibraryModuleName("sdklib")
-	stubsJavacCp := result.ModuleForTests(stubName, "android_common").Rule("javac").Args["classpath"]
-	if strings.Contains(stubsJavacCp, "/foo.jar") || !strings.Contains(stubsJavacCp, "/bar.jar") {
-		t.Errorf("stubs javac classpath %v does not contain bar and not foo", stubsJavacCp)
+	verify := func(sdklib, dep string, cp, combined bool) {
+		sdklibCp := result.ModuleForTests(sdklib, "android_common").Rule("javac").Args["classpath"]
+		expected := cp || combined // Every combined jar is also on the classpath.
+		android.AssertStringContainsEquals(t, "bad classpath for "+sdklib, sdklibCp, "/"+dep+".jar", expected)
+
+		combineJarInputs := result.ModuleForTests(sdklib, "android_common").Rule("combineJar").Inputs.Strings()
+		depPath := filepath.Join("out", "soong", ".intermediates", dep, "android_common", "turbine-combined", dep+".jar")
+		android.AssertStringListContainsEquals(t, "bad combined inputs for "+sdklib, combineJarInputs, depPath, combined)
+	}
+	for _, expectation := range expectations {
+		verify("sdklib", expectation.lib, expectation.on_impl_classpath, expectation.in_impl_combined)
+		verify("sdklib.impl", expectation.lib, expectation.on_impl_classpath, expectation.in_impl_combined)
+
+		stubName := apiScopePublic.stubsLibraryModuleName("sdklib")
+		verify(stubName, expectation.lib, expectation.on_stub_classpath, expectation.in_stub_combined)
 	}
 }
 
diff --git a/java/lint.go b/java/lint.go
index a77daa8..862c9b4 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -26,6 +26,10 @@
 	"android/soong/remoteexec"
 )
 
+// lint checks automatically enforced for modules that have different min_sdk_version than
+// sdk_version
+var updatabilityChecks = []string{"NewApi"}
+
 type LintProperties struct {
 	// Controls for running Android Lint on the module.
 	Lint struct {
@@ -53,6 +57,9 @@
 
 		// Name of the file that lint uses as the baseline. Defaults to "lint-baseline.xml".
 		Baseline_filename *string
+
+		// If true, baselining updatability lint checks (e.g. NewApi) is prohibited. Defaults to false.
+		Strict_updatability_linting *bool
 	}
 }
 
@@ -253,6 +260,13 @@
 	cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
 	cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
 
+	if BoolDefault(l.properties.Lint.Strict_updatability_linting, false) {
+		if baselinePath := l.getBaselineFilepath(ctx); baselinePath.Valid() {
+			cmd.FlagWithInput("--baseline ", baselinePath.Path())
+			cmd.FlagForEachArg("--disallowed_issues ", updatabilityChecks)
+		}
+	}
+
 	return lintPaths{
 		projectXML: projectXMLPath,
 		configXML:  configXMLPath,
@@ -298,7 +312,17 @@
 	}
 
 	if l.minSdkVersion != l.compileSdkVersion {
-		l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, "NewApi")
+		l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, updatabilityChecks...)
+		_, filtered := android.FilterList(l.properties.Lint.Warning_checks, updatabilityChecks)
+		if len(filtered) != 0 {
+			ctx.PropertyErrorf("lint.warning_checks",
+				"Can't treat %v checks as warnings if min_sdk_version is different from sdk_version.", filtered)
+		}
+		_, filtered = android.FilterList(l.properties.Lint.Disabled_checks, updatabilityChecks)
+		if len(filtered) != 0 {
+			ctx.PropertyErrorf("lint.disabled_checks",
+				"Can't disable %v checks if min_sdk_version is different from sdk_version.", filtered)
+		}
 	}
 
 	extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag)
diff --git a/java/lint_test.go b/java/lint_test.go
new file mode 100644
index 0000000..a253df9
--- /dev/null
+++ b/java/lint_test.go
@@ -0,0 +1,204 @@
+// 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 java
+
+import (
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestJavaLint(t *testing.T) {
+	ctx, _ := testJavaWithFS(t, `
+		java_library {
+			name: "foo",
+			srcs: [
+				"a.java",
+				"b.java",
+				"c.java",
+			],
+			min_sdk_version: "29",
+			sdk_version: "system_current",
+		}
+       `, map[string][]byte{
+		"lint-baseline.xml": nil,
+	})
+
+	foo := ctx.ModuleForTests("foo", "android_common")
+
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
+	if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml") {
+		t.Error("did not pass --baseline flag")
+	}
+}
+
+func TestJavaLintWithoutBaseline(t *testing.T) {
+	ctx, _ := testJavaWithFS(t, `
+		java_library {
+			name: "foo",
+			srcs: [
+				"a.java",
+				"b.java",
+				"c.java",
+			],
+			min_sdk_version: "29",
+			sdk_version: "system_current",
+		}
+       `, map[string][]byte{})
+
+	foo := ctx.ModuleForTests("foo", "android_common")
+
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
+	if strings.Contains(*sboxProto.Commands[0].Command, "--baseline") {
+		t.Error("passed --baseline flag for non existent file")
+	}
+}
+
+func TestJavaLintRequiresCustomLintFileToExist(t *testing.T) {
+	android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		android.PrepareForTestDisallowNonExistentPaths,
+	).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern([]string{`source path "mybaseline.xml" does not exist`})).
+		RunTestWithBp(t, `
+			java_library {
+				name: "foo",
+				srcs: [
+				],
+				min_sdk_version: "29",
+				sdk_version: "system_current",
+				lint: {
+					baseline_filename: "mybaseline.xml",
+				},
+			}
+	 `)
+}
+
+func TestJavaLintUsesCorrectBpConfig(t *testing.T) {
+	ctx, _ := testJavaWithFS(t, `
+		java_library {
+			name: "foo",
+			srcs: [
+				"a.java",
+				"b.java",
+				"c.java",
+			],
+			min_sdk_version: "29",
+			sdk_version: "system_current",
+			lint: {
+				error_checks: ["SomeCheck"],
+				baseline_filename: "mybaseline.xml",
+			},
+		}
+       `, map[string][]byte{
+		"mybaseline.xml": nil,
+	})
+
+	foo := ctx.ModuleForTests("foo", "android_common")
+
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
+	if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline mybaseline.xml") {
+		t.Error("did not use the correct file for baseline")
+	}
+
+	if !strings.Contains(*sboxProto.Commands[0].Command, "--error_check NewApi") {
+		t.Error("should check NewApi errors")
+	}
+
+	if !strings.Contains(*sboxProto.Commands[0].Command, "--error_check SomeCheck") {
+		t.Error("should combine NewApi errors with SomeCheck errors")
+	}
+}
+
+func TestJavaLintBypassUpdatableChecks(t *testing.T) {
+	testCases := []struct {
+		name  string
+		bp    string
+		error string
+	}{
+		{
+			name: "warning_checks",
+			bp: `
+				java_library {
+					name: "foo",
+					srcs: [
+						"a.java",
+					],
+					min_sdk_version: "29",
+					sdk_version: "current",
+					lint: {
+						warning_checks: ["NewApi"],
+					},
+				}
+			`,
+			error: "lint.warning_checks: Can't treat \\[NewApi\\] checks as warnings if min_sdk_version is different from sdk_version.",
+		},
+		{
+			name: "disable_checks",
+			bp: `
+				java_library {
+					name: "foo",
+					srcs: [
+						"a.java",
+					],
+					min_sdk_version: "29",
+					sdk_version: "current",
+					lint: {
+						disabled_checks: ["NewApi"],
+					},
+				}
+			`,
+			error: "lint.disabled_checks: Can't disable \\[NewApi\\] checks if min_sdk_version is different from sdk_version.",
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			errorHandler := android.FixtureExpectsAtLeastOneErrorMatchingPattern(testCase.error)
+			android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules).
+				ExtendWithErrorHandler(errorHandler).
+				RunTestWithBp(t, testCase.bp)
+		})
+	}
+}
+
+func TestJavaLintStrictUpdatabilityLinting(t *testing.T) {
+	bp := `
+		java_library {
+			name: "foo",
+			srcs: [
+				"a.java",
+			],
+			min_sdk_version: "29",
+			sdk_version: "current",
+			lint: {
+				strict_updatability_linting: true,
+			},
+		}
+	`
+	fs := android.MockFS{
+		"lint-baseline.xml": nil,
+	}
+
+	result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules, fs.AddToFixture()).
+		RunTestWithBp(t, bp)
+
+	foo := result.ModuleForTests("foo", "android_common")
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
+	if !strings.Contains(*sboxProto.Commands[0].Command,
+		"--baseline lint-baseline.xml --disallowed_issues NewApi") {
+		t.Error("did not restrict baselining NewApi")
+	}
+}
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index ba758dd..568f5e4 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -140,6 +140,8 @@
 }
 
 func (b *platformBootclasspathModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	b.hiddenAPIDepsMutator(ctx)
+
 	if SkipDexpreoptBootJars(ctx) {
 		return
 	}
@@ -149,6 +151,16 @@
 	dexpreopt.RegisterToolDeps(ctx)
 }
 
+func (b *platformBootclasspathModule) hiddenAPIDepsMutator(ctx android.BottomUpMutatorContext) {
+	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+		return
+	}
+
+	// Add dependencies onto the stub lib modules.
+	sdkKindToStubLibModules := hiddenAPIComputeMonolithicStubLibModules(ctx.Config())
+	hiddenAPIAddStubLibDependencies(ctx, sdkKindToStubLibModules)
+}
+
 func platformBootclasspathDepsMutator(ctx android.BottomUpMutatorContext) {
 	m := ctx.Module()
 	if p, ok := m.(*platformBootclasspathModule); ok {
@@ -353,10 +365,24 @@
 	baseFlagsPath := hiddenAPISingletonPaths(ctx).stubFlags
 	ruleToGenerateHiddenApiFlags(ctx, outputPath, baseFlagsPath, moduleSpecificFlagsPaths, flagFileInfo)
 
+	b.generateHiddenAPIStubFlagsRules(ctx, hiddenAPISupportingModules)
 	b.generateHiddenAPIIndexRules(ctx, hiddenAPISupportingModules)
 	b.generatedHiddenAPIMetadataRules(ctx, hiddenAPISupportingModules)
 }
 
+func (b *platformBootclasspathModule) generateHiddenAPIStubFlagsRules(ctx android.ModuleContext, modules []hiddenAPISupportingModule) {
+	bootDexJars := android.Paths{}
+	for _, module := range modules {
+		bootDexJars = append(bootDexJars, module.bootDexJar())
+	}
+
+	sdkKindToStubPaths := hiddenAPIGatherStubLibDexJarPaths(ctx)
+
+	outputPath := hiddenAPISingletonPaths(ctx).stubFlags
+	rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, outputPath, bootDexJars, sdkKindToStubPaths)
+	rule.Build("platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags")
+}
+
 func (b *platformBootclasspathModule) generateHiddenAPIIndexRules(ctx android.ModuleContext, modules []hiddenAPISupportingModule) {
 	indexes := android.Paths{}
 	for _, module := range modules {
diff --git a/java/sdk.go b/java/sdk.go
index d6e20a7..1c097d5 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -247,7 +247,7 @@
 	}
 
 	combinedAidl := sdkFrameworkAidlPath(ctx)
-	tempPath := combinedAidl.ReplaceExtension(ctx, "aidl.tmp")
+	tempPath := tempPathForRestat(ctx, combinedAidl)
 
 	rule := createFrameworkAidl(stubsModules, tempPath, ctx)
 
@@ -261,7 +261,7 @@
 	stubsModules := []string{"android_module_lib_stubs_current"}
 
 	combinedAidl := nonUpdatableFrameworkAidlPath(ctx)
-	tempPath := combinedAidl.ReplaceExtension(ctx, "aidl.tmp")
+	tempPath := tempPathForRestat(ctx, combinedAidl)
 
 	rule := createFrameworkAidl(stubsModules, tempPath, ctx)
 
@@ -270,7 +270,7 @@
 	rule.Build("framework_non_updatable_aidl", "generate framework_non_updatable.aidl")
 }
 
-func createFrameworkAidl(stubsModules []string, path android.OutputPath, ctx android.SingletonContext) *android.RuleBuilder {
+func createFrameworkAidl(stubsModules []string, path android.WritablePath, ctx android.SingletonContext) *android.RuleBuilder {
 	stubsJars := make([]android.Paths, len(stubsModules))
 
 	ctx.VisitAllModules(func(module android.Module) {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 223be5c..05ce97a 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -399,6 +399,9 @@
 	// List of Java libraries that will be in the classpath when building stubs
 	Stub_only_libs []string `android:"arch_variant"`
 
+	// List of Java libraries that will included in stub libraries
+	Stub_only_static_libs []string `android:"arch_variant"`
+
 	// list of package names that will be documented and publicized as API.
 	// This allows the API to be restricted to a subset of the source files provided.
 	// If this is unspecified then all the source files will be treated as being part
@@ -1275,6 +1278,7 @@
 		System_modules *string
 		Patch_module   *string
 		Libs           []string
+		Static_libs    []string
 		Compile_dex    *bool
 		Java_version   *string
 		Openjdk9       struct {
@@ -1299,6 +1303,7 @@
 	props.Patch_module = module.properties.Patch_module
 	props.Installable = proptools.BoolPtr(false)
 	props.Libs = module.sdkLibraryProperties.Stub_only_libs
+	props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs
 	// The stub-annotations library contains special versions of the annotations
 	// with CLASS retention policy, so that they're kept.
 	if proptools.Bool(module.sdkLibraryProperties.Annotations_enabled) {
diff --git a/scripts/hiddenapi/generate_hiddenapi_lists.py b/scripts/hiddenapi/generate_hiddenapi_lists.py
index 6816475..5ab93d1 100755
--- a/scripts/hiddenapi/generate_hiddenapi_lists.py
+++ b/scripts/hiddenapi/generate_hiddenapi_lists.py
@@ -332,7 +332,7 @@
 def main(argv):
     # Parse arguments.
     args = vars(get_args())
-    flagfiles = parse_ordered_flags(args['ordered_flags'])
+    flagfiles = parse_ordered_flags(args['ordered_flags'] or [])
 
     # Initialize API->flags dictionary.
     flags = FlagsDict()
diff --git a/sdk/Android.bp b/sdk/Android.bp
index 7b034e6..09a7286 100644
--- a/sdk/Android.bp
+++ b/sdk/Android.bp
@@ -20,7 +20,7 @@
         "update.go",
     ],
     testSrcs: [
-        "boot_image_sdk_test.go",
+        "bootclasspath_fragment_sdk_test.go",
         "bp_test.go",
         "cc_sdk_test.go",
         "compat_config_sdk_test.go",
diff --git a/sdk/boot_image_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
similarity index 67%
rename from sdk/boot_image_sdk_test.go
rename to sdk/bootclasspath_fragment_sdk_test.go
index 5a03e34..10f86e8 100644
--- a/sdk/boot_image_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -20,17 +20,17 @@
 	"android/soong/android"
 )
 
-func TestSnapshotWithBootImage(t *testing.T) {
+func TestSnapshotWithBootclasspathFragment(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		android.FixtureWithRootAndroidBp(`
 			sdk {
 				name: "mysdk",
-				boot_images: ["mybootimage"],
+				bootclasspath_fragments: ["mybootclasspathfragment"],
 			}
 
-			boot_image {
-				name: "mybootimage",
+			bootclasspath_fragment {
+				name: "mybootclasspathfragment",
 				image_name: "art",
 			}
 		`),
@@ -40,8 +40,8 @@
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
-prebuilt_boot_image {
-    name: "mybootimage",
+prebuilt_bootclasspath_fragment {
+    name: "mybootclasspathfragment",
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
@@ -51,9 +51,9 @@
 		checkVersionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
-prebuilt_boot_image {
-    name: "mysdk_mybootimage@current",
-    sdk_member_name: "mybootimage",
+prebuilt_bootclasspath_fragment {
+    name: "mysdk_mybootclasspathfragment@current",
+    sdk_member_name: "mybootclasspathfragment",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     image_name: "art",
@@ -62,37 +62,37 @@
 sdk_snapshot {
     name: "mysdk@current",
     visibility: ["//visibility:public"],
-    boot_images: ["mysdk_mybootimage@current"],
+    bootclasspath_fragments: ["mysdk_mybootclasspathfragment@current"],
 }
 `),
 		checkAllCopyRules(""))
 }
 
-// Test that boot_image works with sdk.
-func TestBasicSdkWithBootImage(t *testing.T) {
+// Test that bootclasspath_fragment works with sdk.
+func TestBasicSdkWithBootclasspathFragment(t *testing.T) {
 	android.GroupFixturePreparers(
 		prepareForSdkTestWithApex,
 		prepareForSdkTestWithJava,
 		android.FixtureWithRootAndroidBp(`
 		sdk {
 			name: "mysdk",
-			boot_images: ["mybootimage"],
+			bootclasspath_fragments: ["mybootclasspathfragment"],
 		}
 
-		boot_image {
-			name: "mybootimage",
+		bootclasspath_fragment {
+			name: "mybootclasspathfragment",
 			image_name: "art",
 			apex_available: ["myapex"],
 		}
 
 		sdk_snapshot {
 			name: "mysdk@1",
-			boot_images: ["mybootimage_mysdk_1"],
+			bootclasspath_fragments: ["mybootclasspathfragment_mysdk_1"],
 		}
 
-		prebuilt_boot_image {
-			name: "mybootimage_mysdk_1",
-			sdk_member_name: "mybootimage",
+		prebuilt_bootclasspath_fragment {
+			name: "mybootclasspathfragment_mysdk_1",
+			sdk_member_name: "mybootclasspathfragment",
 			prefer: false,
 			visibility: ["//visibility:public"],
 			apex_available: [