Merge "Add overrides support for snapshots"
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 9bba3c3..4ab94c3 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -180,8 +180,10 @@
 		"frameworks/base/tools/aapt2":                        Bp2BuildDefaultTrue,
 		"frameworks/native/libs/adbd_auth":                   Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/arect":                       Bp2BuildDefaultTrueRecursively,
+		"frameworks/native/libs/gui":                         Bp2BuildDefaultTrue,
 		"frameworks/native/libs/math":                        Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/nativebase":                  Bp2BuildDefaultTrueRecursively,
+		"frameworks/native/libs/vr":                          Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/opengl/tests/gl2_cameraeye":       Bp2BuildDefaultTrue,
 		"frameworks/native/opengl/tests/gl2_java":            Bp2BuildDefaultTrue,
 		"frameworks/native/opengl/tests/testLatency":         Bp2BuildDefaultTrue,
@@ -397,14 +399,11 @@
 		"libbinder_headers_platform_shared",
 		"libbinderthreadstateutils",
 		"libbluetooth-types-header",
-		"libbufferhub_headers",
 		"libcodec2",
 		"libcodec2_headers",
 		"libcodec2_internal",
 		"libdmabufheap",
-		"libdvr_headers",
 		"libgsm",
-		"libgui_bufferqueue_sources",
 		"libgrallocusage",
 		"libgralloctypes",
 		"libnativewindow",
@@ -417,7 +416,6 @@
 		"libneuralnetworks_headers",
 		"libneuralnetworks_packageinfo",
 		"libopus",
-		"libpdx_headers",
 		"libprocpartition",
 		"libruy_static",
 		"libandroidio",
@@ -443,7 +441,6 @@
 		"libtextclassifier_hash_static",
 		"libtflite_kernel_utils",
 		"libtinyxml2",
-		"libgui_aidl",
 		"libui",
 		"libui-types",
 		"libui_headers",
@@ -470,7 +467,6 @@
 		"statslog_neuralnetworks.h",
 		"tensorflow_headers",
 
-		"libgui_headers",
 		"libstagefright_bufferpool@2.0",
 		"libstagefright_bufferpool@2.0.1",
 		"libSurfaceFlingerProp",
@@ -584,7 +580,6 @@
 		"libcodec2_hidl_plugin_stub",
 		"libcodec2_hidl_plugin",
 		"libstagefright_bufferqueue_helper_novndk",
-		"libgui_bufferqueue_static",
 		"libGLESv2",
 		"libEGL",
 		"libcodec2_vndk",
@@ -634,13 +629,10 @@
 
 	Bp2buildModuleTypeAlwaysConvertList = []string{
 		"aidl_interface_headers",
-		"api_domain",
 		"license",
 		"linker_config",
 		"java_import",
 		"java_import_host",
-		"ndk_headers",
-		"ndk_library",
 		"sysprop_library",
 		"bpf",
 	}
@@ -653,6 +645,23 @@
 	// the "prebuilt_" prefix to the name, so that it's differentiable from
 	// the source versions within Soong's module graph.
 	Bp2buildModuleDoNotConvertList = []string{
+		// TODO(b/250876486): Created cc_aidl_library doesn't have static libs from parent cc module
+		"libgui_window_info_static",
+		"libgui",     // Depends on unconverted libgui_window_info_static
+		"libdisplay", // Depends on uncovnerted libgui
+		// Depends on unconverted libdisplay
+		"libdvr_static.google",
+		"libdvr.google",
+		"libvrsensor",
+		"dvr_api-test",
+		// Depends on unconverted libandroid, libgui
+		"dvr_buffer_queue-test",
+		"dvr_display-test",
+		// Depends on unconverted libchrome
+		"pdx_benchmarks",
+		"buffer_hub_queue-test",
+		"buffer_hub_queue_producer-test",
+
 		// cc bugs
 		"libactivitymanager_aidl", // TODO(b/207426160): Unsupported use of aidl sources (via Dactivity_manager_procstate_aidl) in a cc_library
 
@@ -935,6 +944,7 @@
 		"libdlext_test_norelro",
 		"libdlext_test_recursive",
 		"libdlext_test_zip",
+		"libdvrcommon_test",
 		"libfortify1-new-tests-clang",
 		"libfortify1-new-tests-clang",
 		"libfortify1-tests-clang",
diff --git a/android/android_test.go b/android/android_test.go
index fb82e37..64ceedc 100644
--- a/android/android_test.go
+++ b/android/android_test.go
@@ -15,10 +15,32 @@
 package android
 
 import (
+	"io/ioutil"
 	"os"
 	"testing"
 )
 
+var buildDir string
+
+func setUp() {
+	var err error
+	buildDir, err = ioutil.TempDir("", "android_test")
+	if err != nil {
+		panic(err)
+	}
+}
+
+func tearDown() {
+	os.RemoveAll(buildDir)
+}
+
 func TestMain(m *testing.M) {
-	os.Exit(m.Run())
+	run := func() int {
+		setUp()
+		defer tearDown()
+
+		return m.Run()
+	}
+
+	os.Exit(run())
 }
diff --git a/android/api_domain.go b/android/api_domain.go
index 8ff4752..7876654 100644
--- a/android/api_domain.go
+++ b/android/api_domain.go
@@ -67,7 +67,6 @@
 	m := &apiDomain{}
 	m.AddProperties(&m.properties)
 	InitAndroidArchModule(m, DeviceSupported, MultilibBoth)
-	InitBazelModule(m)
 	return m
 }
 
@@ -108,7 +107,9 @@
 	Cc_api_contributions bazel.LabelListAttribute
 }
 
-func (a *apiDomain) ConvertWithBp2build(ctx TopDownMutatorContext) {
+var _ ApiProvider = (*apiDomain)(nil)
+
+func (a *apiDomain) ConvertWithApiBp2build(ctx TopDownMutatorContext) {
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "api_domain",
 		Bzl_load_location: "//build/bazel/rules/apis:api_domain.bzl",
diff --git a/android/bazel.go b/android/bazel.go
index dd1de7b..7b227bd 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -134,6 +134,11 @@
 	SetBaseModuleType(baseModuleType string)
 }
 
+// ApiProvider is implemented by modules that contribute to an API surface
+type ApiProvider interface {
+	ConvertWithApiBp2build(ctx TopDownMutatorContext)
+}
+
 // MixedBuildBuildable is an interface that module types should implement in order
 // to be "handled by Bazel" in a mixed build.
 type MixedBuildBuildable interface {
@@ -415,6 +420,13 @@
 		return false
 	}
 
+	// In api_bp2build mode, all soong modules that can provide API contributions should be converted
+	// This is irrespective of its presence/absence in bp2build allowlists
+	if ctx.Config().BuildMode == ApiBp2build {
+		_, providesApis := module.(ApiProvider)
+		return providesApis
+	}
+
 	propValue := b.bazelProperties.Bazel_module.Bp2build_available
 	packagePath := ctx.OtherModuleDir(module)
 
@@ -510,6 +522,17 @@
 	bModule.ConvertWithBp2build(ctx)
 }
 
+func registerApiBp2buildConversionMutator(ctx RegisterMutatorsContext) {
+	ctx.TopDown("apiBp2build_conversion", convertWithApiBp2build).Parallel()
+}
+
+// Generate API contribution targets if the Soong module provides APIs
+func convertWithApiBp2build(ctx TopDownMutatorContext) {
+	if m, ok := ctx.Module().(ApiProvider); ok {
+		m.ConvertWithApiBp2build(ctx)
+	}
+}
+
 // GetMainClassInManifest scans the manifest file specified in filepath and returns
 // the value of attribute Main-Class in the manifest file if it exists, or returns error.
 // WARNING: this is for bp2build converters of java_* modules only.
diff --git a/android/config.go b/android/config.go
index ee432a2..e86fc27 100644
--- a/android/config.go
+++ b/android/config.go
@@ -83,6 +83,9 @@
 	// express build semantics.
 	GenerateQueryView
 
+	// Generate BUILD files for API contributions to API surfaces
+	ApiBp2build
+
 	// Create a JSON representation of the module graph and exit.
 	GenerateModuleGraph
 
diff --git a/android/module.go b/android/module.go
index a150e61..68d9f8e 100644
--- a/android/module.go
+++ b/android/module.go
@@ -3546,10 +3546,29 @@
 		reportPathError(ctx, err)
 		return nil
 	}
+	if len(paths) == 0 {
+		type addMissingDependenciesIntf interface {
+			AddMissingDependencies([]string)
+			OtherModuleName(blueprint.Module) string
+		}
+		if mctx, ok := ctx.(addMissingDependenciesIntf); ok && ctx.Config().AllowMissingDependencies() {
+			mctx.AddMissingDependencies([]string{mctx.OtherModuleName(module)})
+		} else {
+			ReportPathErrorf(ctx, "failed to get output files from module %q", pathContextName(ctx, module))
+		}
+		// Return a fake output file to avoid nil dereferences of Path objects later.
+		// This should never get used for an actual build as the error or missing
+		// dependency has already been reported.
+		p, err := pathForSource(ctx, filepath.Join("missing_output_file", pathContextName(ctx, module)))
+		if err != nil {
+			reportPathError(ctx, err)
+			return nil
+		}
+		return p
+	}
 	if len(paths) > 1 {
 		ReportPathErrorf(ctx, "got multiple output files from module %q, expected exactly one",
 			pathContextName(ctx, module))
-		return nil
 	}
 	return paths[0]
 }
@@ -3561,18 +3580,12 @@
 			return nil, fmt.Errorf("failed to get output file from module %q: %s",
 				pathContextName(ctx, module), err.Error())
 		}
-		if len(paths) == 0 {
-			return nil, fmt.Errorf("failed to get output files from module %q", pathContextName(ctx, module))
-		}
 		return paths, nil
 	} else if sourceFileProducer, ok := module.(SourceFileProducer); ok {
 		if tag != "" {
 			return nil, fmt.Errorf("module %q is a SourceFileProducer, not an OutputFileProducer, and so does not support tag %q", pathContextName(ctx, module), tag)
 		}
 		paths := sourceFileProducer.Srcs()
-		if len(paths) == 0 {
-			return nil, fmt.Errorf("failed to get output files from module %q", pathContextName(ctx, module))
-		}
 		return paths, nil
 	} else {
 		return nil, fmt.Errorf("module %q is not an OutputFileProducer", pathContextName(ctx, module))
diff --git a/android/module_test.go b/android/module_test.go
index 0580bef..1ca7422 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"github.com/google/blueprint"
 	"path/filepath"
 	"runtime"
 	"testing"
@@ -978,3 +979,88 @@
 		})
 	}
 }
