Merge "Support mixed building for cc_prebuilt_binary"
diff --git a/README.md b/README.md
index 87d6948..18cf7b2 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,33 @@
 # Soong
 
-Soong is the replacement for the old Android make-based build system.  It
-replaces Android.mk files with Android.bp files, which are JSON-like simple
-declarative descriptions of modules to build.
+Soong is one of the build systems used in Android. There are altogether three:
+* The legacy Make-based build system that is controlled by files called
+  `Android.mk`.
+* Soong, which is controlled by files called `Android.bp`.
+* The upcoming Bazel-based build system that is controlled by files called
+  `BUILD.bazel`.
+
+`Android.bp` file are JSON-like declarative descriptions of "modules" to build;
+a "module" is the basic unit of building that Soong understands, similarly to
+how "target" is the basic unit of building for Bazel (and Make, although the
+two kinds of "targets" are very different)
 
 See [Simple Build
 Configuration](https://source.android.com/compatibility/tests/development/blueprints)
 on source.android.com to read how Soong is configured for testing.
 
+### Contributing
+
+Code reviews are handled through the usual code review system of Android,
+available [here](https://android-review.googlesource.com/dashboard/self).
+
+For simple changes (fixing typos, obvious optimizations, etc.), sending a code
+review request is enough. For more substantial changes, file a bug in our
+[bug tracker](https://issuetracker.google.com/issues/new?component=381517) or
+or write us at android-building@googlegroups.com .
+
+For Googlers, see our [internal documentation](http://go/soong).
+
 ## Android.bp file format
 
 By design, Android.bp files are very simple.  There are no conditionals or
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 03ce0f1..9b284fc 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -284,6 +284,7 @@
 		"system/core/libvndksupport":                             Bp2BuildDefaultTrueRecursively,
 		"system/core/property_service/libpropertyinfoparser":     Bp2BuildDefaultTrueRecursively,
 		"system/core/property_service/libpropertyinfoserializer": Bp2BuildDefaultTrueRecursively,
+		"system/extras/toolchain-extras":                         Bp2BuildDefaultTrue,
 		"system/incremental_delivery/incfs":                      Bp2BuildDefaultTrue,
 		"system/libartpalette":                                   Bp2BuildDefaultTrueRecursively,
 		"system/libbase":                                         Bp2BuildDefaultTrueRecursively,
diff --git a/android/api_domain.go b/android/api_domain.go
index 7876654..0993e3d 100644
--- a/android/api_domain.go
+++ b/android/api_domain.go
@@ -73,9 +73,9 @@
 func (a *apiDomain) DepsMutator(ctx BottomUpMutatorContext) {
 	for _, cc := range a.properties.Cc_api_contributions {
 		// Use FarVariationDependencies since the variants of api_domain is a subset of the variants of the dependency cc module
-		// Creating a dependency on the first variant is ok since this is a no-op in Soong
+		// Creating a dependency on the first variant that matches (os,arch) is ok since this is a no-op in Soong
 		// The primary function of this dependency is to create a connected graph in the corresponding bp2build workspace
-		ctx.AddFarVariationDependencies([]blueprint.Variation{}, nil, cc)
+		ctx.AddFarVariationDependencies(ctx.Target().Variations(), nil, cc)
 	}
 }
 
diff --git a/android/module.go b/android/module.go
index 68d9f8e..c099e6d 100644
--- a/android/module.go
+++ b/android/module.go
@@ -915,9 +915,16 @@
 type CommonAttributes struct {
 	// Soong nameProperties -> Bazel name
 	Name string
+
 	// Data mapped from: Required
 	Data bazel.LabelListAttribute
 
+	// SkipData is neither a Soong nor Bazel target attribute
+	// If true, this will not fill the data attribute automatically
+	// This is useful for Soong modules that have 1:many Bazel targets
+	// Some of the generated Bazel targets might not have a data attribute
+	SkipData *bool
+
 	Tags bazel.StringListAttribute
 
 	Applicable_licenses bazel.LabelListAttribute
@@ -1305,7 +1312,12 @@
 		platformEnabledAttribute.Add(&l)
 	}
 
-	attrs.Data.Append(required)
+	if !proptools.Bool(attrs.SkipData) {
+		attrs.Data.Append(required)
+	}
+	// SkipData is not an attribute of any Bazel target
+	// Set this to nil so that it does not appear in the generated build file
+	attrs.SkipData = nil
 
 	moduleEnableConstraints := bazel.LabelListAttribute{}
 	moduleEnableConstraints.Append(platformEnabledAttribute)
diff --git a/android/neverallow.go b/android/neverallow.go
index 00078a0..1cdccc3 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -58,6 +58,7 @@
 	AddNeverAllowRules(createMakefileGoalRules()...)
 	AddNeverAllowRules(createInitFirstStageRules()...)
 	AddNeverAllowRules(createProhibitFrameworkAccessRules()...)
+	AddNeverAllowRules(createBp2BuildRule())
 }
 
 // Add a NeverAllow rule to the set of rules to apply.
@@ -65,6 +66,13 @@
 	neverallows = append(neverallows, rules...)
 }
 
+func createBp2BuildRule() Rule {
+	return NeverAllow().
+		With("bazel_module.bp2build_available", "true").
+		Because("setting bp2build_available in Android.bp is not " +
+			"supported for custom conversion, use allowlists.go instead.")
+}
+
 func createIncludeDirsRules() []Rule {
 	notInIncludeDir := []string{
 		"art",
diff --git a/apex/key.go b/apex/key.go
index d449589..2b09f1d 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -44,8 +44,6 @@
 
 	publicKeyFile  android.Path
 	privateKeyFile android.Path
-
-	keyName string
 }
 
 type apexKeyProperties struct {
@@ -102,7 +100,6 @@
 			m.publicKeyFile.String(), pubKeyName, m.privateKeyFile, privKeyName)
 		return
 	}
-	m.keyName = pubKeyName
 }
 
 // //////////////////////////////////////////////////////////////////////
@@ -203,8 +200,11 @@
 // For Bazel / bp2build
 
 type bazelApexKeyAttributes struct {
-	Public_key  bazel.LabelAttribute
-	Private_key bazel.LabelAttribute
+	Public_key      bazel.LabelAttribute
+	Public_key_name bazel.LabelAttribute
+
+	Private_key      bazel.LabelAttribute
+	Private_key_name bazel.LabelAttribute
 }
 
 // ConvertWithBp2build performs conversion apexKey for bp2build
@@ -214,18 +214,33 @@
 
 func apexKeyBp2BuildInternal(ctx android.TopDownMutatorContext, module *apexKey) {
 	var privateKeyLabelAttribute bazel.LabelAttribute
+	var privateKeyNameAttribute bazel.LabelAttribute
 	if module.properties.Private_key != nil {
-		privateKeyLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Private_key))
+		m := String(module.properties.Private_key)
+		if android.SrcIsModule(m) == "" {
+			privateKeyNameAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Private_key))
+		} else {
+			privateKeyLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *module.properties.Private_key))
+		}
 	}
 
 	var publicKeyLabelAttribute bazel.LabelAttribute
+	var publicKeyNameAttribute bazel.LabelAttribute
 	if module.properties.Public_key != nil {
-		publicKeyLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Public_key))
+		m := String(module.properties.Public_key)
+		if android.SrcIsModule(m) == "" {
+			publicKeyNameAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Public_key))
+		} else {
+			publicKeyLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *module.properties.Public_key))
+		}
 	}
 
 	attrs := &bazelApexKeyAttributes{
-		Private_key: privateKeyLabelAttribute,
-		Public_key:  publicKeyLabelAttribute,
+		Private_key:      privateKeyLabelAttribute,
+		Private_key_name: privateKeyNameAttribute,
+
+		Public_key:      publicKeyLabelAttribute,
+		Public_key_name: publicKeyNameAttribute,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/bazel/properties.go b/bazel/properties.go
index c329e41..823cda8 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -73,6 +73,17 @@
 	}
 }
 
