Merge "Fix the ABI dump directory name for vendor libraries"
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index a1b7dbf..bf39404 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -49,9 +49,9 @@
 		"art/runtime":                           Bp2BuildDefaultTrueRecursively,
 		"art/tools":                             Bp2BuildDefaultTrue,
 		"bionic":                                Bp2BuildDefaultTrueRecursively,
+		"bootable/recovery/applypatch":          Bp2BuildDefaultTrue,
 		"bootable/recovery/minadbd":             Bp2BuildDefaultTrue,
 		"bootable/recovery/minui":               Bp2BuildDefaultTrue,
-		"bootable/recovery/applypatch":          Bp2BuildDefaultTrue,
 		"bootable/recovery/recovery_utils":      Bp2BuildDefaultTrue,
 		"bootable/recovery/tools/recovery_l10n": Bp2BuildDefaultTrue,
 
@@ -69,6 +69,9 @@
 		"build/soong/scripts":                Bp2BuildDefaultTrueRecursively,
 
 		"cts/common/device-side/nativetesthelper/jni": Bp2BuildDefaultTrueRecursively,
+
+		"dalvik/tools/dexdeps": Bp2BuildDefaultTrueRecursively,
+
 		"development/apps/DevelopmentSettings":        Bp2BuildDefaultTrue,
 		"development/apps/Fallback":                   Bp2BuildDefaultTrue,
 		"development/apps/WidgetPreview":              Bp2BuildDefaultTrue,
@@ -108,8 +111,8 @@
 
 		"external/aac":                           Bp2BuildDefaultTrueRecursively,
 		"external/arm-optimized-routines":        Bp2BuildDefaultTrueRecursively,
-		"external/auto/android-annotation-stubs": Bp2BuildDefaultTrueRecursively,
 		"external/auto":                          Bp2BuildDefaultTrue,
+		"external/auto/android-annotation-stubs": Bp2BuildDefaultTrueRecursively,
 		"external/auto/common":                   Bp2BuildDefaultTrueRecursively,
 		"external/auto/service":                  Bp2BuildDefaultTrueRecursively,
 		"external/boringssl":                     Bp2BuildDefaultTrueRecursively,
@@ -176,19 +179,19 @@
 		"external/zopfli":                        Bp2BuildDefaultTrueRecursively,
 		"external/zstd":                          Bp2BuildDefaultTrueRecursively,
 
-		"frameworks/av":                                      Bp2BuildDefaultTrue,
-		"frameworks/av/media/codecs":                         Bp2BuildDefaultTrueRecursively,
+		"frameworks/av": Bp2BuildDefaultTrue,
 		"frameworks/av/media/codec2/components/aom":          Bp2BuildDefaultTrueRecursively,
+		"frameworks/av/media/codecs":                         Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/media/liberror":                       Bp2BuildDefaultTrueRecursively,
-		"frameworks/av/services/minijail":                    Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/media/module/minijail":                Bp2BuildDefaultTrueRecursively,
+		"frameworks/av/services/minijail":                    Bp2BuildDefaultTrueRecursively,
 		"frameworks/base/libs/androidfw":                     Bp2BuildDefaultTrue,
 		"frameworks/base/media/tests/MediaDump":              Bp2BuildDefaultTrue,
 		"frameworks/base/services/tests/servicestests/aidl":  Bp2BuildDefaultTrue,
 		"frameworks/base/startop/apps/test":                  Bp2BuildDefaultTrue,
 		"frameworks/base/tests/appwidgets/AppWidgetHostTest": Bp2BuildDefaultTrueRecursively,
-		"frameworks/base/tools/streaming_proto":              Bp2BuildDefaultTrueRecursively,
 		"frameworks/base/tools/aapt2":                        Bp2BuildDefaultTrue,
+		"frameworks/base/tools/streaming_proto":              Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/adbd_auth":                   Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/arect":                       Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/gui":                         Bp2BuildDefaultTrue,
@@ -208,10 +211,10 @@
 		"hardware/interfaces/configstore/1.0":          Bp2BuildDefaultTrue,
 		"hardware/interfaces/configstore/1.1":          Bp2BuildDefaultTrue,
 		"hardware/interfaces/configstore/utils":        Bp2BuildDefaultTrue,
-		"hardware/interfaces/graphics/allocator/aidl":  Bp2BuildDefaultTrue,
 		"hardware/interfaces/graphics/allocator/2.0":   Bp2BuildDefaultTrue,
 		"hardware/interfaces/graphics/allocator/3.0":   Bp2BuildDefaultTrue,
 		"hardware/interfaces/graphics/allocator/4.0":   Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/allocator/aidl":  Bp2BuildDefaultTrue,
 		"hardware/interfaces/graphics/bufferqueue/1.0": Bp2BuildDefaultTrue,
 		"hardware/interfaces/graphics/bufferqueue/2.0": Bp2BuildDefaultTrue,
 		"hardware/interfaces/graphics/common/1.0":      Bp2BuildDefaultTrue,
@@ -249,6 +252,7 @@
 		"packages/apps/Protips":                            Bp2BuildDefaultTrue,
 		"packages/apps/SafetyRegulatoryInfo":               Bp2BuildDefaultTrue,
 		"packages/apps/WallpaperPicker":                    Bp2BuildDefaultTrue,
+		"packages/modules/NeuralNetworks/driver/cache":     Bp2BuildDefaultTrueRecursively,
 		"packages/modules/StatsD/lib/libstatssocket":       Bp2BuildDefaultTrueRecursively,
 		"packages/modules/adb":                             Bp2BuildDefaultTrue,
 		"packages/modules/adb/apex":                        Bp2BuildDefaultTrue,
@@ -259,7 +263,6 @@
 		"packages/modules/adb/proto":                       Bp2BuildDefaultTrueRecursively,
 		"packages/modules/adb/tls":                         Bp2BuildDefaultTrueRecursively,
 		"packages/providers/MediaProvider/tools/dialogs":   Bp2BuildDefaultFalse, // TODO(b/242834374)
-		"packages/modules/NeuralNetworks/driver/cache":     Bp2BuildDefaultTrueRecursively,
 		"packages/screensavers/Basic":                      Bp2BuildDefaultTrue,
 		"packages/services/Car/tests/SampleRearViewCamera": Bp2BuildDefaultFalse, // TODO(b/242834321)
 
@@ -272,8 +275,8 @@
 		"prebuilts/tools":                          Bp2BuildDefaultTrue,
 		"prebuilts/tools/common/m2":                Bp2BuildDefaultTrue,
 
-		"sdk/eventanalyzer": Bp2BuildDefaultTrue,
 		"sdk/dumpeventlog":  Bp2BuildDefaultTrue,
+		"sdk/eventanalyzer": Bp2BuildDefaultTrue,
 
 		"system/apex":                                            Bp2BuildDefaultFalse, // TODO(b/207466993): flaky failures
 		"system/apex/apexer":                                     Bp2BuildDefaultTrue,
@@ -344,6 +347,7 @@
 		".":/*recursive = */ false,
 
 		"build/bazel":/* recursive = */ true,
+		"build/make/core":/* recursive = */ false,
 		"build/bazel_common_rules":/* recursive = */ true,
 		// build/make/tools/signapk BUILD file is generated, so build/make/tools is not recursive.
 		"build/make/tools":/* recursive = */ false,
@@ -379,6 +383,8 @@
 		"prebuilts/sdk":/* recursive = */ false,
 		"prebuilts/sdk/tools":/* recursive = */ false,
 		"prebuilts/r8":/* recursive = */ false,
+
+		"tools/asuite/atest/":/* recursive = */ true,
 	}
 
 	Bp2buildModuleAlwaysConvertList = []string{
@@ -644,6 +650,10 @@
 		"libcodec2_soft_avcenc",
 		"libcodec2_soft_aacdec",
 		"libcodec2_soft_common",
+
+		// kotlin srcs in java libs
+		"CtsPkgInstallerConstants",
+		"kotlinx_atomicfu",
 	}
 
 	Bp2buildModuleTypeAlwaysConvertList = []string{
@@ -1339,9 +1349,12 @@
 		"prebuilt_currysrc_org.eclipse",
 	}
 
+	// Bazel prod-mode allowlist. Modules in this list are built by Bazel
+	// in either prod mode or staging mode.
 	ProdMixedBuildsEnabledList = []string{}
 
-	// Staging builds should be entirely prod, plus some near-ready ones. Add the
-	// new ones to the first argument as needed.
-	StagingMixedBuildsEnabledList = append([]string{}, ProdMixedBuildsEnabledList...)
+	// Staging-mode allowlist. Modules in this list are only built
+	// by Bazel with --bazel-mode-staging. This list should contain modules
+	// which will soon be added to the prod allowlist.
+	StagingMixedBuildsEnabledList = []string{"com.android.tzdata"}
 )
diff --git a/android/arch.go b/android/arch.go
index 086e945..6acf9cf 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -16,6 +16,7 @@
 
 import (
 	"encoding"
+	"encoding/json"
 	"fmt"
 	"reflect"
 	"runtime"
@@ -1681,10 +1682,10 @@
 
 // archConfig describes a built-in configuration.
 type archConfig struct {
-	arch        string
-	archVariant string
-	cpuVariant  string
-	abi         []string
+	Arch        string   `json:"arch"`
+	ArchVariant string   `json:"arch_variant"`
+	CpuVariant  string   `json:"cpu_variant"`
+	Abi         []string `json:"abis"`
 }
 
 // getNdkAbisConfig returns the list of archConfigs that are used for building
@@ -1713,8 +1714,8 @@
 	var ret []Target
 
 	for _, config := range archConfigs {
-		arch, err := decodeArch(Android, config.arch, &config.archVariant,
-			&config.cpuVariant, config.abi)
+		arch, err := decodeArch(Android, config.Arch, &config.ArchVariant,
+			&config.CpuVariant, config.Abi)
 		if err != nil {
 			return nil, err
 		}
@@ -2284,6 +2285,14 @@
 	return starlark_fmt.PrintDict(valDict, 0)
 }
 
+func printArchConfigList(arches []archConfig) string {
+	jsonOut, err := json.MarshalIndent(arches, "", starlark_fmt.Indention(1))
+	if err != nil {
+		panic(fmt.Errorf("Error converting arch configs %#v to json: %q", arches, err))
+	}
+	return fmt.Sprintf("json.decode('''%s''')", string(jsonOut))
+}
+
 func StarlarkArchConfigurations() string {
 	return fmt.Sprintf(`
 _arch_to_variants = %s
@@ -2294,13 +2303,21 @@
 
 _android_arch_feature_for_arch_variant = %s
 
+_aml_arches = %s
+
+_ndk_arches = %s
+
 arch_to_variants = _arch_to_variants
 arch_to_cpu_variants = _arch_to_cpu_variants
 arch_to_features = _arch_to_features
 android_arch_feature_for_arch_variants = _android_arch_feature_for_arch_variant
+aml_arches = _aml_arches
+ndk_arches = _ndk_arches
 `, printArchTypeStarlarkDict(archVariants),
 		printArchTypeStarlarkDict(cpuVariants),
 		printArchTypeStarlarkDict(archFeatures),
 		printArchTypeNestedStarlarkDict(androidArchFeatureMap),
+		printArchConfigList(getAmlAbisConfig()),
+		printArchConfigList(getNdkAbisConfig()),
 	)
 }
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index d4af70b..b3b698e 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -317,7 +317,7 @@
 func (bazelCtx *bazelContext) GetApexInfo(label string, cfgKey configKey) (cquery.ApexInfo, error) {
 	key := makeCqueryKey(label, cquery.GetApexInfo, cfgKey)
 	if rawString, ok := bazelCtx.results[key]; ok {
-		return cquery.GetApexInfo.ParseResult(strings.TrimSpace(rawString)), nil
+		return cquery.GetApexInfo.ParseResult(strings.TrimSpace(rawString))
 	}
 	return cquery.ApexInfo{}, fmt.Errorf("no bazel response found for %v", key)
 }
@@ -325,7 +325,7 @@
 func (bazelCtx *bazelContext) GetCcUnstrippedInfo(label string, cfgKey configKey) (cquery.CcUnstrippedInfo, error) {
 	key := makeCqueryKey(label, cquery.GetCcUnstrippedInfo, cfgKey)
 	if rawString, ok := bazelCtx.results[key]; ok {
-		return cquery.GetCcUnstrippedInfo.ParseResult(strings.TrimSpace(rawString)), nil
+		return cquery.GetCcUnstrippedInfo.ParseResult(strings.TrimSpace(rawString))
 	}
 	return cquery.CcUnstrippedInfo{}, fmt.Errorf("no bazel response for %s", key)
 }
@@ -389,9 +389,12 @@
 		}
 	case BazelStagingMode:
 		modulesDefaultToBazel = false
+		// Staging mode includes all prod modules plus all staging modules.
+		for _, enabledProdModule := range allowlists.ProdMixedBuildsEnabledList {
+			enabledModules[enabledProdModule] = true
+		}
 		for _, enabledStagingMode := range allowlists.StagingMixedBuildsEnabledList {
 			enabledModules[enabledStagingMode] = true
-
 		}
 	case BazelDevMode:
 		modulesDefaultToBazel = true
@@ -568,7 +571,9 @@
 
 		// Suppress noise
 		"--ui_event_filters=-INFO",
-		"--noshow_progress"}
+		"--noshow_progress",
+		"--norun_validations",
+	}
 	cmdFlags = append(cmdFlags, extraFlags...)
 
 	bazelCmd := exec.Command(paths.bazelPath, cmdFlags...)
@@ -865,33 +870,32 @@
 func (context *bazelContext) InvokeBazel(config Config) error {
 	context.results = make(map[cqueryKey]string)
 
-	var err error
-
 	soongInjectionPath := absolutePath(context.paths.injectedFilesDir())
 	mixedBuildsPath := filepath.Join(soongInjectionPath, "mixed_builds")
 	if _, err := os.Stat(mixedBuildsPath); os.IsNotExist(err) {
 		err = os.MkdirAll(mixedBuildsPath, 0777)
-	}
-	if err != nil {
-		return err
-	}
-	if metricsDir := context.paths.BazelMetricsDir(); metricsDir != "" {
-		err = os.MkdirAll(metricsDir, 0777)
 		if err != nil {
 			return err
 		}
 	}
-	if err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666); err != nil {
+
+	if metricsDir := context.paths.BazelMetricsDir(); metricsDir != "" {
+		err := os.MkdirAll(metricsDir, 0777)
+		if err != nil {
+			return err
+		}
+	}
+	if err := ioutil.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666); err != nil {
 		return err
 	}
-	if err = ioutil.WriteFile(filepath.Join(mixedBuildsPath, "main.bzl"), context.mainBzlFileContents(), 0666); err != nil {
+	if err := ioutil.WriteFile(filepath.Join(mixedBuildsPath, "main.bzl"), context.mainBzlFileContents(), 0666); err != nil {
 		return err
 	}
-	if err = ioutil.WriteFile(filepath.Join(mixedBuildsPath, "BUILD.bazel"), context.mainBuildFileContents(), 0666); err != nil {
+	if err := ioutil.WriteFile(filepath.Join(mixedBuildsPath, "BUILD.bazel"), context.mainBuildFileContents(), 0666); err != nil {
 		return err
 	}
 	cqueryFileRelpath := filepath.Join(context.paths.injectedFilesDir(), "buildroot.cquery")
-	if err = ioutil.WriteFile(absolutePath(cqueryFileRelpath), context.cqueryStarlarkFileContents(), 0666); err != nil {
+	if err := ioutil.WriteFile(absolutePath(cqueryFileRelpath), context.cqueryStarlarkFileContents(), 0666); err != nil {
 		return err
 	}
 
@@ -899,12 +903,12 @@
 	cqueryCmd := bazelCommand{"cquery", fmt.Sprintf("deps(%s, 2)", buildrootLabel)}
 	cqueryCommandWithFlag := context.createBazelCommand(context.paths, bazel.CqueryBuildRootRunName, cqueryCmd,
 		"--output=starlark", "--starlark:file="+absolutePath(cqueryFileRelpath))
-	cqueryOutput, cqueryErr, err := context.issueBazelCommand(cqueryCommandWithFlag)
-	if err != nil {
-		return err
+	cqueryOutput, cqueryErrorMessage, cqueryErr := context.issueBazelCommand(cqueryCommandWithFlag)
+	if cqueryErr != nil {
+		return cqueryErr
 	}
 	cqueryCommandPrint := fmt.Sprintf("cquery command line:\n  %s \n\n\n", printableCqueryCommand(cqueryCommandWithFlag))
-	if err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryCommandPrint+cqueryOutput), 0666); err != nil {
+	if err := ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryCommandPrint+cqueryOutput), 0666); err != nil {
 		return err
 	}
 	cqueryResults := map[string]string{}
@@ -919,7 +923,7 @@
 			context.results[val] = cqueryResult
 		} else {
 			return fmt.Errorf("missing result for bazel target %s. query output: [%s], cquery err: [%s]",
-				getCqueryId(val), cqueryOutput, cqueryErr)
+				getCqueryId(val), cqueryOutput, cqueryErrorMessage)
 		}
 	}
 
@@ -932,6 +936,12 @@
 		extraFlags = append(extraFlags, "--collect_code_coverage")
 		paths := make([]string, 0, 2)
 		if p := config.productVariables.NativeCoveragePaths; len(p) > 0 {
+			for i, _ := range p {
+				// TODO(b/259404593) convert path wildcard to regex values
+				if p[i] == "*" {
+					p[i] = ".*"
+				}
+			}
 			paths = append(paths, JoinWithPrefixAndSeparator(p, "+", ","))
 		}
 		if p := config.productVariables.NativeCoverageExcludePaths; len(p) > 0 {
@@ -945,8 +955,7 @@
 	if aqueryOutput, _, err := context.issueBazelCommand(context.createBazelCommand(context.paths, bazel.AqueryBuildRootRunName, aqueryCmd,
 		extraFlags...)); err == nil {
 		context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
-	}
-	if err != nil {
+	} else {
 		return err
 	}
 
@@ -954,7 +963,7 @@
 	// Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
 	// but some of symlinks may be required to resolve source dependencies of the build.
 	buildCmd := bazelCommand{"build", "@soong_injection//mixed_builds:phonyroot"}
-	if _, _, err = context.issueBazelCommand(context.createBazelCommand(context.paths, bazel.BazelBuildPhonyRootRunName, buildCmd)); err != nil {
+	if _, _, err := context.issueBazelCommand(context.createBazelCommand(context.paths, bazel.BazelBuildPhonyRootRunName, buildCmd)); err != nil {
 		return err
 	}
 
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index 6e3acd5..4b8f0f6 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -155,6 +155,10 @@
 	testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1"}
 	verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=-bar1`)
 
+	testConfig.productVariables.NativeCoveragePaths = []string{"*"}
+	testConfig.productVariables.NativeCoverageExcludePaths = nil
+	verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+.*`)
+
 	testConfig.productVariables.ClangCoverage = boolPtr(false)
 	actual := verifyExtraFlags(t, testConfig, ``)
 	if strings.Contains(actual, "--collect_code_coverage") ||
diff --git a/android/config.go b/android/config.go
index 50f63ea..1deb7d4 100644
--- a/android/config.go
+++ b/android/config.go
@@ -536,7 +536,7 @@
 // Returns true if "Bazel builds" is enabled. In this mode, part of build
 // analysis is handled by Bazel.
 func (c *config) IsMixedBuildsEnabled() bool {
-	return c.BuildMode == BazelProdMode || c.BuildMode == BazelDevMode
+	return c.BuildMode == BazelProdMode || c.BuildMode == BazelDevMode || c.BuildMode == BazelStagingMode
 }
 
 func (c *config) SetAllowMissingDependencies() {
@@ -1272,10 +1272,6 @@
 	return coverage
 }
 
-func (c *deviceConfig) AfdoAdditionalProfileDirs() []string {
-	return c.config.productVariables.AfdoAdditionalProfileDirs
-}
-
 func (c *deviceConfig) PgoAdditionalProfileDirs() []string {
 	return c.config.productVariables.PgoAdditionalProfileDirs
 }
diff --git a/android/defs.go b/android/defs.go
index 2a28e98..9ae360e 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -25,7 +25,8 @@
 )
 
 var (
-	pctx = NewPackageContext("android/soong/android")
+	pctx         = NewPackageContext("android/soong/android")
+	exportedVars = NewExportedVariables(pctx)
 
 	cpPreserveSymlinks = pctx.VariableConfigMethod("cpPreserveSymlinks",
 		Config.CpPreserveSymlinksFlags)
@@ -128,6 +129,13 @@
 	pctx.VariableFunc("RBEWrapper", func(ctx PackageVarContext) string {
 		return ctx.Config().RBEWrapper()
 	})
+
+	exportedVars.ExportStringList("NeverAllowNotInIncludeDir", neverallowNotInIncludeDir)
+	exportedVars.ExportStringList("NeverAllowNoUseIncludeDir", neverallowNoUseIncludeDir)
+}
+
+func BazelCcToolchainVars(config Config) string {
+	return BazelToolchainVars(config, exportedVars)
 }
 
 var (
diff --git a/android/fixture.go b/android/fixture.go
index f33e718..3f01f5a 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -658,7 +658,7 @@
 
 func createFixture(t *testing.T, buildDir string, preparers []*simpleFixturePreparer) Fixture {
 	config := TestConfig(buildDir, nil, "", nil)
-	ctx := NewTestContext(config)
+	ctx := newTestContextForFixture(config)
 	fixture := &fixture{
 		preparers: preparers,
 		t:         t,
@@ -790,6 +790,16 @@
 		}
 	}
 
+	// Create and set the Context's NameInterface. It needs to be created here as it depends on the
+	// configuration that has been prepared for this fixture.
+	resolver := NewNameResolver(ctx.config)
+
+	// Set the NameInterface in the main Context.
+	ctx.SetNameInterface(resolver)
+
+	// Set the NameResolver in the TestContext.
+	ctx.NameResolver = resolver
+
 	ctx.Register()
 	var ninjaDeps []string
 	extraNinjaDeps, errs := ctx.ParseBlueprintsFiles("ignored")
diff --git a/android/gen_notice.go b/android/gen_notice.go
index 008aac5..28fddbc 100644
--- a/android/gen_notice.go
+++ b/android/gen_notice.go
@@ -61,6 +61,9 @@
 				if mod == nil {
 					continue
 				}
+				if !mod.Enabled() { // don't depend on variants without build rules
+					continue
+				}
 				modules = append(modules, mod)
 			}
 		}