+
+type fakeBlueprintModule struct{}
+
+func (fakeBlueprintModule) Name() string { return "foo" }
+
+func (fakeBlueprintModule) GenerateBuildActions(blueprint.ModuleContext) {}
+
+type sourceProducerTestModule struct {
+	fakeBlueprintModule
+	source Path
+}
+
+func (s sourceProducerTestModule) Srcs() Paths { return Paths{s.source} }
+
+type outputFileProducerTestModule struct {
+	fakeBlueprintModule
+	output map[string]Path
+	error  map[string]error
+}
+
+func (o outputFileProducerTestModule) OutputFiles(tag string) (Paths, error) {
+	return PathsIfNonNil(o.output[tag]), o.error[tag]
+}
+
+type pathContextAddMissingDependenciesWrapper struct {
+	PathContext
+	missingDeps []string
+}
+
+func (p *pathContextAddMissingDependenciesWrapper) AddMissingDependencies(deps []string) {
+	p.missingDeps = append(p.missingDeps, deps...)
+}
+func (p *pathContextAddMissingDependenciesWrapper) OtherModuleName(module blueprint.Module) string {
+	return module.Name()
+}
+
+func TestOutputFileForModule(t *testing.T) {
+	testcases := []struct {
+		name        string
+		module      blueprint.Module
+		tag         string
+		env         map[string]string
+		config      func(*config)
+		expected    string
+		missingDeps []string
+	}{
+		{
+			name:     "SourceFileProducer",
+			module:   &sourceProducerTestModule{source: PathForTesting("foo.txt")},
+			expected: "foo.txt",
+		},
+		{
+			name:     "OutputFileProducer",
+			module:   &outputFileProducerTestModule{output: map[string]Path{"": PathForTesting("foo.txt")}},
+			expected: "foo.txt",
+		},
+		{
+			name:     "OutputFileProducer_tag",
+			module:   &outputFileProducerTestModule{output: map[string]Path{"foo": PathForTesting("foo.txt")}},
+			tag:      "foo",
+			expected: "foo.txt",
+		},
+		{
+			name: "OutputFileProducer_AllowMissingDependencies",
+			config: func(config *config) {
+				config.TestProductVariables.Allow_missing_dependencies = boolPtr(true)
+			},
+			module:      &outputFileProducerTestModule{},
+			missingDeps: []string{"foo"},
+			expected:    "missing_output_file/foo",
+		},
+	}
+	for _, tt := range testcases {
+		config := TestConfig(buildDir, tt.env, "", nil)
+		if tt.config != nil {
+			tt.config(config.config)
+		}
+		ctx := &pathContextAddMissingDependenciesWrapper{
+			PathContext: PathContextForTesting(config),
+		}
+		got := OutputFileForModule(ctx, tt.module, tt.tag)
+		AssertPathRelativeToTopEquals(t, "expected source path", tt.expected, got)
+		AssertArrayString(t, "expected missing deps", tt.missingDeps, ctx.missingDeps)
+	}
+}
diff --git a/android/mutator.go b/android/mutator.go
index 9e4aa59..83d4e66 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -31,22 +31,33 @@
 
 // RegisterMutatorsForBazelConversion is a alternate registration pipeline for bp2build. Exported for testing.
 func RegisterMutatorsForBazelConversion(ctx *Context, preArchMutators []RegisterMutatorFunc) {
+	bp2buildMutators := append(preArchMutators, registerBp2buildConversionMutator)
+	registerMutatorsForBazelConversion(ctx, bp2buildMutators)
+}
+
+// RegisterMutatorsForApiBazelConversion is an alternate registration pipeline for api_bp2build
+// This pipeline restricts generation of Bazel targets to Soong modules that contribute APIs
+func RegisterMutatorsForApiBazelConversion(ctx *Context, preArchMutators []RegisterMutatorFunc) {
+	bp2buildMutators := append(preArchMutators, registerApiBp2buildConversionMutator)
+	registerMutatorsForBazelConversion(ctx, bp2buildMutators)
+}
+
+func registerMutatorsForBazelConversion(ctx *Context, bp2buildMutators []RegisterMutatorFunc) {
 	mctx := &registerMutatorsContext{
 		bazelConversionMode: true,
 	}
 
-	bp2buildMutators := append([]RegisterMutatorFunc{
+	allMutators := append([]RegisterMutatorFunc{
 		RegisterNamespaceMutator,
 		RegisterDefaultsPreArchMutators,
 		// TODO(b/165114590): this is required to resolve deps that are only prebuilts, but we should
 		// evaluate the impact on conversion.
 		RegisterPrebuiltsPreArchMutators,
 	},
-		preArchMutators...)
-	bp2buildMutators = append(bp2buildMutators, registerBp2buildConversionMutator)
+		bp2buildMutators...)
 
 	// Register bp2build mutators
-	for _, f := range bp2buildMutators {
+	for _, f := range allMutators {
 		f(mctx)
 	}
 
diff --git a/android/paths.go b/android/paths.go
index 27f4bf5..dbcdb23 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1638,6 +1638,10 @@
 	}
 }
 
+func (p InstallPath) Partition() string {
+	return p.partition
+}
+
 // Join creates a new InstallPath with paths... joined with the current path. The
 // provided paths... may not use '..' to escape from the current path.
 func (p InstallPath) Join(ctx PathContext, paths ...string) InstallPath {
diff --git a/android/register.go b/android/register.go
index d4ce5f1..6c69cc5 100644
--- a/android/register.go
+++ b/android/register.go
@@ -180,6 +180,16 @@
 	RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators)
 }
 
+// RegisterForApiBazelConversion is similar to RegisterForBazelConversion except that
+// it only generates API targets in the generated  workspace
+func (ctx *Context) RegisterForApiBazelConversion() {
+	for _, t := range moduleTypes {
+		t.register(ctx)
+	}
+
+	RegisterMutatorsForApiBazelConversion(ctx, bp2buildPreArchMutators)
+}
+
 // Register the pipeline of singletons, module types, and mutators for
 // generating build.ninja and other files for Kati, from Android.bp files.
 func (ctx *Context) Register() {
diff --git a/android/testing.go b/android/testing.go
index 7b74c89..4018659 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -461,6 +461,12 @@
 	RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch)
 }
 
+// RegisterForApiBazelConversion prepares a test context for API bp2build conversion.
+func (ctx *TestContext) RegisterForApiBazelConversion() {
+	ctx.config.BuildMode = ApiBp2build
+	RegisterMutatorsForApiBazelConversion(ctx.Context, ctx.bp2buildPreArch)
+}
+
 func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) {
 	// This function adapts the old style ParseFileList calls that are spread throughout the tests
 	// to the new style that takes a config.
diff --git a/apex/apex.go b/apex/apex.go
index 2e54e7e..4247db4 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -286,6 +286,9 @@
 		Arm64 struct {
 			ApexNativeDependencies
 		}
+		Riscv64 struct {
+			ApexNativeDependencies
+		}
 		X86 struct {
 			ApexNativeDependencies
 		}
@@ -787,6 +790,8 @@
 			depsList = append(depsList, a.archProperties.Arch.Arm.ApexNativeDependencies)
 		case android.Arm64:
 			depsList = append(depsList, a.archProperties.Arch.Arm64.ApexNativeDependencies)
+		case android.Riscv64:
+			depsList = append(depsList, a.archProperties.Arch.Riscv64.ApexNativeDependencies)
 		case android.X86:
 			depsList = append(depsList, a.archProperties.Arch.X86.ApexNativeDependencies)
 		case android.X86_64:
@@ -1559,7 +1564,7 @@
 		dirInApex = filepath.Join(dirInApex, "bionic")
 	}
 
-	fileToCopy := ccMod.OutputFile().Path()
+	fileToCopy := android.OutputFileForModule(ctx, ccMod, "")
 	androidMkModuleName := ccMod.BaseModuleName() + ccMod.Properties.SubName
 	return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, ccMod)
 }
@@ -1570,7 +1575,7 @@
 		dirInApex = filepath.Join(dirInApex, cc.Target().NativeBridgeRelativePath)
 	}
 	dirInApex = filepath.Join(dirInApex, cc.RelativeInstallPath())
-	fileToCopy := cc.OutputFile().Path()
+	fileToCopy := android.OutputFileForModule(ctx, cc, "")
 	androidMkModuleName := cc.BaseModuleName() + cc.Properties.SubName
 	af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, cc)
 	af.symlinks = cc.Symlinks()
@@ -1583,7 +1588,7 @@
 	if rustm.Target().NativeBridge == android.NativeBridgeEnabled {
 		dirInApex = filepath.Join(dirInApex, rustm.Target().NativeBridgeRelativePath)
 	}
-	fileToCopy := rustm.OutputFile().Path()
+	fileToCopy := android.OutputFileForModule(ctx, rustm, "")
 	androidMkModuleName := rustm.BaseModuleName() + rustm.Properties.SubName
 	af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, rustm)
 	return af
@@ -1602,7 +1607,7 @@
 	if rustm.Target().NativeBridge == android.NativeBridgeEnabled {
 		dirInApex = filepath.Join(dirInApex, rustm.Target().NativeBridgeRelativePath)
 	}
-	fileToCopy := rustm.OutputFile().Path()
+	fileToCopy := android.OutputFileForModule(ctx, rustm, "")
 	androidMkModuleName := rustm.BaseModuleName() + rustm.Properties.SubName
 	return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, rustm)
 }
@@ -2659,9 +2664,13 @@
 		}
 
 		// Certificate
-		if overridableProperties.Certificate != nil {
-			attrs.Certificate = bazel.LabelAttribute{}
-			attrs.Certificate.SetValue(android.BazelLabelForModuleDepSingle(ctx, *overridableProperties.Certificate))
+		if overridableProperties.Certificate == nil {
+			// delegated to the rule attr default
+			attrs.Certificate = nil
+		} else {
+			certificateName, certificate := java.ParseCertificateToAttribute(ctx, overridableProperties.Certificate)
+			attrs.Certificate_name = certificateName
+			attrs.Certificate = certificate
 		}
 
 		// Prebuilts
@@ -2726,16 +2735,23 @@
 	// Only override the minSdkVersion value on Apexes which already specify
 	// a min_sdk_version (it's optional for non-updatable apexes), and that its
 	// min_sdk_version value is lower than the one to override with.
-	overrideMinSdkValue := ctx.DeviceConfig().ApexGlobalMinSdkVersionOverride()
-	overrideApiLevel := minSdkVersionFromValue(ctx, overrideMinSdkValue)
-	originalMinApiLevel := minSdkVersionFromValue(ctx, proptools.String(a.properties.Min_sdk_version))
-	isMinSdkSet := a.properties.Min_sdk_version != nil
-	isOverrideValueHigher := overrideApiLevel.CompareTo(originalMinApiLevel) > 0
-	if overrideMinSdkValue != "" && isMinSdkSet && isOverrideValueHigher {
-		return overrideMinSdkValue
+	minApiLevel := minSdkVersionFromValue(ctx, proptools.String(a.properties.Min_sdk_version))
+	if minApiLevel.IsNone() {
+		return ""
 	}
 
-	return proptools.String(a.properties.Min_sdk_version)
+	archMinApiLevel := cc.MinApiForArch(ctx, a.MultiTargets()[0].Arch.ArchType)
+	if !archMinApiLevel.IsNone() && archMinApiLevel.CompareTo(minApiLevel) > 0 {
+		minApiLevel = archMinApiLevel
+	}
+
+	overrideMinSdkValue := ctx.DeviceConfig().ApexGlobalMinSdkVersionOverride()
+	overrideApiLevel := minSdkVersionFromValue(ctx, overrideMinSdkValue)
+	if !overrideApiLevel.IsNone() && overrideApiLevel.CompareTo(minApiLevel) > 0 {
+		minApiLevel = overrideApiLevel
+	}
+
+	return minApiLevel.String()
 }
 
 // Returns apex's min_sdk_version SdkSpec, honoring overrides
@@ -3335,7 +3351,8 @@
 	Android_manifest      bazel.LabelAttribute
 	File_contexts         bazel.LabelAttribute
 	Key                   bazel.LabelAttribute
-	Certificate           bazel.LabelAttribute
+	Certificate           *bazel.Label // used when the certificate prop is a module
+	Certificate_name      *string      // used when the certificate prop is a string
 	Min_sdk_version       *string
 	Updatable             bazel.BoolAttribute
 	Installable           bazel.BoolAttribute
@@ -3397,10 +3414,7 @@
 		keyLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.overridableProperties.Key))
 	}
 
-	var certificateLabelAttribute bazel.LabelAttribute
-	if a.overridableProperties.Certificate != nil {
-		certificateLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.overridableProperties.Certificate))
-	}
+	certificateName, certificate := java.ParseCertificateToAttribute(ctx, a.overridableProperties.Certificate)
 
 	nativeSharedLibs := &convertedNativeSharedLibs{
 		Native_shared_libs_32: bazel.LabelListAttribute{},
@@ -3456,7 +3470,8 @@
 		File_contexts:         fileContextsLabelAttribute,
 		Min_sdk_version:       minSdkVersion,
 		Key:                   keyLabelAttribute,
-		Certificate:           certificateLabelAttribute,
+		Certificate:           certificate,
+		Certificate_name:      certificateName,
 		Updatable:             updatableAttribute,
 		Installable:           installableAttribute,
 		Native_shared_libs_32: nativeSharedLibs.Native_shared_libs_32,
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 25ae5bf..0f57911 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -537,7 +537,11 @@
 	}
 
 	if src == "" {
-		ctx.OtherModuleErrorf(prebuilt, "prebuilt_apex does not support %q", multiTargets[0].Arch.String())
+		if ctx.Config().AllowMissingDependencies() {
+			ctx.AddMissingDependencies([]string{ctx.OtherModuleName(prebuilt)})
+		} else {
+			ctx.OtherModuleErrorf(prebuilt, "prebuilt_apex does not support %q", multiTargets[0].Arch.String())
+		}
 		// Drop through to return an empty string as the src (instead of nil) to avoid the prebuilt
 		// logic from reporting a more general, less useful message.
 	}