+// MakeLabelListFromTargetNames creates a LabelList from unqualified target names
+// This is a utiltity function for bp2build converters of Soong modules that have 1:many generated targets
+func MakeLabelListFromTargetNames(targetNames []string) LabelList {
+	labels := []Label{}
+	for _, name := range targetNames {
+		label := Label{Label: ":" + name}
+		labels = append(labels, label)
+	}
+	return MakeLabelList(labels)
+}
+
 func (ll *LabelList) Equals(other LabelList) bool {
 	if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
 		return false
@@ -1178,6 +1189,11 @@
 	ConfigurableValues configurableStringLists
 }
 
+// IsEmpty returns true if the attribute has no values under any configuration.
+func (sla StringListAttribute) IsEmpty() bool {
+	return len(sla.Value) == 0 && !sla.HasConfigurableValues()
+}
+
 type configurableStringLists map[ConfigurationAxis]stringListSelectValues
 
 func (csl configurableStringLists) Append(other configurableStringLists) {
diff --git a/bp2build/apex_key_conversion_test.go b/bp2build/apex_key_conversion_test.go
index 15bccf7..79b1d89 100644
--- a/bp2build/apex_key_conversion_test.go
+++ b/bp2build/apex_key_conversion_test.go
@@ -27,11 +27,12 @@
 }
 
 func registerApexKeyModuleTypes(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 }
 
-func TestApexKeySimple(t *testing.T) {
+func TestApexKeySimple_KeysAreSrcFiles(t *testing.T) {
 	runApexKeyTestCase(t, Bp2buildTestCase{
-		Description:                "apex key - simple example",
+		Description:                "apex key - keys are src files, use key_name attributes",
 		ModuleTypeUnderTest:        "apex_key",
 		ModuleTypeUnderTestFactory: apex.ApexKeyFactory,
 		Filesystem:                 map[string]string{},
@@ -43,8 +44,29 @@
 }
 `,
 		ExpectedBazelTargets: []string{MakeBazelTargetNoRestrictions("apex_key", "com.android.apogee.key", AttrNameToString{
-			"private_key": `"com.android.apogee.pem"`,
-			"public_key":  `"com.android.apogee.avbpubkey"`,
+			"private_key_name": `"com.android.apogee.pem"`,
+			"public_key_name":  `"com.android.apogee.avbpubkey"`,
+		}),
+		}})
+}
+
+func TestApexKey_KeysAreModules(t *testing.T) {
+	runApexKeyTestCase(t, Bp2buildTestCase{
+		Description:                "apex key - keys are modules, use key attributes",
+		ModuleTypeUnderTest:        "apex_key",
+		ModuleTypeUnderTestFactory: apex.ApexKeyFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: `
+apex_key {
+        name: "com.android.apogee.key",
+        public_key: ":com.android.apogee.avbpubkey",
+        private_key: ":com.android.apogee.pem",
+}
+` + simpleModuleDoNotConvertBp2build("filegroup", "com.android.apogee.avbpubkey") +
+			simpleModuleDoNotConvertBp2build("filegroup", "com.android.apogee.pem"),
+		ExpectedBazelTargets: []string{MakeBazelTargetNoRestrictions("apex_key", "com.android.apogee.key", AttrNameToString{
+			"private_key": `":com.android.apogee.pem"`,
+			"public_key":  `":com.android.apogee.avbpubkey"`,
 		}),
 		}})
 }
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
index 72d3940..8aa2c3e 100644
--- a/bp2build/cc_binary_conversion_test.go
+++ b/bp2build/cc_binary_conversion_test.go
@@ -228,6 +228,38 @@
 	})
 }
 
+func TestCcBinaryLdflagsSplitBySpaceExceptSoongAdded(t *testing.T) {
+	runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+		description: "ldflags are split by spaces except for the ones added by soong (version script and dynamic list)",
+		blueprint: `
+{rule_name} {
+    name: "foo",
+		ldflags: [
+			"--nospace_flag",
+			"-z spaceflag",
+		],
+		version_script: "version_script",
+		dynamic_list: "dynamic.list",
+    include_build_directory: false,
+}
+`,
+		targets: []testBazelTarget{
+			{"cc_binary", "foo", AttrNameToString{
+				"additional_linker_inputs": `[
+        "version_script",
+        "dynamic.list",
+    ]`,
+				"linkopts": `[
+        "--nospace_flag",
+        "-z",
+        "spaceflag",
+        "-Wl,--version-script,$(location version_script)",
+        "-Wl,--dynamic-list,$(location dynamic.list)",
+    ]`,
+			}}},
+	})
+}
+
 func TestCcBinarySplitSrcsByLang(t *testing.T) {
 	runCcHostBinaryTestCase(t, ccBinaryBp2buildTestCase{
 		description: "split srcs by lang",
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 728a4dc..0d6d5b8 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -961,6 +961,46 @@
 	)
 }
 
+func TestCcLibraryLdflagsSplitBySpaceExceptSoongAdded(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "ldflags are split by spaces except for the ones added by soong (version script and dynamic list)",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Filesystem: map[string]string{
+			"version_script": "",
+			"dynamic.list":   "",
+		},
+		Blueprint: `
+cc_library {
+    name: "foo",
+    ldflags: [
+        "--nospace_flag",
+        "-z spaceflag",
+    ],
+    version_script: "version_script",
+    dynamic_list: "dynamic.list",
+    include_build_directory: false,
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"additional_linker_inputs": `[
+        "version_script",
+        "dynamic.list",
+    ]`,
+				"linkopts": `[
+        "--nospace_flag",
+        "-z",
+        "spaceflag",
+        "-Wl,--version-script,$(location version_script)",
+        "-Wl,--dynamic-list,$(location dynamic.list)",
+    ]`,
+			}),
+		},
+	})
+}
+
 func TestCcLibrarySharedLibs(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
 		Description:                "cc_library shared_libs",
@@ -2703,6 +2743,205 @@
 	)
 }
 