diff --git a/android/namespace.go b/android/namespace.go
index a3ff761..b43ffdf 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -91,7 +91,27 @@
 	namespaceExportFilter func(*Namespace) bool
 }
 
-func NewNameResolver(namespaceExportFilter func(*Namespace) bool) *NameResolver {
+// NameResolverConfig provides the subset of the Config interface needed by the
+// NewNameResolver function.
+type NameResolverConfig interface {
+	// ExportedNamespaces is the list of namespaces that Soong must export to
+	// make.
+	ExportedNamespaces() []string
+}
+
+func NewNameResolver(config NameResolverConfig) *NameResolver {
+	namespacePathsToExport := make(map[string]bool)
+
+	for _, namespaceName := range config.ExportedNamespaces() {
+		namespacePathsToExport[namespaceName] = true
+	}
+
+	namespacePathsToExport["."] = true // always export the root namespace
+
+	namespaceExportFilter := func(namespace *Namespace) bool {
+		return namespacePathsToExport[namespace.Path]
+	}
+
 	r := &NameResolver{
 		namespacesByDir:       sync.Map{},
 		namespaceExportFilter: namespaceExportFilter,
diff --git a/android/namespace_test.go b/android/namespace_test.go
index 87d1320..7a387a0 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -602,6 +602,36 @@
 	// RunTest will report any errors
 }
 
+func TestNamespace_Exports(t *testing.T) {
+	result := GroupFixturePreparers(
+		prepareForTestWithNamespace,
+		FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+			variables.NamespacesToExport = []string{"dir1"}
+		}),
+		dirBpToPreparer(map[string]string{
+			"dir1": `
+				soong_namespace {
+				}
+				test_module {
+					name: "a",
+				}
+			`,
+			"dir2": `
+				soong_namespace {
+				}
+				test_module {
+					name: "b",
+				}
+			`,
+		}),
+	).RunTest(t)
+
+	aModule := result.Module("a", "")
+	AssertBoolEquals(t, "a exported", true, aModule.ExportedToMake())
+	bModule := result.Module("b", "")
+	AssertBoolEquals(t, "b not exported", false, bModule.ExportedToMake())
+}
+
 // some utils to support the tests
 
 var prepareForTestWithNamespace = GroupFixturePreparers(
diff --git a/android/neverallow.go b/android/neverallow.go
index d288439..293bac8 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -74,8 +74,8 @@
 			"supported for custom conversion, use allowlists.go instead.")
 }
 
-func createIncludeDirsRules() []Rule {
-	notInIncludeDir := []string{
+var (
+	neverallowNotInIncludeDir = []string{
 		"art",
 		"art/libnativebridge",
 		"art/libnativeloader",
@@ -91,7 +91,7 @@
 		"external/vixl",
 		"external/wycheproof",
 	}
-	noUseIncludeDir := []string{
+	neverallowNoUseIncludeDir = []string{
 		"frameworks/av/apex",
 		"frameworks/av/tools",
 		"frameworks/native/cmds",
@@ -103,10 +103,12 @@
 		"system/libfmq",
 		"system/libvintf",
 	}
+)
 
-	rules := make([]Rule, 0, len(notInIncludeDir)+len(noUseIncludeDir))
+func createIncludeDirsRules() []Rule {
+	rules := make([]Rule, 0, len(neverallowNotInIncludeDir)+len(neverallowNoUseIncludeDir))
 
-	for _, path := range notInIncludeDir {
+	for _, path := range neverallowNotInIncludeDir {
 		rule :=
 			NeverAllow().
 				WithMatcher("include_dirs", StartsWith(path+"/")).
@@ -116,7 +118,7 @@
 		rules = append(rules, rule)
 	}
 
-	for _, path := range noUseIncludeDir {
+	for _, path := range neverallowNoUseIncludeDir {
 		rule := NeverAllow().In(path+"/").WithMatcher("include_dirs", isSetMatcherInstance).
 			Because("include_dirs is deprecated, all usages of them in '" + path + "' have been migrated" +
 				" to use alternate mechanisms and so can no longer be used.")
diff --git a/android/sdk.go b/android/sdk.go
index bd2f5d1..fc0a84e 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -232,12 +232,6 @@
 	// relative path) and add the dest to the zip.
 	CopyToSnapshot(src Path, dest string)
 
-	// EmptyFile returns the path to an empty file.
-	//
-	// This can be used by sdk member types that need to create an empty file in the snapshot, simply
-	// pass the value returned from this to the CopyToSnapshot() method.
-	EmptyFile() Path
-
 	// UnzipToSnapshot generates a rule that will unzip the supplied zip into the snapshot relative
 	// directory destDir.
 	UnzipToSnapshot(zipPath Path, destDir string)
@@ -264,6 +258,14 @@
 	// See sdk/update.go for more information.
 	AddPrebuiltModule(member SdkMember, moduleType string) BpModule
 
+	// AddInternalModule creates a new module in the generated Android.bp file that can only be
+	// referenced by one of the other modules in the snapshot.
+	//
+	// The created module's name is constructed by concatenating the name of this member and the
+	// nameSuffix, separated by "-". It also has the visibility property set to "//visibility:private"
+	// to prevent it from being inadvertently accessed from outside the snapshot.
+	AddInternalModule(properties SdkMemberProperties, moduleType string, nameSuffix string) BpModule
+
 	// SdkMemberReferencePropertyTag returns a property tag to use when adding a property to a
 	// BpModule that contains references to other sdk members.
 	//
@@ -922,6 +924,12 @@
 //
 // Contains common properties that apply across many different member types.
 type SdkMemberPropertiesBase struct {
+	// The name of the member.
+	//
+	// Ignore this property during optimization. This is needed because this property is the same for
+	// all variants of a member and so would be optimized away if it was not ignored.
+	MemberName string `sdk:"ignore"`
+
 	// The number of unique os types supported by the member variants.
 	//
 	// If a member has a variant with more than one os type then it will need to differentiate
@@ -945,6 +953,10 @@
 	Compile_multilib string `android:"arch_variant"`
 }
 
+func (b *SdkMemberPropertiesBase) Name() string {
+	return b.MemberName
+}
+
 // OsPrefix returns the os prefix to use for any file paths in the sdk.
 //
 // Is an empty string if the member only provides variants for a single os type, otherwise
@@ -970,6 +982,8 @@
 	// Base returns the base structure.
 	Base() *SdkMemberPropertiesBase
 
+	Name() string
+
 	// PopulateFromVariant populates this structure with information from a module variant.
 	//
 	// It will typically be called once for each variant of a member module that the SDK depends upon.
diff --git a/android/testing.go b/android/testing.go
index 2256c96..8fcf440 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -30,19 +30,11 @@
 	"github.com/google/blueprint/proptools"
 )
 
-func NewTestContext(config Config) *TestContext {
-	namespaceExportFilter := func(namespace *Namespace) bool {
-		return true
-	}
-
-	nameResolver := NewNameResolver(namespaceExportFilter)
+func newTestContextForFixture(config Config) *TestContext {
 	ctx := &TestContext{
-		Context:      &Context{blueprint.NewContext(), config},
-		NameResolver: nameResolver,
+		Context: &Context{blueprint.NewContext(), config},
 	}
 
-	ctx.SetNameInterface(nameResolver)
-
 	ctx.postDeps = append(ctx.postDeps, registerPathDepsMutator)
 
 	ctx.SetFs(ctx.config.fs)
@@ -53,6 +45,16 @@
 	return ctx
 }
 
+func NewTestContext(config Config) *TestContext {
+	ctx := newTestContextForFixture(config)
+
+	nameResolver := NewNameResolver(config)
+	ctx.NameResolver = nameResolver
+	ctx.SetNameInterface(nameResolver)
+
+	return ctx
+}
+
 var PrepareForTestWithArchMutator = GroupFixturePreparers(
 	// Configure architecture targets in the fixture config.
 	FixtureModifyConfig(modifyTestConfigToSupportArchMutator),
diff --git a/android/variable.go b/android/variable.go
index 37ecab5..28f22c9 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -336,8 +336,7 @@
 
 	NamespacesToExport []string `json:",omitempty"`
 
-	AfdoAdditionalProfileDirs []string `json:",omitempty"`
-	PgoAdditionalProfileDirs  []string `json:",omitempty"`
+	PgoAdditionalProfileDirs []string `json:",omitempty"`
 
 	VndkUseCoreVariant         *bool `json:",omitempty"`
 	VndkSnapshotBuildArtifacts *bool `json:",omitempty"`
diff --git a/apex/apex.go b/apex/apex.go
index ff0f504..04808c1 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -455,9 +455,6 @@
 	// Processed file_contexts files
 	fileContexts android.WritablePath
 
-	// Path to notice file in html.gz format.
-	htmlGzNotice android.WritablePath
-
 	// The built APEX file. This is the main product.
 	// Could be .apex or .capex
 	outputFile android.WritablePath
@@ -1903,15 +1900,12 @@
 	apexType := a.properties.ApexType
 	switch apexType {
 	case imageApex:
-		// TODO(b/190817312): Generate the notice file from the apex rule.
-		a.htmlGzNotice = android.PathForBazelOut(ctx, "NOTICE.html.gz")
-		// TODO(b/239081457): Generate the bazel bundle module file from the apex rule.
-		a.bundleModuleFile = android.PathForBazelOut(ctx, a.Name()+apexType.suffix()+"-base.zip")
+		a.bundleModuleFile = android.PathForBazelOut(ctx, outputs.BundleFile)
 		a.nativeApisUsedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, outputs.SymbolsUsedByApex))
-		// TODO(b/239081456): Generate the backing.txt file from Bazel.
-		a.nativeApisBackedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, a.Name()+"_backing.txt"))
+		a.nativeApisBackedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, outputs.BackingLibs))
 		// TODO(b/239084755): Generate the java api using.xml file from Bazel.
-		a.javaApisUsedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, a.Name()+"_using.xml"))
+		a.javaApisUsedByModuleFile = android.ModuleOutPath(android.PathForBazelOut(ctx, outputs.JavaSymbolsUsedByApex))
+		a.installedFilesFile = android.ModuleOutPath(android.PathForBazelOut(ctx, outputs.InstalledFiles))
 		installSuffix := imageApexSuffix
 		if a.isCompressed {
 			installSuffix = imageCapexSuffix
@@ -2508,7 +2502,8 @@
 		filesToAdd = append(filesToAdd, *af)
 	}
 
-	if pathInApex := bootclasspathFragmentInfo.ProfileInstallPathInApex(); pathInApex != "" {
+	pathInApex := bootclasspathFragmentInfo.ProfileInstallPathInApex()
+	if pathInApex != "" && !java.SkipDexpreoptBootJars(ctx) {
 		pathOnHost := bootclasspathFragmentInfo.ProfilePathOnHost()
 		tempPath := android.PathForModuleOut(ctx, "boot_image_profile", pathInApex)
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 48ad024..ea3e734 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -9768,11 +9768,14 @@
 				OutputBaseDir: outputBaseDir,
 				LabelToApexInfo: map[string]cquery.ApexInfo{
 					"//:foo": cquery.ApexInfo{
-						SignedOutput:      "signed_out.apex",
-						UnsignedOutput:    "unsigned_out.apex",
-						BundleKeyInfo:     []string{"public_key", "private_key"},
-						ContainerKeyInfo:  []string{"container_cert", "container_private"},
-						SymbolsUsedByApex: "foo_using.txt",
+						SignedOutput:          "signed_out.apex",
+						UnsignedOutput:        "unsigned_out.apex",
+						BundleKeyInfo:         []string{"public_key", "private_key"},
+						ContainerKeyInfo:      []string{"container_cert", "container_private"},
+						SymbolsUsedByApex:     "foo_using.txt",
+						JavaSymbolsUsedByApex: "foo_using.xml",
+						BundleFile:            "apex_bundle.zip",
+						InstalledFiles:        "installed-files.txt",
 
 						// unused
 						PackageName:  "pkg_name",
@@ -9813,4 +9816,24 @@
 	if w, g := "out/bazel/execroot/__main__/foo_using.txt", ab.nativeApisUsedByModuleFile.String(); w != g {
 		t.Errorf("Expected output file %q, got %q", w, g)
 	}
+
+	if w, g := "out/bazel/execroot/__main__/foo_using.xml", ab.javaApisUsedByModuleFile.String(); w != g {
+		t.Errorf("Expected output file %q, got %q", w, g)
+	}
+
+	if w, g := "out/bazel/execroot/__main__/installed-files.txt", ab.installedFilesFile.String(); w != g {
+		t.Errorf("Expected installed-files.txt %q, got %q", w, g)
+	}
+
+	mkData := android.AndroidMkDataForTest(t, result.TestContext, m)
+	var builder strings.Builder
+	mkData.Custom(&builder, "foo", "BAZEL_TARGET_", "", mkData)
+
+	data := builder.String()
+	if w := "ALL_MODULES.$(my_register_name).BUNDLE := out/bazel/execroot/__main__/apex_bundle.zip"; !strings.Contains(data, w) {
+		t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
+	}
+	if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/installed-files.txt:foo-installed-files.txt)"; !strings.Contains(data, w) {
+		t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
+	}
 }
diff --git a/apex/builder.go b/apex/builder.go
index e4c1673..9e368b6 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -650,9 +650,9 @@
 		}
 
 		// Create a NOTICE file, and embed it as an asset file in the APEX.
-		a.htmlGzNotice = android.PathForModuleOut(ctx, "NOTICE.html.gz")
+		htmlGzNotice := android.PathForModuleOut(ctx, "NOTICE.html.gz")
 		android.BuildNoticeHtmlOutputFromLicenseMetadata(
-			ctx, a.htmlGzNotice, "", "",
+			ctx, htmlGzNotice, "", "",
 			[]string{
 				android.PathForModuleInstall(ctx).String() + "/",
 				android.PathForModuleInPartitionInstall(ctx, "apex").String() + "/",
@@ -660,7 +660,7 @@
 		noticeAssetPath := android.PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
 		builder := android.NewRuleBuilder(pctx, ctx)
 		builder.Command().Text("cp").
-			Input(a.htmlGzNotice).
+			Input(htmlGzNotice).
 			Output(noticeAssetPath)
 		builder.Build("notice_dir", "Building notice dir")
 		implicitInputs = append(implicitInputs, noticeAssetPath)
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 572bbd1..b675f17 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -30,6 +30,7 @@
 	// be a subset of OutputFiles. (or shared libraries, this will be equal to OutputFiles,
 	// but general cc_library will also have dynamic libraries in output files).
 	RootDynamicLibraries []string
+	TidyFiles            []string
 	TocFile              string
 	UnstrippedOutput     string
 }
@@ -165,6 +166,12 @@
   # NOTE: It's OK if there's no ToC, as Soong just uses it for optimization
   pass
 
+tidy_files = []
+p = providers(target)
+clang_tidy_info = p.get("//build/bazel/rules/cc:clang_tidy.bzl%ClangTidyInfo")
+if clang_tidy_info:
+  tidy_files = [v.path for v in clang_tidy_info.tidy_files.to_list()]
+
 return json_encode({
 	"OutputFiles": outputFiles,
 	"CcObjectFiles": ccObjectFiles,
@@ -175,6 +182,7 @@
 	"Headers": headers,
 	"RootStaticArchives": rootStaticArchives,
 	"RootDynamicLibraries": rootSharedLibraries,
+	"TidyFiles": tidy_files,
 	"TocFile": toc_file,
 	"UnstrippedOutput": unstripped,
 })`
@@ -186,7 +194,9 @@
 // Starlark given in StarlarkFunctionBody.
 func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) {
 	var ccInfo CcInfo
-	parseJson(rawString, &ccInfo)
+	if err := parseJson(rawString, &ccInfo); err != nil {
+		return ccInfo, err
+	}
 	return ccInfo, nil
 }
 
@@ -219,27 +229,35 @@
     "container_key_info": [container_key_info.pem.path, container_key_info.pk8.path, container_key_info.key_name],
     "package_name": info.package_name,
     "symbols_used_by_apex": info.symbols_used_by_apex.path,
+    "java_symbols_used_by_apex": info.java_symbols_used_by_apex.path,
+    "backing_libs": info.backing_libs.path,
+    "bundle_file": info.base_with_config_zip.path,
+    "installed_files": info.installed_files.path,
 })`
 }
 
 type ApexInfo struct {
-	SignedOutput      string   `json:"signed_output"`
-	UnsignedOutput    string   `json:"unsigned_output"`
-	ProvidesLibs      []string `json:"provides_native_libs"`
-	RequiresLibs      []string `json:"requires_native_libs"`
-	BundleKeyInfo     []string `json:"bundle_key_info"`
-	ContainerKeyInfo  []string `json:"container_key_info"`
-	PackageName       string   `json:"package_name"`
-	SymbolsUsedByApex string   `json:"symbols_used_by_apex"`
+	SignedOutput          string   `json:"signed_output"`
+	UnsignedOutput        string   `json:"unsigned_output"`
+	ProvidesLibs          []string `json:"provides_native_libs"`
+	RequiresLibs          []string `json:"requires_native_libs"`
+	BundleKeyInfo         []string `json:"bundle_key_info"`
+	ContainerKeyInfo      []string `json:"container_key_info"`
+	PackageName           string   `json:"package_name"`
+	SymbolsUsedByApex     string   `json:"symbols_used_by_apex"`
+	JavaSymbolsUsedByApex string   `json:"java_symbols_used_by_apex"`
+	BackingLibs           string   `json:"backing_libs"`
+	BundleFile            string   `json:"bundle_file"`
+	InstalledFiles        string   `json:"installed_files"`
 }
 
 // ParseResult returns a value obtained by parsing the result of the request's Starlark function.
 // The given rawString must correspond to the string output which was created by evaluating the
 // Starlark given in StarlarkFunctionBody.
-func (g getApexInfoType) ParseResult(rawString string) ApexInfo {
+func (g getApexInfoType) ParseResult(rawString string) (ApexInfo, error) {
 	var info ApexInfo
-	parseJson(rawString, &info)
-	return info
+	err := parseJson(rawString, &info)
+	return info, err
 }
 
 // getCcUnstrippedInfoType implements cqueryRequest interface. It handles the
@@ -268,10 +286,10 @@
 // ParseResult returns a value obtained by parsing the result of the request's Starlark function.
 // The given rawString must correspond to the string output which was created by evaluating the
 // Starlark given in StarlarkFunctionBody.
-func (g getCcUnstippedInfoType) ParseResult(rawString string) CcUnstrippedInfo {
+func (g getCcUnstippedInfoType) ParseResult(rawString string) (CcUnstrippedInfo, error) {
 	var info CcUnstrippedInfo
-	parseJson(rawString, &info)
-	return info
+	err := parseJson(rawString, &info)
+	return info, err
 }
 
 type CcUnstrippedInfo struct {
@@ -291,10 +309,12 @@
 
 // parseJson decodes json string into the fields of the receiver.
 // Unknown attribute name causes panic.
-func parseJson(jsonString string, info interface{}) {
+func parseJson(jsonString string, info interface{}) error {
 	decoder := json.NewDecoder(strings.NewReader(jsonString))
 	decoder.DisallowUnknownFields() //useful to detect typos, e.g. in unit tests
-	if err := decoder.Decode(info); err != nil {
-		panic(fmt.Errorf("cannot parse cquery result '%s': %s", jsonString, err))
+	err := decoder.Decode(info)
+	if err != nil {
+		return fmt.Errorf("cannot parse cquery result '%s': %s", jsonString, err)
 	}
+	return nil
 }
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index c7eb84e..1d30535 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -3,10 +3,12 @@
 import (
 	"encoding/json"
 	"reflect"
+	"strings"
 	"testing"
 )
 
 func TestGetOutputFilesParseResults(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		description    string
 		input          string
@@ -29,14 +31,17 @@
 		},
 	}
 	for _, tc := range testCases {
-		actualOutput := GetOutputFiles.ParseResult(tc.input)
-		if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
-			t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
-		}
+		t.Run(tc.description, func(t *testing.T) {
+			actualOutput := GetOutputFiles.ParseResult(tc.input)
+			if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+				t.Errorf("expected %#v != actual %#v", tc.expectedOutput, actualOutput)
+			}
+		})
 	}
 }
 
 func TestGetPythonBinaryParseResults(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		description    string
 		input          string
@@ -54,14 +59,17 @@
 		},
 	}
 	for _, tc := range testCases {
-		actualOutput := GetPythonBinary.ParseResult(tc.input)
-		if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
-			t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
-		}
+		t.Run(tc.description, func(t *testing.T) {
+			actualOutput := GetPythonBinary.ParseResult(tc.input)
+			if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+				t.Errorf("expected %#v != actual %#v", tc.expectedOutput, actualOutput)
+			}
+		})
 	}
 }
 
 func TestGetCcInfoParseResults(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		description    string
 		inputCcInfo    CcInfo
@@ -73,24 +81,6 @@
 			expectedOutput: CcInfo{},
 		},
 		{
-			description: "only output",
-			inputCcInfo: CcInfo{
-				OutputFiles: []string{"test", "test3"},
-			},
-			expectedOutput: CcInfo{
-				OutputFiles: []string{"test", "test3"},
-			},
-		},
-		{
-			description: "only ToC",
-			inputCcInfo: CcInfo{
-				TocFile: "test",
-			},
-			expectedOutput: CcInfo{
-				TocFile: "test",
-			},
-		},
-		{
 			description: "all items set",
 			inputCcInfo: CcInfo{
 				OutputFiles:          []string{"out1", "out2"},
@@ -119,17 +109,51 @@
 		},
 	}
 	for _, tc := range testCases {
-		jsonInput, _ := json.Marshal(tc.inputCcInfo)
-		actualOutput, err := GetCcInfo.ParseResult(string(jsonInput))
-		if err != nil {
-			t.Errorf("%q:\n test case get error: %q", tc.description, err)
-		} else if err == nil && !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
-			t.Errorf("%q:\n expected %#v\n!= actual %#v", tc.description, tc.expectedOutput, actualOutput)
-		}
+		t.Run(tc.description, func(t *testing.T) {
+			jsonInput, _ := json.Marshal(tc.inputCcInfo)
+			actualOutput, err := GetCcInfo.ParseResult(string(jsonInput))
+			if err != nil {
+				t.Errorf("error parsing result: %q", err)
+			} else if err == nil && !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+				t.Errorf("expected %#v\n!= actual %#v", tc.expectedOutput, actualOutput)
+			}
+		})
+	}
+}
+
+func TestGetCcInfoParseResultsError(t *testing.T) {
+	t.Parallel()
+	testCases := []struct {
+		description   string
+		input         string
+		expectedError string
+	}{
+		{
+			description:   "not json",
+			input:         ``,
+			expectedError: `cannot parse cquery result '': EOF`,
+		},
+		{
+			description: "invalid field",
+			input: `{
+	"toc_file": "dir/file.so.toc"
+}`,
+			expectedError: `json: unknown field "toc_file"`,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.description, func(t *testing.T) {
+			_, err := GetCcInfo.ParseResult(tc.input)
+			if !strings.Contains(err.Error(), tc.expectedError) {
+				t.Errorf("expected string %q in error message, got %q", tc.expectedError, err)
+			}
+		})
 	}
 }
 
 func TestGetApexInfoParseResults(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		description    string
 		input          string
@@ -142,14 +166,19 @@
 		},
 		{
 			description: "one result",
-			input: `{"signed_output":"my.apex",` +
-				`"unsigned_output":"my.apex.unsigned",` +
-				`"requires_native_libs":["//bionic/libc:libc","//bionic/libdl:libdl"],` +
-				`"bundle_key_info":["foo.pem", "foo.privkey"],` +
-				`"container_key_info":["foo.x509.pem", "foo.pk8", "foo"],` +
-				`"package_name":"package.name",` +
-				`"symbols_used_by_apex": "path/to/my.apex_using.txt",` +
-				`"provides_native_libs":[]}`,
+			input: `{
+	"signed_output":"my.apex",
+	"unsigned_output":"my.apex.unsigned",
+	"requires_native_libs":["//bionic/libc:libc","//bionic/libdl:libdl"],
+	"bundle_key_info":["foo.pem", "foo.privkey"],
+	"container_key_info":["foo.x509.pem", "foo.pk8", "foo"],
+	"package_name":"package.name",
+	"symbols_used_by_apex": "path/to/my.apex_using.txt",
+	"backing_libs":"path/to/backing.txt",
+	"bundle_file": "dir/bundlefile.zip",
+	"installed_files":"path/to/installed-files.txt",
+	"provides_native_libs":[]
+}`,
 			expectedOutput: ApexInfo{
 				SignedOutput:      "my.apex",
 				UnsignedOutput:    "my.apex.unsigned",
@@ -159,18 +188,58 @@
 				ContainerKeyInfo:  []string{"foo.x509.pem", "foo.pk8", "foo"},
 				PackageName:       "package.name",
 				SymbolsUsedByApex: "path/to/my.apex_using.txt",
+				BackingLibs:       "path/to/backing.txt",
+				BundleFile:        "dir/bundlefile.zip",
+				InstalledFiles:    "path/to/installed-files.txt",
 			},
 		},
 	}
 	for _, tc := range testCases {
-		actualOutput := GetApexInfo.ParseResult(tc.input)
-		if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
-			t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
-		}
+		t.Run(tc.description, func(t *testing.T) {
+			actualOutput, err := GetApexInfo.ParseResult(tc.input)
+			if err != nil {
+				t.Errorf("Unexpected error %q", err)
+			}
+			if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+				t.Errorf("expected %#v != actual %#v", tc.expectedOutput, actualOutput)
+			}
+		})
+	}
+}
+
+func TestGetApexInfoParseResultsError(t *testing.T) {
+	t.Parallel()
+	testCases := []struct {
+		description   string
+		input         string
+		expectedError string
+	}{
+		{
+			description:   "not json",
+			input:         ``,
+			expectedError: `cannot parse cquery result '': EOF`,
+		},
+		{
+			description: "invalid field",
+			input: `{
+	"fake_field": "path/to/file"
+}`,
+			expectedError: `json: unknown field "fake_field"`,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.description, func(t *testing.T) {
+			_, err := GetApexInfo.ParseResult(tc.input)
+			if !strings.Contains(err.Error(), tc.expectedError) {
+				t.Errorf("expected string %q in error message, got %q", tc.expectedError, err)
+			}
+		})
 	}
 }
 
 func TestGetCcUnstrippedParseResults(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		description    string
 		input          string
@@ -191,9 +260,45 @@
 		},
 	}
 	for _, tc := range testCases {
-		actualOutput := GetCcUnstrippedInfo.ParseResult(tc.input)
-		if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
-			t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
-		}
+		t.Run(tc.description, func(t *testing.T) {
+			actualOutput, err := GetCcUnstrippedInfo.ParseResult(tc.input)
+			if err != nil {
+				t.Errorf("Unexpected error %q", err)
+			}
+			if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+				t.Errorf("expected %#v != actual %#v", tc.expectedOutput, actualOutput)
+			}
+		})
+	}
+}
+
+func TestGetCcUnstrippedParseResultsErrors(t *testing.T) {
+	t.Parallel()
+	testCases := []struct {
+		description   string
+		input         string
+		expectedError string
+	}{
+		{
+			description:   "not json",
+			input:         ``,
+			expectedError: `cannot parse cquery result '': EOF`,
+		},
+		{
+			description: "invalid field",
+			input: `{
+	"fake_field": "path/to/file"
+}`,
+			expectedError: `json: unknown field "fake_field"`,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.description, func(t *testing.T) {
+			_, err := GetCcUnstrippedInfo.ParseResult(tc.input)
+			if !strings.Contains(err.Error(), tc.expectedError) {
+				t.Errorf("expected string %q in error message, got %q", tc.expectedError, err)
+			}
+		})
 	}
 }
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 0d6d5b8..4c86374 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -3508,3 +3508,38 @@
 		},
 	})
 }