diff --git a/bp2build/apex_conversion_test.go b/bp2build/apex_conversion_test.go
index b0a2966..233fce4 100644
--- a/bp2build/apex_conversion_test.go
+++ b/bp2build/apex_conversion_test.go
@@ -120,7 +120,7 @@
 	file_contexts: ":com.android.apogee-file_contexts",
 	min_sdk_version: "29",
 	key: "com.android.apogee.key",
-	certificate: "com.android.apogee.certificate",
+	certificate: ":com.android.apogee.certificate",
 	updatable: false,
 	installable: false,
 	compressible: false,
@@ -582,7 +582,7 @@
 	file_contexts: ":com.android.apogee-file_contexts",
 	min_sdk_version: "29",
 	key: "com.android.apogee.key",
-	certificate: "com.android.apogee.certificate",
+	certificate: ":com.android.apogee.certificate",
 	updatable: false,
 	installable: false,
 	compressible: false,
@@ -618,7 +618,7 @@
 	name: "com.google.android.apogee",
 	base: ":com.android.apogee",
 	key: "com.google.android.apogee.key",
-	certificate: "com.google.android.apogee.certificate",
+	certificate: ":com.google.android.apogee.certificate",
 	prebuilts: [],
 	compressible: true,
 }
@@ -1016,3 +1016,193 @@
 			}),
 		}})
 }
+
+func TestBp2BuildOverrideApex_CertificateNil(t *testing.T) {
+	runOverrideApexTestCase(t, Bp2buildTestCase{
+		Description:                "override_apex - don't set default certificate",
+		ModuleTypeUnderTest:        "override_apex",
+		ModuleTypeUnderTestFactory: apex.OverrideApexFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: `
+android_app_certificate {
+	name: "com.android.apogee.certificate",
+	certificate: "com.android.apogee",
+	bazel_module: { bp2build_available: false },
+}
+
+filegroup {
+	name: "com.android.apogee-file_contexts",
+	srcs: [
+		"com.android.apogee-file_contexts",
+	],
+	bazel_module: { bp2build_available: false },
+}
+
+apex {
+	name: "com.android.apogee",
+	manifest: "apogee_manifest.json",
+	file_contexts: ":com.android.apogee-file_contexts",
+	certificate: ":com.android.apogee.certificate",
+	bazel_module: { bp2build_available: false },
+}
+
+override_apex {
+	name: "com.google.android.apogee",
+	base: ":com.android.apogee",
+	// certificate is deliberately omitted, and not converted to bazel,
+	// because the overridden apex shouldn't be using the base apex's cert.
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+				"file_contexts": `":com.android.apogee-file_contexts"`,
+				"manifest":      `"apogee_manifest.json"`,
+			}),
+		}})
+}
+
+func TestApexCertificateIsModule(t *testing.T) {
+	runApexTestCase(t, Bp2buildTestCase{
+		Description:                "apex - certificate is module",
+		ModuleTypeUnderTest:        "apex",
+		ModuleTypeUnderTestFactory: apex.BundleFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: `
+android_app_certificate {
+	name: "com.android.apogee.certificate",
+	certificate: "com.android.apogee",
+	bazel_module: { bp2build_available: false },
+}
+
+apex {
+	name: "com.android.apogee",
+	manifest: "apogee_manifest.json",
+	file_contexts: ":com.android.apogee-file_contexts",
+	certificate: ":com.android.apogee.certificate",
+}
+` + simpleModuleDoNotConvertBp2build("filegroup", "com.android.apogee-file_contexts"),
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
+				"certificate":   `":com.android.apogee.certificate"`,
+				"file_contexts": `":com.android.apogee-file_contexts"`,
+				"manifest":      `"apogee_manifest.json"`,
+			}),
+		}})
+}
+
+func TestApexCertificateIsSrc(t *testing.T) {
+	runApexTestCase(t, Bp2buildTestCase{
+		Description:                "apex - certificate is src",
+		ModuleTypeUnderTest:        "apex",
+		ModuleTypeUnderTestFactory: apex.BundleFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: `
+apex {
+	name: "com.android.apogee",
+	manifest: "apogee_manifest.json",
+	file_contexts: ":com.android.apogee-file_contexts",
+	certificate: "com.android.apogee.certificate",
+}
+` + simpleModuleDoNotConvertBp2build("filegroup", "com.android.apogee-file_contexts"),
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
+				"certificate_name": `"com.android.apogee.certificate"`,
+				"file_contexts":    `":com.android.apogee-file_contexts"`,
+				"manifest":         `"apogee_manifest.json"`,
+			}),
+		}})
+}
+
+func TestBp2BuildOverrideApex_CertificateIsModule(t *testing.T) {
+	runOverrideApexTestCase(t, Bp2buildTestCase{
+		Description:                "override_apex - certificate is module",
+		ModuleTypeUnderTest:        "override_apex",
+		ModuleTypeUnderTestFactory: apex.OverrideApexFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: `
+android_app_certificate {
+	name: "com.android.apogee.certificate",
+	certificate: "com.android.apogee",
+	bazel_module: { bp2build_available: false },
+}
+
+filegroup {
+	name: "com.android.apogee-file_contexts",
+	srcs: [
+		"com.android.apogee-file_contexts",
+	],
+	bazel_module: { bp2build_available: false },
+}
+
+apex {
+	name: "com.android.apogee",
+	manifest: "apogee_manifest.json",
+	file_contexts: ":com.android.apogee-file_contexts",
+	certificate: ":com.android.apogee.certificate",
+	bazel_module: { bp2build_available: false },
+}
+
+android_app_certificate {
+	name: "com.google.android.apogee.certificate",
+	certificate: "com.google.android.apogee",
+	bazel_module: { bp2build_available: false },
+}
+
+override_apex {
+	name: "com.google.android.apogee",
+	base: ":com.android.apogee",
+	certificate: ":com.google.android.apogee.certificate",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+				"file_contexts": `":com.android.apogee-file_contexts"`,
+				"certificate":   `":com.google.android.apogee.certificate"`,
+				"manifest":      `"apogee_manifest.json"`,
+			}),
+		}})
+}
+
+func TestBp2BuildOverrideApex_CertificateIsSrc(t *testing.T) {
+	runOverrideApexTestCase(t, Bp2buildTestCase{
+		Description:                "override_apex - certificate is src",
+		ModuleTypeUnderTest:        "override_apex",
+		ModuleTypeUnderTestFactory: apex.OverrideApexFactory,
+		Filesystem:                 map[string]string{},
+		Blueprint: `
+android_app_certificate {
+	name: "com.android.apogee.certificate",
+	certificate: "com.android.apogee",
+	bazel_module: { bp2build_available: false },
+}
+
+filegroup {
+	name: "com.android.apogee-file_contexts",
+	srcs: [
+		"com.android.apogee-file_contexts",
+	],
+	bazel_module: { bp2build_available: false },
+}
+
+apex {
+	name: "com.android.apogee",
+	manifest: "apogee_manifest.json",
+	file_contexts: ":com.android.apogee-file_contexts",
+	certificate: ":com.android.apogee.certificate",
+	bazel_module: { bp2build_available: false },
+}
+
+override_apex {
+	name: "com.google.android.apogee",
+	base: ":com.android.apogee",
+	certificate: "com.google.android.apogee.certificate",
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+				"file_contexts":    `":com.android.apogee-file_contexts"`,
+				"certificate_name": `"com.google.android.apogee.certificate"`,
+				"manifest":         `"apogee_manifest.json"`,
+			}),
+		}})
+}
diff --git a/bp2build/api_domain_conversion_test.go b/bp2build/api_domain_conversion_test.go
index fc9d1d5..224008f 100644
--- a/bp2build/api_domain_conversion_test.go
+++ b/bp2build/api_domain_conversion_test.go
@@ -60,7 +60,7 @@
 			"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
 		},
 	)
-	RunBp2BuildTestCase(t, registerApiDomainModuleTypes, Bp2buildTestCase{
+	RunApiBp2BuildTestCase(t, registerApiDomainModuleTypes, Bp2buildTestCase{
 		Blueprint:            bp,
 		ExpectedBazelTargets: []string{expectedBazelTarget},
 		Filesystem:           fs,
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 36c3a48..82ce115 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -163,6 +163,9 @@
 	// This mode is used for discovering and introspecting the existing Soong
 	// module graph.
 	QueryView
+
+	// ApiBp2build - generate BUILD files for API contribution targets
+	ApiBp2build
 )
 
 type unconvertedDepsMode int
@@ -181,6 +184,8 @@
 		return "Bp2Build"
 	case QueryView:
 		return "QueryView"
+	case ApiBp2build:
+		return "ApiBp2build"
 	default:
 		return fmt.Sprintf("%d", mode)
 	}
@@ -327,6 +332,10 @@
 				errs = append(errs, err)
 			}
 			targets = append(targets, t)
+		case ApiBp2build:
+			if aModule, ok := m.(android.Module); ok && aModule.IsConvertedByBp2build() {
+				targets, errs = generateBazelTargets(bpCtx, aModule)
+			}
 		default:
 			errs = append(errs, fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode()))
 			return
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index c1fb800..7c24a94 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -1853,3 +1853,27 @@
 			},
 		})
 }
+
+func TestGenerateApiBazelTargets(t *testing.T) {
+	bp := `
+	custom {
+		name: "foo",
+		api: "foo.txt",
+	}
+	`
+	expectedBazelTarget := MakeBazelTarget(
+		"custom_api_contribution",
+		"foo",
+		AttrNameToString{
+			"api": `"foo.txt"`,
+		},
+	)
+	registerCustomModule := func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
+	}
+	RunApiBp2BuildTestCase(t, registerCustomModule, Bp2buildTestCase{
+		Blueprint:            bp,
+		ExpectedBazelTargets: []string{expectedBazelTarget},
+		Description:          "Generating API contribution Bazel targets for custom module",
+	})
+}
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index 28d2c75..a8e557d 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -85,6 +85,7 @@
         "soong_module_name": attr.string(mandatory = True),
         "soong_module_variant": attr.string(),
         "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
+        "api": attr.string(),
         "arch_paths": attr.string_list(),
         "arch_paths_exclude": attr.string_list(),
         # bazel_module start
@@ -119,6 +120,7 @@
         "soong_module_name": attr.string(mandatory = True),
         "soong_module_variant": attr.string(),
         "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
+        "api": attr.string(),
         "arch_paths": attr.string_list(),
         "arch_paths_exclude": attr.string_list(),
         "bool_prop": attr.bool(),
@@ -149,6 +151,7 @@
         "soong_module_name": attr.string(mandatory = True),
         "soong_module_variant": attr.string(),
         "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
+        "api": attr.string(),
         "arch_paths": attr.string_list(),
         "arch_paths_exclude": attr.string_list(),
         "bool_prop": attr.bool(),
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
index c23779e..72d3940 100644
--- a/bp2build/cc_binary_conversion_test.go
+++ b/bp2build/cc_binary_conversion_test.go
@@ -201,20 +201,27 @@
 	})
 }
 
-func TestCcBinaryVersionScript(t *testing.T) {
+func TestCcBinaryVersionScriptAndDynamicList(t *testing.T) {
 	runCcBinaryTests(t, ccBinaryBp2buildTestCase{
-		description: `version script`,
+		description: `version script and dynamic list`,
 		blueprint: `
 {rule_name} {
     name: "foo",
     include_build_directory: false,
     version_script: "vs",
+    dynamic_list: "dynamic.list",
 }
 `,
 		targets: []testBazelTarget{
 			{"cc_binary", "foo", AttrNameToString{
-				"additional_linker_inputs": `["vs"]`,
-				"linkopts":                 `["-Wl,--version-script,$(location vs)"]`,
+				"additional_linker_inputs": `[
+        "vs",
+        "dynamic.list",
+    ]`,
+				"linkopts": `[
+        "-Wl,--version-script,$(location vs)",
+        "-Wl,--dynamic-list,$(location dynamic.list)",
+    ]`,
 			},
 			},
 		},
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 1b8e9b4..728a4dc 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -870,9 +870,9 @@
 			})}})
 }
 
-func TestCcLibraryNonConfiguredVersionScript(t *testing.T) {
+func TestCcLibraryNonConfiguredVersionScriptAndDynamicList(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
-		Description:                "cc_library non-configured version script",
+		Description:                "cc_library non-configured version script and dynamic list",
 		ModuleTypeUnderTest:        "cc_library",
 		ModuleTypeUnderTestFactory: cc.LibraryFactory,
 		Dir:                        "foo/bar",
@@ -882,6 +882,7 @@
     name: "a",
     srcs: ["a.cpp"],
     version_script: "v.map",
+    dynamic_list: "dynamic.list",
     bazel_module: { bp2build_available: true },
     include_build_directory: false,
 }
@@ -889,17 +890,23 @@
 		},
 		Blueprint: soongCcLibraryPreamble,
 		ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{
-			"additional_linker_inputs": `["v.map"]`,
-			"linkopts":                 `["-Wl,--version-script,$(location v.map)"]`,
-			"srcs":                     `["a.cpp"]`,
+			"additional_linker_inputs": `[
+        "v.map",
+        "dynamic.list",
+    ]`,
+			"linkopts": `[
+        "-Wl,--version-script,$(location v.map)",
+        "-Wl,--dynamic-list,$(location dynamic.list)",
+    ]`,
+			"srcs": `["a.cpp"]`,
 		}),
 	},
 	)
 }
 
-func TestCcLibraryConfiguredVersionScript(t *testing.T) {
+func TestCcLibraryConfiguredVersionScriptAndDynamicList(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
-		Description:                "cc_library configured version script",
+		Description:                "cc_library configured version script and dynamic list",
 		ModuleTypeUnderTest:        "cc_library",
 		ModuleTypeUnderTestFactory: cc.LibraryFactory,
 		Dir:                        "foo/bar",
@@ -911,9 +918,11 @@
    arch: {
      arm: {
        version_script: "arm.map",
+       dynamic_list: "dynamic_arm.list",
      },
      arm64: {
        version_script: "arm64.map",
+       dynamic_list: "dynamic_arm64.list",
      },
    },
 
@@ -925,13 +934,25 @@
 		Blueprint: soongCcLibraryPreamble,
 		ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{
 			"additional_linker_inputs": `select({
-        "//build/bazel/platforms/arch:arm": ["arm.map"],
-        "//build/bazel/platforms/arch:arm64": ["arm64.map"],
+        "//build/bazel/platforms/arch:arm": [
+            "arm.map",
+            "dynamic_arm.list",
+        ],
+        "//build/bazel/platforms/arch:arm64": [
+            "arm64.map",
+            "dynamic_arm64.list",
+        ],
         "//conditions:default": [],
     })`,
 			"linkopts": `select({
-        "//build/bazel/platforms/arch:arm": ["-Wl,--version-script,$(location arm.map)"],
-        "//build/bazel/platforms/arch:arm64": ["-Wl,--version-script,$(location arm64.map)"],
+        "//build/bazel/platforms/arch:arm": [
+            "-Wl,--version-script,$(location arm.map)",
+            "-Wl,--dynamic-list,$(location dynamic_arm.list)",
+        ],
+        "//build/bazel/platforms/arch:arm64": [
+            "-Wl,--version-script,$(location arm64.map)",
+            "-Wl,--dynamic-list,$(location dynamic_arm64.list)",
+        ],
         "//conditions:default": [],
     })`,
 			"srcs": `["a.cpp"]`,
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 522c10e..4d8b8a4 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -97,7 +97,7 @@
 		targets.sort()
 
 		var content string
-		if mode == Bp2Build {
+		if mode == Bp2Build || mode == ApiBp2build {
 			content = `# READ THIS FIRST:
 # This file was automatically generated by bp2build for the Bazel migration project.
 # Feel free to edit or test it, but do *not* check it into your version control system.
diff --git a/bp2build/ndk_headers_conversion_test.go b/bp2build/ndk_headers_conversion_test.go
index c7cc6b2..9d0f1f2 100644
--- a/bp2build/ndk_headers_conversion_test.go
+++ b/bp2build/ndk_headers_conversion_test.go
@@ -79,7 +79,7 @@
 				"hdrs": testCase.expectedHdrs,
 			},
 		)
-		RunBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+		RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
 			Description:          testCase.desc,
 			Blueprint:            fmt.Sprintf(bpTemplate, testCase.srcs, testCase.excludeSrcs),
 			ExpectedBazelTargets: []string{expectedBazelTarget},
@@ -121,7 +121,7 @@
 				"include_dir": testCase.expectedIncludeDir,
 			},
 		)