+func TestCcApiContributionsWithHdrs(t *testing.T) {
+	bp := `
+	cc_library {
+		name: "libfoo",
+		stubs: { symbol_file: "libfoo.map.txt", versions: ["28", "29", "current"] },
+		llndk: { symbol_file: "libfoo.map.txt", override_export_include_dirs: ["dir2"]},
+		export_include_dirs: ["dir1"],
+	}
+	`
+	expectedBazelTargets := []string{
+		MakeBazelTarget(
+			"cc_api_library_headers",
+			"libfoo.systemapi.headers",
+			AttrNameToString{
+				"export_includes": `["dir1"]`,
+			}),
+		MakeBazelTarget(
+			"cc_api_library_headers",
+			"libfoo.vendorapi.headers",
+			AttrNameToString{
+				"export_includes": `["dir2"]`,
+			}),
+		MakeBazelTarget(
+			"cc_api_contribution",
+			"libfoo.contribution",
+			AttrNameToString{
+				"api":          `"libfoo.map.txt"`,
+				"library_name": `"libfoo"`,
+				"api_surfaces": `[
+        "systemapi",
+        "vendorapi",
+    ]`,
+				"hdrs": `[
+        ":libfoo.systemapi.headers",
+        ":libfoo.vendorapi.headers",
+    ]`,
+			}),
+	}
+	RunApiBp2BuildTestCase(t, cc.RegisterLibraryBuildComponents, Bp2buildTestCase{
+		Blueprint:            bp,
+		Description:          "cc API contributions to systemapi and vendorapi",
+		ExpectedBazelTargets: expectedBazelTargets,
+	})
+}
+
+func TestCcApiSurfaceCombinations(t *testing.T) {
+	testCases := []struct {
+		bp                  string
+		expectedApi         string
+		expectedApiSurfaces string
+		description         string
+	}{
+		{
+			bp: `
+			cc_library {
+				name: "a",
+				stubs: {symbol_file: "a.map.txt"},
+			}`,
+			expectedApi:         `"a.map.txt"`,
+			expectedApiSurfaces: `["systemapi"]`,
+			description:         "Library that contributes to systemapi",
+		},
+		{
+			bp: `
+			cc_library {
+				name: "a",
+				llndk: {symbol_file: "a.map.txt"},
+			}`,
+			expectedApi:         `"a.map.txt"`,
+			expectedApiSurfaces: `["vendorapi"]`,
+			description:         "Library that contributes to vendorapi",
+		},
+		{
+			bp: `
+			cc_library {
+				name: "a",
+				llndk: {symbol_file: "a.map.txt"},
+				stubs: {symbol_file: "a.map.txt"},
+			}`,
+			expectedApi: `"a.map.txt"`,
+			expectedApiSurfaces: `[
+        "systemapi",
+        "vendorapi",
+    ]`,
+			description: "Library that contributes to systemapi and vendorapi",
+		},
+	}
+	for _, testCase := range testCases {
+		expectedBazelTargets := []string{
+			MakeBazelTarget(
+				"cc_api_contribution",
+				"a.contribution",
+				AttrNameToString{
+					"library_name": `"a"`,
+					"hdrs":         `[]`,
+					"api":          testCase.expectedApi,
+					"api_surfaces": testCase.expectedApiSurfaces,
+				},
+			),
+		}
+		RunApiBp2BuildTestCase(t, cc.RegisterLibraryBuildComponents, Bp2buildTestCase{
+			Blueprint:            testCase.bp,
+			Description:          testCase.description,
+			ExpectedBazelTargets: expectedBazelTargets,
+		})
+	}
+}
+
+// llndk struct property in Soong provides users with several options to configure the exported include dirs
+// Test the generated bazel targets for the different configurations
+func TestCcVendorApiHeaders(t *testing.T) {
+	testCases := []struct {
+		bp                     string
+		expectedIncludes       string
+		expectedSystemIncludes string
+		description            string
+	}{
+		{
+			bp: `
+			cc_library {
+				name: "a",
+				export_include_dirs: ["include"],
+				export_system_include_dirs: ["base_system_include"],
+				llndk: {
+					symbol_file: "a.map.txt",
+					export_headers_as_system: true,
+				},
+			}
+			`,
+			expectedIncludes: "",
+			expectedSystemIncludes: `[
+        "base_system_include",
+        "include",
+    ]`,
+			description: "Headers are exported as system to API surface",
+		},
+		{
+			bp: `
+			cc_library {
+				name: "a",
+				export_include_dirs: ["include"],
+				export_system_include_dirs: ["base_system_include"],
+				llndk: {
+					symbol_file: "a.map.txt",
+					override_export_include_dirs: ["llndk_include"],
+				},
+			}
+			`,
+			expectedIncludes:       `["llndk_include"]`,
+			expectedSystemIncludes: `["base_system_include"]`,
+			description:            "Non-system Headers are ovverriden before export to API surface",
+		},
+		{
+			bp: `
+			cc_library {
+				name: "a",
+				export_include_dirs: ["include"],
+				export_system_include_dirs: ["base_system_include"],
+				llndk: {
+					symbol_file: "a.map.txt",
+					override_export_include_dirs: ["llndk_include"],
+					export_headers_as_system: true,
+				},
+			}
+			`,
+			expectedIncludes: "", // includes are set to nil
+			expectedSystemIncludes: `[
+        "base_system_include",
+        "llndk_include",
+    ]`,
+			description: "System Headers are extended before export to API surface",
+		},
+	}
+	for _, testCase := range testCases {
+		attrs := AttrNameToString{}
+		if testCase.expectedIncludes != "" {
+			attrs["export_includes"] = testCase.expectedIncludes
+		}
+		if testCase.expectedSystemIncludes != "" {
+			attrs["export_system_includes"] = testCase.expectedSystemIncludes
+		}
+
+		expectedBazelTargets := []string{
+			MakeBazelTarget("cc_api_library_headers", "a.vendorapi.headers", attrs),
+			// Create a target for cc_api_contribution target
+			MakeBazelTarget("cc_api_contribution", "a.contribution", AttrNameToString{
+				"api":          `"a.map.txt"`,
+				"api_surfaces": `["vendorapi"]`,
+				"hdrs":         `[":a.vendorapi.headers"]`,
+				"library_name": `"a"`,
+			}),
+		}
+		RunApiBp2BuildTestCase(t, cc.RegisterLibraryBuildComponents, Bp2buildTestCase{
+			Blueprint:            testCase.bp,
+			ExpectedBazelTargets: expectedBazelTargets,
+		})
+	}
+}
+
 func TestCcLibraryStubsAcrossConfigsDuplicatesRemoved(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
 		Description:                "stub target generation of the same lib across configs should not result in duplicates",
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index 5fa7cac..7d9db6f 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -123,6 +123,69 @@
 	})
 }
 
+func TestCcApiHeaders(t *testing.T) {
+	fs := map[string]string{
+		"bar/Android.bp": `cc_library_headers { name: "bar_headers", }`,
+	}
+	bp := `
+	cc_library_headers {
+		name: "foo_headers",
+		export_include_dirs: ["dir1", "dir2"],
+		export_header_lib_headers: ["bar_headers"],
+
+		arch: {
+			arm: {
+				export_include_dirs: ["dir_arm"],
+			},
+			x86: {
+				export_include_dirs: ["dir_x86"],
+			},
+		},
+
+		target: {
+			android: {
+				export_include_dirs: ["dir1", "dir_android"],
+			},
+			windows: {
+				export_include_dirs: ["dir_windows"],
+			},
+		}
+	}
+	`
+	expectedBazelTargets := []string{
+		MakeBazelTarget("cc_api_library_headers", "foo_headers.contribution.arm", AttrNameToString{
+			"export_includes": `["dir_arm"]`,
+			"arch":            `"arm"`,
+		}),
+		MakeBazelTarget("cc_api_library_headers", "foo_headers.contribution.x86", AttrNameToString{
+			"export_includes": `["dir_x86"]`,
+			"arch":            `"x86"`,
+		}),
+		MakeBazelTarget("cc_api_library_headers", "foo_headers.contribution.androidos", AttrNameToString{
+			"export_includes": `["dir_android"]`, // common includes are deduped
+		}),
+		// Windows headers are not exported
+		MakeBazelTarget("cc_api_library_headers", "foo_headers.contribution", AttrNameToString{
+			"export_includes": `[
+        "dir1",
+        "dir2",
+    ]`,
+			"deps": `[
+        "//bar:bar_headers.contribution",
+        ":foo_headers.contribution.arm",
+        ":foo_headers.contribution.x86",
+        ":foo_headers.contribution.androidos",
+    ]`,
+		}),
+	}
+	RunApiBp2BuildTestCase(t, cc.RegisterLibraryHeadersBuildComponents, Bp2buildTestCase{
+		Blueprint:            bp,
+		Description:          "Header library contributions to API surfaces",
+		ExpectedBazelTargets: expectedBazelTargets,
+		Filesystem:           fs,
+	})
+}
+
 func TestCcLibraryHeadersOsSpecificHeader(t *testing.T) {
 	runCcLibraryHeadersTestCase(t, Bp2buildTestCase{
 		Description:                "cc_library_headers test with os-specific header_libs props",
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index 6253de6..b86f607 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -367,6 +367,42 @@
 	})
 }
 