+
+func TestCcLibraryWithTidy(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library uses tidy properties",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+cc_library_static {
+    name: "foo",
+    srcs: ["foo.cpp"],
+	tidy: true,
+	tidy_checks: ["check1", "check2"],
+	tidy_checks_as_errors: ["check1error", "check2error"],
+	tidy_disabled_srcs: ["bar.cpp"],
+	tidy_timeout_srcs: ["baz.cpp"],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+				"srcs":           `["foo.cpp"]`,
+				"tidy":           `True`,
+				"tidy_checks": `[
+        "check1",
+        "check2",
+    ]`,
+				"tidy_checks_as_errors": `[
+        "check1error",
+        "check2error",
+    ]`,
+				"tidy_disabled_srcs": `["bar.cpp"]`,
+				"tidy_timeout_srcs":  `["baz.cpp"]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 8ca13b8..6eb93bc 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -23,6 +23,9 @@
 func CreateSoongInjectionFiles(cfg android.Config, metrics CodegenMetrics) []BazelFile {
 	var files []BazelFile
 
+	files = append(files, newFile("android", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
+	files = append(files, newFile("android", "constants.bzl", android.BazelCcToolchainVars(cfg)))
+
 	files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
 	files = append(files, newFile("cc_toolchain", "constants.bzl", cc_config.BazelCcToolchainVars(cfg)))
 
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index cfd6128..8de2f83 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -88,6 +88,14 @@
 
 	expectedFilePaths := []bazelFilepath{
 		{
+			dir:      "android",
+			basename: GeneratedBuildFileName,
+		},
+		{
+			dir:      "android",
+			basename: "constants.bzl",
+		},
+		{
 			dir:      "cc_toolchain",
 			basename: GeneratedBuildFileName,
 		},
diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
index 74e2dbd..00056f8 100644
--- a/bp2build/java_library_conversion_test.go
+++ b/bp2build/java_library_conversion_test.go
@@ -649,3 +649,46 @@
 				}),
 		}})
 }
+
+func TestJavaLibraryKotlinSrcs(t *testing.T) {
+	runJavaLibraryTestCase(t, Bp2buildTestCase{
+		Description: "java_library with kotlin  srcs",
+		Blueprint: `java_library {
+    name: "java-lib-1",
+    srcs: ["a.java", "b.java", "c.kt"],
+    bazel_module: { bp2build_available: true },
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("kt_jvm_library", "java-lib-1", AttrNameToString{
+				"srcs": `[
+        "a.java",
+        "b.java",
+        "c.kt",
+    ]`,
+			}),
+		},
+	})
+}
+
+func TestJavaLibraryKotlinCommonSrcs(t *testing.T) {
+	runJavaLibraryTestCase(t, Bp2buildTestCase{
+		Description: "java_library with kotlin  common_srcs",
+		Blueprint: `java_library {
+    name: "java-lib-1",
+    srcs: ["a.java", "b.java"],
+    common_srcs: ["c.kt"],
+    bazel_module: { bp2build_available: true },
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("kt_jvm_library", "java-lib-1", AttrNameToString{
+				"srcs": `[
+        "a.java",
+        "b.java",
+    ]`,
+				"common_srcs": `["c.kt"]`,
+			}),
+		},
+	})
+}
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 60a410d..a840fa3 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -18,11 +18,13 @@
 	"fmt"
 	"io"
 	"path/filepath"
+	"runtime"
 	"strings"
 
 	"android/soong/android"
 	"android/soong/bazel"
 	"android/soong/bazel/cquery"
+	"android/soong/cc"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -31,6 +33,7 @@
 func init() {
 	registerBpfBuildComponents(android.InitRegistrationContext)
 	pctx.Import("android/soong/cc/config")
+	pctx.StaticVariable("relPwd", cc.PwdPrefix())
 }
 
 var (
@@ -40,7 +43,7 @@
 		blueprint.RuleParams{
 			Depfile:     "${out}.d",
 			Deps:        blueprint.DepsGCC,
-			Command:     "$ccCmd --target=bpf -c $cFlags -MD -MF ${out}.d -o $out $in",
+			Command:     "$relPwd $ccCmd --target=bpf -c $cFlags -MD -MF ${out}.d -o $out $in",
 			CommandDeps: []string{"$ccCmd"},
 		},
 		"ccCmd", "cFlags")
@@ -164,6 +167,9 @@
 
 	if proptools.Bool(bpf.properties.Btf) {
 		cflags = append(cflags, "-g")
+		if runtime.GOOS != "darwin" {
+			cflags = append(cflags, "-fdebug-prefix-map=/proc/self/cwd=")
+		}
 	}
 
 	srcs := android.PathsForModuleSrc(ctx, bpf.properties.Srcs)
diff --git a/cc/afdo.go b/cc/afdo.go
index fb66bbe..d36f4af 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -36,7 +36,7 @@
 
 func getAfdoProfileProjects(config android.DeviceConfig) []string {
 	return config.OnceStringSlice(afdoProfileProjectsConfigKey, func() []string {
-		return append(globalAfdoProfileProjects, config.AfdoAdditionalProfileDirs()...)
+		return globalAfdoProfileProjects
 	})
 }
 
diff --git a/cc/binary.go b/cc/binary.go
index d09e744..c2868e7 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -646,7 +646,7 @@
 		sdkAttributes: bp2BuildParseSdkAttributes(m),
 	}
 
-	m.convertTidyAttributes(&attrs.tidyAttributes)
+	m.convertTidyAttributes(ctx, &attrs.tidyAttributes)
 
 	return attrs
 }
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 2f79cae..6caa854 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -75,9 +75,11 @@
 	Tidy_flags            []string
 	Tidy_checks           []string
 	Tidy_checks_as_errors []string
+	Tidy_disabled_srcs    bazel.LabelListAttribute
+	Tidy_timeout_srcs     bazel.LabelListAttribute
 }
 
-func (m *Module) convertTidyAttributes(moduleAttrs *tidyAttributes) {
+func (m *Module) convertTidyAttributes(ctx android.BaseMutatorContext, moduleAttrs *tidyAttributes) {
 	for _, f := range m.features {
 		if tidy, ok := f.(*tidyFeature); ok {
 			moduleAttrs.Tidy = tidy.Properties.Tidy
@@ -85,6 +87,18 @@
 			moduleAttrs.Tidy_checks = tidy.Properties.Tidy_checks
 			moduleAttrs.Tidy_checks_as_errors = tidy.Properties.Tidy_checks_as_errors
 		}
+
+	}
+	archVariantProps := m.GetArchVariantProperties(ctx, &BaseCompilerProperties{})
+	for axis, configToProps := range archVariantProps {
+		for config, _props := range configToProps {
+			if archProps, ok := _props.(*BaseCompilerProperties); ok {
+				archDisabledSrcs := android.BazelLabelForModuleSrc(ctx, archProps.Tidy_disabled_srcs)
+				moduleAttrs.Tidy_disabled_srcs.SetSelectValue(axis, config, archDisabledSrcs)
+				archTimeoutSrcs := android.BazelLabelForModuleSrc(ctx, archProps.Tidy_timeout_srcs)
+				moduleAttrs.Tidy_timeout_srcs.SetSelectValue(axis, config, archTimeoutSrcs)
+			}
+		}
 	}
 }
 
diff --git a/cc/cc.go b/cc/cc.go
index a8011b8..306e483 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1853,6 +1853,11 @@
 func (c *Module) ProcessBazelQueryResponse(ctx android.ModuleContext) {
 	bazelModuleLabel := c.getBazelModuleLabel(ctx)
 
+	bazelCtx := ctx.Config().BazelContext
+	if ccInfo, err := bazelCtx.GetCcInfo(bazelModuleLabel, android.GetConfigKey(ctx)); err == nil {
+		c.tidyFiles = android.PathsForBazelOut(ctx, ccInfo.TidyFiles)
+	}
+
 	c.bazelHandler.ProcessBazelQueryResponse(ctx, bazelModuleLabel)
 
 	c.Properties.SubName = GetSubnameProperty(ctx, c)
@@ -2540,6 +2545,8 @@
 			}, vndkExtDepTag, GetReplaceModuleName(vndkdep.getVndkExtendsModuleName(), GetSnapshot(c, &snapshotInfo, actx).SharedLibs))
 		}
 	}
+
+	updateImportedLibraryDependency(ctx)
 }
 
 func BeginMutator(ctx android.BottomUpMutatorContext) {
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index b53a097..981d1ea 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -222,6 +222,7 @@
 		"":               "${config.ArmGenericCflags}",
 		"cortex-a7":      "${config.ArmCortexA7Cflags}",
 		"cortex-a8":      "${config.ArmCortexA8Cflags}",
+		"cortex-a9":      "${config.ArmGenericCflags}",
 		"cortex-a15":     "${config.ArmCortexA15Cflags}",
 		"cortex-a53":     "${config.ArmCortexA53Cflags}",
 		"cortex-a53.a57": "${config.ArmCortexA53Cflags}",
diff --git a/cc/config/riscv64_device.go b/cc/config/riscv64_device.go
index 825be7f..67208b2 100644
--- a/cc/config/riscv64_device.go
+++ b/cc/config/riscv64_device.go
@@ -32,7 +32,6 @@
 
 	riscv64Ldflags = []string{
 		"-Wl,--hash-style=gnu",
-		"-Wl,-z,separate-code",
 	}
 
 	riscv64Lldflags = append(riscv64Ldflags,
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index f32ebf2..1180da4 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -55,6 +55,21 @@
 		// http://b/241819232
 		"-misc-const-correctness",
 	}
+
+	extraArgFlags = []string{
+		// We might be using the static analyzer through clang tidy.
+		// https://bugs.llvm.org/show_bug.cgi?id=32914
+		"-D__clang_analyzer__",
+
+		// A recent change in clang-tidy (r328258) enabled destructor inlining, which
+		// appears to cause a number of false positives. Until that's resolved, this turns
+		// off the effects of r328258.
+		// https://bugs.llvm.org/show_bug.cgi?id=37459
+		"-Xclang",
+		"-analyzer-config",
+		"-Xclang",
+		"c++-temp-dtor-inlining=false",
+	}
 )
 
 func init() {
@@ -145,6 +160,8 @@
 		return strings.Join(globalNoErrorCheckList, ",")
 	})
 
+	exportedVars.ExportStringListStaticVariable("TidyExtraArgFlags", extraArgFlags)
+
 	// To reduce duplicate warnings from the same header files,
 	// header-filter will contain only the module directory and
 	// those specified by DEFAULT_TIDY_HEADER_DIRS.
@@ -235,6 +252,10 @@
 	return ""
 }
 
+func TidyExtraArgFlags() []string {
+	return extraArgFlags
+}
+
 func TidyFlagsForSrcFile(srcFile android.Path, flags string) string {
 	// Disable clang-analyzer-* checks globally for generated source files
 	// because some of them are too huge. Local .bp files can add wanted
diff --git a/cc/library.go b/cc/library.go
index e55b516..b639930 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -956,7 +956,7 @@
 	for _, path := range paths {
 		dir := path.String()
 		// Skip if dir is for generated headers
-		if strings.HasPrefix(dir, android.PathForOutput(ctx).String()) {
+		if strings.HasPrefix(dir, ctx.Config().OutDir()) {
 			continue
 		}
 
@@ -2598,11 +2598,12 @@
 	m := mctx.Module().(*Module)
 	isLLNDK := m.IsLlndk()
 	isVendorPublicLibrary := m.IsVendorPublicLibrary()
+	isImportedApiLibrary := m.isImportedApiLibrary()
 
 	modules := mctx.CreateLocalVariations(variants...)
 	for i, m := range modules {
 
-		if variants[i] != "" || isLLNDK || isVendorPublicLibrary {
+		if variants[i] != "" || isLLNDK || isVendorPublicLibrary || isImportedApiLibrary {
 			// A stubs or LLNDK stubs variant.
 			c := m.(*Module)
 			c.sanitize = nil
@@ -2792,7 +2793,7 @@
 		Runtime_deps:                      linkerAttrs.runtimeDeps,
 	}
 
-	module.convertTidyAttributes(&commonAttrs.tidyAttributes)
+	module.convertTidyAttributes(ctx, &commonAttrs.tidyAttributes)
 
 	var attrs interface{}
 	if isStatic {
diff --git a/cc/library_stub.go b/cc/library_stub.go
index 760d36a..043c03c 100644
--- a/cc/library_stub.go
+++ b/cc/library_stub.go
@@ -15,6 +15,10 @@
 package cc
 
 import (
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/multitree"
 )
@@ -26,6 +30,30 @@
 func RegisterLibraryStubBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("cc_api_library", CcApiLibraryFactory)
 	ctx.RegisterModuleType("cc_api_headers", CcApiHeadersFactory)
+	ctx.RegisterModuleType("cc_api_variant", CcApiVariantFactory)
+}
+
+func updateImportedLibraryDependency(ctx android.BottomUpMutatorContext) {
+	m, ok := ctx.Module().(*Module)
+	if !ok {
+		return
+	}
+
+	apiLibrary, ok := m.linker.(*apiLibraryDecorator)
+	if !ok {
+		return
+	}
+
+	if m.UseVndk() && apiLibrary.hasLLNDKStubs() {
+		// Add LLNDK dependencies
+		for _, variant := range apiLibrary.properties.Variants {
+			if variant == "llndk" {
+				variantName := BuildApiVariantName(m.BaseModuleName(), "llndk", "")
+				ctx.AddDependency(m, nil, variantName)
+				break
+			}
+		}
+	}
 }
 
 // 'cc_api_library' is a module type which is from the exported API surface
@@ -33,7 +61,8 @@
 // offer a link to the module that generates shared library object from the
 // map file.
 type apiLibraryProperties struct {
-	Src *string `android:"arch_variant"`
+	Src      *string `android:"arch_variant"`
+	Variants []string
 }
 
 type apiLibraryDecorator struct {
@@ -55,11 +84,9 @@
 	module.compiler = nil
 	module.linker = apiLibraryDecorator
 	module.installer = nil
+	module.library = apiLibraryDecorator
 	module.AddProperties(&module.Properties, &apiLibraryDecorator.properties)
 
-	// Mark module as stub, so APEX would not include this stub in the package.
-	module.library.setBuildStubs(true)
-
 	// Prevent default system libs (libc, libm, and libdl) from being linked
 	if apiLibraryDecorator.baseLinker.Properties.System_shared_libs == nil {
 		apiLibraryDecorator.baseLinker.Properties.System_shared_libs = []string{}
@@ -91,12 +118,45 @@
 }
 
 func (d *apiLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objects Objects) android.Path {
-	// Export headers as system include dirs if specified. Mostly for libc
-	if Bool(d.libraryDecorator.Properties.Llndk.Export_headers_as_system) {
-		d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs = append(
-			d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs,
-			d.libraryDecorator.flagExporter.Properties.Export_include_dirs...)
-		d.libraryDecorator.flagExporter.Properties.Export_include_dirs = nil
+	m, _ := ctx.Module().(*Module)
+
+	var in android.Path
+
+	if src := proptools.String(d.properties.Src); src != "" {
+		in = android.PathForModuleSrc(ctx, src)
+	}
+
+	// LLNDK variant
+	if m.UseVndk() && d.hasLLNDKStubs() {
+		apiVariantModule := BuildApiVariantName(m.BaseModuleName(), "llndk", "")
+
+		var mod android.Module
+
+		ctx.VisitDirectDeps(func(depMod android.Module) {
+			if depMod.Name() == apiVariantModule {
+				mod = depMod
+			}
+		})
+
+		if mod != nil {
+			variantMod, ok := mod.(*CcApiVariant)
+			if ok {
+				in = variantMod.Src()
+
+				// Copy LLDNK properties to cc_api_library module
+				d.libraryDecorator.flagExporter.Properties.Export_include_dirs = append(
+					d.libraryDecorator.flagExporter.Properties.Export_include_dirs,
+					variantMod.exportProperties.Export_headers...)
+
+				// Export headers as system include dirs if specified. Mostly for libc
+				if proptools.Bool(variantMod.exportProperties.Export_headers_as_system) {
+					d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs = append(
+						d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs,
+						d.libraryDecorator.flagExporter.Properties.Export_include_dirs...)
+					d.libraryDecorator.flagExporter.Properties.Export_include_dirs = nil
+				}
+			}
+		}
 	}
 
 	// Flags reexported from dependencies. (e.g. vndk_prebuilt_shared)
@@ -107,13 +167,10 @@
 	d.libraryDecorator.reexportDeps(deps.ReexportedDeps...)
 	d.libraryDecorator.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...)
 
-	if d.properties.Src == nil {
-		ctx.PropertyErrorf("src", "src is a required property")
+	if in == nil {
+		ctx.PropertyErrorf("src", "Unable to locate source property")
+		return nil
 	}
-	// Skip the existence check of the stub prebuilt file.
-	// The file is not guaranteed to exist during Soong analysis.
-	// Build orchestrator will be responsible for creating a connected ninja graph.
-	in := android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), *d.properties.Src)
 
 	// Make the _compilation_ of rdeps have an order-only dep on cc_api_library.src (an .so file)
 	// The .so file itself has an order-only dependency on the headers contributed by this library.
@@ -143,6 +200,43 @@
 	return true
 }
 
+func (d *apiLibraryDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
+	m, ok := ctx.Module().(*Module)
+
+	if !ok {
+		return nil
+	}
+
+	if d.hasLLNDKStubs() && m.UseVndk() {
+		// LLNDK libraries only need a single stubs variant.
+		return []string{android.FutureApiLevel.String()}
+	}
+
+	// TODO(b/244244438) Create more version information for NDK and APEX variations
+	// NDK variants
+	if m.MinSdkVersion() == "" {
+		return nil
+	}
+
+	firstVersion, err := nativeApiLevelFromUser(ctx,
+		m.MinSdkVersion())
+
+	if err != nil {
+		return nil
+	}
+
+	return ndkLibraryVersions(ctx, firstVersion)
+}
+
+func (d *apiLibraryDecorator) hasLLNDKStubs() bool {
+	for _, variant := range d.properties.Variants {
+		if strings.Contains(variant, "llndk") {
+			return true
+		}
+	}
+	return false
+}
+
 // 'cc_api_headers' is similar with 'cc_api_library', but which replaces
 // header libraries. The module will replace any dependencies to existing
 // original header libraries.
@@ -165,9 +259,6 @@
 	module.linker = apiHeadersDecorator
 	module.installer = nil
 
-	// Mark module as stub, so APEX would not include this stub in the package.
-	module.library.setBuildStubs(true)
-
 	// Prevent default system libs (libc, libm, and libdl) from being linked
 	if apiHeadersDecorator.baseLinker.Properties.System_shared_libs == nil {
 		apiHeadersDecorator.baseLinker.Properties.System_shared_libs = []string{}
@@ -189,3 +280,91 @@
 	// Stub from API surface should be available for any APEX.
 	return true
 }
+
+type ccApiexportProperties struct {
+	Src     *string `android:"arch_variant"`
+	Variant *string
+	Version *string
+}
+
+type variantExporterProperties struct {
+	// Header directory to export
+	Export_headers []string `android:"arch_variant"`
+
+	// Export all headers as system include
+	Export_headers_as_system *bool
+}
+
+type CcApiVariant struct {
+	android.ModuleBase
+
+	properties       ccApiexportProperties
+	exportProperties variantExporterProperties
+
+	src android.Path
+}
+
+var _ android.Module = (*CcApiVariant)(nil)
+var _ android.ImageInterface = (*CcApiVariant)(nil)
+
+func CcApiVariantFactory() android.Module {
+	module := &CcApiVariant{}
+
+	module.AddProperties(&module.properties)
+	module.AddProperties(&module.exportProperties)
+
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
+	return module
+}
+
+func (v *CcApiVariant) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// No need to build
+
+	if proptools.String(v.properties.Src) == "" {
+		ctx.PropertyErrorf("src", "src is a required property")
+	}
+
+	// Skip the existence check of the stub prebuilt file.
+	// The file is not guaranteed to exist during Soong analysis.
+	// Build orchestrator will be responsible for creating a connected ninja graph.
+	v.src = android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), proptools.String(v.properties.Src))
+}
+
+func (v *CcApiVariant) Name() string {
+	version := proptools.String(v.properties.Version)
+	return BuildApiVariantName(v.BaseModuleName(), *v.properties.Variant, version)
+}
+
+func (v *CcApiVariant) Src() android.Path {
+	return v.src
+}
+
+func BuildApiVariantName(baseName string, variant string, version string) string {
+	names := []string{baseName, variant}
+	if version != "" {
+		names = append(names, version)
+	}
+
+	return strings.Join(names[:], ".") + multitree.GetApiImportSuffix()
+}
+
+// Implement ImageInterface to generate image variants
+func (v *CcApiVariant) ImageMutatorBegin(ctx android.BaseModuleContext)               {}
+func (v *CcApiVariant) CoreVariantNeeded(ctx android.BaseModuleContext) bool          { return false }
+func (v *CcApiVariant) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool       { return false }
+func (v *CcApiVariant) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false }
+func (v *CcApiVariant) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool  { return false }
+func (v *CcApiVariant) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool      { return false }
+func (v *CcApiVariant) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+	var variations []string
+	platformVndkVersion := ctx.DeviceConfig().PlatformVndkVersion()
+
+	if proptools.String(v.properties.Variant) == "llndk" {
+		variations = append(variations, VendorVariationPrefix+platformVndkVersion)
+		variations = append(variations, ProductVariationPrefix+platformVndkVersion)
+	}
+
+	return variations
+}
+func (v *CcApiVariant) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+}
diff --git a/cc/library_stub_test.go b/cc/library_stub_test.go
index 54b0ba6..8ce74c4 100644
--- a/cc/library_stub_test.go
+++ b/cc/library_stub_test.go
@@ -284,3 +284,54 @@
 	// These header files are required for compiling the other API domain (vendor in this case)
 	android.AssertStringListContains(t, "Vendor binary compilation should have an implicit dep on the stub .so file", vendorImplicits, "libfoo.so")
 }
+
+func TestApiLibraryWithLlndkVariant(t *testing.T) {
+	bp := `
+		cc_binary {
+			name: "binfoo",
+			vendor: true,
+			srcs: ["binfoo.cc"],
+			shared_libs: ["libbar"],
+		}
+
+		cc_api_library {
+			name: "libbar",
+			// TODO(b/244244438) Remove src property once all variants are implemented.
+			src: "libbar.so",
+			vendor_available: true,
+			variants: [
+				"llndk",
+			],
+		}
+
+		cc_api_variant {
+			name: "libbar",
+			variant: "llndk",
+			src: "libbar_llndk.so",
+			export_headers: ["libbar_llndk_include"]
+		}
+
+		api_imports {
+			name: "api_imports",
+			shared_libs: [
+				"libbar",
+			],
+			header_libs: [],
+		}
+	`
+
+	ctx := prepareForCcTest.RunTestWithBp(t, bp)
+
+	libfoo := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Module()
+	libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_arm64_armv8-a_shared").Module()
+	libbarApiVariant := ctx.ModuleForTests("libbar.llndk.apiimport", "android_vendor.29_arm64_armv8-a").Module()
+
+	android.AssertBoolEquals(t, "Stub library from API surface should be linked", true, hasDirectDependency(t, ctx, libfoo, libbarApiImport))
+	android.AssertBoolEquals(t, "Stub library variant from API surface should be linked", true, hasDirectDependency(t, ctx, libbarApiImport, libbarApiVariant))
+
+	libFooLibFlags := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Rule("ld").Args["libFlags"]
+	android.AssertStringDoesContain(t, "Vendor binary should be linked with LLNDK variant source", libFooLibFlags, "libbar_llndk.so")
+
+	libFooCFlags := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Rule("cc").Args["cFlags"]
+	android.AssertStringDoesContain(t, "Vendor binary should include headers from the LLNDK variant source", libFooCFlags, "-Ilibbar_llndk_include")
+}
diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go
index 48ac650..2393f3e 100644
--- a/cc/sanitize_test.go
+++ b/cc/sanitize_test.go
@@ -41,6 +41,7 @@
 			static_libs: [
 				"libstatic",
 				"libnoasan",
+				"libstatic_asan",
 			],
 			sanitize: {
 				address: true,
@@ -57,6 +58,7 @@
 			static_libs: [
 				"libstatic",
 				"libnoasan",
+				"libstatic_asan",
 			],
 		}
 
@@ -92,6 +94,15 @@
 				address: false,
 			}
 		}
+
+		cc_library_static {
+			name: "libstatic_asan",
+			host_supported: true,
+			sanitize: {
+				address: true,
+			}
+		}
+
 	`
 
 	result := android.GroupFixturePreparers(
@@ -125,6 +136,10 @@
 		// Static library that never uses asan.
 		libNoAsan := result.ModuleForTests("libnoasan", staticVariant)
 
+		// Static library that specifies asan
+		libStaticAsan := result.ModuleForTests("libstatic_asan", staticAsanVariant)
+		libStaticAsanNoAsanVariant := result.ModuleForTests("libstatic_asan", staticVariant)
+
 		// expectSharedLinkDep verifies that the from module links against the to module as a
 		// shared library.
 		expectSharedLinkDep := func(from, to android.TestingModule) {
@@ -176,6 +191,7 @@
 
 		expectStaticLinkDep(binWithAsan, libStaticAsanVariant)
 		expectStaticLinkDep(binWithAsan, libNoAsan)
+		expectStaticLinkDep(binWithAsan, libStaticAsan)
 
 		expectInstallDep(binWithAsan, libShared)
 		expectInstallDep(binWithAsan, libAsan)
@@ -190,6 +206,7 @@
 
 		expectStaticLinkDep(binNoAsan, libStaticNoAsanVariant)
 		expectStaticLinkDep(binNoAsan, libNoAsan)
+		expectStaticLinkDep(binNoAsan, libStaticAsanNoAsanVariant)
 
 		expectInstallDep(binNoAsan, libShared)
 		expectInstallDep(binNoAsan, libAsan)
diff --git a/cc/sdk.go b/cc/sdk.go
index a83e5ad..a0d196b 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -78,5 +78,7 @@
 		}
 	case *snapshotModule:
 		ctx.CreateVariations("")
+	case *CcApiVariant:
+		ctx.CreateVariations("")
 	}
 }
diff --git a/cc/strip.go b/cc/strip.go
index 5c32d8b..c60e135 100644
--- a/cc/strip.go
+++ b/cc/strip.go
@@ -56,9 +56,7 @@
 	forceEnable := Bool(stripper.StripProperties.Strip.All) ||
 		Bool(stripper.StripProperties.Strip.Keep_symbols) ||
 		Bool(stripper.StripProperties.Strip.Keep_symbols_and_debug_frame)
-	// create_minidebuginfo doesn't work for riscv64 yet, disable stripping for now
-	riscv64 := actx.Arch().ArchType == android.Riscv64
-	return !forceDisable && (forceEnable || defaultEnable) && !riscv64
+	return !forceDisable && (forceEnable || defaultEnable)
 }
 
 // Keep this consistent with //build/bazel/rules/stripped_shared_library.bzl.
diff --git a/cc/test.go b/cc/test.go
index 92055fa..536210b 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -678,7 +678,7 @@
 		}
 	}
 