-		RunBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+		RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
 			Description:          testCase.desc,
 			Blueprint:            fmt.Sprintf(bpTemplate, testCase.from),
 			ExpectedBazelTargets: []string{expectedBazelTarget},
@@ -156,7 +156,7 @@
     ]`,
 		},
 	)
-	RunBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+	RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
 		Blueprint:            bp,
 		Filesystem:           fs,
 		ExpectedBazelTargets: []string{expectedBazelTarget},
diff --git a/bp2build/ndk_library_conversion_test.go b/bp2build/ndk_library_conversion_test.go
index 244ce20..819ab25 100644
--- a/bp2build/ndk_library_conversion_test.go
+++ b/bp2build/ndk_library_conversion_test.go
@@ -37,7 +37,7 @@
 			"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
 		},
 	)
-	RunBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+	RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
 		Blueprint:            bp,
 		ExpectedBazelTargets: []string{expectedBazelTarget},
 	})
@@ -69,7 +69,7 @@
 			"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
 		},
 	)
-	RunBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
+	RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
 		Blueprint:            bp,
 		Filesystem:           fs,
 		ExpectedBazelTargets: []string{expectedBazelTarget},
diff --git a/bp2build/testing.go b/bp2build/testing.go
index c2c1b19..31aa830 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -24,6 +24,8 @@
 	"strings"
 	"testing"
 
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/android/allowlists"
 	"android/soong/bazel"
@@ -88,6 +90,22 @@
 }
 
 func RunBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc Bp2buildTestCase) {
+	bp2buildSetup := func(ctx *android.TestContext) {
+		registerModuleTypes(ctx)
+		ctx.RegisterForBazelConversion()
+	}
+	runBp2BuildTestCaseWithSetup(t, bp2buildSetup, tc)
+}
+
+func RunApiBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc Bp2buildTestCase) {
+	apiBp2BuildSetup := func(ctx *android.TestContext) {
+		registerModuleTypes(ctx)
+		ctx.RegisterForApiBazelConversion()
+	}
+	runBp2BuildTestCaseWithSetup(t, apiBp2BuildSetup, tc)
+}
+
+func runBp2BuildTestCaseWithSetup(t *testing.T, setup func(ctx *android.TestContext), tc Bp2buildTestCase) {
 	t.Helper()
 	dir := "."
 	filesystem := make(map[string][]byte)
@@ -103,7 +121,7 @@
 	config := android.TestConfig(buildDir, nil, tc.Blueprint, filesystem)
 	ctx := android.NewTestContext(config)
 
-	registerModuleTypes(ctx)
+	setup(ctx)
 	ctx.RegisterModuleType(tc.ModuleTypeUnderTest, tc.ModuleTypeUnderTestFactory)
 
 	// A default configuration for tests to not have to specify bp2build_available on top level targets.
@@ -118,7 +136,6 @@
 		})
 	}
 	ctx.RegisterBp2BuildConfig(bp2buildConfig)
-	ctx.RegisterForBazelConversion()
 
 	_, parseErrs := ctx.ParseFileList(dir, toParse)
 	if errored(t, tc, parseErrs) {
@@ -198,6 +215,8 @@
 
 	// Prop used to indicate this conversion should be 1 module -> multiple targets
 	One_to_many_prop *bool
+
+	Api *string // File describing the APIs of this module
 }
 
 type customModule struct {
@@ -320,6 +339,7 @@
 	String_ptr_prop     *string
 	String_list_prop    []string
 	Arch_paths          bazel.LabelListAttribute
+	Api                 bazel.LabelAttribute
 }
 
 func (m *customModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
@@ -364,6 +384,23 @@
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
 }
 
+var _ android.ApiProvider = (*customModule)(nil)
+
+func (c *customModule) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class: "custom_api_contribution",
+	}
+	apiAttribute := bazel.MakeLabelAttribute(
+		android.BazelLabelForModuleSrcSingle(ctx, proptools.String(c.props.Api)).Label,
+	)
+	attrs := &customBazelModuleAttributes{
+		Api: *apiAttribute,
+	}
+	ctx.CreateBazelTargetModule(props,
+		android.CommonAttributes{Name: c.Name()},
+		attrs)
+}
+
 // A bp2build mutator that uses load statements and creates a 1:M mapping from
 // module to target.
 func customBp2buildOneToMany(ctx android.TopDownMutatorContext, m *customModule) {
diff --git a/build_test.bash b/build_test.bash
index 8b91e2c..6f0fba7 100755
--- a/build_test.bash
+++ b/build_test.bash
@@ -59,7 +59,7 @@
 
 echo
 echo "Running Bazel smoke test..."
-STANDALONE_BAZEL=true "${TOP}/tools/bazel" --batch --max_idle_secs=1 info
+STANDALONE_BAZEL=true "${TOP}/tools/bazel" --batch --max_idle_secs=1 help
 
 echo
 echo "Running Soong test..."
diff --git a/cc/api_level.go b/cc/api_level.go
index fd145a9..8f9e1f6 100644
--- a/cc/api_level.go
+++ b/cc/api_level.go
@@ -20,7 +20,7 @@
 	"android/soong/android"
 )
 
-func minApiForArch(ctx android.BaseModuleContext,
+func MinApiForArch(ctx android.EarlyModuleContext,
 	arch android.ArchType) android.ApiLevel {
 
 	switch arch {
@@ -28,6 +28,8 @@
 		return ctx.Config().MinSupportedSdkVersion()
 	case android.Arm64, android.X86_64:
 		return android.FirstLp64Version
+	case android.Riscv64:
+		return android.FutureApiLevel
 	default:
 		panic(fmt.Errorf("Unknown arch %q", arch))
 	}
@@ -36,7 +38,7 @@
 func nativeApiLevelFromUser(ctx android.BaseModuleContext,
 	raw string) (android.ApiLevel, error) {
 
-	min := minApiForArch(ctx, ctx.Arch().ArchType)
+	min := MinApiForArch(ctx, ctx.Arch().ArchType)
 	if raw == "minimum" {
 		return min, nil
 	}
diff --git a/cc/builder.go b/cc/builder.go
index 0bea9da..39f7dc3 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -672,11 +672,16 @@
 			tidyCmd := "${config.ClangBin}/clang-tidy"
 
 			rule := clangTidy
+			reducedCFlags := moduleFlags
 			if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_CLANG_TIDY") {
 				rule = clangTidyRE
+				// b/248371171, work around RBE input processor problem
+				// some cflags rejected by input processor, but usually
+				// do not affect included files or clang-tidy
+				reducedCFlags = config.TidyReduceCFlags(reducedCFlags)
 			}
 
-			sharedCFlags := shareFlags("cFlags", moduleFlags)
+			sharedCFlags := shareFlags("cFlags", reducedCFlags)
 			srcRelPath := srcFile.Rel()
 
 			// Add the .tidy rule
diff --git a/cc/cc.go b/cc/cc.go
index 598f6be..d4eaa53 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -3334,6 +3334,11 @@
 			return android.Paths{c.outputFile.Path()}, nil
 		}
 		return android.Paths{}, nil
+	case "unstripped":
+		if c.linker != nil {
+			return android.PathsIfNonNil(c.linker.unstrippedOutputFilePath()), nil
+		}
+		return nil, nil
 	default:
 		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
 	}
@@ -3740,6 +3745,13 @@
 		} else {
 			sharedOrStaticLibraryBp2Build(ctx, c, false)
 		}
+	}
+}
+
+var _ android.ApiProvider = (*Module)(nil)
+
+func (c *Module) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
+	switch c.typ() {
 	case ndkLibrary:
 		ndkLibraryBp2build(ctx, c)
 	}
@@ -3832,6 +3844,15 @@
 	}
 }
 
+func (c *Module) Partition() string {
+	if p, ok := c.installer.(interface {
+		getPartition() string
+	}); ok {
+		return p.getPartition()
+	}
+	return ""
+}
+
 var Bool = proptools.Bool
 var BoolDefault = proptools.BoolDefault
 var BoolPtr = proptools.BoolPtr
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index 23bda66..af49e88 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"regexp"
 	"strings"
 )
 
@@ -237,3 +238,11 @@
 	}
 	return flags
 }
+
+var (
+	removedCFlags = regexp.MustCompile(" -fsanitize=[^ ]*memtag-[^ ]* ")
+)
+
+func TidyReduceCFlags(flags string) string {
+	return removedCFlags.ReplaceAllString(flags, " ")
+}
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 8a8c107..13c94ad 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -103,6 +103,7 @@
 	*baseCompiler
 	fuzzPackagedModule  fuzz.FuzzPackagedModule
 	installedSharedDeps []string
+	sharedLibraries     android.Paths
 }
 
 func (fuzz *fuzzBinary) fuzzBinary() bool {
@@ -142,13 +143,6 @@
 	return flags
 }
 
-func UnstrippedOutputFile(module android.Module) android.Path {
-	if mod, ok := module.(LinkableInterface); ok {
-		return mod.UnstrippedOutputFile()
-	}
-	panic("UnstrippedOutputFile called on non-LinkableInterface module: " + module.Name())
-}
-
 // IsValidSharedDependency takes a module and determines if it is a unique shared library
 // that should be installed in the fuzz target output directories. This function
 // returns true, unless:
@@ -270,22 +264,9 @@
 	}
 
 	// Grab the list of required shared libraries.
-	seen := make(map[string]bool)
-	var sharedLibraries android.Paths
-	ctx.WalkDeps(func(child, parent android.Module) bool {
-		if seen[child.Name()] {
-			return false
-		}
-		seen[child.Name()] = true
+	fuzzBin.sharedLibraries = CollectAllSharedDependencies(ctx)
 
-		if IsValidSharedDependency(child) {
-			sharedLibraries = append(sharedLibraries, child.(*Module).UnstrippedOutputFile())
-			return true
-		}
-		return false
-	})
-
-	for _, lib := range sharedLibraries {
+	for _, lib := range fuzzBin.sharedLibraries {
 		fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
 			sharedLibraryInstallLocation(
 				lib, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
@@ -412,9 +393,6 @@
 		archDir := android.PathForIntermediates(ctx, intermediatePath, hostOrTargetString, archString)
 		archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
 
-		// Grab the list of required shared libraries.
-		sharedLibraries := fuzz.CollectAllSharedDependencies(ctx, module, UnstrippedOutputFile, IsValidSharedDependency)
-
 		var files []fuzz.FileToZip
 		builder := android.NewRuleBuilder(pctx, ctx)
 
@@ -422,10 +400,10 @@
 		files = s.PackageArtifacts(ctx, module, fpm, archDir, builder)
 
 		// Package shared libraries
-		files = append(files, GetSharedLibsToZip(sharedLibraries, ccModule, &s.FuzzPackager, archString, sharedLibsInstallDirPrefix, &sharedLibraryInstalled)...)
+		files = append(files, GetSharedLibsToZip(fuzzModule.sharedLibraries, ccModule, &s.FuzzPackager, archString, sharedLibsInstallDirPrefix, &sharedLibraryInstalled)...)
 
 		// The executable.
-		files = append(files, fuzz.FileToZip{ccModule.UnstrippedOutputFile(), ""})
+		files = append(files, fuzz.FileToZip{android.OutputFileForModule(ctx, ccModule, "unstripped"), ""})
 
 		archDirs[archOs], ok = s.BuildZipFile(ctx, module, fpm, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
 		if !ok {
@@ -494,3 +472,46 @@
 	}
 	return files
 }
+
+// CollectAllSharedDependencies search over the provided module's dependencies using
+// VisitDirectDeps and WalkDeps to enumerate all shared library dependencies.
+// VisitDirectDeps is used first to avoid incorrectly using the core libraries (sanitizer
+// runtimes, libc, libdl, etc.) from a dependency. This may cause issues when dependencies
+// have explicit sanitizer tags, as we may get a dependency on an unsanitized libc, etc.
+func CollectAllSharedDependencies(ctx android.ModuleContext) android.Paths {
+	seen := make(map[string]bool)
+	recursed := make(map[string]bool)
+
+	var sharedLibraries android.Paths
+
+	// Enumerate the first level of dependencies, as we discard all non-library
+	// modules in the BFS loop below.
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		if !IsValidSharedDependency(dep) {
+			return
+		}
+		if seen[ctx.OtherModuleName(dep)] {
+			return
+		}
+		seen[ctx.OtherModuleName(dep)] = true
+		sharedLibraries = append(sharedLibraries, android.OutputFileForModule(ctx, dep, "unstripped"))
+	})
+
+	ctx.WalkDeps(func(child, parent android.Module) bool {
+		if !IsValidSharedDependency(child) {
+			return false
+		}
+		if !seen[ctx.OtherModuleName(child)] {
+			seen[ctx.OtherModuleName(child)] = true
+			sharedLibraries = append(sharedLibraries, android.OutputFileForModule(ctx, child, "unstripped"))
+		}
+
+		if recursed[ctx.OtherModuleName(child)] {
+			return false
+		}
+		recursed[ctx.OtherModuleName(child)] = true
+		return true
+	})
+
+	return sharedLibraries
+}
diff --git a/cc/library.go b/cc/library.go
index 6594808..13a7a3e 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -1089,6 +1089,12 @@
 		} else {
 			flag = "--systemapi"
 		}
+		// b/184712170, unless the lib is an NDK library, exclude all public symbols from
+		// the stub so that it is mandated that all symbols are explicitly marked with
+		// either apex or systemapi.
+		if !ctx.Module().(*Module).IsNdk(ctx.Config()) {
+			flag = flag + " --no-ndk"
+		}
 		nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile,
 			android.ApiLevelOrPanic(ctx, library.MutatedProperties.StubsVersion), flag)
 		objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
@@ -2227,6 +2233,10 @@
 	mod.ModuleBase.MakeUninstallable()
 }
 
+func (library *libraryDecorator) getPartition() string {
+	return library.path.Partition()
+}
+
 func (library *libraryDecorator) getAPIListCoverageXMLPath() android.ModuleOutPath {
 	return library.apiListCoverageXmlPath
 }
diff --git a/cc/linkable.go b/cc/linkable.go
index 2316d86..0522fc6 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -253,6 +253,9 @@
 
 	// VndkVersion returns the VNDK version string for this module.
 	VndkVersion() string
+
+	// Partition returns the partition string for this module.
+	Partition() string
 }
 
 var (
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 08e2a39..7354be9 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -173,7 +173,9 @@
 	}, attrs)
 }
 
-func (h *headerModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+var _ android.ApiProvider = (*headerModule)(nil)
+
+func (h *headerModule) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
 	// Generate `cc_api_headers` target for Multi-tree API export
 	createCcApiHeadersTarget(ctx, h.properties.Srcs, h.properties.Exclude_srcs, h.properties.From)
 }
@@ -192,7 +194,6 @@
 	module := &headerModule{}
 	module.AddProperties(&module.properties)
 	android.InitAndroidModule(module)
-	android.InitBazelModule(module)
 	return module
 }
 
@@ -263,7 +264,9 @@
 	processHeadersWithVersioner(ctx, fromSrcPath, toOutputPath, srcFiles, installPaths)
 }
 
-func (h *versionedHeaderModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+var _ android.ApiProvider = (*versionedHeaderModule)(nil)
+
+func (h *versionedHeaderModule) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
 	// Glob all .h files under `From`
 	includePattern := headerGlobPattern(proptools.String(h.properties.From))
 	// Generate `cc_api_headers` target for Multi-tree API export
@@ -319,7 +322,6 @@
 	module.AddProperties(&module.properties)
 
 	android.InitAndroidModule(module)
-	android.InitBazelModule(module)
 
 	return module
 }
diff --git a/cc/ndkstubgen/__init__.py b/cc/ndkstubgen/__init__.py
index f893d41..efad70a 100755
--- a/cc/ndkstubgen/__init__.py
+++ b/cc/ndkstubgen/__init__.py
@@ -111,6 +111,11 @@
         action='store_true',
         dest='systemapi',
         help='Use the SystemAPI variant.')
+    parser.add_argument(
+        '--no-ndk',
+        action='store_false',
+        dest='ndk',
+        help='Do not include NDK APIs.')
 
     parser.add_argument('--api-map',
                         type=resolved_path,
@@ -147,7 +152,7 @@
         verbosity = 2
     logging.basicConfig(level=verbose_map[verbosity])
 
-    filt = symbolfile.Filter(args.arch, api, args.llndk, args.apex, args.systemapi)
+    filt = symbolfile.Filter(args.arch, api, args.llndk, args.apex, args.systemapi, args.ndk)
     with args.symbol_file.open() as symbol_file:
         try:
           versions = symbolfile.SymbolFileParser(symbol_file, api_map, filt).parse()
diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py
index 450719b..1e0bdf3 100755
--- a/cc/ndkstubgen/test_ndkstubgen.py
+++ b/cc/ndkstubgen/test_ndkstubgen.py
@@ -424,6 +424,45 @@
         """)
         self.assertEqual(expected_version, version_file.getvalue())
 
+    def test_integration_with_nondk(self) -> None:
+        input_file = io.StringIO(textwrap.dedent("""\
+            VERSION_1 {
+                global:
+                    foo;
+                    bar; # apex
+                local:
+                    *;
+            };
+        """))
+        f = copy(self.filter)
+        f.apex = True
+        f.ndk = False   # ndk symbols should be excluded
+        parser = symbolfile.SymbolFileParser(input_file, {}, f)
+        versions = parser.parse()
+
+        src_file = io.StringIO()
+        version_file = io.StringIO()
+        symbol_list_file = io.StringIO()
+        f = copy(self.filter)
+        f.apex = True
+        f.ndk = False   # ndk symbols should be excluded
+        generator = ndkstubgen.Generator(src_file,
+                                         version_file, symbol_list_file, f)
+        generator.write(versions)
+
+        expected_src = textwrap.dedent("""\
+            void bar() {}
+        """)
+        self.assertEqual(expected_src, src_file.getvalue())
+
+        expected_version = textwrap.dedent("""\
+            VERSION_1 {
+                global:
+                    bar;
+            };
+        """)
+        self.assertEqual(expected_version, version_file.getvalue())
+
     def test_empty_stub(self) -> None:
         """Tests that empty stubs can be generated.
 
diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py
index 471a12f..9bf07f2 100644
--- a/cc/symbolfile/__init__.py
+++ b/cc/symbolfile/__init__.py
@@ -208,12 +208,14 @@
     symbol should be omitted or not
     """
 
-    def __init__(self, arch: Arch, api: int, llndk: bool = False, apex: bool = False, systemapi: bool = False):
+    def __init__(self, arch: Arch, api: int, llndk: bool = False, apex: bool = False, systemapi:
+                 bool = False, ndk: bool = True):
         self.arch = arch
         self.api = api
         self.llndk = llndk
         self.apex = apex
         self.systemapi = systemapi
+        self.ndk = ndk
 
     def _should_omit_tags(self, tags: Tags) -> bool:
         """Returns True if the tagged object should be omitted.
@@ -253,8 +255,13 @@
 
     def should_omit_symbol(self, symbol: Symbol) -> bool:
         """Returns True if the symbol should be omitted."""
-        return self._should_omit_tags(symbol.tags)
+        if not symbol.tags.has_mode_tags and not self.ndk:
+            # Symbols that don't have mode tags are NDK. They are usually
+            # included, but have to be omitted if NDK symbols are explicitly
+            # filtered-out
+            return True
 
+        return self._should_omit_tags(symbol.tags)
 
 def symbol_in_arch(tags: Tags, arch: Arch) -> bool:
     """Returns true if the symbol is present for the given architecture."""
diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py
index e17a8d0..856b9d7 100644
--- a/cc/symbolfile/test_symbolfile.py
+++ b/cc/symbolfile/test_symbolfile.py
@@ -308,6 +308,20 @@
     def assertInclude(self, f: Filter, s: Symbol) -> None:
         self.assertFalse(f.should_omit_symbol(s))
 
+    def test_omit_ndk(self) -> None:
+        f_ndk = self.filter
+        f_nondk = copy(f_ndk)
+        f_nondk.ndk = False
+        f_nondk.apex = True
+
+        s_ndk = Symbol('foo', Tags())
+        s_nonndk = Symbol('foo', Tags.from_strs(['apex']))
+
+        self.assertInclude(f_ndk, s_ndk)
+        self.assertOmit(f_ndk, s_nonndk)
+        self.assertOmit(f_nondk, s_ndk)
+        self.assertInclude(f_nondk, s_nonndk)
+
     def test_omit_llndk(self) -> None:
         f_none = self.filter
         f_llndk = copy(f_none)
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 0b8cc88..770ad0c 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -24,6 +24,7 @@
 	"time"
 
 	"android/soong/android"
+	"android/soong/bazel"
 	"android/soong/bp2build"
 	"android/soong/shared"
 	"android/soong/ui/metrics/bp2build_metrics_proto"
@@ -48,11 +49,12 @@
 	delveListen string
 	delvePath   string
 
-	moduleGraphFile   string
-	moduleActionsFile string
-	docFile           string
-	bazelQueryViewDir string
-	bp2buildMarker    string
+	moduleGraphFile     string
+	moduleActionsFile   string
+	docFile             string
+	bazelQueryViewDir   string
+	bazelApiBp2buildDir string
+	bp2buildMarker      string
 
 	cmdlineArgs bootstrap.Args
 )
@@ -81,6 +83,7 @@
 	flag.StringVar(&moduleActionsFile, "module_actions_file", "", "JSON file to output inputs/outputs of actions of modules")
 	flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output")
 	flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory relative to --top")
+	flag.StringVar(&bazelApiBp2buildDir, "bazel_api_bp2build_dir", "", "path to the bazel api_bp2build directory relative to --top")
 	flag.StringVar(&bp2buildMarker, "bp2build_marker", "", "If set, run bp2build, touch the specified marker file then exit")
 	flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output")
 	flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
@@ -129,6 +132,8 @@
 		buildMode = android.Bp2build
 	} else if bazelQueryViewDir != "" {
 		buildMode = android.GenerateQueryView
+	} else if bazelApiBp2buildDir != "" {
+		buildMode = android.ApiBp2build
 	} else if moduleGraphFile != "" {
 		buildMode = android.GenerateModuleGraph
 	} else if docFile != "" {
@@ -178,7 +183,7 @@
 	defer ctx.EventHandler.End("queryview")
 	codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.QueryView)
 	absoluteQueryViewDir := shared.JoinPath(topDir, queryviewDir)
-	if err := createBazelQueryView(codegenContext, absoluteQueryViewDir); err != nil {
+	if err := createBazelWorkspace(codegenContext, absoluteQueryViewDir); err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
 	}
@@ -186,6 +191,96 @@
 	touch(shared.JoinPath(topDir, queryviewMarker))
 }
 
+// Run the code-generation phase to convert API contributions to BUILD files.
+// Return marker file for the new synthetic workspace
+func runApiBp2build(configuration android.Config, extraNinjaDeps []string) string {
+	// Create a new context and register mutators that are only meaningful to API export
+	ctx := android.NewContext(configuration)
+	ctx.EventHandler.Begin("api_bp2build")
+	defer ctx.EventHandler.End("api_bp2build")
+	ctx.SetNameInterface(newNameResolver(configuration))
+	ctx.RegisterForApiBazelConversion()
+
+	// Register the Android.bp files in the tree
+	// Add them to the workspace's .d file
+	ctx.SetModuleListFile(cmdlineArgs.ModuleListFile)
+	if paths, err := ctx.ListModulePaths("."); err == nil {
+		extraNinjaDeps = append(extraNinjaDeps, paths...)
+	} else {
+		panic(err)
+	}
+
+	// Run the loading and analysis phase
+	ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs,
+		bootstrap.StopBeforePrepareBuildActions,
+		ctx.Context,
+		configuration)
+	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
+
+	// Add the globbed dependencies
+	globs := writeBuildGlobsNinjaFile(ctx, configuration.SoongOutDir(), configuration)
+	ninjaDeps = append(ninjaDeps, globs...)
+
+	// Run codegen to generate BUILD files
+	codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.ApiBp2build)
+	absoluteApiBp2buildDir := shared.JoinPath(topDir, bazelApiBp2buildDir)
+	if err := createBazelWorkspace(codegenContext, absoluteApiBp2buildDir); err != nil {
+		fmt.Fprintf(os.Stderr, "%s", err)
+		os.Exit(1)
+	}
+	ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
+
+	// Create soong_injection repository
+	soongInjectionFiles := bp2build.CreateSoongInjectionFiles(configuration, bp2build.CodegenMetrics{})
+	absoluteSoongInjectionDir := shared.JoinPath(topDir, configuration.SoongOutDir(), bazel.SoongInjectionDirName)
+	for _, file := range soongInjectionFiles {
+		writeReadOnlyFile(absoluteSoongInjectionDir, file)
+	}
+
+	workspace := shared.JoinPath(configuration.SoongOutDir(), "api_bp2build")
+
+	excludes := bazelArtifacts()
+	// Exclude all src BUILD files
+	excludes = append(excludes, apiBuildFileExcludes()...)
+
+	// Create the symlink forest
+	symlinkDeps := bp2build.PlantSymlinkForest(
+		configuration,
+		topDir,
+		workspace,
+		bazelApiBp2buildDir,
+		".",
+		excludes)
+	ninjaDeps = append(ninjaDeps, symlinkDeps...)
+
+	workspaceMarkerFile := workspace + ".marker"
+	writeDepFile(workspaceMarkerFile, *ctx.EventHandler, ninjaDeps)
+	touch(shared.JoinPath(topDir, workspaceMarkerFile))
+	return workspaceMarkerFile
+}
+
+// With some exceptions, api_bp2build does not have any dependencies on the checked-in BUILD files
+// Exclude them from the generated workspace to prevent unrelated errors during the loading phase
+func apiBuildFileExcludes() []string {
+	ret := make([]string, 0)
+
+	srcs, err := getExistingBazelRelatedFiles(topDir)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error determining existing Bazel-related files: %s\n", err)
+		os.Exit(1)
+	}
+	for _, src := range srcs {
+		if src != "WORKSPACE" &&
+			src != "BUILD" &&
+			src != "BUILD.bazel" &&
+			!strings.HasPrefix(src, "build/bazel") &&
+			!strings.HasPrefix(src, "prebuilts/clang") {
+			ret = append(ret, src)
+		}
+	}
+	return ret
+}
+
 func writeMetrics(configuration android.Config, eventHandler metrics.EventHandler, metricsDir string) {
 	if len(metricsDir) < 1 {
 		fmt.Fprintf(os.Stderr, "\nMissing required env var for generating soong metrics: LOG_DIR\n")
@@ -248,6 +343,8 @@
 		return bp2buildMarker
 	} else if configuration.IsMixedBuildsEnabled() {
 		runMixedModeBuild(configuration, ctx, extraNinjaDeps)
+	} else if configuration.BuildMode == android.ApiBp2build {
+		return runApiBp2build(configuration, extraNinjaDeps)
 	} else {
 		var stopBefore bootstrap.StopBefore
 		if configuration.BuildMode == android.GenerateModuleGraph {
@@ -476,6 +573,16 @@
 	return files, nil
 }
 
+func bazelArtifacts() []string {
+	return []string{
+		"bazel-bin",
+		"bazel-genfiles",
+		"bazel-out",
+		"bazel-testlogs",
+		"bazel-" + filepath.Base(topDir),
+	}
+}
+
 // Run Soong in the bp2build mode. This creates a standalone context that registers
 // an alternate pipeline of mutators and singletons specifically for generating
 // Bazel BUILD files instead of Ninja files.
@@ -524,13 +631,7 @@
 		generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build")
 		workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace")
 
-		excludes := []string{
-			"bazel-bin",
-			"bazel-genfiles",
-			"bazel-out",
-			"bazel-testlogs",
-			"bazel-" + filepath.Base(topDir),
-		}
+		excludes := bazelArtifacts()
 
 		if outDir[0] != '/' {
 			excludes = append(excludes, outDir)
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index 983dbf0..cd1d6fb 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -23,8 +23,9 @@
 	"android/soong/bp2build"
 )
 
-func createBazelQueryView(ctx *bp2build.CodegenContext, bazelQueryViewDir string) error {
-	os.RemoveAll(bazelQueryViewDir)
+// A helper function to generate a Read-only Bazel workspace in outDir
+func createBazelWorkspace(ctx *bp2build.CodegenContext, outDir string) error {
+	os.RemoveAll(outDir)
 	ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
 
 	res, err := bp2build.GenerateBazelTargets(ctx, true)
@@ -33,9 +34,9 @@
 	}
 
 	filesToWrite := bp2build.CreateBazelFiles(ctx.Config(), ruleShims, res.BuildDirToTargets(),
-		bp2build.QueryView)
+		ctx.Mode())
 	for _, f := range filesToWrite {
-		if err := writeReadOnlyFile(bazelQueryViewDir, f); err != nil {
+		if err := writeReadOnlyFile(outDir, f); err != nil {
 			return err
 		}
 	}
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 362a8ef..baad58e 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -296,27 +296,37 @@
 }
 
 func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	if p.properties.Src == nil {
-		ctx.PropertyErrorf("src", "missing prebuilt source file")
-		return
-	}
-	p.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(p.properties.Src))
-
-	// Determine the output file basename.
-	// If Filename is set, use the name specified by the property.
-	// If Filename_from_src is set, use the source file name.
-	// Otherwise use the module name.
 	filename := proptools.String(p.properties.Filename)
 	filenameFromSrc := proptools.Bool(p.properties.Filename_from_src)
-	if filename != "" {
-		if filenameFromSrc {
-			ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
-			return
+	if p.properties.Src != nil {
+		p.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(p.properties.Src))
+
+		// Determine the output file basename.
+		// If Filename is set, use the name specified by the property.
+		// If Filename_from_src is set, use the source file name.
+		// Otherwise use the module name.
+		if filename != "" {
+			if filenameFromSrc {
+				ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
+				return
+			}
+		} else if filenameFromSrc {
+			filename = p.sourceFilePath.Base()
+		} else {
+			filename = ctx.ModuleName()
 		}
-	} else if filenameFromSrc {
-		filename = p.sourceFilePath.Base()
+	} else if ctx.Config().AllowMissingDependencies() {
+		// If no srcs was set and AllowMissingDependencies is enabled then
+		// mark the module as missing dependencies and set a fake source path
+		// and file name.
+		ctx.AddMissingDependencies([]string{"MISSING_PREBUILT_SRC_FILE"})
+		p.sourceFilePath = android.PathForModuleSrc(ctx)
+		if filename == "" {
+			filename = ctx.ModuleName()
+		}
 	} else {
-		filename = ctx.ModuleName()
+		ctx.PropertyErrorf("src", "missing prebuilt source file")
+		return
 	}
 	p.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
 
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index cf1f6d7..a6477dd 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -195,6 +195,30 @@
 	}
 }
 
+func TestPrebuiltEtcAllowMissingDependencies(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForPrebuiltEtcTest,
+		android.PrepareForTestDisallowNonExistentPaths,
+		android.FixtureModifyConfig(
+			func(config android.Config) {
+				config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
+			}),
+	).RunTestWithBp(t, `
+		prebuilt_etc {
+			name: "foo.conf",
+			filename_from_src: true,
+			arch: {
+				x86: {
+					src: "x86.conf",
+				},
+			},
+		}
+	`)
+
+	android.AssertStringEquals(t, "expected error rule", "android/soong/android.Error",
+		result.ModuleForTests("foo.conf", "android_arm64_armv8-a").Output("foo.conf").Rule.String())
+}
+
 func TestPrebuiltRootInstallDirPath(t *testing.T) {
 	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_root {
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index c8cd21b..eb248bb 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -379,42 +379,3 @@
 	sort.Strings(fuzzTargets)
 	ctx.Strict(targets, strings.Join(fuzzTargets, " "))
 }
-
-// CollectAllSharedDependencies performs a breadth-first search over the provided module's
-// dependencies using `visitDirectDeps` to enumerate all shared library
-// dependencies. We require breadth-first expansion, as otherwise we may
-// incorrectly use the core libraries (sanitizer runtimes, libc, libdl, etc.)
-// from a dependency. This may cause issues when dependencies have explicit
-// sanitizer tags, as we may get a dependency on an unsanitized libc, etc.
-func CollectAllSharedDependencies(ctx android.SingletonContext, module android.Module, unstrippedOutputFile func(module android.Module) android.Path, isValidSharedDependency func(dependency android.Module) bool) android.Paths {
-	var fringe []android.Module
-
-	seen := make(map[string]bool)
-
-	// Enumerate the first level of dependencies, as we discard all non-library
-	// modules in the BFS loop below.
-	ctx.VisitDirectDeps(module, func(dep android.Module) {
-		if isValidSharedDependency(dep) {
-			fringe = append(fringe, dep)
-		}
-	})
-
-	var sharedLibraries android.Paths
-
-	for i := 0; i < len(fringe); i++ {
-		module := fringe[i]
-		if seen[module.Name()] {
-			continue
-		}
-		seen[module.Name()] = true
-
-		sharedLibraries = append(sharedLibraries, unstrippedOutputFile(module))
-		ctx.VisitDirectDeps(module, func(dep android.Module) {
-			if isValidSharedDependency(dep) && !seen[dep.Name()] {
-				fringe = append(fringe, dep)
-			}
-		})
-	}
-
-	return sharedLibraries
-}
diff --git a/java/androidmk.go b/java/androidmk.go
index 75ac0e7..cd86880 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"io"
+	"strings"
 
 	"android/soong/android"
 )
@@ -398,6 +399,19 @@
 				} else {
 					for _, jniLib := range app.jniLibs {
 						entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name)
+						var partitionTag string
+
+						// Mimic the creation of partition_tag in build/make,
+						// which defaults to an empty string when the partition is system.
+						// Otherwise, capitalize with a leading _
+						if jniLib.partition == "system" {
+							partitionTag = ""
+						} else {
+							split := strings.Split(jniLib.partition, "/")
+							partitionTag = "_" + strings.ToUpper(split[len(split)-1])
+						}
+						entries.AddStrings("LOCAL_SOONG_JNI_LIBS_PARTITION_"+jniLib.target.Arch.ArchType.String(),
+							jniLib.name+":"+partitionTag)
 					}
 				}
 
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 197da4f..1232cd1 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -19,6 +19,9 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/cc"
+
+	"github.com/google/blueprint/proptools"
 )
 
 func TestRequired(t *testing.T) {
@@ -252,3 +255,149 @@
 		android.AssertDeepEquals(t, "overrides property", expected.overrides, actual)
 	}
 }
+
+func TestJniPartition(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "libjni_system",
+			system_shared_libs: [],
+			sdk_version: "current",
+			stl: "none",
+		}
+
+		cc_library {
+			name: "libjni_system_ext",
+			system_shared_libs: [],
+			sdk_version: "current",
+			stl: "none",
+			system_ext_specific: true,
+		}
+
+		cc_library {
+			name: "libjni_odm",
+			system_shared_libs: [],
+			sdk_version: "current",
+			stl: "none",
+			device_specific: true,
+		}
+
+		cc_library {
+			name: "libjni_product",
+			system_shared_libs: [],
+			sdk_version: "current",
+			stl: "none",
+			product_specific: true,
+		}
+
+		cc_library {
+			name: "libjni_vendor",
+			system_shared_libs: [],
+			sdk_version: "current",
+			stl: "none",
+			soc_specific: true,
+		}
+
+		android_app {
+			name: "test_app_system_jni_system",
+			privileged: true,
+			platform_apis: true,
+			certificate: "platform",
+			jni_libs: ["libjni_system"],
+		}
+
+		android_app {
+			name: "test_app_system_jni_system_ext",
+			privileged: true,
+			platform_apis: true,
+			certificate: "platform",
+			jni_libs: ["libjni_system_ext"],
+		}
+
+		android_app {
+			name: "test_app_system_ext_jni_system",
+			privileged: true,
+			platform_apis: true,
+			certificate: "platform",
+			jni_libs: ["libjni_system"],
+			system_ext_specific: true
+		}
+
+		android_app {
+			name: "test_app_system_ext_jni_system_ext",
+			sdk_version: "core_platform",
+			jni_libs: ["libjni_system_ext"],
+			system_ext_specific: true
+		}
+
+		android_app {
+			name: "test_app_product_jni_product",
+			sdk_version: "core_platform",
+			jni_libs: ["libjni_product"],
+			product_specific: true
+		}
+
+		android_app {
+			name: "test_app_vendor_jni_odm",
+			sdk_version: "core_platform",
+			jni_libs: ["libjni_odm"],
+			soc_specific: true
+		}
+
+		android_app {
+			name: "test_app_odm_jni_vendor",
+			sdk_version: "core_platform",
+			jni_libs: ["libjni_vendor"],
+			device_specific: true
+		}
+		android_app {
+			name: "test_app_system_jni_multiple",
+			privileged: true,
+			platform_apis: true,
+			certificate: "platform",
+			jni_libs: ["libjni_system", "libjni_system_ext"],
+		}
+		android_app {
+			name: "test_app_vendor_jni_multiple",
+			sdk_version: "core_platform",
+			jni_libs: ["libjni_odm", "libjni_vendor"],
+			soc_specific: true
+		}
+		`
+	arch := "arm64"
+	ctx := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		cc.PrepareForTestWithCcDefaultModules,
+		android.PrepareForTestWithAndroidMk,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.TestProductVariables.DeviceArch = proptools.StringPtr(arch)
+		}),
+	).
+		RunTestWithBp(t, bp)
+	testCases := []struct {
+		name           string
+		partitionNames []string
+		partitionTags  []string
+	}{
+		{"test_app_system_jni_system", []string{"libjni_system"}, []string{""}},
+		{"test_app_system_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}},
+		{"test_app_system_ext_jni_system", []string{"libjni_system"}, []string{""}},
+		{"test_app_system_ext_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}},
+		{"test_app_product_jni_product", []string{"libjni_product"}, []string{"_PRODUCT"}},
+		{"test_app_vendor_jni_odm", []string{"libjni_odm"}, []string{"_ODM"}},
+		{"test_app_odm_jni_vendor", []string{"libjni_vendor"}, []string{"_VENDOR"}},
+		{"test_app_system_jni_multiple", []string{"libjni_system", "libjni_system_ext"}, []string{"", "_SYSTEM_EXT"}},
+		{"test_app_vendor_jni_multiple", []string{"libjni_odm", "libjni_vendor"}, []string{"_ODM", "_VENDOR"}},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.name, func(t *testing.T) {
+			mod := ctx.ModuleForTests(test.name, "android_common").Module()
+			entry := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)[0]
+			for i := range test.partitionNames {
+				actual := entry.EntryMap["LOCAL_SOONG_JNI_LIBS_PARTITION_"+arch][i]
+				expected := test.partitionNames[i] + ":" + test.partitionTags[i]
+				android.AssertStringEquals(t, "Expected and actual differ", expected, actual)
+			}
+		})
+	}
+}
diff --git a/java/app.go b/java/app.go
index 1955e2a..bbd9d2d 100755
--- a/java/app.go
+++ b/java/app.go
@@ -798,6 +798,7 @@
 						target:         module.Target(),
 						coverageFile:   dep.CoverageOutputFile(),
 						unstrippedFile: dep.UnstrippedOutputFile(),