+func TestCcLibraryLdflagsSplitBySpaceSoongAdded(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description: "ldflags are split by spaces except for the ones added by soong (version script and dynamic list)",
+		Filesystem: map[string]string{
+			"version_script": "",
+			"dynamic.list":   "",
+		},
+		Blueprint: `
+cc_library_shared {
+    name: "foo",
+    ldflags: [
+        "--nospace_flag",
+        "-z spaceflag",
+    ],
+    version_script: "version_script",
+    dynamic_list: "dynamic.list",
+    include_build_directory: false,
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"additional_linker_inputs": `[
+        "version_script",
+        "dynamic.list",
+    ]`,
+				"linkopts": `[
+        "--nospace_flag",
+        "-z",
+        "spaceflag",
+        "-Wl,--version-script,$(location version_script)",
+        "-Wl,--dynamic-list,$(location dynamic.list)",
+    ]`,
+			}),
+		},
+	})
+}
+
 func TestCcLibrarySharedNoCrtTrue(t *testing.T) {
 	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
 		Description: "cc_library_shared - nocrt: true emits attribute",
diff --git a/bp2build/symlink_forest.go b/bp2build/symlink_forest.go
index 023ec96..7c39a11 100644
--- a/bp2build/symlink_forest.go
+++ b/bp2build/symlink_forest.go
@@ -285,6 +285,17 @@
 			// Neither is a directory. Merge them.
 			srcBuildFile := shared.JoinPath(topdir, srcChild)
 			generatedBuildFile := shared.JoinPath(topdir, buildFilesChild)
+			// Add the src and generated build files as dependencies so that bp2build
+			// is rerun when they change. Currently, this is only really necessary
+			// for srcBuildFile, because if we regenerate the generated build files
+			// we will always rerun the symlink forest generation as well. If that
+			// is later split up into separate, fully dependency-tracing steps, then
+			// we'll need srcBuildFile as well. Adding srcBuildFile here today
+			// technically makes it a dependency of bp2build_workspace_marker, which
+			// also implicitly outputs that file, but since bp2build_workspace_marker
+			// will always have a newer timestamp than the generatedBuildFile it
+			// shouldn't be a problem.
+			*acc = append(*acc, srcBuildFile, generatedBuildFile)
 			err = mergeBuildFiles(shared.JoinPath(topdir, forestChild), srcBuildFile, generatedBuildFile, cfg.IsEnvTrue("BP2BUILD_VERBOSE"))
 			if err != nil {
 				fmt.Fprintf(os.Stderr, "Error merging %s and %s: %s",
diff --git a/bpf/bpf.go b/bpf/bpf.go
index dbbce50..60a410d 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -22,6 +22,7 @@
 
 	"android/soong/android"
 	"android/soong/bazel"
+	"android/soong/bazel/cquery"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -239,6 +240,35 @@
 	}
 }
 
+var _ android.MixedBuildBuildable = (*bpf)(nil)
+
+func (bpf *bpf) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
+	return true
+}
+
+func (bpf *bpf) QueueBazelCall(ctx android.BaseModuleContext) {
+	bazelCtx := ctx.Config().BazelContext
+	bazelCtx.QueueBazelRequest(
+		bpf.GetBazelLabel(ctx, bpf),
+		cquery.GetOutputFiles,
+		android.GetConfigKey(ctx))
+}
+
+func (bpf *bpf) ProcessBazelQueryResponse(ctx android.ModuleContext) {
+	bazelCtx := ctx.Config().BazelContext
+	objPaths, err := bazelCtx.GetOutputFiles(bpf.GetBazelLabel(ctx, bpf), android.GetConfigKey(ctx))
+	if err != nil {
+		ctx.ModuleErrorf(err.Error())
+		return
+	}
+
+	bazelOuts := android.Paths{}
+	for _, p := range objPaths {
+		bazelOuts = append(bazelOuts, android.PathForBazelOut(ctx, p))
+	}
+	bpf.objs = bazelOuts
+}
+
 // Implements OutputFileFileProducer interface so that the obj output can be used in the data property
 // of other modules.
 func (bpf *bpf) OutputFiles(tag string) (android.Paths, error) {
diff --git a/bpf/bpf_test.go b/bpf/bpf_test.go
index 6e39096..a2010ff 100644
--- a/bpf/bpf_test.go
+++ b/bpf/bpf_test.go
@@ -71,3 +71,26 @@
 		`\QAndroid.bp:2:3: module "bpf_invalid_name.o" variant "android_common": invalid character '_' in source name\E`)).
 		RunTestWithBp(t, bp)
 }
+
+func TestBpfWithBazel(t *testing.T) {
+	bp := `
+		bpf {
+			name: "bpf.o",
+			srcs: ["bpf.c"],
+			bazel_module: { label: "//bpf" },
+		}
+	`
+
+	result := android.GroupFixturePreparers(
+		prepareForBpfTest, android.FixtureModifyConfig(func(config android.Config) {
+			config.BazelContext = android.MockBazelContext{
+				OutputBaseDir: "outputbase",
+				LabelToOutputFiles: map[string][]string{
+					"//bpf": []string{"bpf.o"}}}
+		})).RunTestWithBp(t, bp)
+
+	output := result.Module("bpf.o", "android_common").(*bpf)
+
+	expectedOutputFiles := []string{"outputbase/execroot/__main__/bpf.o"}
+	android.AssertDeepEquals(t, "output files", expectedOutputFiles, output.objs.Strings())
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 0861a51..d6d052f 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -182,7 +182,7 @@
 	attrs := staticOrSharedAttributes{}
 
 	setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
-		attrs.Copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, true, filterOutStdFlag))
+		attrs.Copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag))
 		attrs.Srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
 		attrs.System_dynamic_deps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, props.System_shared_libs))
 
@@ -376,7 +376,7 @@
 	return false
 }
 
-func parseCommandLineFlags(soongFlags []string, noCoptsTokenization bool, filterOut ...filterOutFn) []string {
+func parseCommandLineFlags(soongFlags []string, filterOut ...filterOutFn) []string {
 	var result []string
 	for _, flag := range soongFlags {
 		skipFlag := false
@@ -391,15 +391,7 @@
 		// Soong's cflags can contain spaces, like `-include header.h`. For
 		// Bazel's copts, split them up to be compatible with the
 		// no_copts_tokenization feature.
-		if noCoptsTokenization {
-			result = append(result, strings.Split(flag, " ")...)
-		} else {
-			// Soong's Version Script and Dynamic List Properties are added as flags
-			// to Bazel's linkopts using "($location label)" syntax.
-			// Splitting on spaces would separate this into two different flags
-			// "($ location" and "label)"
-			result = append(result, flag)
-		}
+		result = append(result, strings.Split(flag, " ")...)
 	}
 	return result
 }
@@ -433,10 +425,10 @@
 	// overridden. In Bazel we always allow overriding, via flags; however, this can cause
 	// incompatibilities, so we remove "-std=" flags from Cflag properties while leaving it in other
 	// cases.
-	ca.copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, true, filterOutStdFlag, filterOutClangUnknownCflags))
-	ca.asFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Asflags, true, nil))
-	ca.conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Conlyflags, true, filterOutClangUnknownCflags))
-	ca.cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Cppflags, true, filterOutClangUnknownCflags))
+	ca.copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag, filterOutClangUnknownCflags))
+	ca.asFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Asflags, nil))
+	ca.conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Conlyflags, filterOutClangUnknownCflags))
+	ca.cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Cppflags, filterOutClangUnknownCflags))
 	ca.rtti.SetSelectValue(axis, config, props.Rtti)
 }
 
@@ -1042,6 +1034,11 @@
 			axisFeatures = append(axisFeatures, "-static_flag")
 		}
 	}
+
+	// This must happen before the addition of flags for Version Script and
+	// Dynamic List, as these flags must be split on spaces and those must not
+	linkerFlags = parseCommandLineFlags(linkerFlags, filterOutClangUnknownCflags)
+
 	additionalLinkerInputs := bazel.LabelList{}
 	if props.Version_script != nil {
 		label := android.BazelLabelForModuleSrcSingle(ctx, *props.Version_script)
@@ -1056,7 +1053,7 @@
 	}
 
 	la.additionalLinkerInputs.SetSelectValue(axis, config, additionalLinkerInputs)
-	la.linkopts.SetSelectValue(axis, config, parseCommandLineFlags(linkerFlags, false, filterOutClangUnknownCflags))
+	la.linkopts.SetSelectValue(axis, config, linkerFlags)
 	la.useLibcrt.SetSelectValue(axis, config, props.libCrt())
 
 	// it's very unlikely for nocrt to be arch variant, so bp2build doesn't support it.
diff --git a/cc/cc.go b/cc/cc.go
index faa8571..d0362fc 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -3753,7 +3753,18 @@
 var _ android.ApiProvider = (*Module)(nil)
 
 func (c *Module) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
+	if c.IsPrebuilt() {
+		return
+	}
 	switch c.typ() {
+	case fullLibrary:
+		apiContributionBp2Build(ctx, c)
+	case sharedLibrary:
+		apiContributionBp2Build(ctx, c)
+	case headerLibrary:
+		// Aggressively generate api targets for all header modules
+		// This is necessary since the header module does not know if it is a dep of API surface stub library
+		apiLibraryHeadersBp2Build(ctx, c)
 	case ndkLibrary:
 		ndkLibraryBp2build(ctx, c)
 	}
diff --git a/cc/library.go b/cc/library.go
index 8e262eb..77f686e 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -30,6 +30,7 @@
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/pathtools"
+	"github.com/google/blueprint/proptools"
 )
 
 // LibraryProperties is a collection of properties shared by cc library rules/cc.
@@ -470,6 +471,147 @@
 	}
 }
 
+func apiContributionBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+	apiSurfaces := make([]string, 0)
+	apiHeaders := make([]string, 0)
+	// systemapi (non-null `stubs` property)
+	if module.HasStubsVariants() {
+		apiSurfaces = append(apiSurfaces, android.SystemApi.String())
+		apiIncludes := getSystemApiIncludes(ctx, module)
+		if !apiIncludes.isEmpty() {
+			createApiHeaderTarget(ctx, apiIncludes)
+			apiHeaders = append(apiHeaders, apiIncludes.name)
+		}
+	}
+	// vendorapi (non-null `llndk` property)
+	if module.HasLlndkStubs() {
+		apiSurfaces = append(apiSurfaces, android.VendorApi.String())
+		apiIncludes := getVendorApiIncludes(ctx, module)
+		if !apiIncludes.isEmpty() {
+			createApiHeaderTarget(ctx, apiIncludes)
+			apiHeaders = append(apiHeaders, apiIncludes.name)
+		}
+	}
+	// create a target only if this module contributes to an api surface
+	// TODO: Currently this does not distinguish systemapi-only headers and vendrorapi-only headers
+	// TODO: Update so that systemapi-only headers do not get exported to vendorapi (and vice-versa)
+	if len(apiSurfaces) > 0 {
+		props := bazel.BazelTargetModuleProperties{
+			Rule_class:        "cc_api_contribution",
+			Bzl_load_location: "//build/bazel/rules/apis:cc_api_contribution.bzl",
+		}
+		attrs := &bazelCcApiContributionAttributes{
+			Library_name: module.Name(),
+			Api_surfaces: bazel.MakeStringListAttribute(apiSurfaces),
+			Api:          apiLabelAttribute(ctx, module),
+			Hdrs: bazel.MakeLabelListAttribute(
+				bazel.MakeLabelListFromTargetNames(apiHeaders),
+			),
+		}
+		ctx.CreateBazelTargetModule(
+			props,
+			android.CommonAttributes{
+				Name:     android.ApiContributionTargetName(module.Name()),
+				SkipData: proptools.BoolPtr(true),
+			},
+			attrs,
+		)
+	}
+}
+
+// Native apis are versioned in a single .map.txt for all api surfaces
+// Pick any one of the .map.txt files
+func apiLabelAttribute(ctx android.TopDownMutatorContext, module *Module) bazel.LabelAttribute {
+	var apiFile *string
+	linker := module.linker.(*libraryDecorator)
+	if llndkApi := linker.Properties.Llndk.Symbol_file; llndkApi != nil {
+		apiFile = llndkApi
+	} else if systemApi := linker.Properties.Stubs.Symbol_file; systemApi != nil {
+		apiFile = systemApi
+	} else {
+		ctx.ModuleErrorf("API surface library does not have any API file")
+	}
+	apiLabel := android.BazelLabelForModuleSrcSingle(ctx, proptools.String(apiFile)).Label
+	return *bazel.MakeLabelAttribute(apiLabel)
+}
+
+// wrapper struct to flatten the arch and os specific export_include_dirs
+// flattening is necessary since we want to export apis of all arches even when we build for x86 (e.g.)
+type bazelCcApiLibraryHeadersAttributes struct {
+	bazelCcLibraryHeadersAttributes
+
+	Arch *string
+}
+
+func (a *bazelCcApiLibraryHeadersAttributes) isEmpty() bool {
+	return a.Export_includes.IsEmpty() &&
+		a.Export_system_includes.IsEmpty() &&
+		a.Deps.IsEmpty()
+}
+
+type apiIncludes struct {
+	name  string // name of the Bazel target in the generated bp2build workspace
+	attrs bazelCcApiLibraryHeadersAttributes
+}
+
+func (includes *apiIncludes) isEmpty() bool {
+	return includes.attrs.isEmpty()
+}
+
+func (includes *apiIncludes) addDep(name string) {
+	l := bazel.Label{Label: ":" + name}
+	ll := bazel.MakeLabelList([]bazel.Label{l})
+	lla := bazel.MakeLabelListAttribute(ll)
+	includes.attrs.Deps.Append(lla)
+}
+
+func getSystemApiIncludes(ctx android.TopDownMutatorContext, c *Module) apiIncludes {
+	flagProps := c.library.(*libraryDecorator).flagExporter.Properties
+	linkProps := c.library.(*libraryDecorator).baseLinker.Properties
+	includes := android.FirstUniqueStrings(flagProps.Export_include_dirs)
+	systemIncludes := android.FirstUniqueStrings(flagProps.Export_system_include_dirs)
+	headerLibs := android.FirstUniqueStrings(linkProps.Export_header_lib_headers)
+	attrs := bazelCcLibraryHeadersAttributes{
+		Export_includes:        bazel.MakeStringListAttribute(includes),
+		Export_system_includes: bazel.MakeStringListAttribute(systemIncludes),
+		Deps:                   bazel.MakeLabelListAttribute(apiHeaderLabels(ctx, headerLibs)),
+	}
+
+	return apiIncludes{
+		name: c.Name() + ".systemapi.headers",
+		attrs: bazelCcApiLibraryHeadersAttributes{
+			bazelCcLibraryHeadersAttributes: attrs,
+		},
+	}
+}
+
+func getVendorApiIncludes(ctx android.TopDownMutatorContext, c *Module) apiIncludes {
+	baseProps := c.library.(*libraryDecorator).flagExporter.Properties
+	llndkProps := c.library.(*libraryDecorator).Properties.Llndk
+	includes := baseProps.Export_include_dirs
+	systemIncludes := baseProps.Export_system_include_dirs
+	// LLNDK can override the base includes
+	if llndkIncludes := llndkProps.Override_export_include_dirs; llndkIncludes != nil {
+		includes = llndkIncludes
+	}
+	if proptools.Bool(llndkProps.Export_headers_as_system) {
+		systemIncludes = append(systemIncludes, includes...)
+		includes = nil
+	}
+
+	attrs := bazelCcLibraryHeadersAttributes{
+		Export_includes:        bazel.MakeStringListAttribute(includes),
+		Export_system_includes: bazel.MakeStringListAttribute(systemIncludes),
+		Deps:                   bazel.MakeLabelListAttribute(apiHeaderLabels(ctx, llndkProps.Export_llndk_headers)),
+	}
+	return apiIncludes{
+		name: c.Name() + ".vendorapi.headers",
+		attrs: bazelCcApiLibraryHeadersAttributes{
+			bazelCcLibraryHeadersAttributes: attrs,
+		},
+	}
+}
+
 // cc_library creates both static and/or shared libraries for a device and/or
 // host. By default, a cc_library has a single variant that targets the device.
 // Specifying `host_supported: true` also creates a library that targets the
diff --git a/cc/library_headers.go b/cc/library_headers.go
index a683f58..1c4f354 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -15,6 +15,8 @@
 package cc
 
 import (
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/bazel"
 	"android/soong/bazel/cquery"
@@ -145,3 +147,118 @@
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
 }
+
+// Append .contribution suffix to input labels
+func apiBazelTargets(ll bazel.LabelList) bazel.LabelList {
+	labels := make([]bazel.Label, 0)
+	for _, l := range ll.Includes {
+		labels = append(labels, bazel.Label{
+			Label: android.ApiContributionTargetName(l.Label),
+		})
+	}
+	return bazel.MakeLabelList(labels)
+}
+
+func apiLibraryHeadersBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+	// cc_api_library_headers have a 1:1 mapping to arch/no-arch
+	// For API export, create a top-level arch-agnostic target and list the arch-specific targets as its deps
+
+	// arch-agnostic includes
+	apiIncludes := getSystemApiIncludes(ctx, module)
+	// arch and os specific includes
+	archApiIncludes, androidOsIncludes := archOsSpecificApiIncludes(ctx, module)
+	for _, arch := range allArches { // sorted iteration
+		archApiInclude := archApiIncludes[arch]
+		if !archApiInclude.isEmpty() {
+			createApiHeaderTarget(ctx, archApiInclude)
+			apiIncludes.addDep(archApiInclude.name)
+		}
+	}
+	// os==android includes
+	if !androidOsIncludes.isEmpty() {
+		createApiHeaderTarget(ctx, androidOsIncludes)
+		apiIncludes.addDep(androidOsIncludes.name)
+	}
+
+	if !apiIncludes.isEmpty() {
+		// override the name from <mod>.systemapi.headers --> <mod>.contribution
+		apiIncludes.name = android.ApiContributionTargetName(module.Name())
+		createApiHeaderTarget(ctx, apiIncludes)
+	}
+}
+
+func createApiHeaderTarget(ctx android.TopDownMutatorContext, includes apiIncludes) {
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "cc_api_library_headers",
+		Bzl_load_location: "//build/bazel/rules/apis:cc_api_contribution.bzl",
+	}
+	ctx.CreateBazelTargetModule(
+		props,
+		android.CommonAttributes{
+			Name:     includes.name,
+			SkipData: proptools.BoolPtr(true),
+		},
+		&includes.attrs,
+	)
+}
+
+var (
+	allArches = []string{"arm", "arm64", "x86", "x86_64"}
+)
+
+type archApiIncludes map[string]apiIncludes
+
+func archOsSpecificApiIncludes(ctx android.TopDownMutatorContext, module *Module) (archApiIncludes, apiIncludes) {
+	baseProps := bp2BuildParseBaseProps(ctx, module)
+	i := bp2BuildParseExportedIncludes(ctx, module, &baseProps.includes)
+	archRet := archApiIncludes{}
+	for _, arch := range allArches {
+		includes := i.Includes.SelectValue(
+			bazel.ArchConfigurationAxis,
+			arch)
+		systemIncludes := i.SystemIncludes.SelectValue(
+			bazel.ArchConfigurationAxis,
+			arch)
+		deps := baseProps.deps.SelectValue(
+			bazel.ArchConfigurationAxis,
+			arch)
+		attrs := bazelCcLibraryHeadersAttributes{
+			Export_includes:        bazel.MakeStringListAttribute(includes),
+			Export_system_includes: bazel.MakeStringListAttribute(systemIncludes),
+		}
+		apiDeps := apiBazelTargets(deps)
+		if !apiDeps.IsEmpty() {
+			attrs.Deps = bazel.MakeLabelListAttribute(apiDeps)
+		}
+		apiIncludes := apiIncludes{
+			name: android.ApiContributionTargetName(module.Name()) + "." + arch,
+			attrs: bazelCcApiLibraryHeadersAttributes{
+				bazelCcLibraryHeadersAttributes: attrs,
+				Arch:                            proptools.StringPtr(arch),
+			},
+		}
+		archRet[arch] = apiIncludes
+	}
+
+	// apiIncludes for os == Android
+	androidOsDeps := baseProps.deps.SelectValue(bazel.OsConfigurationAxis, bazel.OsAndroid)
+	androidOsAttrs := bazelCcLibraryHeadersAttributes{
+		Export_includes: bazel.MakeStringListAttribute(
+			i.Includes.SelectValue(bazel.OsConfigurationAxis, bazel.OsAndroid),
+		),
+		Export_system_includes: bazel.MakeStringListAttribute(
+			i.SystemIncludes.SelectValue(bazel.OsConfigurationAxis, bazel.OsAndroid),
+		),
+	}
+	androidOsApiDeps := apiBazelTargets(androidOsDeps)
+	if !androidOsApiDeps.IsEmpty() {
+		androidOsAttrs.Deps = bazel.MakeLabelListAttribute(androidOsApiDeps)
+	}
+	osRet := apiIncludes{
+		name: android.ApiContributionTargetName(module.Name()) + ".androidos",
+		attrs: bazelCcApiLibraryHeadersAttributes{
+			bazelCcLibraryHeadersAttributes: androidOsAttrs,
+		},
+	}
+	return archRet, osRet
+}
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 06ded3f..49a919e 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -581,12 +581,12 @@
 }
 
 // Names of the cc_api_header targets in the bp2build workspace
-func (s *stubDecorator) apiHeaderLabels(ctx android.TopDownMutatorContext) bazel.LabelList {
+func apiHeaderLabels(ctx android.TopDownMutatorContext, hdrLibs []string) bazel.LabelList {
 	addSuffix := func(ctx android.BazelConversionPathContext, module blueprint.Module) string {
 		label := android.BazelModuleLabel(ctx, module)
 		return android.ApiContributionTargetName(label)
 	}
-	return android.BazelLabelForModuleDepsWithFn(ctx, s.properties.Export_header_libs, addSuffix)
+	return android.BazelLabelForModuleDepsWithFn(ctx, hdrLibs, addSuffix)
 }
 
 func ndkLibraryBp2build(ctx android.TopDownMutatorContext, m *Module) {
@@ -604,7 +604,7 @@
 		apiLabel := android.BazelLabelForModuleSrcSingle(ctx, proptools.String(symbolFile)).Label
 		attrs.Api = *bazel.MakeLabelAttribute(apiLabel)
 	}
-	apiHeaders := stubLibrary.apiHeaderLabels(ctx)
+	apiHeaders := apiHeaderLabels(ctx, stubLibrary.properties.Export_header_libs)
 	attrs.Hdrs = bazel.MakeLabelListAttribute(apiHeaders)
 	apiContributionTargetName := android.ApiContributionTargetName(ctx.ModuleName())
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: apiContributionTargetName}, attrs)
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 0b47f0e..66dfef5 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -564,6 +564,11 @@
 		s.Scudo = nil
 	}
 
+	if Bool(s.Undefined) || Bool(s.All_undefined) || len(s.Misc_undefined) > 0 {
+		// TODO(b/251249010): re-enable Hwaddress with UBSan once fixed.
+		s.Hwaddress = nil
+	}
+
 	if Bool(s.Hwaddress) {
 		s.Address = nil
 		s.Thread = nil
diff --git a/cc/strip.go b/cc/strip.go
index c60e135..5c32d8b 100644
--- a/cc/strip.go
+++ b/cc/strip.go
@@ -56,7 +56,9 @@
 	forceEnable := Bool(stripper.StripProperties.Strip.All) ||
 		Bool(stripper.StripProperties.Strip.Keep_symbols) ||
 		Bool(stripper.StripProperties.Strip.Keep_symbols_and_debug_frame)
-	return !forceDisable && (forceEnable || defaultEnable)
+	// create_minidebuginfo doesn't work for riscv64 yet, disable stripping for now
+	riscv64 := actx.Arch().ArchType == android.Riscv64
+	return !forceDisable && (forceEnable || defaultEnable) && !riscv64
 }
 
 // Keep this consistent with //build/bazel/rules/stripped_shared_library.bzl.
diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py
index 9bf07f2..94c8567 100644
--- a/cc/symbolfile/__init__.py
+++ b/cc/symbolfile/__init__.py
@@ -41,6 +41,7 @@
 ALL_ARCHITECTURES = (
     Arch('arm'),
     Arch('arm64'),
+    Arch('riscv64'),
     Arch('x86'),
     Arch('x86_64'),
 )
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index eb248bb..5e5769b 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -151,6 +151,8 @@
 	// If there's a Java fuzzer with JNI, a different version of Jazzer would
 	// need to be added to the fuzzer package than one without JNI
 	IsJni *bool `json:"is_jni,omitempty"`
+	// List of modules for monitoring coverage drops in directories (e.g. "libicu")
+	Target_modules []string `json:"target_modules,omitempty"`
 }
 
 type FuzzFrameworks struct {
diff --git a/java/base.go b/java/base.go
index bcb7226..ab5a7d9 100644
--- a/java/base.go
+++ b/java/base.go
@@ -447,9 +447,11 @@
 	// installed file for hostdex copy
 	hostdexInstallFile android.InstallPath
 
-	// list of .java files and srcjars that was passed to javac
-	compiledJavaSrcs android.Paths
-	compiledSrcJars  android.Paths
+	// list of unique .java and .kt source files
+	uniqueSrcFiles android.Paths
+
+	// list of srcjars that was passed to javac
+	compiledSrcJars android.Paths
 
 	// manifest file to use instead of properties.Manifest
 	overrideManifest android.OptionalPath
@@ -1078,15 +1080,26 @@
 
 	jarName := ctx.ModuleName() + ".jar"
 
-	javaSrcFiles := srcFiles.FilterByExt(".java")
-	var uniqueSrcFiles android.Paths
+	var uniqueJavaFiles android.Paths
 	set := make(map[string]bool)
-	for _, v := range javaSrcFiles {
+	for _, v := range srcFiles.FilterByExt(".java") {
 		if _, found := set[v.String()]; !found {
 			set[v.String()] = true
-			uniqueSrcFiles = append(uniqueSrcFiles, v)
+			uniqueJavaFiles = append(uniqueJavaFiles, v)
 		}
 	}
+	var uniqueKtFiles android.Paths
+	for _, v := range srcFiles.FilterByExt(".kt") {
+		if _, found := set[v.String()]; !found {
+			set[v.String()] = true
+			uniqueKtFiles = append(uniqueKtFiles, v)
+		}
+	}
+
+	var uniqueSrcFiles android.Paths
+	uniqueSrcFiles = append(uniqueSrcFiles, uniqueJavaFiles...)
+	uniqueSrcFiles = append(uniqueSrcFiles, uniqueKtFiles...)
+	j.uniqueSrcFiles = uniqueSrcFiles
 
 	// We don't currently run annotation processors in turbine, which means we can't use turbine
 	// generated header jars when an annotation processor that generates API is enabled.  One
@@ -1094,7 +1107,7 @@
 	//  is used to run all of the annotation processors.
 	disableTurbine := deps.disableTurbine
 
-	// Collect .java files for AIDEGen
+	// Collect .java and .kt files for AIDEGen
 	j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, uniqueSrcFiles.Strings()...)
 
 	var kotlinJars android.Paths
@@ -1132,12 +1145,7 @@
 			flags.kotlincFlags += "$kotlincFlags"
 		}
 
-		var kotlinSrcFiles android.Paths
-		kotlinSrcFiles = append(kotlinSrcFiles, uniqueSrcFiles...)
-		kotlinSrcFiles = append(kotlinSrcFiles, srcFiles.FilterByExt(".kt")...)
-
-		// Collect .kt files for AIDEGen
-		j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, srcFiles.FilterByExt(".kt").Strings()...)
+		// Collect common .kt files for AIDEGen
 		j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, kotlinCommonSrcFiles.Strings()...)
 
 		flags.classpath = append(flags.classpath, deps.kotlinStdlib...)
@@ -1150,7 +1158,7 @@
 			// Use kapt for annotation processing
 			kaptSrcJar := android.PathForModuleOut(ctx, "kapt", "kapt-sources.jar")
 			kaptResJar := android.PathForModuleOut(ctx, "kapt", "kapt-res.jar")
-			kotlinKapt(ctx, kaptSrcJar, kaptResJar, kotlinSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
+			kotlinKapt(ctx, kaptSrcJar, kaptResJar, uniqueSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
 			srcJars = append(srcJars, kaptSrcJar)
 			kotlinJars = append(kotlinJars, kaptResJar)
 			// Disable annotation processing in javac, it's already been handled by kapt
@@ -1160,7 +1168,7 @@
 
 		kotlinJar := android.PathForModuleOut(ctx, "kotlin", jarName)
 		kotlinHeaderJar := android.PathForModuleOut(ctx, "kotlin_headers", jarName)
-		kotlinCompile(ctx, kotlinJar, kotlinHeaderJar, kotlinSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
+		kotlinCompile(ctx, kotlinJar, kotlinHeaderJar, uniqueSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
 		if ctx.Failed() {
 			return
 		}
@@ -1185,8 +1193,6 @@
 
 	jars := append(android.Paths(nil), kotlinJars...)
 
-	// Store the list of .java files that was passed to javac
-	j.compiledJavaSrcs = uniqueSrcFiles
 	j.compiledSrcJars = srcJars
 
 	enableSharding := false
@@ -1201,12 +1207,12 @@
 			// with sharding enabled. See: b/77284273.
 		}
 		headerJarFileWithoutDepsOrJarjar, j.headerJarFile =
-			j.compileJavaHeader(ctx, uniqueSrcFiles, srcJars, deps, flags, jarName, kotlinHeaderJars)
+			j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName, kotlinHeaderJars)
 		if ctx.Failed() {
 			return
 		}
 	}
-	if len(uniqueSrcFiles) > 0 || len(srcJars) > 0 {
+	if len(uniqueJavaFiles) > 0 || len(srcJars) > 0 {
 		hasErrorproneableFiles := false
 		for _, ext := range j.sourceExtensions {
 			if ext != ".proto" && ext != ".aidl" {
@@ -1231,7 +1237,7 @@
 			errorproneFlags := enableErrorproneFlags(flags)
 			errorprone := android.PathForModuleOut(ctx, "errorprone", jarName)
 
-			transformJavaToClasses(ctx, errorprone, -1, uniqueSrcFiles, srcJars, errorproneFlags, nil,
+			transformJavaToClasses(ctx, errorprone, -1, uniqueJavaFiles, srcJars, errorproneFlags, nil,
 				"errorprone", "errorprone")
 
 			extraJarDeps = append(extraJarDeps, errorprone)
@@ -1243,8 +1249,8 @@
 			}
 			shardSize := int(*(j.properties.Javac_shard_size))
 			var shardSrcs []android.Paths
-			if len(uniqueSrcFiles) > 0 {
-				shardSrcs = android.ShardPaths(uniqueSrcFiles, shardSize)
+			if len(uniqueJavaFiles) > 0 {
+				shardSrcs = android.ShardPaths(uniqueJavaFiles, shardSize)
 				for idx, shardSrc := range shardSrcs {
 					classes := j.compileJavaClasses(ctx, jarName, idx, shardSrc,
 						nil, flags, extraJarDeps)
@@ -1257,7 +1263,7 @@
 				jars = append(jars, classes)
 			}
 		} else {
-			classes := j.compileJavaClasses(ctx, jarName, -1, uniqueSrcFiles, srcJars, flags, extraJarDeps)
+			classes := j.compileJavaClasses(ctx, jarName, -1, uniqueJavaFiles, srcJars, flags, extraJarDeps)
 			jars = append(jars, classes)
 		}
 		if ctx.Failed() {
diff --git a/java/config/config.go b/java/config/config.go
index ea44aaa..a84c315 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -132,12 +132,7 @@
 		if override := ctx.Config().Getenv("OVERRIDE_JLINK_VERSION_NUMBER"); override != "" {
 			return override
 		}
-		switch ctx.Config().Getenv("EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN") {
-		case "true":
-			return "17"
-		default:
-			return "11"
-		}
+		return "17"
 	})
 
 	pctx.SourcePathVariable("JavaToolchain", "${JavaHome}/bin")
diff --git a/java/dexpreopt_config_test.go b/java/dexpreopt_config_test.go
index eaa6a8c..b704d09 100644
--- a/java/dexpreopt_config_test.go
+++ b/java/dexpreopt_config_test.go
@@ -15,12 +15,17 @@
 package java
 
 import (
+	"runtime"
 	"testing"
 
 	"android/soong/android"
 )
 
 func TestBootImageConfig(t *testing.T) {
+	if runtime.GOOS != "linux" {
+		t.Skipf("Skipping as boot image config test is only supported on linux not %s", runtime.GOOS)
+	}
+
 	result := android.GroupFixturePreparers(
 		PrepareForBootImageConfigTest,
 	).RunTest(t)
diff --git a/java/java_test.go b/java/java_test.go
index 7f0cea7..d2373e3 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -588,8 +588,8 @@
 	sdklibStubsJar := ctx.ModuleForTests("sdklib.stubs", "android_common").Rule("combineJar").Output
 
 	fooLibrary := fooModule.Module().(*Library)
-	assertDeepEquals(t, "foo java sources incorrect",
-		[]string{"a.java"}, fooLibrary.compiledJavaSrcs.Strings())
+	assertDeepEquals(t, "foo unique sources incorrect",
+		[]string{"a.java"}, fooLibrary.uniqueSrcFiles.Strings())
 
 	assertDeepEquals(t, "foo java source jars incorrect",
 		[]string{".intermediates/stubs-source/android_common/stubs-source-stubs.srcjar"},
diff --git a/java/lint.go b/java/lint.go
index 931820d..fcd6d31 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -473,20 +473,23 @@
 
 	cmd.FlagWithOutput("--write-reference-baseline ", baseline)
 
-	cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)")
+	cmd.Text("; EXITCODE=$?; ")
+
+	// The sources in the sandbox may have been modified by --apply-suggestions, zip them up and
+	// export them out of the sandbox.  Do this before exiting so that the suggestions exit even after
+	// a fatal error.
+	cmd.BuiltTool("soong_zip").
+		FlagWithOutput("-o ", android.PathForModuleOut(ctx, "lint", "suggested-fixes.zip")).
+		FlagWithArg("-C ", cmd.PathForInput(android.PathForSource(ctx))).
+		FlagWithInput("-r ", srcsList)
+
+	cmd.Text("; if [ $EXITCODE != 0 ]; then if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit $EXITCODE; fi")
 
 	rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
 
 	// The HTML output contains a date, remove it to make the output deterministic.
 	rule.Command().Text(`sed -i.tmp -e 's|Check performed at .*\(</nav>\)|\1|'`).Output(html)
 
-	// The sources in the sandbox may have been modified by --apply-suggestions, zip them up and
-	// export them out of the sandbox.
-	rule.Command().BuiltTool("soong_zip").
-		FlagWithOutput("-o ", android.PathForModuleOut(ctx, "lint", "suggested-fixes.zip")).
-		FlagWithArg("-C ", cmd.PathForInput(android.PathForSource(ctx))).
-		FlagWithInput("-r ", srcsList)
-
 	rule.Build("lint", "lint")
 
 	l.outputs = lintOutputs{
diff --git a/java/robolectric.go b/java/robolectric.go
index 7f2981f..b6116ec 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -188,9 +188,9 @@
 
 	// TODO: this could all be removed if tradefed was used as the test runner, it will find everything
 	// annotated as a test and run it.
-	for _, src := range r.compiledJavaSrcs {
+	for _, src := range r.uniqueSrcFiles {
 		s := src.Rel()
-		if !strings.HasSuffix(s, "Test.java") {
+		if !strings.HasSuffix(s, "Test.java") && !strings.HasSuffix(s, "Test.kt") {
 			continue
 		} else if strings.HasSuffix(s, "/BaseRobolectricTest.java") {
 			continue
diff --git a/java/rro.go b/java/rro.go
index cd8c635..9d0667c 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -142,6 +142,10 @@
 		aaptLinkFlags = append(aaptLinkFlags,
 			"--rename-overlay-target-package "+*r.overridableProperties.Target_package_name)
 	}
+	if r.overridableProperties.Category != nil {
+		aaptLinkFlags = append(aaptLinkFlags,
+			"--rename-overlay-category "+*r.overridableProperties.Category)
+	}
 	r.aapt.buildActions(ctx, r, nil, nil, false, aaptLinkFlags...)
 
 	// Sign the built package
@@ -220,6 +224,9 @@
 
 	// the target package name of this overlay app. The target package name in the manifest file is used if one was not given.
 	Target_package_name *string
+
+	// the rro category of this overlay. The category in the manifest file is used if one was not given.
+	Category *string
 }
 
 type OverrideRuntimeResourceOverlay struct {
diff --git a/java/rro_test.go b/java/rro_test.go
index 00ba5ba..8067a47 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -201,6 +201,7 @@
 			base: "foo_overlay",
 			package_name: "com.android.bar.overlay",
 			target_package_name: "com.android.bar",
+			category: "mycategory",
 		}
 		`)
 