-	m.convertTidyAttributes(&testBinaryAttrs.tidyAttributes)
+	m.convertTidyAttributes(ctx, &testBinaryAttrs.tidyAttributes)
 
 	for _, propIntf := range m.GetProperties() {
 		if testLinkerProps, ok := propIntf.(*TestLinkerProperties); ok {
diff --git a/cc/tidy.go b/cc/tidy.go
index a3d548b..bbcaece 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -136,19 +136,7 @@
 		flags.TidyFlags = append(flags.TidyFlags, "-extra-arg-before=-fno-caret-diagnostics")
 	}
 
-	extraArgFlags := []string{
-		// We might be using the static analyzer through clang tidy.
-		// https://bugs.llvm.org/show_bug.cgi?id=32914
-		"-D__clang_analyzer__",
-
-		// A recent change in clang-tidy (r328258) enabled destructor inlining, which
-		// appears to cause a number of false positives. Until that's resolved, this turns
-		// off the effects of r328258.
-		// https://bugs.llvm.org/show_bug.cgi?id=37459
-		"-Xclang", "-analyzer-config", "-Xclang", "c++-temp-dtor-inlining=false",
-	}
-
-	for _, f := range extraArgFlags {
+	for _, f := range config.TidyExtraArgFlags() {
 		flags.TidyFlags = append(flags.TidyFlags, "-extra-arg-before="+f)
 	}
 
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 3fed1a1..51d2345 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -106,24 +106,11 @@
 }
 
 func newNameResolver(config android.Config) *android.NameResolver {
-	namespacePathsToExport := make(map[string]bool)
-
-	for _, namespaceName := range config.ExportedNamespaces() {
-		namespacePathsToExport[namespaceName] = true
-	}
-
-	namespacePathsToExport["."] = true // always export the root namespace
-
-	exportFilter := func(namespace *android.Namespace) bool {
-		return namespacePathsToExport[namespace.Path]
-	}
-
-	return android.NewNameResolver(exportFilter)
+	return android.NewNameResolver(config)
 }
 
 func newContext(configuration android.Config) *android.Context {
 	ctx := android.NewContext(configuration)
-	ctx.Register()
 	ctx.SetNameInterface(newNameResolver(configuration))
 	ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
 	return ctx
@@ -165,7 +152,7 @@
 // Bazel-enabled mode. Attaches a mutator to queue Bazel requests, adds a
 // BeforePrepareBuildActionsHook to invoke Bazel, and then uses Bazel metadata
 // for modules that should be handled by Bazel.
-func runMixedModeBuild(configuration android.Config, ctx *android.Context, extraNinjaDeps []string) {
+func runMixedModeBuild(configuration android.Config, ctx *android.Context, extraNinjaDeps []string) string {
 	ctx.EventHandler.Begin("mixed_build")
 	defer ctx.EventHandler.End("mixed_build")
 
@@ -188,6 +175,7 @@
 	ninjaDeps = append(ninjaDeps, globListFiles...)
 
 	writeDepFile(cmdlineArgs.OutFile, ctx.EventHandler, ninjaDeps)
+	return cmdlineArgs.OutFile
 }
 
 // Run the code-generation phase to convert BazelTargetModules to BUILD files.
@@ -206,12 +194,11 @@
 
 // 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)
+func runApiBp2build(configuration android.Config, ctx *android.Context, extraNinjaDeps []string) string {
 	ctx.EventHandler.Begin("api_bp2build")
 	defer ctx.EventHandler.End("api_bp2build")
-	ctx.SetNameInterface(newNameResolver(configuration))
+	// Do not allow missing dependencies.
+	ctx.SetAllowMissingDependencies(false)
 	ctx.RegisterForApiBazelConversion()
 
 	// Register the Android.bp files in the tree
@@ -256,6 +243,11 @@
 	// Exclude all src BUILD files
 	excludes = append(excludes, apiBuildFileExcludes()...)
 
+	// Android.bp files for api surfaces are mounted to out/, but out/ should not be a
+	// dep for api_bp2build.
+	// Otherwise api_bp2build will be run every single time
+	excludes = append(excludes, configuration.OutDir())
+
 	// Create the symlink forest
 	symlinkDeps := bp2build.PlantSymlinkForest(
 		configuration.IsEnvTrue("BP2BUILD_VERBOSE"),
@@ -349,61 +341,77 @@
 // output file of the specific activity.
 func doChosenActivity(ctx *android.Context, configuration android.Config, extraNinjaDeps []string, metricsDir string) string {
 	if configuration.BuildMode == android.SymlinkForest {
-		runSymlinkForestCreation(configuration, extraNinjaDeps, metricsDir)
-		return symlinkForestMarker
+		return runSymlinkForestCreation(configuration, ctx, extraNinjaDeps, metricsDir)
 	} else if configuration.BuildMode == android.Bp2build {
 		// Run the alternate pipeline of bp2build mutators and singleton to convert
 		// Blueprint to BUILD files before everything else.
-		runBp2Build(configuration, extraNinjaDeps, metricsDir)
-		return bp2buildMarker
-	} else if configuration.IsMixedBuildsEnabled() {
-		runMixedModeBuild(configuration, ctx, extraNinjaDeps)
+		return runBp2Build(configuration, ctx, extraNinjaDeps, metricsDir)
 	} else if configuration.BuildMode == android.ApiBp2build {
-		return runApiBp2build(configuration, extraNinjaDeps)
+		outputFile := runApiBp2build(configuration, ctx, extraNinjaDeps)
+		writeMetrics(configuration, ctx.EventHandler, metricsDir)
+		return outputFile
 	} else {
-		var stopBefore bootstrap.StopBefore
-		if configuration.BuildMode == android.GenerateModuleGraph {
-			stopBefore = bootstrap.StopBeforeWriteNinja
-		} else if configuration.BuildMode == android.GenerateQueryView || configuration.BuildMode == android.GenerateDocFile {
-			stopBefore = bootstrap.StopBeforePrepareBuildActions
+		ctx.Register()
+
+		var outputFile string
+		if configuration.IsMixedBuildsEnabled() {
+			outputFile = runMixedModeBuild(configuration, ctx, extraNinjaDeps)
 		} else {
-			stopBefore = bootstrap.DoEverything
+			outputFile = runSoongOnlyBuild(configuration, ctx, extraNinjaDeps)
 		}
 
-		ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs, stopBefore, ctx.Context, configuration)
-		ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
+		writeMetrics(configuration, ctx.EventHandler, metricsDir)
 
-		globListFiles := writeBuildGlobsNinjaFile(ctx, configuration.SoongOutDir(), configuration)
-		ninjaDeps = append(ninjaDeps, globListFiles...)
+		return outputFile
+	}
+}
 
-		// Convert the Soong module graph into Bazel BUILD files.
-		if configuration.BuildMode == android.GenerateQueryView {
-			queryviewMarkerFile := bazelQueryViewDir + ".marker"
-			runQueryView(bazelQueryViewDir, queryviewMarkerFile, configuration, ctx)
-			writeDepFile(queryviewMarkerFile, ctx.EventHandler, ninjaDeps)
-			return queryviewMarkerFile
-		} else if configuration.BuildMode == android.GenerateModuleGraph {
-			writeJsonModuleGraphAndActions(ctx, moduleGraphFile, moduleActionsFile)
-			writeDepFile(moduleGraphFile, ctx.EventHandler, ninjaDeps)
-			return moduleGraphFile
-		} else if configuration.BuildMode == android.GenerateDocFile {
-			// TODO: we could make writeDocs() return the list of documentation files
-			// written and add them to the .d file. Then soong_docs would be re-run
-			// whenever one is deleted.
-			if err := writeDocs(ctx, shared.JoinPath(topDir, docFile)); err != nil {
-				fmt.Fprintf(os.Stderr, "error building Soong documentation: %s\n", err)
-				os.Exit(1)
-			}
-			writeDepFile(docFile, ctx.EventHandler, ninjaDeps)
-			return docFile
-		} else {
-			// The actual output (build.ninja) was written in the RunBlueprint() call
-			// above
-			writeDepFile(cmdlineArgs.OutFile, ctx.EventHandler, ninjaDeps)
-		}
+// runSoongOnlyBuild runs the standard Soong build in a number of different modes.
+func runSoongOnlyBuild(configuration android.Config, ctx *android.Context, extraNinjaDeps []string) string {
+	ctx.EventHandler.Begin("soong_build")
+	defer ctx.EventHandler.End("soong_build")
+
+	var stopBefore bootstrap.StopBefore
+	if configuration.BuildMode == android.GenerateModuleGraph {
+		stopBefore = bootstrap.StopBeforeWriteNinja
+	} else if configuration.BuildMode == android.GenerateQueryView || configuration.BuildMode == android.GenerateDocFile {
+		stopBefore = bootstrap.StopBeforePrepareBuildActions
+	} else {
+		stopBefore = bootstrap.DoEverything
 	}
 
-	return cmdlineArgs.OutFile
+	ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs, stopBefore, ctx.Context, configuration)
+	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
+
+	globListFiles := writeBuildGlobsNinjaFile(ctx, configuration.SoongOutDir(), configuration)
+	ninjaDeps = append(ninjaDeps, globListFiles...)
+
+	// Convert the Soong module graph into Bazel BUILD files.
+	if configuration.BuildMode == android.GenerateQueryView {
+		queryviewMarkerFile := bazelQueryViewDir + ".marker"
+		runQueryView(bazelQueryViewDir, queryviewMarkerFile, configuration, ctx)
+		writeDepFile(queryviewMarkerFile, ctx.EventHandler, ninjaDeps)
+		return queryviewMarkerFile
+	} else if configuration.BuildMode == android.GenerateModuleGraph {
+		writeJsonModuleGraphAndActions(ctx, moduleGraphFile, moduleActionsFile)
+		writeDepFile(moduleGraphFile, ctx.EventHandler, ninjaDeps)
+		return moduleGraphFile
+	} else if configuration.BuildMode == android.GenerateDocFile {
+		// TODO: we could make writeDocs() return the list of documentation files
+		// written and add them to the .d file. Then soong_docs would be re-run
+		// whenever one is deleted.
+		if err := writeDocs(ctx, shared.JoinPath(topDir, docFile)); err != nil {
+			fmt.Fprintf(os.Stderr, "error building Soong documentation: %s\n", err)
+			os.Exit(1)
+		}
+		writeDepFile(docFile, ctx.EventHandler, ninjaDeps)
+		return docFile
+	} else {
+		// The actual output (build.ninja) was written in the RunBlueprint() call
+		// above
+		writeDepFile(cmdlineArgs.OutFile, ctx.EventHandler, ninjaDeps)
+		return cmdlineArgs.OutFile
+	}
 }
 
 // soong_ui dumps the available environment variables to
@@ -463,13 +471,9 @@
 	logDir := availableEnv["LOG_DIR"]
 
 	ctx := newContext(configuration)
-	ctx.EventHandler.Begin("soong_build")
 
 	finalOutputFile := doChosenActivity(ctx, configuration, extraNinjaDeps, logDir)
 
-	ctx.EventHandler.End("soong_build")
-	writeMetrics(configuration, ctx.EventHandler, logDir)
-
 	writeUsedEnvironmentFile(configuration, finalOutputFile)
 }
 
@@ -614,9 +618,7 @@
 // Ideally, bp2build would write a file that contains instructions to the
 // symlink tree creation binary. Then the latter would not need to depend on
 // the very heavy-weight machinery of soong_build .
-func runSymlinkForestCreation(configuration android.Config, extraNinjaDeps []string, metricsDir string) {
-	eventHandler := &metrics.EventHandler{}
-
+func runSymlinkForestCreation(configuration android.Config, ctx *android.Context, extraNinjaDeps []string, metricsDir string) string {
 	var ninjaDeps []string
 	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
@@ -643,13 +645,13 @@
 	// Such a directory SHOULD be added to `ninjaDeps` so that a child directory
 	// or file created/deleted under it would trigger an update of the symlink
 	// forest.
-	eventHandler.Do("symlink_forest", func() {
+	ctx.EventHandler.Do("symlink_forest", func() {
 		symlinkForestDeps := bp2build.PlantSymlinkForest(
 			configuration.IsEnvTrue("BP2BUILD_VERBOSE"), topDir, workspaceRoot, generatedRoot, excludes)
 		ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
 	})
 
-	writeDepFile(symlinkForestMarker, eventHandler, ninjaDeps)
+	writeDepFile(symlinkForestMarker, ctx.EventHandler, ninjaDeps)
 	touch(shared.JoinPath(topDir, symlinkForestMarker))
 	codegenMetrics := bp2build.ReadCodegenMetrics(metricsDir)
 	if codegenMetrics == nil {
@@ -659,27 +661,24 @@
 		//TODO (usta) we cannot determine if we loaded a stale file, i.e. from an unrelated prior
 		//invocation of codegen. We should simply use a separate .pb file
 	}
-	writeBp2BuildMetrics(codegenMetrics, eventHandler, metricsDir)
+	writeBp2BuildMetrics(codegenMetrics, ctx.EventHandler, metricsDir)
+
+	return symlinkForestMarker
 }
 
 // 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.
-func runBp2Build(configuration android.Config, extraNinjaDeps []string, metricsDir string) {
+func runBp2Build(configuration android.Config, ctx *android.Context, extraNinjaDeps []string, metricsDir string) string {
 	var codegenMetrics *bp2build.CodegenMetrics
-	eventHandler := &metrics.EventHandler{}
-	eventHandler.Do("bp2build", func() {
-
-		// Register an alternate set of singletons and mutators for bazel
-		// conversion for Bazel conversion.
-		bp2buildCtx := android.NewContext(configuration)
+	ctx.EventHandler.Do("bp2build", func() {
 
 		// Propagate "allow misssing dependencies" bit. This is normally set in
-		// newContext(), but we create bp2buildCtx without calling that method.
-		bp2buildCtx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
-		bp2buildCtx.SetNameInterface(newNameResolver(configuration))
-		bp2buildCtx.RegisterForBazelConversion()
-		bp2buildCtx.SetModuleListFile(cmdlineArgs.ModuleListFile)
+		// newContext(), but we create ctx without calling that method.
+		ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
+		ctx.SetNameInterface(newNameResolver(configuration))
+		ctx.RegisterForBazelConversion()
+		ctx.SetModuleListFile(cmdlineArgs.ModuleListFile)
 
 		var ninjaDeps []string
 		ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
@@ -687,25 +686,25 @@
 		// Run the loading and analysis pipeline to prepare the graph of regular
 		// Modules parsed from Android.bp files, and the BazelTargetModules mapped
 		// from the regular Modules.
-		eventHandler.Do("bootstrap", func() {
+		ctx.EventHandler.Do("bootstrap", func() {
 			blueprintArgs := cmdlineArgs
-			bootstrapDeps := bootstrap.RunBlueprint(blueprintArgs, bootstrap.StopBeforePrepareBuildActions, bp2buildCtx.Context, configuration)
+			bootstrapDeps := bootstrap.RunBlueprint(blueprintArgs, bootstrap.StopBeforePrepareBuildActions, ctx.Context, configuration)
 			ninjaDeps = append(ninjaDeps, bootstrapDeps...)
 		})
 
-		globListFiles := writeBuildGlobsNinjaFile(bp2buildCtx, configuration.SoongOutDir(), configuration)
+		globListFiles := writeBuildGlobsNinjaFile(ctx, configuration.SoongOutDir(), configuration)
 		ninjaDeps = append(ninjaDeps, globListFiles...)
 
 		// Run the code-generation phase to convert BazelTargetModules to BUILD files
 		// and print conversion codegenMetrics to the user.
-		codegenContext := bp2build.NewCodegenContext(configuration, bp2buildCtx, bp2build.Bp2Build)
-		eventHandler.Do("codegen", func() {
+		codegenContext := bp2build.NewCodegenContext(configuration, ctx, bp2build.Bp2Build)
+		ctx.EventHandler.Do("codegen", func() {
 			codegenMetrics = bp2build.Codegen(codegenContext)
 		})
 
 		ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
 
-		writeDepFile(bp2buildMarker, eventHandler, ninjaDeps)
+		writeDepFile(bp2buildMarker, ctx.EventHandler, ninjaDeps)
 		touch(shared.JoinPath(topDir, bp2buildMarker))
 	})
 
@@ -715,7 +714,8 @@
 	if configuration.IsEnvTrue("BP2BUILD_VERBOSE") {
 		codegenMetrics.Print()
 	}
-	writeBp2BuildMetrics(codegenMetrics, eventHandler, metricsDir)
+	writeBp2BuildMetrics(codegenMetrics, ctx.EventHandler, metricsDir)
+	return bp2buildMarker
 }
 
 // Write Bp2Build metrics into $LOG_DIR
diff --git a/go.mod b/go.mod
index 5f0b91a..7239f6d 100644
--- a/go.mod
+++ b/go.mod
@@ -1,24 +1,21 @@
 module android/soong
 
-require google.golang.org/protobuf v0.0.0
+require (
+  google.golang.org/protobuf v0.0.0
+  github.com/google/blueprint v0.0.0
+  prebuilts/bazel/common/proto/analysis_v2 v0.0.0
+  prebuilts/bazel/common/proto/build v0.0.0 // indirect
+)
 
-require github.com/google/blueprint v0.0.0
-
-replace google.golang.org/protobuf v0.0.0 => ../../external/golang-protobuf
-
-replace github.com/google/blueprint v0.0.0 => ../blueprint
+replace (
+  google.golang.org/protobuf v0.0.0 => ../../external/golang-protobuf
+  github.com/google/blueprint v0.0.0 => ../blueprint
+  github.com/google/go-cmp v0.5.5 => ../../external/go-cmp
+  prebuilts/bazel/common/proto/analysis_v2 => ../../prebuilts/bazel/common/proto/analysis_v2
+  prebuilts/bazel/common/proto/build => ../../prebuilts/bazel/common/proto/build
+)
 
 // Indirect deps from golang-protobuf
 exclude github.com/golang/protobuf v1.5.0
 
-replace github.com/google/go-cmp v0.5.5 => ../../external/go-cmp
-
-require prebuilts/bazel/common/proto/analysis_v2 v0.0.0
-
-replace prebuilts/bazel/common/proto/analysis_v2 => ../../prebuilts/bazel/common/proto/analysis_v2
-
-require prebuilts/bazel/common/proto/build v0.0.0 // indirect
-
-replace prebuilts/bazel/common/proto/build => ../../prebuilts/bazel/common/proto/build
-
-go 1.18
+go 2.0
diff --git a/java/aar.go b/java/aar.go
index 6261f29..0fdde03 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -1046,7 +1046,8 @@
 }
 
 func (a *AndroidLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
-	commonAttrs, depLabels := a.convertLibraryAttrsBp2Build(ctx)
+	commonAttrs, bp2buildInfo := a.convertLibraryAttrsBp2Build(ctx)
+	depLabels := bp2buildInfo.DepLabels
 
 	deps := depLabels.Deps
 	if !commonAttrs.Srcs.IsEmpty() {
diff --git a/java/app.go b/java/app.go
index 2a51e10..e36808f 100755
--- a/java/app.go
+++ b/java/app.go
@@ -1492,7 +1492,8 @@
 
 // ConvertWithBp2build is used to convert android_app to Bazel.
 func (a *AndroidApp) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
-	commonAttrs, depLabels := a.convertLibraryAttrsBp2Build(ctx)
+	commonAttrs, bp2BuildInfo := a.convertLibraryAttrsBp2Build(ctx)
+	depLabels := bp2BuildInfo.DepLabels
 
 	deps := depLabels.Deps
 	deps.Append(depLabels.StaticDeps)
diff --git a/java/base.go b/java/base.go
index 602e8d8..5d24981 100644
--- a/java/base.go
+++ b/java/base.go
@@ -868,7 +868,7 @@
 	flags = append(flags, genAidlIncludeFlags(ctx, aidlSrcs, includeDirs))
 
 	sdkVersion := (j.SdkVersion(ctx)).Kind
-	defaultTrace := ((sdkVersion == android.SdkSystemServer) || (sdkVersion == android.SdkCore) || (sdkVersion == android.SdkCorePlatform))
+	defaultTrace := ((sdkVersion == android.SdkSystemServer) || (sdkVersion == android.SdkCore) || (sdkVersion == android.SdkCorePlatform) || (sdkVersion == android.SdkModule))
 	if proptools.BoolDefault(j.deviceProperties.Aidl.Generate_traces, defaultTrace) {
 		flags = append(flags, "-t")
 	}
diff --git a/java/invalid_implementation_jar.sh b/java/invalid_implementation_jar.sh
new file mode 100755
index 0000000..3820058
--- /dev/null
+++ b/java/invalid_implementation_jar.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+# Copyright 2022 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Script to detect and report an attempt to access an invalid implementation
+# jar.
+
+MOD=$1
+
+cat <<EOF
+
+    $MOD is a java_library that generates a jar file which must not be accessed
+    from outside the mainline module that provides it. If you are seeing this
+    message it means that you are incorrectly attempting to use the jar file
+    from a java_import prebuilt of $MOD.
+
+    This is most likely due to an incorrect dependency on $MOD in an Android.mk
+    or Android.bp file. Please remove that dependency and replace with
+    something more appropriate, e.g. a dependency on an API provided by the
+    module.
+
+    If you do not know where the extraneous dependency was added then you can
+    run the following command to find a list of all the paths from the target
+    which you are trying to build to the target which produced this error.
+
+        prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-\${TARGET_PRODUCT}.ninja -t path <target> <invalid-jar>
+
+    Where <target> is the build target you specified on the command line which
+    produces this error and <invalid-jar> is the rule that failed with this
+    message. If you are specifying multiple build targets then you will need to
+    run the above command for every target until you find the cause.
+
+    The command will output one (of the possibly many) dependency paths from
+    <target> to <invalid-jar>, one file/phony target per line. e.g. it may
+    output something like this:
+
+        ....
+        out/soong/.intermediates/acme/broken/android_common/combined/broken.jar
+        out/soong/.intermediates/prebuilts/module_sdk/art/current/sdk/prebuilt_core-libart/android_common/combined/core-libart.jar
+        out/soong/.intermediates/prebuilts/module_sdk/art/current/sdk/art-module-sdk_core-libart-error/gen/this-file-will-never-be-created.jar
+
+    The last line is the failing target, the second to last line is a dependency
+    from the core-libart java_import onto the failing target, the third to last
+    line is the source of the dependency so you should look in acme/Android.bp
+    file for the "broken" module.
+
+EOF
+
+exit 1
diff --git a/java/java.go b/java/java.go
index b6fc6b8..ad46e98 100644
--- a/java/java.go
+++ b/java/java.go
@@ -25,6 +25,7 @@
 
 	"android/soong/bazel"
 	"android/soong/bazel/cquery"
+	"android/soong/remoteexec"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -59,6 +60,8 @@
 	ctx.RegisterModuleType("java_device_for_host", DeviceForHostFactory)
 	ctx.RegisterModuleType("java_host_for_device", HostForDeviceFactory)
 	ctx.RegisterModuleType("dex_import", DexImportFactory)
+	ctx.RegisterModuleType("java_api_library", ApiLibraryFactory)
+	ctx.RegisterModuleType("java_api_contribution", ApiContributionFactory)
 
 	// This mutator registers dependencies on dex2oat for modules that should be
 	// dexpreopted. This is done late when the final variants have been
@@ -86,11 +89,11 @@
 var (
 	// Supports adding java header libraries to module_exports and sdk.
 	javaHeaderLibsSdkMemberType = &librarySdkMemberType{
-		android.SdkMemberTypeBase{
+		SdkMemberTypeBase: android.SdkMemberTypeBase{
 			PropertyName: "java_header_libs",
 			SupportsSdk:  true,
 		},
-		func(_ android.SdkMemberContext, j *Library) android.Path {
+		jarToExportGetter: func(_ android.SdkMemberContext, j *Library) android.Path {
 			headerJars := j.HeaderJars()
 			if len(headerJars) != 1 {
 				panic(fmt.Errorf("there must be only one header jar from %q", j.Name()))
@@ -98,8 +101,8 @@
 
 			return headerJars[0]
 		},
-		sdkSnapshotFilePathForJar,
-		copyEverythingToSnapshot,
+		snapshotPathGetter:    sdkSnapshotFilePathForJar,
+		onlyCopyJarToSnapshot: copyEverythingToSnapshot,
 	}
 
 	// Export implementation classes jar as part of the sdk.
@@ -113,12 +116,12 @@
 
 	// Supports adding java implementation libraries to module_exports but not sdk.
 	javaLibsSdkMemberType = &librarySdkMemberType{
-		android.SdkMemberTypeBase{
+		SdkMemberTypeBase: android.SdkMemberTypeBase{
 			PropertyName: "java_libs",
 		},
-		exportImplementationClassesJar,
-		sdkSnapshotFilePathForJar,
-		copyEverythingToSnapshot,
+		jarToExportGetter:     exportImplementationClassesJar,
+		snapshotPathGetter:    sdkSnapshotFilePathForJar,
+		onlyCopyJarToSnapshot: copyEverythingToSnapshot,
 	}
 
 	snapshotRequiresImplementationJar = func(ctx android.SdkMemberContext) bool {
@@ -143,11 +146,11 @@
 	// necessary. The java_boot_libs property to allow those modules to be exported as part of the
 	// sdk/module_exports without exposing any unnecessary information.
 	javaBootLibsSdkMemberType = &librarySdkMemberType{
-		android.SdkMemberTypeBase{
+		SdkMemberTypeBase: android.SdkMemberTypeBase{
 			PropertyName: "java_boot_libs",
 			SupportsSdk:  true,
 		},
-		func(ctx android.SdkMemberContext, j *Library) android.Path {
+		jarToExportGetter: func(ctx android.SdkMemberContext, j *Library) android.Path {
 			if snapshotRequiresImplementationJar(ctx) {
 				return exportImplementationClassesJar(ctx, j)
 			}
@@ -156,9 +159,9 @@
 			// jar for use by dexpreopting and boot jars package check. They do not need to provide an
 			// actual implementation jar but the java_import will need a file that exists so just copy an
 			// empty file. Any attempt to use that file as a jar will cause a build error.
-			return ctx.SnapshotBuilder().EmptyFile()
+			return nil
 		},
-		func(ctx android.SdkMemberContext, osPrefix, name string) string {
+		snapshotPathGetter: func(ctx android.SdkMemberContext, osPrefix, name string) string {
 			if snapshotRequiresImplementationJar(ctx) {
 				return sdkSnapshotFilePathForJar(ctx, osPrefix, name)
 			}
@@ -168,7 +171,7 @@
 			// TODO(b/175714559): Provide a proper error message in Soong not ninja.
 			return filepath.Join(osPrefix, "java_boot_libs", "snapshot", "jars", "are", "invalid", name+jarFileSuffix)
 		},
-		onlyCopyJarToSnapshot,
+		onlyCopyJarToSnapshot: onlyCopyJarToSnapshot,
 	}
 
 	// Supports adding java systemserver libraries to module_exports and sdk.
@@ -182,27 +185,27 @@
 	// necessary. The java_systemserver_libs property to allow those modules to be exported as part of
 	// the sdk/module_exports without exposing any unnecessary information.
 	javaSystemserverLibsSdkMemberType = &librarySdkMemberType{
-		android.SdkMemberTypeBase{
+		SdkMemberTypeBase: android.SdkMemberTypeBase{
 			PropertyName: "java_systemserver_libs",
 			SupportsSdk:  true,
 
 			// This was only added in Tiramisu.
 			SupportedBuildReleaseSpecification: "Tiramisu+",
 		},
-		func(ctx android.SdkMemberContext, j *Library) android.Path {
+		jarToExportGetter: func(ctx android.SdkMemberContext, j *Library) android.Path {
 			// Java systemserver libs are only provided in the SDK to provide access to their dex
 			// implementation jar for use by dexpreopting. They do not need to provide an actual
 			// implementation jar but the java_import will need a file that exists so just copy an empty
 			// file. Any attempt to use that file as a jar will cause a build error.
-			return ctx.SnapshotBuilder().EmptyFile()
+			return nil
 		},
-		func(_ android.SdkMemberContext, osPrefix, name string) string {
+		snapshotPathGetter: func(_ android.SdkMemberContext, osPrefix, name string) string {
 			// Create a special name for the implementation jar to try and provide some useful information
 			// to a developer that attempts to compile against this.
 			// TODO(b/175714559): Provide a proper error message in Soong not ninja.
 			return filepath.Join(osPrefix, "java_systemserver_libs", "snapshot", "jars", "are", "invalid", name+jarFileSuffix)
 		},
-		onlyCopyJarToSnapshot,
+		onlyCopyJarToSnapshot: onlyCopyJarToSnapshot,
 	}
 
 	// Supports adding java test libraries to module_exports but not sdk.
@@ -232,7 +235,7 @@
 	ImplementationAndResourcesJars android.Paths
 
 	// ImplementationJars is a list of jars that contain the implementations of classes in the
-	//module.
+	// module.
 	ImplementationJars android.Paths
 
 	// ResourceJars is a list of jars that contain the resources included in the module.
@@ -718,7 +721,8 @@
 	android.SdkMemberTypeBase
 
 	// Function to retrieve the appropriate output jar (implementation or header) from
-	// the library.
+	// the library, if this returns nil then it is assumed that the snapshot must not provide access
+	// to the jar.
 	jarToExportGetter func(ctx android.SdkMemberContext, j *Library) android.Path
 
 	// Function to compute the snapshot relative path to which the named library's
@@ -755,7 +759,11 @@
 type librarySdkMemberProperties struct {
 	android.SdkMemberPropertiesBase
 
-	JarToExport     android.Path `android:"arch_variant"`
+	JarToExport android.Path `android:"arch_variant"`
+
+	// The path to a script to use when the jar is invalid.
+	InvalidJarScript android.Path
+
 	AidlIncludeDirs android.Paths
 
 	// The list of permitted packages that need to be passed to the prebuilts as they are used to
@@ -766,7 +774,15 @@
 func (p *librarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
 	j := variant.(*Library)
 
-	p.JarToExport = ctx.MemberType().(*librarySdkMemberType).jarToExportGetter(ctx, j)
+	memberType := ctx.MemberType().(*librarySdkMemberType)
+	p.JarToExport = memberType.jarToExportGetter(ctx, j)
+
+	// If no jar was provided for export then disallow access to it completely.
+	if p.JarToExport == nil {
+		// Copy the script to prevent access to the jar into the snapshot.
+		p.InvalidJarScript = android.PathForSource(ctx.SdkModuleContext(),
+			"build/soong/java/invalid_implementation_jar.sh")
+	}
 
 	p.AidlIncludeDirs = j.AidlIncludeDirs()
 
@@ -789,6 +805,21 @@
 		propertySet.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
 	}
 
+	if scriptSrc := p.InvalidJarScript; scriptSrc != nil {
+		// Copy the script to prevent access to the jar into the snapshot.
+		scriptDest := filepath.Join("scripts", scriptSrc.Base())
+		builder.CopyToSnapshot(scriptSrc, scriptDest)
+
+		// Generate a genrule module that will invoke the script passing in the module name.
+		genrule := builder.AddInternalModule(p, "genrule", "error")
+		genRuleName := genrule.Name()
+		genrule.AddProperty("out", []string{"this-file-will-never-be-created.jar"})
+		genrule.AddProperty("tool_files", []string{scriptDest})
+		genrule.AddProperty("cmd", fmt.Sprintf("$(location %s) %s", scriptDest, p.Name()))
+
+		propertySet.AddPropertyWithTag("jars", []string{":" + genRuleName}, builder.SdkMemberReferencePropertyTag(true))
+	}
+
 	if len(p.PermittedPackages) > 0 {
 		propertySet.AddProperty("permitted_packages", p.PermittedPackages)
 	}
@@ -1501,6 +1532,182 @@
 	return module
 }
 
+type JavaApiContribution struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	properties struct {
+		// name of the API surface
+		Api_surface *string
+
+		// relative path to the API signature text file
+		Api_file *string `android:"path"`
+	}
+}
+
+func ApiContributionFactory() android.Module {
+	module := &JavaApiContribution{}
+	android.InitAndroidModule(module)
+	android.InitDefaultableModule(module)
+	module.AddProperties(&module.properties)
+	return module
+}
+
+type JavaApiImportInfo struct {
+	ApiFile android.Path
+}
+
+var JavaApiImportProvider = blueprint.NewProvider(JavaApiImportInfo{})
+
+func (ap *JavaApiContribution) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	apiFile := android.PathForModuleSrc(ctx, String(ap.properties.Api_file))
+	ctx.SetProvider(JavaApiImportProvider, JavaApiImportInfo{
+		ApiFile: apiFile,
+	})
+}
+
+type ApiLibrary struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	properties JavaApiLibraryProperties
+
+	stubsSrcJar android.WritablePath
+	stubsJar    android.WritablePath
+}
+
+type JavaApiLibraryProperties struct {
+	// name of the API surface
+	Api_surface *string
+
+	// list of API provider modules that consists this API surface
+	Api_providers []string
+
+	// List of flags to be passed to the javac compiler to generate jar file
+	Javacflags []string
+}
+
+func ApiLibraryFactory() android.Module {
+	module := &ApiLibrary{}
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	module.AddProperties(&module.properties)
+	return module
+}
+
+func (al *ApiLibrary) ApiSurface() *string {
+	return al.properties.Api_surface
+}
+
+func (al *ApiLibrary) StubsJar() android.Path {
+	return al.stubsJar
+}
+
+func metalavaStubCmd(ctx android.ModuleContext, rule *android.RuleBuilder,
+	srcs android.Paths, homeDir android.WritablePath) *android.RuleBuilderCommand {
+	rule.Command().Text("rm -rf").Flag(homeDir.String())
+	rule.Command().Text("mkdir -p").Flag(homeDir.String())
+
+	cmd := rule.Command()
+	cmd.FlagWithArg("ANDROID_PREFS_ROOT=", homeDir.String())
+
+	if metalavaUseRbe(ctx) {
+		rule.Remoteable(android.RemoteRuleSupports{RBE: true})
+		execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
+		labels := map[string]string{"type": "tool", "name": "metalava"}
+
+		pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "java16")
+		rule.Rewrapper(&remoteexec.REParams{
+			Labels:          labels,
+			ExecStrategy:    execStrategy,
+			ToolchainInputs: []string{config.JavaCmd(ctx).String()},
+			Platform:        map[string]string{remoteexec.PoolKey: pool},
+		})
+	}
+
+	cmd.BuiltTool("metalava").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "metalava.jar")).
+		Flag(config.JavacVmFlags).
+		Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED").
+		FlagWithArg("-encoding ", "UTF-8").
+		FlagWithInputList("--source-files ", srcs, " ")
+
+	cmd.Flag("--no-banner").
+		Flag("--color").
+		Flag("--quiet").
+		Flag("--format=v2").
+		FlagWithArg("--repeat-errors-max ", "10").
+		FlagWithArg("--hide ", "UnresolvedImport").
+		FlagWithArg("--hide ", "InvalidNullabilityOverride").
+		FlagWithArg("--hide ", "ChangedDefault")
+
+	return cmd
+}
+
+func (al *ApiLibrary) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath) {
+	if stubsDir.Valid() {
+		cmd.FlagWithArg("--stubs ", stubsDir.String())
+	}
+}
+
+var javaApiProviderTag = dependencyTag{name: "java-api-provider"}
+
+func (al *ApiLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
+	apiProviders := al.properties.Api_providers
+	for _, apiProviderName := range apiProviders {
+		ctx.AddDependency(ctx.Module(), javaApiProviderTag, apiProviderName)
+	}
+}
+
+func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+
+	rule := android.NewRuleBuilder(pctx, ctx)
+
+	rule.Sbox(android.PathForModuleOut(ctx, "metalava"),
+		android.PathForModuleOut(ctx, "metalava.sbox.textproto")).
+		SandboxInputs()
+
+	var stubsDir android.OptionalPath
+	stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, "metalava", "stubsDir"))
+	rule.Command().Text("rm -rf").Text(stubsDir.String())
+	rule.Command().Text("mkdir -p").Text(stubsDir.String())
+
+	homeDir := android.PathForModuleOut(ctx, "metalava", "home")
+
+	apiProviders := al.properties.Api_providers
+	srcFiles := make([]android.Path, len(apiProviders))
+	for i, apiProviderName := range apiProviders {
+		apiProvider := ctx.GetDirectDepWithTag(apiProviderName, javaApiProviderTag)
+		if apiProvider == nil {
+			panic(fmt.Errorf("Java API provider module %s not found, called from %s", apiProviderName, al.Name()))
+		}
+		provider := ctx.OtherModuleProvider(apiProvider, JavaApiImportProvider).(JavaApiImportInfo)
+		srcFiles[i] = android.PathForModuleSrc(ctx, provider.ApiFile.String())
+	}
+
+	cmd := metalavaStubCmd(ctx, rule, srcFiles, homeDir)
+
+	al.stubsFlags(ctx, cmd, stubsDir)
+
+	al.stubsSrcJar = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-"+"stubs.srcjar")
+	rule.Command().
+		BuiltTool("soong_zip").
+		Flag("-write_if_changed").
+		Flag("-jar").
+		FlagWithOutput("-o ", al.stubsSrcJar).
+		FlagWithArg("-C ", stubsDir.String()).
+		FlagWithArg("-D ", stubsDir.String())
+
+	rule.Build("metalava", "metalava merged")
+
+	al.stubsJar = android.PathForModuleOut(ctx, ctx.ModuleName(), "android.jar")
+
+	var flags javaBuilderFlags
+	flags.javacFlags = strings.Join(al.properties.Javacflags, " ")
+
+	TransformJavaToClasses(ctx, al.stubsJar, 0, android.Paths{},
+		android.Paths{al.stubsSrcJar}, flags, android.Paths{})
+}
+
 //
 // Java prebuilts
 //
@@ -1650,7 +1857,7 @@
 }
 
 func (j *Import) commonBuildActions(ctx android.ModuleContext) {
-	//TODO(b/231322772) these should come from Bazel once available
+	// TODO(b/231322772) these should come from Bazel once available
 	j.sdkVersion = j.SdkVersion(ctx)
 	j.minSdkVersion = j.MinSdkVersion(ctx)
 
@@ -2253,7 +2460,7 @@
 		resources.Append(android.BazelLabelForModuleSrc(ctx, m.properties.Java_resources))
 	}
 
-	//TODO(b/179889880) handle case where glob includes files outside package
+	// TODO(b/179889880) handle case where glob includes files outside package
 	resDeps := ResourceDirsToFiles(
 		ctx,
 		m.properties.Java_resource_dirs,
@@ -2307,11 +2514,21 @@
 	Deps bazel.LabelListAttribute
 }
 
-// convertLibraryAttrsBp2Build converts a few shared attributes from java_* modules
-// and also separates dependencies into dynamic dependencies and static dependencies.
-// Each corresponding Bazel target type, can have a different method for handling
-// dynamic vs. static dependencies, and so these are returned to the calling function.
-func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) (*javaCommonAttributes, *javaDependencyLabels) {
+// bp2BuildJavaInfo has information needed for the conversion of  java*_modules
+// that is needed bor Bp2Build conversion but that requires different handling
+// depending on the module type.
+type bp2BuildJavaInfo struct {
+	// separates dependencies into dynamic dependencies and static dependencies.
+	DepLabels     *javaDependencyLabels
+	hasKotlinSrcs bool
+}
+
+// convertLibraryAttrsBp2Build returns a javaCommonAttributes struct with
+// converted attributes shared across java_* modules and a bp2BuildJavaInfo struct
+// which has other non-attribute information needed for bp2build conversion
+// that needs different handling depending on the module types, and thus needs
+// to be returned to the calling function.
+func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) (*javaCommonAttributes, *bp2BuildJavaInfo) {
 	var srcs bazel.LabelListAttribute
 	var deps bazel.LabelList
 	var staticDeps bazel.LabelList
@@ -2330,14 +2547,18 @@
 	protoSrcPartition := "proto"
 	logtagSrcPartition := "logtag"
 	aidlSrcPartition := "aidl"
+	kotlinPartition := "kotlin"
 	srcPartitions := bazel.PartitionLabelListAttribute(ctx, &srcs, bazel.LabelPartitions{
 		javaSrcPartition:   bazel.LabelPartition{Extensions: []string{".java"}, Keep_remainder: true},
 		logtagSrcPartition: bazel.LabelPartition{Extensions: []string{".logtags", ".logtag"}},
 		protoSrcPartition:  android.ProtoSrcLabelPartition,
 		aidlSrcPartition:   android.AidlSrcLabelPartition,
+		kotlinPartition:    bazel.LabelPartition{Extensions: []string{".kt"}},
 	})
 
 	javaSrcs := srcPartitions[javaSrcPartition]
+	kotlinSrcs := srcPartitions[kotlinPartition]
+	javaSrcs.Append(kotlinSrcs)
 
 	if !srcPartitions[logtagSrcPartition].IsEmpty() {
 		logtagsLibName := m.Name() + "_logtags"
@@ -2401,7 +2622,7 @@
 	}
 
 	epEnabled := m.properties.Errorprone.Enabled
-	//TODO(b/227504307) add configuration that depends on RUN_ERROR_PRONE environment variable
+	// TODO(b/227504307) add configuration that depends on RUN_ERROR_PRONE environment variable
 	if Bool(epEnabled) {
 		javacopts = append(javacopts, m.properties.Errorprone.Javacflags...)
 	}
@@ -2447,18 +2668,25 @@
 	depLabels.Deps = bazel.MakeLabelListAttribute(deps)
 	depLabels.StaticDeps = bazel.MakeLabelListAttribute(staticDeps)
 
-	return commonAttrs, depLabels
+	bp2BuildInfo := &bp2BuildJavaInfo{
+		DepLabels:     depLabels,
+		hasKotlinSrcs: !kotlinSrcs.IsEmpty(),
+	}
+
+	return commonAttrs, bp2BuildInfo
 }
 
 type javaLibraryAttributes struct {
 	*javaCommonAttributes
-	Deps      bazel.LabelListAttribute
-	Exports   bazel.LabelListAttribute
-	Neverlink bazel.BoolAttribute
+	Deps        bazel.LabelListAttribute
+	Exports     bazel.LabelListAttribute
+	Neverlink   bazel.BoolAttribute
+	Common_srcs bazel.LabelListAttribute
 }
 
 func javaLibraryBp2Build(ctx android.TopDownMutatorContext, m *Library) {
-	commonAttrs, depLabels := m.convertLibraryAttrsBp2Build(ctx)
+	commonAttrs, bp2BuildInfo := m.convertLibraryAttrsBp2Build(ctx)
+	depLabels := bp2BuildInfo.DepLabels
 
 	deps := depLabels.Deps
 	if !commonAttrs.Srcs.IsEmpty() {
@@ -2473,15 +2701,25 @@
 		ctx.ModuleErrorf("Module has direct dependencies but no sources. Bazel will not allow this.")
 	}
 
+	var props bazel.BazelTargetModuleProperties
 	attrs := &javaLibraryAttributes{
 		javaCommonAttributes: commonAttrs,
 		Deps:                 deps,
 		Exports:              depLabels.StaticDeps,
 	}
 
-	props := bazel.BazelTargetModuleProperties{
-		Rule_class:        "java_library",
-		Bzl_load_location: "//build/bazel/rules/java:library.bzl",
+	if !bp2BuildInfo.hasKotlinSrcs && len(m.properties.Common_srcs) == 0 {
+		props = bazel.BazelTargetModuleProperties{
+			Rule_class:        "java_library",
+			Bzl_load_location: "//build/bazel/rules/java:library.bzl",
+		}
+	} else {
+		attrs.Common_srcs = bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Common_srcs))
+
+		props = bazel.BazelTargetModuleProperties{
+			Rule_class:        "kt_jvm_library",
+			Bzl_load_location: "@rules_kotlin//kotlin:jvm_library.bzl",
+		}
 	}
 
 	name := m.Name()
@@ -2498,7 +2736,8 @@
 
 // JavaBinaryHostBp2Build is for java_binary_host bp2build.
 func javaBinaryHostBp2Build(ctx android.TopDownMutatorContext, m *Binary) {
-	commonAttrs, depLabels := m.convertLibraryAttrsBp2Build(ctx)
+	commonAttrs, bp2BuildInfo := m.convertLibraryAttrsBp2Build(ctx)
+	depLabels := bp2BuildInfo.DepLabels
 
 	deps := depLabels.Deps
 	deps.Append(depLabels.StaticDeps)
@@ -2637,7 +2876,7 @@
 		HeaderJars:                     android.PathsIfNonNil(i.combinedClasspathFile),
 		ImplementationAndResourcesJars: android.PathsIfNonNil(i.combinedClasspathFile),
 		ImplementationJars:             android.PathsIfNonNil(i.combinedClasspathFile),
-		//TODO(b/240308299) include AIDL information from Bazel
+		// TODO(b/240308299) include AIDL information from Bazel
 	})
 
 	i.maybeInstall(ctx, jarName, outputFile)
diff --git a/java/java_test.go b/java/java_test.go
index f06b520..3f8cd8e 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1806,3 +1806,108 @@
 			srcs: ["foo.java"],
 		}`)
 }
+
+func TestJavaApiLibraryAndProviderLink(t *testing.T) {
+	provider_bp_a := `
+	java_api_contribution {
+		name: "foo1",
+		api_file: "foo1.txt",
+	}
+	`
+	provider_bp_b := `java_api_contribution {
+		name: "foo2",
+		api_file: "foo2.txt",
+	}
+	`
+	ctx, _ := testJavaWithFS(t, `
+		java_api_library {
+			name: "bar1",
+			api_surface: "public",
+			api_providers: ["foo1"],
+		}
+
+		java_api_library {
+			name: "bar2",
+			api_surface: "system",
+			api_providers: ["foo1", "foo2"],
+		}
+		`,
+		map[string][]byte{
+			"a/Android.bp": []byte(provider_bp_a),
+			"b/Android.bp": []byte(provider_bp_b),
+		})
+
+	testcases := []struct {
+		moduleName         string
+		sourceTextFileDirs []string
+	}{
+		{
+			moduleName:         "bar1",
+			sourceTextFileDirs: []string{"a/foo1.txt"},
+		},
+		{
+			moduleName:         "bar2",
+			sourceTextFileDirs: []string{"a/foo1.txt", "b/foo2.txt"},
+		},
+	}
+	for _, c := range testcases {
+		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		manifest := m.Output("metalava.sbox.textproto")
+		sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest)
+		manifestCommand := sboxProto.Commands[0].GetCommand()
+		sourceFilesFlag := "--source-files " + strings.Join(c.sourceTextFileDirs, " ")
+		android.AssertStringDoesContain(t, "source text files not present", manifestCommand, sourceFilesFlag)
+	}
+}
+
+func TestJavaApiLibraryJarGeneration(t *testing.T) {
+	provider_bp_a := `
+	java_api_contribution {
+		name: "foo1",
+		api_file: "foo1.txt",
+	}
+	`
+	provider_bp_b := `java_api_contribution {
+		name: "foo2",
+		api_file: "foo2.txt",
+	}
+	`
+	ctx, _ := testJavaWithFS(t, `
+		java_api_library {
+			name: "bar1",
+			api_surface: "public",
+			api_providers: ["foo1"],
+		}
+
+		java_api_library {
+			name: "bar2",
+			api_surface: "system",
+			api_providers: ["foo1", "foo2"],
+		}
+		`,
+		map[string][]byte{
+			"a/Android.bp": []byte(provider_bp_a),
+			"b/Android.bp": []byte(provider_bp_b),
+		})
+
+	testcases := []struct {
+		moduleName    string
+		outputJarName string
+	}{
+		{
+			moduleName:    "bar1",
+			outputJarName: "bar1/android.jar",
+		},
+		{
+			moduleName:    "bar2",
+			outputJarName: "bar2/android.jar",
+		},
+	}
+	for _, c := range testcases {
+		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		outputs := fmt.Sprint(m.AllOutputs())
+		if !strings.Contains(outputs, c.outputJarName) {
+			t.Errorf("Module output does not contain expected jar %s", c.outputJarName)
+		}
+	}
+}
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
index 8e22491..7a5da5c 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -22,30 +22,25 @@
 var legacyCorePlatformApiModules = []string{
 	"AAECarSystemUI",
 	"AAECarSystemUI-tests",
-	"ArcSettings",
 	"ahat-test-dump",
 	"android.car",
 	"android.test.mock",
 	"android.test.mock.impl",
 	"AoapTestDeviceApp",
 	"AoapTestHostApp",
-	"api-stubs-docs",
+	"ArcSettings",
 	"art_cts_jvmti_test_library",
 	"art-gtest-jars-MyClassNatives",
-	"BackupEncryption",
 	"BackupFrameworksServicesRoboTests",
-	"backuplib",
 	"BandwidthEnforcementTest",
 	"BlockedNumberProvider",
 	"BluetoothInstrumentationTests",
 	"BluetoothMidiLib",
 	"BluetoothMidiService",
 	"BTTestApp",
-	"CallEnhancement",
 	"CapCtrlInterface",
 	"CarService",
 	"CarServiceTest",
-	"car-service-test-lib",
 	"car-service-test-static-lib",
 	"CertInstaller",
 	"com.qti.location.sdk",
@@ -60,17 +55,13 @@
 	"CtsAppExitTestCases",
 	"CtsContentTestCases",
 	"CtsLibcoreWycheproofBCTestCases",
-	"CtsMediaTestCases",
 	"CtsNetTestCases",
 	"CtsNetTestCasesLatestSdk",
 	"CtsSecurityTestCases",
 	"CtsSuspendAppsTestCases",
 	"CtsUsageStatsTestCases",
-	"DeadpoolService",
-	"DeadpoolServiceBtServices",
 	"DeviceInfo",
 	"DiagnosticTools",
-	"DisplayCutoutEmulationEmu01Overlay",
 	"DocumentsUIGoogleTests",
 	"DocumentsUIPerfTests",
 	"DocumentsUITests",
@@ -78,10 +69,9 @@
 	"DownloadProvider",
 	"DownloadProviderTests",
 	"DownloadProviderUi",
-	"ds-car-docs", // for AAOS API documentation only
+	"ds-car-docs",
 	"DynamicSystemInstallationService",
 	"EmergencyInfo-lib",
-	"EthernetServiceTests",
 	"ExternalStorageProvider",
 	"face-V1-0-javalib",
 	"FloralClocks",
@@ -91,11 +81,11 @@
 	"FrameworkOverlayG6QU3",
 	"FrameworksCoreTests",
 	"FrameworksIkeTests",
+	"FrameworksMockingServicesTests",
 	"FrameworksNetCommonTests",
 	"FrameworksNetTests",
 	"FrameworksServicesRoboTests",
 	"FrameworksServicesTests",
-	"FrameworksMockingServicesTests",
 	"FrameworksUtilTests",
 	"GtsIncrementalInstallTestCases",
 	"GtsIncrementalInstallTriggerApp",
@@ -103,11 +93,9 @@
 	"HelloOslo",
 	"hid",
 	"hidl_test_java_java",
-	"hwbinder",
 	"imssettings",
 	"izat.lib.glue",
 	"KeyChain",
-	"LocalSettingsLib",
 	"LocalTransport",
 	"lockagent",
 	"mediaframeworktest",
@@ -128,7 +116,6 @@
 	"online-gts-docs",
 	"PerformanceMode",
 	"platform_library-docs",
-	"PowerStatsService",
 	"PrintSpooler",
 	"pxp-monitor",
 	"QColor",
@@ -157,18 +144,6 @@
 	"SettingsGoogleOverlayCoral",
 	"SettingsGoogleOverlayFlame",
 	"SettingsLib",
-	"SettingsOverlayG020A",
-	"SettingsOverlayG020B",
-	"SettingsOverlayG020C",
-	"SettingsOverlayG020D",
-	"SettingsOverlayG020E",
-	"SettingsOverlayG020E_VN",
-	"SettingsOverlayG020F",
-	"SettingsOverlayG020F_VN",
-	"SettingsOverlayG020G",
-	"SettingsOverlayG020G_VN",
-	"SettingsOverlayG020H",
-	"SettingsOverlayG020H_VN",
 	"SettingsOverlayG020I",
 	"SettingsOverlayG020I_VN",
 	"SettingsOverlayG020J",
@@ -191,9 +166,6 @@
 	"SimSettings",
 	"sl4a.Common",
 	"StatementService",
-	"SystemUI-core",
-	"SystemUISharedLib",
-	"SystemUI-tests",
 	"tcmiface",
 	"Telecom",
 	"TelecomUnitTests",
@@ -202,11 +174,6 @@
 	"TeleService",
 	"testables",
 	"TetheringTests",
-	"TetheringTestsLib",
-	"time_zone_distro_installer",
-	"time_zone_distro_installer-tests",
-	"time_zone_distro-tests",
-	"time_zone_updater",
 	"TMobilePlanProvider",
 	"TvProvider",
 	"uiautomator-stubs-docs",
diff --git a/java/plugin.go b/java/plugin.go
index 123dbd4..731dfda 100644
--- a/java/plugin.go
+++ b/java/plugin.go
@@ -66,7 +66,8 @@
 // ConvertWithBp2build is used to convert android_app to Bazel.
 func (p *Plugin) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
 	pluginName := p.Name()
-	commonAttrs, depLabels := p.convertLibraryAttrsBp2Build(ctx)
+	commonAttrs, bp2BuildInfo := p.convertLibraryAttrsBp2Build(ctx)
+	depLabels := bp2BuildInfo.DepLabels
 
 	deps := depLabels.Deps
 	deps.Append(depLabels.StaticDeps)
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 1b64130..58c1647 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -169,7 +169,15 @@
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["com.android.art"],
-    jars: ["java_boot_libs/snapshot/jars/are/invalid/core1.jar"],
+    jars: [":mysdk_core1-error"],
+}
+
+genrule {
+    name: "mysdk_core1-error",
+    visibility: ["//visibility:private"],
+    out: ["this-file-will-never-be-created.jar"],
+    tool_files: ["scripts/invalid_implementation_jar.sh"],
+    cmd: "$(location scripts/invalid_implementation_jar.sh) core1",
 }
 
 java_import {
@@ -177,7 +185,15 @@
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["com.android.art"],
-    jars: ["java_boot_libs/snapshot/jars/are/invalid/core2.jar"],
+    jars: [":mysdk_core2-error"],
+}
+
+genrule {
+    name: "mysdk_core2-error",
+    visibility: ["//visibility:private"],
+    out: ["this-file-will-never-be-created.jar"],
+    tool_files: ["scripts/invalid_implementation_jar.sh"],
+    cmd: "$(location scripts/invalid_implementation_jar.sh) core2",
 }
 `),
 		checkAllCopyRules(`
@@ -187,8 +203,7 @@
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
-.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/core1.jar
-.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/core2.jar
+build/soong/java/invalid_implementation_jar.sh -> scripts/invalid_implementation_jar.sh
 		`),
 		snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
 
@@ -357,10 +372,18 @@
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
-    jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
+    jars: [":mysdk_mybootlib-error"],
     permitted_packages: ["mybootlib"],
 }
 
+genrule {
+    name: "mysdk_mybootlib-error",
+    visibility: ["//visibility:private"],
+    out: ["this-file-will-never-be-created.jar"],
+    tool_files: ["scripts/invalid_implementation_jar.sh"],
+    cmd: "$(location scripts/invalid_implementation_jar.sh) mybootlib",
+}
+
 java_sdk_library_import {
     name: "myothersdklibrary",
     prefer: false,
@@ -467,7 +490,7 @@
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
-.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
+build/soong/java/invalid_implementation_jar.sh -> scripts/invalid_implementation_jar.sh
 .intermediates/myothersdklibrary.stubs/android_common/javac/myothersdklibrary.stubs.jar -> sdk_library/public/myothersdklibrary-stubs.jar
 .intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
 .intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-removed.txt
@@ -487,7 +510,7 @@
 .intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
 .intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
 .intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
-.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
+build/soong/java/invalid_implementation_jar.sh -> scripts/invalid_implementation_jar.sh
 .intermediates/myothersdklibrary.stubs/android_common/javac/myothersdklibrary.stubs.jar -> sdk_library/public/myothersdklibrary-stubs.jar
 .intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
 .intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-removed.txt
@@ -876,10 +899,18 @@
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
-    jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
+    jars: [":mysdk_mybootlib-error"],
     permitted_packages: ["mybootlib"],
 }
 
+genrule {
+    name: "mysdk_mybootlib-error",
+    visibility: ["//visibility:private"],
+    out: ["this-file-will-never-be-created.jar"],
+    tool_files: ["scripts/invalid_implementation_jar.sh"],
+    cmd: "$(location scripts/invalid_implementation_jar.sh) mybootlib",
+}
+
 java_sdk_library_import {
     name: "mynewlibrary",
     prefer: false,
@@ -930,7 +961,7 @@
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
-.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
+build/soong/java/invalid_implementation_jar.sh -> scripts/invalid_implementation_jar.sh
 .intermediates/mynewlibrary.stubs/android_common/javac/mynewlibrary.stubs.jar -> sdk_library/public/mynewlibrary-stubs.jar
 .intermediates/mynewlibrary.stubs.source/android_common/metalava/mynewlibrary.stubs.source_api.txt -> sdk_library/public/mynewlibrary.txt
 .intermediates/mynewlibrary.stubs.source/android_common/metalava/mynewlibrary.stubs.source_removed.txt -> sdk_library/public/mynewlibrary-removed.txt
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 51903ce3..c6cb6c2 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -19,11 +19,13 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/genrule"
 	"android/soong/java"
 )
 
 var prepareForSdkTestWithJava = android.GroupFixturePreparers(
 	java.PrepareForTestWithJavaBuildComponents,
+	genrule.PrepareForTestWithGenRuleBuildComponents,
 	PrepareForTestWithSdkBuildComponents,
 
 	// Ensure that all source paths are provided. This helps ensure that the snapshot generation is
@@ -34,6 +36,7 @@
 	// Files needs by most of the tests.
 	android.MockFS{
 		"Test.java": nil,
+		"build/soong/java/invalid_implementation_jar.sh": nil,
 	}.AddToFixture(),
 )
 
@@ -288,18 +291,26 @@
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
-    jars: ["java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar"],
+    jars: [":mysdk_myjavalib-error"],
     permitted_packages: ["pkg.myjavalib"],
 }
+
+genrule {
+    name: "mysdk_myjavalib-error",
+    visibility: ["//visibility:private"],
+    out: ["this-file-will-never-be-created.jar"],
+    tool_files: ["scripts/invalid_implementation_jar.sh"],
+    cmd: "$(location scripts/invalid_implementation_jar.sh) myjavalib",
+}
 `),
 		checkAllCopyRules(`
-.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar
+build/soong/java/invalid_implementation_jar.sh -> scripts/invalid_implementation_jar.sh
 `),
 	)
 }
 
 func TestSnapshotWithJavaBootLibrary_UpdatableMedia(t *testing.T) {
-	runTest := func(t *testing.T, targetBuildRelease, expectedJarPath, expectedCopyRule string) {
+	runTest := func(t *testing.T, targetBuildRelease, expectedJarPath, expectedGenRule, expectedCopyRule string) {
 		result := android.GroupFixturePreparers(
 			prepareForSdkTestWithJava,
 			android.FixtureMergeEnv(map[string]string{
@@ -334,20 +345,27 @@
     jars: ["%s"],
     permitted_packages: ["pkg.media"],
 }
-`, expectedJarPath)),
+%s`, expectedJarPath, expectedGenRule)),
 			checkAllCopyRules(expectedCopyRule),
 		)
 	}
 
 	t.Run("updatable-media in S", func(t *testing.T) {
-		runTest(t, "S", "java/updatable-media.jar", `
+		runTest(t, "S", "java/updatable-media.jar", "", `
 .intermediates/updatable-media/android_common/package-check/updatable-media.jar -> java/updatable-media.jar
 `)
 	})
 
 	t.Run("updatable-media in T", func(t *testing.T) {
-		runTest(t, "Tiramisu", "java_boot_libs/snapshot/jars/are/invalid/updatable-media.jar", `
-.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/updatable-media.jar
+		runTest(t, "Tiramisu", ":mysdk_updatable-media-error", `
+genrule {
+    name: "mysdk_updatable-media-error",
+    visibility: ["//visibility:private"],
+    out: ["this-file-will-never-be-created.jar"],
+    tool_files: ["scripts/invalid_implementation_jar.sh"],
+    cmd: "$(location scripts/invalid_implementation_jar.sh) updatable-media",
+}`, `
+build/soong/java/invalid_implementation_jar.sh -> scripts/invalid_implementation_jar.sh
 `)
 	})
 }
@@ -389,12 +407,20 @@
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
-    jars: ["java_systemserver_libs/snapshot/jars/are/invalid/myjavalib.jar"],
+    jars: [":myexports_myjavalib-error"],
     permitted_packages: ["pkg.myjavalib"],
 }
+
+genrule {
+    name: "myexports_myjavalib-error",
+    visibility: ["//visibility:private"],
+    out: ["this-file-will-never-be-created.jar"],
+    tool_files: ["scripts/invalid_implementation_jar.sh"],
+    cmd: "$(location scripts/invalid_implementation_jar.sh) myjavalib",
+}
 `),
 		checkAllCopyRules(`
-.intermediates/myexports/common_os/empty -> java_systemserver_libs/snapshot/jars/are/invalid/myjavalib.jar
+build/soong/java/invalid_implementation_jar.sh -> scripts/invalid_implementation_jar.sh
 `),
 	)
 }
diff --git a/sdk/systemserverclasspath_fragment_sdk_test.go b/sdk/systemserverclasspath_fragment_sdk_test.go
index 1ac405d..9540a6b 100644
--- a/sdk/systemserverclasspath_fragment_sdk_test.go
+++ b/sdk/systemserverclasspath_fragment_sdk_test.go
@@ -119,10 +119,18 @@
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
-    jars: ["java_systemserver_libs/snapshot/jars/are/invalid/mylib.jar"],
+    jars: [":mysdk_mylib-error"],
     permitted_packages: ["mylib"],
 }
 
+genrule {
+    name: "mysdk_mylib-error",
+    visibility: ["//visibility:private"],
+    out: ["this-file-will-never-be-created.jar"],
+    tool_files: ["scripts/invalid_implementation_jar.sh"],
+    cmd: "$(location scripts/invalid_implementation_jar.sh) mylib",
+}
+
 prebuilt_systemserverclasspath_fragment {
     name: "mysystemserverclasspathfragment",
     prefer: false,
@@ -180,10 +188,18 @@
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
-    jars: ["java_systemserver_libs/snapshot/jars/are/invalid/mylib.jar"],
+    jars: [":mysdk_mylib-error"],
     permitted_packages: ["mylib"],
 }
 
+genrule {
+    name: "mysdk_mylib-error",
+    visibility: ["//visibility:private"],
+    out: ["this-file-will-never-be-created.jar"],
+    tool_files: ["scripts/invalid_implementation_jar.sh"],
+    cmd: "$(location scripts/invalid_implementation_jar.sh) mylib",
+}
+
 prebuilt_systemserverclasspath_fragment {
     name: "mysystemserverclasspathfragment",
     prefer: false,
diff --git a/sdk/update.go b/sdk/update.go
index 92a13fa..6ebbf09 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -1049,9 +1049,6 @@
 	filesToZip  android.Paths
 	zipsToMerge android.Paths
 
-	// The path to an empty file.
-	emptyFile android.WritablePath
-
 	prebuiltModules map[string]*bpModule
 	prebuiltOrder   []*bpModule
 
@@ -1111,19 +1108,6 @@
 	s.zipsToMerge = append(s.zipsToMerge, tmpZipPath)
 }
 
-func (s *snapshotBuilder) EmptyFile() android.Path {
-	if s.emptyFile == nil {
-		ctx := s.ctx
-		s.emptyFile = android.PathForModuleOut(ctx, "empty")
-		s.ctx.Build(pctx, android.BuildParams{
-			Rule:   android.Touch,
-			Output: s.emptyFile,
-		})
-	}
-
-	return s.emptyFile
-}
-
 func (s *snapshotBuilder) AddPrebuiltModule(member android.SdkMember, moduleType string) android.BpModule {
 	name := member.Name()
 	if s.prebuiltModules[name] != nil {
@@ -1200,6 +1184,24 @@
 	return m
 }
 
+func (s *snapshotBuilder) AddInternalModule(properties android.SdkMemberProperties, moduleType string, nameSuffix string) android.BpModule {
+	name := properties.Name() + "-" + nameSuffix
+
+	if s.prebuiltModules[name] != nil {
+		panic(fmt.Sprintf("Duplicate module detected, module %s has already been added", name))
+	}
+
+	m := s.bpFile.newModule(moduleType)
+	m.AddProperty("name", name)
+	m.AddProperty("visibility", []string{"//visibility:private"})
+
+	s.prebuiltModules[name] = m
+	s.prebuiltOrder = append(s.prebuiltOrder, m)
+
+	s.allMembersByName[name] = struct{}{}
+	return m
+}
+
 func addHostDeviceSupportedProperties(deviceSupported bool, hostSupported bool, bpModule *bpModule) {
 	// If neither device or host is supported then this module does not support either so will not
 	// recognize the properties.
@@ -1230,18 +1232,23 @@
 // Get a name for sdk snapshot member. If the member is private then generate a snapshot specific
 // name. As part of the processing this checks to make sure that any required members are part of
 // the snapshot.
-func (s *snapshotBuilder) snapshotSdkMemberName(name string, required bool) string {
+func (s *snapshotBuilder) snapshotSdkMemberName(reference string, required bool) string {
+	prefix := ""
+	name := strings.TrimPrefix(reference, ":")
+	if name != reference {
+		prefix = ":"
+	}
 	if _, ok := s.allMembersByName[name]; !ok {
 		if required {
 			s.ctx.ModuleErrorf("Required member reference %s is not a member of the sdk", name)
 		}
-		return name
+		return reference
 	}
 
 	if s.isInternalMember(name) {
-		return s.ctx.ModuleName() + "_" + name
+		return prefix + s.ctx.ModuleName() + "_" + name
 	} else {
-		return name
+		return reference
 	}
 }
 
@@ -2057,6 +2064,7 @@
 	variantPropertiesFactory := func() android.SdkMemberProperties {
 		properties := memberType.CreateVariantPropertiesStruct()
 		base := properties.Base()
+		base.MemberName = member.Name()
 		base.Os_count = osCount
 		return properties
 	}
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index cfcf804..7a8fca9 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -46,7 +46,6 @@
         "soong-ui-tracer",
     ],
     srcs: [
-        "bazel.go",
         "build.go",
         "cleanbuild.go",
         "config.go",
diff --git a/ui/build/bazel.go b/ui/build/bazel.go
deleted file mode 100644
index bd469a4..0000000
--- a/ui/build/bazel.go
+++ /dev/null
@@ -1,257 +0,0 @@
-// Copyright 2020 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package build
-
-import (
-	"bytes"
-	"fmt"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"strings"
-
-	"android/soong/bazel"
-	"android/soong/shared"
-	"android/soong/ui/metrics"
-)
-
-func getBazelInfo(ctx Context, config Config, bazelExecutable string, bazelEnv map[string]string, query string) string {
-	infoCmd := Command(ctx, config, "bazel", bazelExecutable)
-
-	if extraStartupArgs, ok := infoCmd.Environment.Get("BAZEL_STARTUP_ARGS"); ok {
-		infoCmd.Args = append(infoCmd.Args, strings.Fields(extraStartupArgs)...)
-	}
-
-	// Obtain the output directory path in the execution root.
-	infoCmd.Args = append(infoCmd.Args,
-		"info",
-		query,
-	)
-
-	for k, v := range bazelEnv {
-		infoCmd.Environment.Set(k, v)
-	}
-
-	infoCmd.Dir = filepath.Join(config.OutDir(), "..")
-
-	queryResult := strings.TrimSpace(string(infoCmd.OutputOrFatal()))
-	return queryResult
-}
-
-// Main entry point to construct the Bazel build command line, environment
-// variables and post-processing steps (e.g. converge output directories)
-func runBazel(ctx Context, config Config) {
-	ctx.BeginTrace(metrics.RunBazel, "bazel")
-	defer ctx.EndTrace()
-
-	// "droid" is the default ninja target.
-	// TODO(b/160568333): stop hardcoding 'droid' to support building any
-	// Ninja target.
-	outputGroups := "droid"
-	if len(config.ninjaArgs) > 0 {
-		// At this stage, the residue slice of args passed to ninja
-		// are the ninja targets to build, which can correspond directly
-		// to ninja_build's output_groups.
-		outputGroups = strings.Join(config.ninjaArgs, ",")
-	}
-
-	// Environment variables are the primary mechanism to pass information from
-	// soong_ui configuration or context to Bazel.
-	bazelEnv := make(map[string]string)
-
-	// Use *_NINJA variables to pass the root-relative path of the combined,
-	// kati-generated, soong-generated, and packaging Ninja files to Bazel.
-	// Bazel reads these from the lunch() repository rule.
-	bazelEnv["COMBINED_NINJA"] = config.CombinedNinjaFile()
-	bazelEnv["KATI_NINJA"] = config.KatiBuildNinjaFile()
-	bazelEnv["PACKAGE_NINJA"] = config.KatiPackageNinjaFile()
-	bazelEnv["SOONG_NINJA"] = config.SoongNinjaFile()
-
-	// NOTE: When Bazel is used, config.DistDir() is rigged to return a fake distdir under config.OutDir()
-	// This is to ensure that Bazel can actually write there. See config.go for more details.
-	bazelEnv["DIST_DIR"] = config.DistDir()
-
-	bazelEnv["SHELL"] = "/bin/bash"
-
-	// `build/bazel/bin/bazel` is the default entry point for executing Bazel in the AOSP
-	// source tree.
-	bazelExecutable := filepath.Join("build", "bazel", "bin", "bazel")
-	cmd := Command(ctx, config, "bazel", bazelExecutable)
-
-	// Append custom startup flags to the Bazel command. Startup flags affect
-	// the Bazel server itself, and any changes to these flags would incur a
-	// restart of the server, losing much of the in-memory incrementality.
-	if extraStartupArgs, ok := cmd.Environment.Get("BAZEL_STARTUP_ARGS"); ok {
-		cmd.Args = append(cmd.Args, strings.Fields(extraStartupArgs)...)
-	}
-
-	// Start constructing the `build` command.
-	actionName := bazel.BazelNinjaExecRunName
-	cmd.Args = append(cmd.Args,
-		"build",
-		// Use output_groups to select the set of outputs to produce from a
-		// ninja_build target.
-		"--output_groups="+outputGroups,
-		// Generate a performance profile
-		"--profile="+filepath.Join(shared.BazelMetricsFilename(config, actionName)),
-		"--slim_profile=true",
-	)
-
-	if config.UseRBE() {
-		for _, envVar := range []string{
-			// RBE client
-			"RBE_compare",
-			"RBE_exec_strategy",
-			"RBE_invocation_id",
-			"RBE_log_dir",
-			"RBE_num_retries_if_mismatched",
-			"RBE_platform",
-			"RBE_remote_accept_cache",
-			"RBE_remote_update_cache",
-			"RBE_server_address",
-			// TODO: remove old FLAG_ variables.
-			"FLAG_compare",
-			"FLAG_exec_root",
-			"FLAG_exec_strategy",
-			"FLAG_invocation_id",
-			"FLAG_log_dir",
-			"FLAG_platform",
-			"FLAG_remote_accept_cache",
-			"FLAG_remote_update_cache",
-			"FLAG_server_address",
-		} {
-			cmd.Args = append(cmd.Args,
-				"--action_env="+envVar)
-		}
-
-		// We need to calculate --RBE_exec_root ourselves
-		ctx.Println("Getting Bazel execution_root...")
-		cmd.Args = append(cmd.Args, "--action_env=RBE_exec_root="+getBazelInfo(ctx, config, bazelExecutable, bazelEnv, "execution_root"))
-	}
-
-	// Ensure that the PATH environment variable value used in the action
-	// environment is the restricted set computed from soong_ui, and not a
-	// user-provided one, for hermeticity reasons.
-	if pathEnvValue, ok := config.environ.Get("PATH"); ok {
-		cmd.Environment.Set("PATH", pathEnvValue)
-		cmd.Args = append(cmd.Args, "--action_env=PATH="+pathEnvValue)
-	}
-
-	// Allow Bazel actions to see the SHELL variable (passed to Bazel above)
-	cmd.Args = append(cmd.Args, "--action_env=SHELL")
-
-	// Append custom build flags to the Bazel command. Changes to these flags
-	// may invalidate Bazel's analysis cache.
-	// These should be appended as the final args, so that they take precedence.
-	if extraBuildArgs, ok := cmd.Environment.Get("BAZEL_BUILD_ARGS"); ok {
-		cmd.Args = append(cmd.Args, strings.Fields(extraBuildArgs)...)
-	}
-
-	// Append the label of the default ninja_build target.
-	cmd.Args = append(cmd.Args,
-		"//:"+config.TargetProduct()+"-"+config.TargetBuildVariant(),
-	)
-
-	// Execute the command at the root of the directory.
-	cmd.Dir = filepath.Join(config.OutDir(), "..")
-
-	for k, v := range bazelEnv {
-		cmd.Environment.Set(k, v)
-	}
-
-	// Make a human-readable version of the bazelEnv map
-	bazelEnvStringBuffer := new(bytes.Buffer)
-	for k, v := range bazelEnv {
-		fmt.Fprintf(bazelEnvStringBuffer, "%s=%s ", k, v)
-	}
-
-	// Print the implicit command line
-	ctx.Println("Bazel implicit command line: " + strings.Join(cmd.Environment.Environ(), " ") + " " + cmd.Cmd.String() + "\n")
-
-	// Print the explicit command line too
-	ctx.Println("Bazel explicit command line: " + bazelEnvStringBuffer.String() + cmd.Cmd.String() + "\n")
-
-	// Execute the build command.
-	cmd.RunAndStreamOrFatal()
-
-	// Post-processing steps start here. Once the Bazel build completes, the
-	// output files are still stored in the execution root, not in $OUT_DIR.
-	// Ensure that the $OUT_DIR contains the expected set of files by symlinking
-	// the files from the execution root's output direction into $OUT_DIR.
-
-	ctx.Println("Getting Bazel output_path...")
-	outputBasePath := getBazelInfo(ctx, config, bazelExecutable, bazelEnv, "output_path")
-	// TODO: Don't hardcode out/ as the bazel output directory. This is
-	// currently hardcoded as ninja_build.output_root.
-	bazelNinjaBuildOutputRoot := filepath.Join(outputBasePath, "..", "out")
-
-	ctx.Println("Populating output directory...")
-	populateOutdir(ctx, config, bazelNinjaBuildOutputRoot, ".")
-}
-
-// For all files F recursively under rootPath/relativePath, creates symlinks
-// such that OutDir/F resolves to rootPath/F via symlinks.
-// NOTE: For distdir paths we rename files instead of creating symlinks, so that the distdir is independent.
-func populateOutdir(ctx Context, config Config, rootPath string, relativePath string) {
-	destDir := filepath.Join(rootPath, relativePath)
-	os.MkdirAll(destDir, 0755)
-	files, err := ioutil.ReadDir(destDir)
-	if err != nil {
-		ctx.Fatal(err)
-	}
-
-	for _, f := range files {
-		// The original Bazel file path
-		destPath := filepath.Join(destDir, f.Name())
-
-		// The desired Soong file path
-		srcPath := filepath.Join(config.OutDir(), relativePath, f.Name())
-
-		destLstatResult, destLstatErr := os.Lstat(destPath)
-		if destLstatErr != nil {
-			ctx.Fatalf("Unable to Lstat dest %s: %s", destPath, destLstatErr)
-		}
-
-		srcLstatResult, srcLstatErr := os.Lstat(srcPath)
-
-		if srcLstatErr == nil {
-			if srcLstatResult.IsDir() && destLstatResult.IsDir() {
-				// src and dest are both existing dirs - recurse on the dest dir contents...
-				populateOutdir(ctx, config, rootPath, filepath.Join(relativePath, f.Name()))
-			} else {
-				// Ignore other pre-existing src files (could be pre-existing files, directories, symlinks, ...)
-				// This can arise for files which are generated under OutDir outside of soong_build, such as .bootstrap files.
-				// FIXME: This might cause a problem later e.g. if a symlink in the build graph changes...
-			}
-		} else {
-			if !os.IsNotExist(srcLstatErr) {
-				ctx.Fatalf("Unable to Lstat src %s: %s", srcPath, srcLstatErr)
-			}
-
-			if strings.Contains(destDir, config.DistDir()) {
-				// We need to make a "real" file/dir instead of making a symlink (because the distdir can't have symlinks)
-				// Rename instead of copy in order to save disk space.
-				if err := os.Rename(destPath, srcPath); err != nil {
-					ctx.Fatalf("Unable to rename %s -> %s due to error %s", srcPath, destPath, err)
-				}
-			} else {
-				// src does not exist, so try to create a src -> dest symlink (i.e. a Soong path -> Bazel path symlink)
-				if err := os.Symlink(destPath, srcPath); err != nil {
-					ctx.Fatalf("Unable to create symlink %s -> %s due to error %s", srcPath, destPath, err)
-				}
-			}
-		}
-	}
-}
diff --git a/ui/build/build.go b/ui/build/build.go
index b9bd898..d49a754 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -90,7 +90,7 @@
 	}
 }
 
-// These are bitmasks which can be used to check whether various flags are set e.g. whether to use Bazel.
+// These are bitmasks which can be used to check whether various flags are set
 const (
 	_ = iota
 	// Whether to run the kati config step.
@@ -102,9 +102,7 @@
 	// Whether to include the kati-generated ninja file in the combined ninja.
 	RunKatiNinja = 1 << iota
 	// Whether to run ninja on the combined ninja.
-	RunNinja = 1 << iota
-	// Whether to run bazel on the combined ninja.
-	RunBazel      = 1 << iota
+	RunNinja      = 1 << iota
 	RunBuildTests = 1 << iota
 	RunAll        = RunProductConfig | RunSoong | RunKati | RunKatiNinja | RunNinja
 )
@@ -324,11 +322,6 @@
 
 		runNinjaForBuild(ctx, config)
 	}
-
-	// Currently, using Bazel requires Kati and Soong to run first, so check whether to run Bazel last.
-	if what&RunBazel != 0 {
-		runBazel(ctx, config)
-	}
 }
 
 func evaluateWhatToRun(config Config, verboseln func(v ...interface{})) int {
diff --git a/ui/build/config.go b/ui/build/config.go
index de10112..c98601e 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -82,6 +82,7 @@
 	skipSoong       bool
 	skipNinja       bool
 	skipSoongTests  bool
+	searchApiDir    bool // Scan the Android.bp files generated in out/api_surfaces
 
 	// From the product config
 	katiArgs        []string
@@ -738,6 +739,8 @@
 			c.bazelDevMode = true
 		} else if arg == "--bazel-mode-staging" {
 			c.bazelStagingMode = true
+		} else if arg == "--search-api-dir" {
+			c.searchApiDir = true
 		} else if len(arg) > 0 && arg[0] == '-' {
 			parseArgNum := func(def int) int {
 				if len(arg) > 2 {
@@ -896,10 +899,18 @@
 	return filepath.Join(c.OutDir(), "bazel")
 }
 
+func (c *configImpl) bazelOutputBase() string {
+	return filepath.Join(c.BazelOutDir(), "output")
+}
+
 func (c *configImpl) SoongOutDir() string {
 	return filepath.Join(c.OutDir(), "soong")
 }
 
+func (c *configImpl) ApiSurfacesOutDir() string {
+	return filepath.Join(c.OutDir(), "api_surfaces")
+}
+
 func (c *configImpl) PrebuiltOS() string {
 	switch runtime.GOOS {
 	case "linux":
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 4d6ad42..3f628cf 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -63,7 +63,7 @@
 	// Set up configuration parameters for the Finder cache.
 	cacheParams := finder.CacheParams{
 		WorkingDirectory: dir,
-		RootDirs:         []string{"."},
+		RootDirs:         androidBpSearchDirs(config),
 		FollowSymlinks:   config.environ.IsEnvTrue("ALLOW_BP_UNDER_SYMLINKS"),
 		ExcludeDirs:      []string{".git", ".repo"},
 		PruneFiles:       pruneFiles,
@@ -100,6 +100,15 @@
 	return f
 }
 
+func androidBpSearchDirs(config Config) []string {
+	dirs := []string{"."} // always search from root of source tree.
+	if config.searchApiDir {
+		// Search in out/api_surfaces
+		dirs = append(dirs, config.ApiSurfacesOutDir())
+	}
+	return dirs
+}
+
 // Finds the list of Bazel-related files (BUILD, WORKSPACE and Starlark) in the tree.
 func findBazelFiles(entries finder.DirEntries) (dirNames []string, fileNames []string) {
 	matches := []string{}
diff --git a/ui/build/sandbox_config.go b/ui/build/sandbox_config.go
index 1b46459..1d32d86 100644
--- a/ui/build/sandbox_config.go
+++ b/ui/build/sandbox_config.go
@@ -27,6 +27,15 @@
 	return sc.srcDirIsRO
 }
 
+// Return the mount flag of the source directory in the nsjail command
+func (sc *SandboxConfig) SrcDirMountFlag() string {
+	ret := "-B" // Read-write
+	if sc.SrcDirIsRO() {
+		ret = "-R" // Read-only
+	}
+	return ret
+}
+
 func (sc *SandboxConfig) SetSrcDirRWAllowlist(allowlist []string) {
 	sc.srcDirRWAllowlist = allowlist
 }
diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go
index 5b2046e..edb3b66 100644
--- a/ui/build/sandbox_linux.go
+++ b/ui/build/sandbox_linux.go
@@ -101,7 +101,7 @@
 			// srcDir is /tmp/.* in integration tests, which is a child dir of /tmp
 			// nsjail throws an error if a child dir is mounted before its parent
 			"-B", "/tmp",
-			"-B", sandboxConfig.srcDir,
+			c.config.sandboxConfig.SrcDirMountFlag(), sandboxConfig.srcDir,
 			"-B", sandboxConfig.outDir,
 		}
 
@@ -148,13 +148,6 @@
 func (c *Cmd) wrapSandbox() {
 	wd, _ := os.Getwd()
 
-	var srcDirMountFlag string
-	if c.config.sandboxConfig.SrcDirIsRO() {
-		srcDirMountFlag = "-R"
-	} else {
-		srcDirMountFlag = "-B" //Read-Write
-	}
-
 	sandboxArgs := []string{
 		// The executable to run
 		"-x", c.Path,
@@ -195,7 +188,7 @@
 		"-B", "/tmp",
 
 		// Mount source
-		srcDirMountFlag, sandboxConfig.srcDir,
+		c.config.sandboxConfig.SrcDirMountFlag(), sandboxConfig.srcDir,
 
 		//Mount out dir as read-write
 		"-B", sandboxConfig.outDir,
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 4aded17..c0bee4e 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -302,7 +302,7 @@
 	)
 
 	bp2buildWorkspaceInvocation.Inputs = append(bp2buildWorkspaceInvocation.Inputs,
-		config.Bp2BuildFilesMarkerFile())
+		config.Bp2BuildFilesMarkerFile(), filepath.Join(config.FileListDir(), "bazel.list"))
 
 	jsonModuleGraphInvocation := primaryBuilderInvocation(
 		config,
@@ -418,7 +418,7 @@
 	// Bazel's HOME var is set to an output subdirectory which doesn't exist. This
 	// prevents Bazel from file I/O in the actual user HOME directory.
 	soongBuildEnv.Set("BAZEL_HOME", absPath(ctx, filepath.Join(config.BazelOutDir(), "bazelhome")))
-	soongBuildEnv.Set("BAZEL_OUTPUT_BASE", filepath.Join(config.BazelOutDir(), "output"))
+	soongBuildEnv.Set("BAZEL_OUTPUT_BASE", config.bazelOutputBase())
 	soongBuildEnv.Set("BAZEL_WORKSPACE", absPath(ctx, "."))
 	soongBuildEnv.Set("BAZEL_METRICS_DIR", config.BazelMetricsDir())
 	soongBuildEnv.Set("LOG_DIR", config.LogsDir())
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index 86c8568..2efc732 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -18,14 +18,44 @@
 	"bufio"
 	"fmt"
 	"path/filepath"
+	"regexp"
 	"runtime"
 	"sort"
 	"strings"
+	"sync"
 
 	"android/soong/ui/metrics"
 	"android/soong/ui/status"
 )
 
+var (
+	// bazel output paths are in __main__/bazel-out/<config-specific-path>/bin
+	bazelOutputPathRegexOnce sync.Once
+	bazelOutputPathRegexp    *regexp.Regexp
+)
+
+func bazelOutputPathPattern(config Config) *regexp.Regexp {
+	bazelOutputPathRegexOnce.Do(func() {
+		// Bazel output files are in <Bazel output base>/execroot/__main__/bazel-out/<config>/bin
+		bazelOutRoot := filepath.Join(regexp.QuoteMeta(config.bazelOutputBase()), "execroot", "__main__", "bazel-out")
+		bazelOutputPathRegexp = regexp.MustCompile(bazelOutRoot + "/[^/]+/bin")
+	})
+	return bazelOutputPathRegexp
+}
+
+func ignoreBazelPath(config Config, path string) bool {
+	bazelRoot := filepath.Join(config.bazelOutputBase(), "execroot")
+	// Don't check bazel output regexp unless it is Bazel path
+	if strings.HasPrefix(path, bazelRoot) {
+		bazelOutputRegexp := bazelOutputPathPattern(config)
+		// if the file is a bazel path that is _not_ a Bazel generated file output, we rely on Bazel to
+		// ensure the paths to exist. If it _is_ a Bazel output path, we expect that it should be built
+		// by Ninja.
+		return !bazelOutputRegexp.MatchString(path)
+	}
+	return false
+}
+
 // Checks for files in the out directory that have a rule that depends on them but no rule to
 // create them. This catches a common set of build failures where a rule to generate a file is
 // deleted (either by deleting a module in an Android.mk file, or by modifying the build system
@@ -97,6 +127,10 @@
 			// full build rules in the primary build.ninja file.
 			continue
 		}
+
+		if ignoreBazelPath(config, line) {
+			continue
+		}
 		danglingRules[line] = true
 	}