+						partition:      dep.Partition(),
 					})
 				} else {
 					ctx.ModuleErrorf("dependency %q missing output file", otherName)
@@ -1489,6 +1490,20 @@
 	Certificate_name *string
 }
 
+// ParseCertificateToAttribute splits the certificate prop into a certificate
+// label attribute or a certificate_name string attribute.
+func ParseCertificateToAttribute(ctx android.TopDownMutatorContext, certificate *string) (*string, *bazel.Label) {
+	var certificateLabel *bazel.Label
+	certificateName := proptools.StringDefault(certificate, "")
+	certModule := android.SrcIsModule(certificateName)
+	if certModule != "" {
+		c := android.BazelLabelForModuleDepSingle(ctx, certificateName)
+		certificateLabel = &c
+		certificate = nil
+	}
+	return certificate, certificateLabel
+}
+
 // ConvertWithBp2build is used to convert android_app to Bazel.
 func (a *AndroidApp) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
 	commonAttrs, depLabels := a.convertLibraryAttrsBp2Build(ctx)
@@ -1498,15 +1513,7 @@
 
 	aapt := a.convertAaptAttrsWithBp2Build(ctx)
 
-	var certificate *bazel.Label
-	certificateNamePtr := a.overridableAppProperties.Certificate
-	certificateName := proptools.StringDefault(certificateNamePtr, "")
-	certModule := android.SrcIsModule(certificateName)
-	if certModule != "" {
-		c := android.BazelLabelForModuleDepSingle(ctx, certificateName)
-		certificate = &c
-		certificateNamePtr = nil
-	}
+	certificateName, certificate := ParseCertificateToAttribute(ctx, a.overridableAppProperties.Certificate)
 	attrs := &bazelAndroidAppAttributes{
 		commonAttrs,
 		aapt,
@@ -1514,7 +1521,7 @@
 		// TODO(b/209576404): handle package name override by product variable PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
 		a.overridableAppProperties.Package_name,
 		certificate,
-		certificateNamePtr,
+		certificateName,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/java/app_set.go b/java/app_set.go
index 694b167..d99fadb 100644
--- a/java/app_set.go
+++ b/java/app_set.go
@@ -90,10 +90,11 @@
 }
 
 var TargetCpuAbi = map[string]string{
-	"arm":    "ARMEABI_V7A",
-	"arm64":  "ARM64_V8A",
-	"x86":    "X86",
-	"x86_64": "X86_64",
+	"arm":     "ARMEABI_V7A",
+	"arm64":   "ARM64_V8A",
+	"riscv64": "RISCV64",
+	"x86":     "X86",
+	"x86_64":  "X86_64",
 }
 
 func SupportedAbis(ctx android.ModuleContext) []string {
diff --git a/java/java.go b/java/java.go
index fc2af3b..5091d26 100644
--- a/java/java.go
+++ b/java/java.go
@@ -421,6 +421,7 @@
 	target         android.Target
 	coverageFile   android.OptionalPath
 	unstrippedFile android.Path
+	partition      string
 }
 
 func sdkDeps(ctx android.BottomUpMutatorContext, sdkContext android.SdkContext, d dexer) {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 59ffff5..fad1df7 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -65,8 +65,27 @@
 	name string
 
 	// The api scope that this scope extends.
+	//
+	// This organizes the scopes into an extension hierarchy.
+	//
+	// If set this means that the API provided by this scope includes the API provided by the scope
+	// set in this field.
 	extends *apiScope
 
+	// The next api scope that a library that uses this scope can access.
+	//
+	// This organizes the scopes into an access hierarchy.
+	//
+	// If set this means that a library that can access this API can also access the API provided by
+	// the scope set in this field.
+	//
+	// A module that sets sdk_version: "<scope>_current" should have access to the <scope> API of
+	// every java_sdk_library that it depends on. If the library does not provide an API for <scope>
+	// then it will traverse up this access hierarchy to find an API that it does provide.
+	//
+	// If this is not set then it defaults to the scope set in extends.
+	canAccess *apiScope
+
 	// The legacy enabled status for a specific scope can be dependent on other
 	// properties that have been specified on the library so it is provided by
 	// a function that can determine the status by examining those properties.
@@ -107,7 +126,7 @@
 	// The scope specific prefix to add to the api file base of "current.txt" or "removed.txt".
 	apiFilePrefix string
 
-	// The scope specific prefix to add to the sdk library module name to construct a scope specific
+	// The scope specific suffix to add to the sdk library module name to construct a scope specific
 	// module name.
 	moduleSuffix string
 
@@ -193,6 +212,11 @@
 		}
 	}
 