@@ -212,6 +213,7 @@
 		targetVariant     string
 		packageFlag       string
 		targetPackageFlag string
+		categoryFlag      string
 	}{
 		{
 			variantName:       "android_common",
@@ -228,6 +230,7 @@
 			targetVariant:     "android_common_bar",
 			packageFlag:       "com.android.bar.overlay",
 			targetPackageFlag: "com.android.bar",
+			categoryFlag:      "mycategory",
 		},
 	}
 	for _, expected := range expectedVariants {
@@ -249,6 +252,7 @@
 		checkAapt2LinkFlag(t, aapt2Flags, "rename-manifest-package", expected.packageFlag)
 		checkAapt2LinkFlag(t, aapt2Flags, "rename-resources-package", "")
 		checkAapt2LinkFlag(t, aapt2Flags, "rename-overlay-target-package", expected.targetPackageFlag)
+		checkAapt2LinkFlag(t, aapt2Flags, "rename-overlay-category", expected.categoryFlag)
 	}
 }
 
diff --git a/python/binary.go b/python/binary.go
index e6324a3..f4ad626 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -192,8 +192,8 @@
 		})
 	}
 
-	addTopDirectoriesToPath := !proptools.BoolDefault(binary.binaryProperties.Dont_add_top_level_directories_to_path, false)
-	dontAddEntrypointFolderToPath := proptools.BoolDefault(binary.binaryProperties.Dont_add_entrypoint_folder_to_path, false)
+	addTopDirectoriesToPath := !proptools.BoolDefault(binary.binaryProperties.Dont_add_top_level_directories_to_path, true)
+	dontAddEntrypointFolderToPath := proptools.BoolDefault(binary.binaryProperties.Dont_add_entrypoint_folder_to_path, true)
 
 	binFile := registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
 		binary.getHostInterpreterName(ctx, actualVersion),