+	// By default, a library that can access a scope can also access the scope it extends.
+	if scope.canAccess == nil {
+		scope.canAccess = scope.extends
+	}
+
 	// Escape any special characters in the arguments. This is needed because droidstubs
 	// passes these directly to the shell command.
 	scope.droidstubsArgs = proptools.ShellEscapeList(scopeSpecificArgs)
@@ -310,6 +334,14 @@
 	apiScopeSystemServer = initApiScope(&apiScope{
 		name:    "system-server",
 		extends: apiScopePublic,
+
+		// The system-server scope can access the module-lib scope.
+		//
+		// A module that provides a system-server API is appended to the standard bootclasspath that is
+		// used by the system server. So, it should be able to access module-lib APIs provided by
+		// libraries on the bootclasspath.
+		canAccess: apiScopeModuleLib,
+
 		// The system-server scope is disabled by default in legacy mode.
 		//
 		// Enabling this would break existing usages.
@@ -926,7 +958,7 @@
 // If this does not support the requested api scope then find the closest available
 // scope it does support. Returns nil if no such scope is available.
 func (c *commonToSdkLibraryAndImport) findClosestScopePath(scope *apiScope) *scopePaths {
-	for s := scope; s != nil; s = s.extends {
+	for s := scope; s != nil; s = s.canAccess {
 		if paths := c.findScopePaths(s); paths != nil {
 			return paths
 		}
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index ea7b2f7..096bca8 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"path/filepath"
 	"regexp"
+	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -699,6 +700,80 @@
 		`)
 }
 
+func TestJavaSdkLibrary_SystemServer_AccessToStubScopeLibs(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo-public", "foo-system", "foo-module-lib", "foo-system-server"),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo-public",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+			public: {
+				enabled: true,
+			},
+		}
+
+		java_sdk_library {
+			name: "foo-system",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+			system: {
+				enabled: true,
+			},
+		}
+
+		java_sdk_library {
+			name: "foo-module-lib",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+			system: {
+				enabled: true,
+			},
+			module_lib: {
+				enabled: true,
+			},
+		}
+
+		java_sdk_library {
+			name: "foo-system-server",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+			system_server: {
+				enabled: true,
+			},
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["a.java"],
+			libs: ["foo-public", "foo-system", "foo-module-lib", "foo-system-server"],
+			sdk_version: "system_server_current",
+		}
+		`)
+
+	stubsPath := func(name string, scope *apiScope) string {
+		name = scope.stubsLibraryModuleName(name)
+		return fmt.Sprintf("out/soong/.intermediates/%[1]s/android_common/turbine-combined/%[1]s.jar", name)
+	}
+
+	// The bar library should depend on the highest (where system server is highest and public is
+	// lowest) API scopes provided by each of the foo-* modules. The highest API scope provided by the
+	// foo-<x> module is <x>.
+	barLibrary := result.ModuleForTests("bar", "android_common").Rule("javac")
+	stubLibraries := []string{
+		stubsPath("foo-public", apiScopePublic),
+		stubsPath("foo-system", apiScopeSystem),
+		stubsPath("foo-module-lib", apiScopeModuleLib),
+		stubsPath("foo-system-server", apiScopeSystemServer),
+	}
+	expectedPattern := fmt.Sprintf(`^-classpath .*:\Q%s\E$`, strings.Join(stubLibraries, ":"))
+	if expected, actual := expectedPattern, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+		t.Errorf("expected pattern %q to match %#q", expected, actual)
+	}
+}
+
 func TestJavaSdkLibrary_MissingScope(t *testing.T) {
 	prepareForJavaTest.
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`requires api scope module-lib from foo but it only has \[\] available`)).
diff --git a/rust/compiler.go b/rust/compiler.go
index bf6a488..6055158 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -161,7 +161,7 @@
 	// This is primarily meant for rust_binary and rust_ffi modules where the default
 	// linkage of libstd might need to be overridden in some use cases. This should
 	// generally be avoided with other module types since it may cause collisions at
-	// linkage if all dependencies of the root binary module do not link against libstd\
+	// linkage if all dependencies of the root binary module do not link against libstd
 	// the same way.
 	Prefer_rlib *bool `android:"arch_variant"`
 
diff --git a/rust/config/riscv64_device.go b/rust/config/riscv64_device.go
index 3b41a10..d014dbf 100644
--- a/rust/config/riscv64_device.go
+++ b/rust/config/riscv64_device.go
@@ -25,7 +25,7 @@
 	Riscv64ArchFeatureRustFlags = map[string][]string{"": {}}
 	Riscv64LinkFlags            = []string{}
 
-	Riscv64ArchVariantRustFlags = map[string][]string{}
+	Riscv64ArchVariantRustFlags = map[string][]string{"": {}}
 )
 
 func init() {
diff --git a/rust/fuzz.go b/rust/fuzz.go
index 586095c..76cf21a 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -34,6 +34,7 @@
 	*binaryDecorator
 
 	fuzzPackagedModule fuzz.FuzzPackagedModule
+	sharedLibraries    android.Paths
 }
 
 var _ compiler = (*fuzzDecorator)(nil)
@@ -86,6 +87,15 @@
 		&fuzzer.fuzzPackagedModule.FuzzProperties)
 }
 
+func (fuzzer *fuzzDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
+	out := fuzzer.binaryDecorator.compile(ctx, flags, deps)
+
+	// Grab the list of required shared libraries.
+	fuzzer.sharedLibraries = cc.CollectAllSharedDependencies(ctx)
+
+	return out
+}
+
 func (fuzzer *fuzzDecorator) stdLinkage(ctx *depsContext) RustLinkage {
 	return RlibLinkage
 }
@@ -149,11 +159,8 @@
 		// The executable.
 		files = append(files, fuzz.FileToZip{rustModule.UnstrippedOutputFile(), ""})
 
-		// Grab the list of required shared libraries.
-		sharedLibraries := fuzz.CollectAllSharedDependencies(ctx, module, cc.UnstrippedOutputFile, cc.IsValidSharedDependency)
-
 		// Package shared libraries
-		files = append(files, cc.GetSharedLibsToZip(sharedLibraries, rustModule, &s.FuzzPackager, archString, "lib", &sharedLibraryInstalled)...)
+		files = append(files, cc.GetSharedLibsToZip(fuzzModule.sharedLibraries, rustModule, &s.FuzzPackager, archString, "lib", &sharedLibraryInstalled)...)
 
 		archDirs[archOs], ok = s.BuildZipFile(ctx, module, fuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
 		if !ok {
diff --git a/rust/rust.go b/rust/rust.go
index 7342a14..28a300b 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -1664,6 +1664,10 @@
 	}
 }
 