diff --git a/python/python.go b/python/python.go
index f6029c2..6f3a0ec 100644
--- a/python/python.go
+++ b/python/python.go
@@ -677,8 +677,7 @@
 		protoFlags := android.GetProtoFlags(ctx, &p.protoProperties)
 		protoFlags.OutTypeFlag = "--python_out"
 
-		// TODO(b/247578564): Change the default to true, and then eventually remove respect_pkg_path
-		protosRespectPkgPath := proptools.BoolDefault(p.properties.Proto.Respect_pkg_path, false)
+		protosRespectPkgPath := proptools.BoolDefault(p.properties.Proto.Respect_pkg_path, true)
 		pkgPathForProtos := pkgPath
 		if pkgPathForProtos != "" && protosRespectPkgPath {
 			pkgPathStagingDir := android.PathForModuleGen(ctx, "protos_staged_for_pkg_path")
diff --git a/ui/build/config.go b/ui/build/config.go
index 2060660..f6f5b46 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -388,13 +388,13 @@
 		if override, ok := ret.environ.Get("OVERRIDE_ANDROID_JAVA_HOME"); ok {
 			return override
 		}
-		if ret.environ.IsEnvTrue("EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN") {
-			return java17Home
-		}
 		if toolchain11, ok := ret.environ.Get("EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN"); ok && toolchain11 != "true" {
 			ctx.Fatalln("The environment variable EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN is no longer supported. An OpenJDK 11 toolchain is now the global default.")
 		}
-		return java11Home
+		if toolchain17, ok := ret.environ.Get("EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN"); ok && toolchain17 != "true" {
+			ctx.Fatalln("The environment variable EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN is no longer supported. An OpenJDK 17 toolchain is now the global default.")
+		}
+		return java17Home
 	}()
 	absJavaHome := absPath(ctx, javaHome)