+func (c *Module) Partition() string {
+	return ""
+}
+
 var Bool = proptools.Bool
 var BoolDefault = proptools.BoolDefault
 var String = proptools.String
diff --git a/tests/apex_comparison_tests.sh b/tests/apex_comparison_tests.sh
index 6bc0165..61d131b 100755
--- a/tests/apex_comparison_tests.sh
+++ b/tests/apex_comparison_tests.sh
@@ -58,7 +58,7 @@
 ######################
 build/soong/soong_ui.bash --make-mode BP2BUILD_VERBOSE=1 --skip-soong-tests bp2build
 
-BAZEL_OUT="$(call_bazel info output_path)"
+BAZEL_OUT="$(call_bazel info --config=bp2build output_path)"
 
 export TARGET_PRODUCT="module_arm"
 call_bazel build --config=bp2build --config=ci --config=android \
diff --git a/tests/mixed_mode_test.sh b/tests/mixed_mode_test.sh
index b408fd3..f6fffad 100755
--- a/tests/mixed_mode_test.sh
+++ b/tests/mixed_mode_test.sh
@@ -14,7 +14,9 @@
   setup
   create_mock_bazel
 
-  STANDALONE_BAZEL=true run_bazel info
+  run_soong bp2build
+
+  run_bazel info --config=bp2build
 }
 
 test_bazel_smoke
diff --git a/ui/build/build.go b/ui/build/build.go
index ab8cd56..2022e50 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -112,10 +112,6 @@
 // checkBazelMode fails the build if there are conflicting arguments for which bazel
 // build mode to use.
 func checkBazelMode(ctx Context, config Config) {
-	if config.Environment().IsEnvTrue("USE_BAZEL_ANALYSIS") {
-		ctx.Fatalln("USE_BAZEL_ANALYSIS is deprecated. Unset USE_BAZEL_ANALYSIS.\n" +
-			"Use --bazel-mode-dev instead. For example: `m --bazel-mode-dev nothing`")
-	}
 	if config.bazelProdMode && config.bazelDevMode {
 		ctx.Fatalln("Conflicting bazel mode.\n" +
 			"Do not specify both --bazel-mode and --bazel-mode-dev")
diff --git a/ui/build/config.go b/ui/build/config.go
index 14a99d0..2060660 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -71,6 +71,7 @@
 	checkbuild      bool
 	dist            bool
 	jsonModuleGraph bool
+	apiBp2build     bool // Generate BUILD files for Soong modules that contribute APIs
 	bp2build        bool
 	queryview       bool
 	reportMkMetrics bool // Collect and report mk2bp migration progress metrics.
@@ -756,6 +757,8 @@
 			c.jsonModuleGraph = true
 		} else if arg == "bp2build" {
 			c.bp2build = true
+		} else if arg == "api_bp2build" {
+			c.apiBp2build = true
 		} else if arg == "queryview" {
 			c.queryview = true
 		} else if arg == "soong_docs" {
@@ -833,7 +836,7 @@
 		return true
 	}
 
-	if !c.JsonModuleGraph() && !c.Bp2Build() && !c.Queryview() && !c.SoongDocs() {
+	if !c.JsonModuleGraph() && !c.Bp2Build() && !c.Queryview() && !c.SoongDocs() && !c.ApiBp2build() {
 		// Command line was empty, the default Ninja target is built
 		return true
 	}
@@ -916,6 +919,10 @@
 	return shared.JoinPath(c.SoongOutDir(), "queryview.marker")
 }
 
+func (c *configImpl) ApiBp2buildMarkerFile() string {
+	return shared.JoinPath(c.SoongOutDir(), "api_bp2build.marker")
+}
+
 func (c *configImpl) ModuleGraphFile() string {
 	return shared.JoinPath(c.SoongOutDir(), "module-graph.json")
 }
@@ -957,6 +964,10 @@
 	return c.bp2build
 }
 
+func (c *configImpl) ApiBp2build() bool {
+	return c.apiBp2build
+}
+
 func (c *configImpl) Queryview() bool {
 	return c.queryview
 }
diff --git a/ui/build/soong.go b/ui/build/soong.go
index ac00fe6..e0d67cc 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -45,6 +45,7 @@
 	bp2buildTag        = "bp2build"
 	jsonModuleGraphTag = "modulegraph"
 	queryviewTag       = "queryview"
+	apiBp2buildTag     = "api_bp2build"
 	soongDocsTag       = "soong_docs"
 
 	// bootstrapEpoch is used to determine if an incremental build is incompatible with the current
@@ -237,6 +238,7 @@
 		config.NamedGlobFile(bp2buildTag),
 		config.NamedGlobFile(jsonModuleGraphTag),
 		config.NamedGlobFile(queryviewTag),
+		config.NamedGlobFile(apiBp2buildTag),
 		config.NamedGlobFile(soongDocsTag),
 	}
 }
@@ -307,6 +309,19 @@
 		fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir),
 	)
 
+	// The BUILD files will be generated in out/soong/.api_bp2build (no symlinks to src files)
+	// The final workspace will be generated in out/soong/api_bp2build
+	apiBp2buildDir := filepath.Join(config.SoongOutDir(), ".api_bp2build")
+	apiBp2buildInvocation := primaryBuilderInvocation(
+		config,
+		apiBp2buildTag,
+		config.ApiBp2buildMarkerFile(),
+		[]string{
+			"--bazel_api_bp2build_dir", apiBp2buildDir,
+		},
+		fmt.Sprintf("generating BUILD files for API contributions at %s", apiBp2buildDir),
+	)
+
 	soongDocsInvocation := primaryBuilderInvocation(
 		config,
 		soongDocsTag,
@@ -345,6 +360,7 @@
 			bp2buildInvocation,
 			jsonModuleGraphInvocation,
 			queryviewInvocation,
+			apiBp2buildInvocation,
 			soongDocsInvocation},
 	}
 
@@ -417,6 +433,10 @@
 			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(queryviewTag))
 		}
 
+		if config.ApiBp2build() {
+			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(apiBp2buildTag))
+		}
+
 		if config.SoongDocs() {
 			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(soongDocsTag))
 		}
@@ -480,6 +500,10 @@
 		targets = append(targets, config.QueryviewMarkerFile())
 	}
 
+	if config.ApiBp2build() {
+		targets = append(targets, config.ApiBp2buildMarkerFile())
+	}
+
 	if config.SoongDocs() {
 		targets = append(targets, config.SoongDocsHtml())
 	}