Merge "Enable target features for riscv64 Rust builds" into main
diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go
index 5cdf5b6..ed0961b 100644
--- a/aconfig/aconfig_declarations.go
+++ b/aconfig/aconfig_declarations.go
@@ -15,16 +15,18 @@
 package aconfig
 
 import (
-	"android/soong/android"
 	"fmt"
 	"strings"
 
+	"android/soong/android"
+	"android/soong/bazel"
 	"github.com/google/blueprint"
 )
 
 type DeclarationsModule struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
+	android.BazelModuleBase
 
 	// Properties for "aconfig_declarations"
 	properties struct {
@@ -47,8 +49,7 @@
 	android.InitAndroidModule(module)
 	android.InitDefaultableModule(module)
 	module.AddProperties(&module.properties)
-	// TODO: bp2build
-	//android.InitBazelModule(module)
+	android.InitBazelModule(module)
 
 	return module
 }
@@ -73,7 +74,9 @@
 	// RELEASE_ACONFIG_VALUE_SETS, and add any aconfig_values that
 	// match our package.
 	valuesFromConfig := ctx.Config().ReleaseAconfigValueSets()
-	ctx.AddDependency(ctx.Module(), implicitValuesTag, valuesFromConfig...)
+	if valuesFromConfig != "" {
+		ctx.AddDependency(ctx.Module(), implicitValuesTag, valuesFromConfig)
+	}
 }
 
 func (module *DeclarationsModule) OutputFiles(tag string) (android.Paths, error) {
@@ -159,3 +162,26 @@
 	})
 
 }
+
+type bazelAconfigDeclarationsAttributes struct {
+	Srcs    bazel.LabelListAttribute
+	Package string
+}
+
+func (module *DeclarationsModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	if ctx.ModuleType() != "aconfig_declarations" {
+		return
+	}
+	srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, module.properties.Srcs))
+
+	attrs := bazelAconfigDeclarationsAttributes{
+		Srcs:    srcs,
+		Package: module.properties.Package,
+	}
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "aconfig_declarations",
+		Bzl_load_location: "//build/bazel/rules/aconfig:aconfig_declarations.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, &attrs)
+}
diff --git a/aconfig/aconfig_value_set.go b/aconfig/aconfig_value_set.go
index 252908f..af9ddd3 100644
--- a/aconfig/aconfig_value_set.go
+++ b/aconfig/aconfig_value_set.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"android/soong/bazel"
 	"github.com/google/blueprint"
 )
 
@@ -23,6 +24,7 @@
 type ValueSetModule struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
+	android.BazelModuleBase
 
 	properties struct {
 		// aconfig_values modules
@@ -36,8 +38,7 @@
 	android.InitAndroidModule(module)
 	android.InitDefaultableModule(module)
 	module.AddProperties(&module.properties)
-	// TODO: bp2build
-	//android.InitBazelModule(module)
+	android.InitBazelModule(module)
 
 	return module
 }
@@ -90,3 +91,23 @@
 		AvailablePackages: packages,
 	})
 }
+
+type bazelAconfigValueSetAttributes struct {
+	Values bazel.LabelListAttribute
+}
+
+func (module *ValueSetModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	if ctx.ModuleType() != "aconfig_value_set" {
+		return
+	}
+
+	attrs := bazelAconfigValueSetAttributes{
+		Values: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, module.properties.Values)),
+	}
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "aconfig_value_set",
+		Bzl_load_location: "//build/bazel/rules/aconfig:aconfig_value_set.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, &attrs)
+}
diff --git a/aconfig/aconfig_values.go b/aconfig/aconfig_values.go
index 91f1c90..0aa6a72 100644
--- a/aconfig/aconfig_values.go
+++ b/aconfig/aconfig_values.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"android/soong/bazel"
 	"github.com/google/blueprint"
 )
 
@@ -23,6 +24,7 @@
 type ValuesModule struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
+	android.BazelModuleBase
 
 	properties struct {
 		// aconfig files, relative to this Android.bp file
@@ -39,8 +41,7 @@
 	android.InitAndroidModule(module)
 	android.InitDefaultableModule(module)
 	module.AddProperties(&module.properties)
-	// TODO: bp2build
-	//android.InitBazelModule(module)
+	android.InitBazelModule(module)
 
 	return module
 }
@@ -68,3 +69,27 @@
 	}
 	ctx.SetProvider(valuesProviderKey, providerData)
 }
+
+type bazelAconfigValuesAttributes struct {
+	Srcs    bazel.LabelListAttribute
+	Package string
+}
+
+func (module *ValuesModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	if ctx.ModuleType() != "aconfig_values" {
+		return
+	}
+
+	srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, module.properties.Srcs))
+
+	attrs := bazelAconfigValuesAttributes{
+		Srcs:    srcs,
+		Package: module.properties.Package,
+	}
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "aconfig_values",
+		Bzl_load_location: "//build/bazel/rules/aconfig:aconfig_values.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, &attrs)
+}
diff --git a/aconfig/init.go b/aconfig/init.go
index 797388d..c14f8ae 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -97,12 +97,12 @@
 )
 
 func init() {
-	registerBuildComponents(android.InitRegistrationContext)
+	RegisterBuildComponents(android.InitRegistrationContext)
 	pctx.HostBinToolVariable("aconfig", "aconfig")
 	pctx.HostBinToolVariable("soong_zip", "soong_zip")
 }
 
-func registerBuildComponents(ctx android.RegistrationContext) {
+func RegisterBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("aconfig_declarations", DeclarationsFactory)
 	ctx.RegisterModuleType("aconfig_values", ValuesFactory)
 	ctx.RegisterModuleType("aconfig_value_set", ValueSetFactory)
diff --git a/aconfig/testing.go b/aconfig/testing.go
index 60cefeb..f6489ec 100644
--- a/aconfig/testing.go
+++ b/aconfig/testing.go
@@ -20,7 +20,7 @@
 	"android/soong/android"
 )
 
-var PrepareForTestWithAconfigBuildComponents = android.FixtureRegisterWithContext(registerBuildComponents)
+var PrepareForTestWithAconfigBuildComponents = android.FixtureRegisterWithContext(RegisterBuildComponents)
 
 func runTest(t *testing.T, errorHandler android.FixtureErrorHandler, bp string) *android.TestResult {
 	return android.GroupFixturePreparers(PrepareForTestWithAconfigBuildComponents).
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 76ae2d0..9059125 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -79,6 +79,7 @@
 		"build/soong/scripts":                Bp2BuildDefaultTrueRecursively,
 
 		"cts/common/device-side/nativetesthelper/jni": Bp2BuildDefaultTrueRecursively,
+		"cts/libs/json": Bp2BuildDefaultTrueRecursively,
 
 		"dalvik/tools/dexdeps": Bp2BuildDefaultTrueRecursively,
 
@@ -127,6 +128,7 @@
 		"external/auto/android-annotation-stubs": Bp2BuildDefaultTrueRecursively,
 		"external/auto/common":                   Bp2BuildDefaultTrueRecursively,
 		"external/auto/service":                  Bp2BuildDefaultTrueRecursively,
+		"external/auto/value":                    Bp2BuildDefaultTrueRecursively,
 		"external/boringssl":                     Bp2BuildDefaultTrueRecursively,
 		"external/bouncycastle":                  Bp2BuildDefaultTrue,
 		"external/brotli":                        Bp2BuildDefaultTrue,
@@ -222,6 +224,7 @@
 		"frameworks/av/media/module/foundation":              Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/media/module/minijail":                Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/services/minijail":                    Bp2BuildDefaultTrueRecursively,
+		"frameworks/base/apex/jobscheduler/service/jni":      Bp2BuildDefaultTrueRecursively,
 		"frameworks/base/core/java":                          Bp2BuildDefaultTrue,
 		"frameworks/base/libs/androidfw":                     Bp2BuildDefaultTrue,
 		"frameworks/base/libs/services":                      Bp2BuildDefaultTrue,
@@ -233,9 +236,11 @@
 		"frameworks/base/tools/aapt":                         Bp2BuildDefaultTrue,
 		"frameworks/base/tools/aapt2":                        Bp2BuildDefaultTrue,
 		"frameworks/base/tools/codegen":                      Bp2BuildDefaultTrueRecursively,
+		"frameworks/base/tools/locked_region_code_injection": Bp2BuildDefaultTrueRecursively,
 		"frameworks/base/tools/streaming_proto":              Bp2BuildDefaultTrueRecursively,
 		"frameworks/hardware/interfaces/stats/aidl":          Bp2BuildDefaultTrue,
 		"frameworks/libs/modules-utils/build":                Bp2BuildDefaultTrueRecursively,
+		"frameworks/libs/modules-utils/java":                 Bp2BuildDefaultTrue,
 		"frameworks/libs/net/common/native":                  Bp2BuildDefaultTrueRecursively, // TODO(b/296014682): Remove this path
 		"frameworks/native":                                  Bp2BuildDefaultTrue,
 		"frameworks/native/libs/adbd_auth":                   Bp2BuildDefaultTrueRecursively,
@@ -422,12 +427,13 @@
 		"system/tools/xsdc/utils":                                Bp2BuildDefaultTrueRecursively,
 		"system/unwinding/libunwindstack":                        Bp2BuildDefaultTrueRecursively,
 
-		"tools/apifinder":                            Bp2BuildDefaultTrue,
-		"tools/apksig":                               Bp2BuildDefaultTrue,
-		"tools/external_updater":                     Bp2BuildDefaultTrueRecursively,
-		"tools/metalava":                             Bp2BuildDefaultTrueRecursively,
-		"tools/platform-compat/java/android/compat":  Bp2BuildDefaultTrueRecursively,
-		"tools/tradefederation/prebuilts/filegroups": Bp2BuildDefaultTrueRecursively,
+		"tools/apifinder":                             Bp2BuildDefaultTrue,
+		"tools/apksig":                                Bp2BuildDefaultTrue,
+		"tools/external_updater":                      Bp2BuildDefaultTrueRecursively,
+		"tools/metalava":                              Bp2BuildDefaultTrueRecursively,
+		"tools/platform-compat/java/android/compat":   Bp2BuildDefaultTrueRecursively,
+		"tools/platform-compat/java/androidprocessor": Bp2BuildDefaultTrueRecursively,
+		"tools/tradefederation/prebuilts/filegroups":  Bp2BuildDefaultTrueRecursively,
 	}
 
 	Bp2buildKeepExistingBuildFile = map[string]bool{
@@ -550,6 +556,7 @@
 		"remote-color-resources-compile-colors",
 
 		// framework-minus-apex
+		"ImmutabilityAnnotationProcessor",
 		"android.mime.types.minimized",
 		"debian.mime.types.minimized",
 		"framework-javastream-protos",
@@ -559,7 +566,6 @@
 		"apache-commons-math",
 		"cbor-java",
 		"icu4j_calendar_astronomer",
-		"json",
 		"remote-color-resources-compile-public",
 		"statslog-art-java-gen",
 
@@ -847,11 +853,6 @@
 		"aidl",
 		"libaidl-common",
 
-		// java_resources containing only a single filegroup
-		"libauto_value_plugin",
-		"auto_value_plugin_resources",
-		"auto_value_extension",
-
 		// Used by xsd_config
 		"xsdc",
 
@@ -896,14 +897,13 @@
 
 		// java_resources with multiple resource_dirs
 		"emma",
-
-		"modules-utils-preconditions-srcs",
 	}
 
 	Bp2buildModuleTypeAlwaysConvertList = []string{
 		"aidl_interface_headers",
 		"bpf",
 		"combined_apis",
+		"droiddoc_exported_dir",
 		"license",
 		"linker_config",
 		"java_import",
@@ -911,6 +911,9 @@
 		"java_sdk_library",
 		"sysprop_library",
 		"xsd_config",
+		"aconfig_declarations",
+		"aconfig_values",
+		"aconfig_value_set",
 	}
 
 	// Add the names of modules that bp2build should never convert, if it is
diff --git a/android/config.go b/android/config.go
index 645a263..445c6cd 100644
--- a/android/config.go
+++ b/android/config.go
@@ -197,9 +197,22 @@
 	return c.config.productVariables.ReleaseVersion
 }
 
-// The flag values files passed to aconfig, derived from RELEASE_VERSION
-func (c Config) ReleaseAconfigValueSets() []string {
-	return c.config.productVariables.ReleaseAconfigValueSets
+// The aconfig value set passed to aconfig, derived from RELEASE_VERSION
+func (c Config) ReleaseAconfigValueSets() string {
+	// This logic to handle both Soong module name and bazel target is temporary in order to
+	// provide backward compatibility where aosp and vendor/google both have the release
+	// aconfig value set but can't be updated at the same time to use bazel target
+	value := strings.Split(c.config.productVariables.ReleaseAconfigValueSets, ":")
+	value_len := len(value)
+	if value_len > 2 {
+		// This shouldn't happen as this should be either a module name or a bazel target path.
+		panic(fmt.Errorf("config file: invalid value for release aconfig value sets: %s",
+			c.config.productVariables.ReleaseAconfigValueSets))
+	}
+	if value_len > 0 {
+		return value[value_len-1]
+	}
+	return ""
 }
 
 // The flag default permission value passed to aconfig
diff --git a/android/fixture.go b/android/fixture.go
index 6660afd..5ad47e8 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -275,6 +275,15 @@
 	})
 }
 
+// Sync the mock filesystem with the current config, then modify the context,
+// This allows context modification that requires filesystem access.
+func FixtureModifyContextWithMockFs(mutator func(ctx *TestContext)) FixturePreparer {
+	return newSimpleFixturePreparer(func(f *fixture) {
+		f.config.mockFileSystem("", f.mockFS)
+		mutator(f.ctx)
+	})
+}
+
 func FixtureRegisterWithContext(registeringFunc func(ctx RegistrationContext)) FixturePreparer {
 	return FixtureModifyContext(func(ctx *TestContext) { registeringFunc(ctx) })
 }
diff --git a/android/register.go b/android/register.go
index df97c75..f1c2986 100644
--- a/android/register.go
+++ b/android/register.go
@@ -15,9 +15,13 @@
 package android
 
 import (
+	"bufio"
 	"fmt"
+	"path/filepath"
 	"reflect"
+	"regexp"
 
+	"android/soong/shared"
 	"github.com/google/blueprint"
 )
 
@@ -197,6 +201,56 @@
 	RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators)
 }
 
+// RegisterExistingBazelTargets reads Bazel BUILD.bazel and BUILD files under
+// the workspace, and returns a map containing names of Bazel targets defined in
+// these BUILD files.
+// For example, maps "//foo/bar" to ["baz", "qux"] if `//foo/bar:{baz,qux}` exist.
+func (c *Context) RegisterExistingBazelTargets(topDir string, existingBazelFiles []string) error {
+	result := map[string][]string{}
+
+	// Search for instances of `name = "$NAME"` (with arbitrary spacing).
+	targetNameRegex := regexp.MustCompile(`(?m)^\s*name\s*=\s*\"([^\"]+)\"`)
+
+	parseBuildFile := func(path string) error {
+		fullPath := shared.JoinPath(topDir, path)
+		sourceDir := filepath.Dir(path)
+
+		fileInfo, err := c.Config().fs.Stat(fullPath)
+		if err != nil {
+			return fmt.Errorf("Error accessing Bazel file '%s': %s", path, err)
+		}
+		if !fileInfo.IsDir() &&
+			(fileInfo.Name() == "BUILD" || fileInfo.Name() == "BUILD.bazel") {
+			f, err := c.Config().fs.Open(fullPath)
+			if err != nil {
+				return fmt.Errorf("Error reading Bazel file '%s': %s", path, err)
+			}
+			defer f.Close()
+			scanner := bufio.NewScanner(f)
+			for scanner.Scan() {
+				line := scanner.Text()
+				matches := targetNameRegex.FindAllStringSubmatch(line, -1)
+				for _, match := range matches {
+					result[sourceDir] = append(result[sourceDir], match[1])
+				}
+			}
+		}
+		return nil
+	}
+
+	for _, path := range existingBazelFiles {
+		if !c.Config().Bp2buildPackageConfig.ShouldKeepExistingBuildFileForDir(filepath.Dir(path)) {
+			continue
+		}
+		err := parseBuildFile(path)
+		if err != nil {
+			return err
+		}
+	}
+	c.Config().SetBazelBuildFileTargets(result)
+	return nil
+}
+
 // Register the pipeline of singletons, module types, and mutators for
 // generating build.ninja and other files for Kati, from Android.bp files.
 func (ctx *Context) Register() {
diff --git a/android/variable.go b/android/variable.go
index 524cdf7..6d9cc4b 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -476,8 +476,8 @@
 	ProductBrand        string   `json:",omitempty"`
 	BuildVersionTags    []string `json:",omitempty"`
 
-	ReleaseVersion          string   `json:",omitempty"`
-	ReleaseAconfigValueSets []string `json:",omitempty"`
+	ReleaseVersion          string `json:",omitempty"`
+	ReleaseAconfigValueSets string `json:",omitempty"`
 
 	ReleaseAconfigFlagDefaultPermission string `json:",omitempty"`
 
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index b675e5e..161a7ff 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -62,6 +62,7 @@
         "cc_test_conversion_test.go",
         "cc_yasm_conversion_test.go",
         "conversion_test.go",
+        "droiddoc_exported_dir_conversion_test.go",
         "filegroup_conversion_test.go",
         "genrule_conversion_test.go",
         "gensrcs_conversion_test.go",
diff --git a/bp2build/aconfig_conversion_test.go b/bp2build/aconfig_conversion_test.go
new file mode 100644
index 0000000..ddb62f7
--- /dev/null
+++ b/bp2build/aconfig_conversion_test.go
@@ -0,0 +1,92 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+	"testing"
+
+	"android/soong/aconfig"
+	"android/soong/android"
+)
+
+func registerAconfigModuleTypes(ctx android.RegistrationContext) {
+	aconfig.RegisterBuildComponents(ctx)
+}
+
+func TestAconfigDeclarations(t *testing.T) {
+	bp := `
+	aconfig_declarations {
+		name: "foo",
+		srcs: [
+			"foo1.aconfig",
+			"test/foo2.aconfig",
+		],
+		package: "com.android.foo",
+	}
+	`
+	expectedBazelTarget := MakeBazelTargetNoRestrictions(
+		"aconfig_declarations",
+		"foo",
+		AttrNameToString{
+			"srcs": `[
+        "foo1.aconfig",
+        "test/foo2.aconfig",
+    ]`,
+			"package": `"com.android.foo"`,
+		},
+	)
+	RunBp2BuildTestCase(t, registerAconfigModuleTypes, Bp2buildTestCase{
+		Blueprint:            bp,
+		ExpectedBazelTargets: []string{expectedBazelTarget},
+	})
+}
+
+func TestAconfigValues(t *testing.T) {
+	bp := `
+	aconfig_values {
+		name: "foo",
+		srcs: [
+			"foo1.textproto",
+		],
+		package: "com.android.foo",
+	}
+	aconfig_value_set {
+    name: "bar",
+    values: [
+        "foo"
+    ]
+	}
+	`
+	expectedBazelTargets := []string{
+		MakeBazelTargetNoRestrictions(
+			"aconfig_values",
+			"foo",
+			AttrNameToString{
+				"srcs":    `["foo1.textproto"]`,
+				"package": `"com.android.foo"`,
+			},
+		),
+		MakeBazelTargetNoRestrictions(
+			"aconfig_value_set",
+			"bar",
+			AttrNameToString{
+				"values": `[":foo"]`,
+			},
+		)}
+	RunBp2BuildTestCase(t, registerAconfigModuleTypes, Bp2buildTestCase{
+		Blueprint:            bp,
+		ExpectedBazelTargets: expectedBazelTargets,
+	})
+}
diff --git a/bp2build/apex_conversion_test.go b/bp2build/apex_conversion_test.go
index 5aed4ad..d6db677 100644
--- a/bp2build/apex_conversion_test.go
+++ b/bp2build/apex_conversion_test.go
@@ -1315,6 +1315,7 @@
 				"tags":              `["apex_available=myapex"]`,
 			}),
 			MakeBazelTarget("cc_stub_suite", "foo_stub_libs", AttrNameToString{
+				"api_surface":          `"module-libapi"`,
 				"soname":               `"foo.so"`,
 				"source_library_label": `"//:foo"`,
 				"symbol_file":          `"foo.map.txt"`,
diff --git a/bp2build/bp2build_product_config.go b/bp2build/bp2build_product_config.go
index 50b8358..3622e67 100644
--- a/bp2build/bp2build_product_config.go
+++ b/bp2build/bp2build_product_config.go
@@ -285,6 +285,12 @@
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:platform_version_name=%s\n", proptools.String(productVariables.Platform_version_name)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:product_brand=%s\n", productVariables.ProductBrand))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:product_manufacturer=%s\n", productVariables.ProductManufacturer))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:release_aconfig_flag_default_permission=%s\n", productVariables.ReleaseAconfigFlagDefaultPermission))
+		// Empty string can't be used as label_flag on the bazel side
+		if len(productVariables.ReleaseAconfigValueSets) > 0 {
+			result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:release_aconfig_value_sets=%s\n", productVariables.ReleaseAconfigValueSets))
+		}
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:release_version=%s\n", productVariables.ReleaseVersion))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:platform_sdk_version=%d\n", platform_sdk_version))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:safestack=%t\n", proptools.Bool(productVariables.Safestack)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:target_build_variant=%s\n", targetBuildVariant))
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 4897566..cefa171 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -1926,6 +1926,69 @@
 	android.AssertStringEquals(t, "Print the common value if all keys in an axis have the same value", `[":libfoo.impl"]`, actual)
 }
 
+func TestAlreadyPresentBuildTarget(t *testing.T) {
+	bp := `
+	custom {
+		name: "foo",
+	}
+	custom {
+		name: "bar",
+	}
+	`
+	alreadyPresentBuildFile :=
+		MakeBazelTarget(
+			"custom",
+			"foo",
+			AttrNameToString{},
+		)
+	expectedBazelTargets := []string{
+		MakeBazelTarget(
+			"custom",
+			"bar",
+			AttrNameToString{},
+		),
+	}
+	registerCustomModule := func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
+	}
+	RunBp2BuildTestCase(t, registerCustomModule, Bp2buildTestCase{
+		AlreadyExistingBuildContents: alreadyPresentBuildFile,
+		Blueprint:                    bp,
+		ExpectedBazelTargets:         expectedBazelTargets,
+		Description:                  "Not duplicating work for an already-present BUILD target.",
+	})
+}
+
+// Verifies that if a module is defined in pkg1/Android.bp, that a target present
+// in pkg2/BUILD.bazel does not result in the module being labeled "already defined
+// in a BUILD file".
+func TestBuildTargetPresentOtherDirectory(t *testing.T) {
+	bp := `
+	custom {
+		name: "foo",
+	}
+	`
+	expectedBazelTargets := []string{
+		MakeBazelTarget(
+			"custom",
+			"foo",
+			AttrNameToString{},
+		),
+	}
+	registerCustomModule := func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
+	}
+	RunBp2BuildTestCase(t, registerCustomModule, Bp2buildTestCase{
+		KeepBuildFileForDirs: []string{"other_pkg"},
+		Filesystem: map[string]string{
+			"other_pkg/BUILD.bazel": MakeBazelTarget("custom", "foo", AttrNameToString{}),
+		},
+		Blueprint:            bp,
+		ExpectedBazelTargets: expectedBazelTargets,
+		Description:          "Not treating a BUILD target as the bazel definition for a module in another package",
+	})
+}
+
 // If CommonAttributes.Dir is set, the bazel target should be created in that dir
 func TestCreateBazelTargetInDifferentDir(t *testing.T) {
 	t.Parallel()
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 7af788e..b667fe9 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -2809,6 +2809,7 @@
 		"stubs_symbol_file": `"a.map.txt"`,
 	})
 	expectedBazelTargets = append(expectedBazelTargets, makeCcStubSuiteTargets("a", AttrNameToString{
+		"api_surface":          `"module-libapi"`,
 		"soname":               `"a.so"`,
 		"source_library_label": `"//foo/bar:a"`,
 		"stubs_symbol_file":    `"a.map.txt"`,
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index 44b9722..921e6e3 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -218,12 +218,12 @@
 
 func TestCcLibrarySharedOsSpecificSharedLib(t *testing.T) {
 	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
-		Description: "cc_library_shared os-specific shared_libs",
-		Filesystem:  map[string]string{},
+		StubbedBuildDefinitions: []string{"shared_dep"},
+		Description:             "cc_library_shared os-specific shared_libs",
+		Filesystem:              map[string]string{},
 		Blueprint: soongCcLibrarySharedPreamble + `
 cc_library_shared {
     name: "shared_dep",
-    bazel_module: { bp2build_available: false },
 }
 cc_library_shared {
     name: "foo_shared",
@@ -243,20 +243,18 @@
 
 func TestCcLibrarySharedBaseArchOsSpecificSharedLib(t *testing.T) {
 	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
-		Description: "cc_library_shared base, arch, and os-specific shared_libs",
-		Filesystem:  map[string]string{},
+		StubbedBuildDefinitions: []string{"shared_dep", "shared_dep2", "shared_dep3"},
+		Description:             "cc_library_shared base, arch, and os-specific shared_libs",
+		Filesystem:              map[string]string{},
 		Blueprint: soongCcLibrarySharedPreamble + `
 cc_library_shared {
     name: "shared_dep",
-    bazel_module: { bp2build_available: false },
 }
 cc_library_shared {
     name: "shared_dep2",
-    bazel_module: { bp2build_available: false },
 }
 cc_library_shared {
     name: "shared_dep3",
-    bazel_module: { bp2build_available: false },
 }
 cc_library_shared {
     name: "foo_shared",
@@ -543,6 +541,7 @@
 		},
 		Blueprint: soongCcLibraryPreamble,
 		ExpectedBazelTargets: []string{makeCcStubSuiteTargets("a", AttrNameToString{
+			"api_surface":          `"module-libapi"`,
 			"soname":               `"a.so"`,
 			"source_library_label": `"//foo/bar:a"`,
 			"stubs_symbol_file":    `"a.map.txt"`,
@@ -1442,6 +1441,7 @@
 `,
 		ExpectedBazelTargets: []string{
 			makeCcStubSuiteTargets("a", AttrNameToString{
+				"api_surface":          `"module-libapi"`,
 				"soname":               `"a.so"`,
 				"source_library_label": `"//:a"`,
 				"stubs_symbol_file":    `"a.map.txt"`,
@@ -1456,6 +1456,7 @@
 				"stubs_symbol_file": `"a.map.txt"`,
 			}),
 			makeCcStubSuiteTargets("b", AttrNameToString{
+				"api_surface":          `"module-libapi"`,
 				"soname":               `"b.so"`,
 				"source_library_label": `"//:b"`,
 				"stubs_symbol_file":    `"b.map.txt"`,
diff --git a/bp2build/cc_prebuilt_library_conversion_test.go b/bp2build/cc_prebuilt_library_conversion_test.go
index b88960e..8c33be3 100644
--- a/bp2build/cc_prebuilt_library_conversion_test.go
+++ b/bp2build/cc_prebuilt_library_conversion_test.go
@@ -17,6 +17,7 @@
 	"fmt"
 	"testing"
 
+	"android/soong/android"
 	"android/soong/cc"
 )
 
@@ -360,3 +361,52 @@
 		},
 	})
 }
+
+func TestPrebuiltNdkStlConversion(t *testing.T) {
+	registerNdkStlModuleTypes := func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("ndk_prebuilt_static_stl", cc.NdkPrebuiltStaticStlFactory)
+		ctx.RegisterModuleType("ndk_prebuilt_shared_stl", cc.NdkPrebuiltSharedStlFactory)
+	}
+	RunBp2BuildTestCase(t, registerNdkStlModuleTypes, Bp2buildTestCase{
+		Description: "TODO",
+		Blueprint: `
+ndk_prebuilt_static_stl {
+	name: "ndk_libfoo_static",
+	export_include_dirs: ["dir1", "dir2"],
+}
+ndk_prebuilt_shared_stl {
+	name: "ndk_libfoo_shared",
+	export_include_dirs: ["dir1", "dir2"],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_prebuilt_library_static", "ndk_libfoo_static", AttrNameToString{
+				"static_library": `select({
+        "//build/bazel/platforms/os_arch:android_arm": "current/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libfoo_static.a",
+        "//build/bazel/platforms/os_arch:android_arm64": "current/sources/cxx-stl/llvm-libc++/libs/arm64-v8a/libfoo_static.a",
+        "//build/bazel/platforms/os_arch:android_riscv64": "current/sources/cxx-stl/llvm-libc++/libs/riscv64/libfoo_static.a",
+        "//build/bazel/platforms/os_arch:android_x86": "current/sources/cxx-stl/llvm-libc++/libs/x86/libfoo_static.a",
+        "//build/bazel/platforms/os_arch:android_x86_64": "current/sources/cxx-stl/llvm-libc++/libs/x86_64/libfoo_static.a",
+        "//conditions:default": None,
+    })`,
+				"export_system_includes": `[
+        "dir1",
+        "dir2",
+    ]`,
+			}),
+			MakeBazelTarget("cc_prebuilt_library_shared", "ndk_libfoo_shared", AttrNameToString{
+				"shared_library": `select({
+        "//build/bazel/platforms/os_arch:android_arm": "current/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libfoo_shared.so",
+        "//build/bazel/platforms/os_arch:android_arm64": "current/sources/cxx-stl/llvm-libc++/libs/arm64-v8a/libfoo_shared.so",
+        "//build/bazel/platforms/os_arch:android_riscv64": "current/sources/cxx-stl/llvm-libc++/libs/riscv64/libfoo_shared.so",
+        "//build/bazel/platforms/os_arch:android_x86": "current/sources/cxx-stl/llvm-libc++/libs/x86/libfoo_shared.so",
+        "//build/bazel/platforms/os_arch:android_x86_64": "current/sources/cxx-stl/llvm-libc++/libs/x86_64/libfoo_shared.so",
+        "//conditions:default": None,
+    })`,
+				"export_system_includes": `[
+        "dir1",
+        "dir2",
+    ]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/droiddoc_exported_dir_conversion_test.go b/bp2build/droiddoc_exported_dir_conversion_test.go
new file mode 100644
index 0000000..dee67f4
--- /dev/null
+++ b/bp2build/droiddoc_exported_dir_conversion_test.go
@@ -0,0 +1,60 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+	"regexp"
+	"testing"
+
+	"android/soong/java"
+)
+
+func TestDroiddocExportedDir(t *testing.T) {
+	bp := `
+	droiddoc_exported_dir {
+		name: "test-module",
+		path: "docs",
+	}
+	`
+	p := regexp.MustCompile(`\t*\|`)
+	dedent := func(s string) string {
+		return p.ReplaceAllString(s, "")
+	}
+	expectedBazelTargets := []string{
+		MakeBazelTargetNoRestrictions(
+			"droiddoc_exported_dir",
+			"test-module",
+			AttrNameToString{
+				"dir": `"docs"`,
+				"srcs": dedent(`[
+				|        "docs/android/1.txt",
+				|        "docs/android/nested-1/2.txt",
+				|        "//docs/android/nested-2:3.txt",
+				|        "//docs/android/nested-2:Android.bp",
+				|    ]`),
+			}),
+		//note we are not excluding Android.bp files from subpackages for now
+	}
+	RunBp2BuildTestCase(t, java.RegisterDocsBuildComponents, Bp2buildTestCase{
+		Blueprint:            bp,
+		ExpectedBazelTargets: expectedBazelTargets,
+		Filesystem: map[string]string{
+			"docs/android/1.txt":               "",
+			"docs/android/nested-1/2.txt":      "",
+			"docs/android/nested-2/Android.bp": "",
+			"docs/android/nested-2/3.txt":      "",
+		},
+	})
+}
diff --git a/bp2build/symlink_forest.go b/bp2build/symlink_forest.go
index 5c33308..966b94a 100644
--- a/bp2build/symlink_forest.go
+++ b/bp2build/symlink_forest.go
@@ -43,7 +43,7 @@
 // clean the whole symlink forest and recreate it. This number can be bumped whenever there's
 // an incompatible change to the forest layout or a bug in incrementality that needs to be fixed
 // on machines that may still have the bug present in their forest.
-const symlinkForestVersion = 1
+const symlinkForestVersion = 2
 
 type instructionsNode struct {
 	name     string
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 0e7ef44..82d40cf 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -21,6 +21,7 @@
 
 import (
 	"fmt"
+	"path/filepath"
 	"sort"
 	"strings"
 	"testing"
@@ -82,7 +83,16 @@
 	// ExpectedBazelTargets compares the BazelTargets generated in `Dir` (if not empty).
 	// Otherwise, it checks the BazelTargets generated by `Blueprint` in the root directory.
 	ExpectedBazelTargets []string
-	Filesystem           map[string]string
+	// AlreadyExistingBuildContents, if non-empty, simulates an already-present source BUILD file
+	// in the directory under test. The BUILD file has the given contents. This BUILD file
+	// will also be treated as "BUILD file to keep" by the simulated bp2build environment.
+	AlreadyExistingBuildContents string
+	// StubbedBuildDefinitions, if non-empty, adds stub definitions to an already-present source
+	// BUILD file in the directory under test, for each target name given. These stub definitions
+	// are added to the BUILD file given in AlreadyExistingBuildContents, if it is set.
+	StubbedBuildDefinitions []string
+
+	Filesystem map[string]string
 	// Dir sets the directory which will be compared against the targets in ExpectedBazelTargets.
 	// This should used in conjunction with the Filesystem property to check for targets
 	// generated from a directory that is not the root.
@@ -110,11 +120,31 @@
 
 func runBp2BuildTestCaseWithSetup(t *testing.T, extraPreparer android.FixturePreparer, tc Bp2buildTestCase) {
 	t.Helper()
-	dir := "."
+	checkDir := "."
+	if tc.Dir != "" {
+		checkDir = tc.Dir
+	}
+	keepExistingBuildDirs := tc.KeepBuildFileForDirs
+	buildFilesToParse := []string{}
 	filesystem := make(map[string][]byte)
 	for f, content := range tc.Filesystem {
 		filesystem[f] = []byte(content)
 	}
+	alreadyExistingBuildContents := tc.AlreadyExistingBuildContents
+	if len(tc.StubbedBuildDefinitions) > 0 {
+		for _, targetName := range tc.StubbedBuildDefinitions {
+			alreadyExistingBuildContents += MakeBazelTarget(
+				"bp2build_test_stub",
+				targetName,
+				AttrNameToString{})
+		}
+	}
+	if len(alreadyExistingBuildContents) > 0 {
+		buildFilePath := filepath.Join(checkDir, "BUILD")
+		filesystem[buildFilePath] = []byte(alreadyExistingBuildContents)
+		keepExistingBuildDirs = append(keepExistingBuildDirs, checkDir)
+		buildFilesToParse = append(buildFilesToParse, buildFilePath)
+	}
 
 	preparers := []android.FixturePreparer{
 		extraPreparer,
@@ -123,7 +153,7 @@
 		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
 			ctx.RegisterModuleType(tc.ModuleTypeUnderTest, tc.ModuleTypeUnderTestFactory)
 		}),
-		android.FixtureModifyContext(func(ctx *android.TestContext) {
+		android.FixtureModifyContextWithMockFs(func(ctx *android.TestContext) {
 			// A default configuration for tests to not have to specify bp2build_available on top level
 			// targets.
 			bp2buildConfig := android.NewBp2BuildAllowlist().SetDefaultConfig(
@@ -131,7 +161,7 @@
 					android.Bp2BuildTopLevel: allowlists.Bp2BuildDefaultTrueRecursively,
 				},
 			)
-			for _, f := range tc.KeepBuildFileForDirs {
+			for _, f := range keepExistingBuildDirs {
 				bp2buildConfig.SetKeepExistingBuildFile(map[string]bool{
 					f: /*recursive=*/ false,
 				})
@@ -141,6 +171,10 @@
 			// from cloning modules to their original state after mutators run. This
 			// would lose some data intentionally set by these mutators.
 			ctx.SkipCloneModulesAfterMutators = true
+			err := ctx.RegisterExistingBazelTargets(".", buildFilesToParse)
+			if err != nil {
+				t.Errorf("error parsing build files in test setup: %s", err)
+			}
 		}),
 		android.FixtureModifyEnv(func(env map[string]string) {
 			if tc.UnconvertedDepsMode == errorModulesUnconvertedDeps {
@@ -159,10 +193,6 @@
 		return
 	}
 
-	checkDir := dir
-	if tc.Dir != "" {
-		checkDir = tc.Dir
-	}
 	expectedTargets := map[string][]string{
 		checkDir: tc.ExpectedBazelTargets,
 	}
@@ -644,6 +674,7 @@
 		return ""
 	}
 	STUB_SUITE_ATTRS := map[string]string{
+		"api_surface":          "api_surface",
 		"stubs_symbol_file":    "symbol_file",
 		"stubs_versions":       "versions",
 		"soname":               "soname",
diff --git a/cc/cc.go b/cc/cc.go
index 3b92696..8d79df2 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -879,16 +879,16 @@
 	installer    installer
 	bazelHandler BazelHandler
 
-	features []feature
-	stl      *stl
-	sanitize *sanitize
-	coverage *coverage
-	fuzzer   *fuzzer
-	sabi     *sabi
-	vndkdep  *vndkdep
-	lto      *lto
-	afdo     *afdo
-	pgo      *pgo
+	features  []feature
+	stl       *stl
+	sanitize  *sanitize
+	coverage  *coverage
+	fuzzer    *fuzzer
+	sabi      *sabi
+	vndkdep   *vndkdep
+	lto       *lto
+	afdo      *afdo
+	pgo       *pgo
 	orderfile *orderfile
 
 	library libraryInterface
@@ -1104,6 +1104,16 @@
 	return false
 }
 
+func (c *Module) IsNdkPrebuiltStl() bool {
+	if c.linker == nil {
+		return false
+	}
+	if _, ok := c.linker.(*ndkPrebuiltStlLinker); ok {
+		return true
+	}
+	return false
+}
+
 func (c *Module) RlibStd() bool {
 	panic(fmt.Errorf("RlibStd called on non-Rust module: %q", c.BaseModuleName()))
 }
@@ -4158,6 +4168,7 @@
 	headerLibrary
 	testBin // testBinary already declared
 	ndkLibrary
+	ndkPrebuiltStl
 )
 
 func (c *Module) typ() moduleType {
@@ -4196,6 +4207,8 @@
 		return sharedLibrary
 	} else if c.isNDKStubLibrary() {
 		return ndkLibrary
+	} else if c.IsNdkPrebuiltStl() {
+		return ndkPrebuiltStl
 	}
 	return unknownType
 }
@@ -4240,6 +4253,8 @@
 		} else {
 			sharedOrStaticLibraryBp2Build(ctx, c, false)
 		}
+	case ndkPrebuiltStl:
+		ndkPrebuiltStlBp2build(ctx, c)
 	default:
 		ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_TYPE_UNSUPPORTED, "")
 	}
diff --git a/cc/config/riscv64_device.go b/cc/config/riscv64_device.go
index e048622..40919c0 100644
--- a/cc/config/riscv64_device.go
+++ b/cc/config/riscv64_device.go
@@ -26,14 +26,14 @@
 		// Help catch common 32/64-bit errors.
 		"-Werror=implicit-function-declaration",
 		"-fno-emulated-tls",
-		"-march=rv64gcv_zba_zbb_zbs",
+		"-march=rv64gc_zba_zbb_zbs",
 	}
 
 	riscv64ArchVariantCflags = map[string][]string{}
 
 	riscv64Ldflags = []string{
 		"-Wl,--hash-style=gnu",
-		"-march=rv64gcv_zba_zbb_zbs",
+		"-march=rv64gc_zba_zbb_zbs",
 	}
 
 	riscv64Lldflags = append(riscv64Ldflags,
diff --git a/cc/library.go b/cc/library.go
index 2d4d604..7e0c55a 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -494,6 +494,7 @@
 			Soname:               &soname,
 			Source_library_label: proptools.StringPtr(m.GetBazelLabel(ctx, m)),
 			Deps:                 baseAttributes.deps,
+			Api_surface:          proptools.StringPtr("module-libapi"),
 		}
 		ctx.CreateBazelTargetModule(stubSuitesProps,
 			android.CommonAttributes{Name: m.Name() + "_stub_libs"},
@@ -3121,6 +3122,7 @@
 	Source_library_label *string
 	Soname               *string
 	Deps                 bazel.LabelListAttribute
+	Api_surface          *string
 }
 
 type bazelCcHeaderAbiCheckerAttributes struct {
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index d0ae4a5..1a8e90f 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -82,6 +82,7 @@
 
 	properties headerProperties
 
+	srcPaths     android.Paths
 	installPaths android.Paths
 	licensePath  android.Path
 }
@@ -125,8 +126,8 @@
 
 	m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License))
 
-	srcFiles := android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
-	for _, header := range srcFiles {
+	m.srcPaths = android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
+	for _, header := range m.srcPaths {
 		installDir := getHeaderInstallDir(ctx, header, String(m.properties.From),
 			String(m.properties.To))
 		installedPath := ctx.InstallFile(installDir, header.Base(), header)
@@ -193,6 +194,7 @@
 
 	properties versionedHeaderProperties
 
+	srcPaths     android.Paths
 	installPaths android.Paths
 	licensePath  android.Path
 }
@@ -211,9 +213,9 @@
 
 	fromSrcPath := android.PathForModuleSrc(ctx, String(m.properties.From))
 	toOutputPath := getCurrentIncludePath(ctx).Join(ctx, String(m.properties.To))
-	srcFiles := ctx.GlobFiles(headerGlobPattern(fromSrcPath.String()), nil)
+	m.srcPaths = ctx.GlobFiles(headerGlobPattern(fromSrcPath.String()), nil)
 	var installPaths []android.WritablePath
-	for _, header := range srcFiles {
+	for _, header := range m.srcPaths {
 		installDir := getHeaderInstallDir(ctx, header, String(m.properties.From), String(m.properties.To))
 		installPath := installDir.Join(ctx, header.Base())
 		installPaths = append(installPaths, installPath)
@@ -224,11 +226,11 @@
 		ctx.ModuleErrorf("glob %q matched zero files", String(m.properties.From))
 	}
 
-	processHeadersWithVersioner(ctx, fromSrcPath, toOutputPath, srcFiles, installPaths)
+	processHeadersWithVersioner(ctx, fromSrcPath, toOutputPath, m.srcPaths, installPaths)
 }
 
 func processHeadersWithVersioner(ctx android.ModuleContext, srcDir, outDir android.Path,
-	srcFiles android.Paths, installPaths []android.WritablePath) android.Path {
+	srcPaths android.Paths, installPaths []android.WritablePath) android.Path {
 	// The versioner depends on a dependencies directory to simplify determining include paths
 	// when parsing headers. This directory contains architecture specific directories as well
 	// as a common directory, each of which contains symlinks to the actually directories to
@@ -253,7 +255,7 @@
 		Rule:            versionBionicHeaders,
 		Description:     "versioner preprocess " + srcDir.Rel(),
 		Output:          timestampFile,
-		Implicits:       append(srcFiles, depsGlob...),
+		Implicits:       append(srcPaths, depsGlob...),
 		ImplicitOutputs: installPaths,
 		Args: map[string]string{
 			"depsPath": depsPath.String(),
@@ -317,6 +319,7 @@
 
 	properties preprocessedHeadersProperties
 
+	srcPaths     android.Paths
 	installPaths android.Paths
 	licensePath  android.Path
 }
@@ -329,9 +332,9 @@
 	preprocessor := android.PathForModuleSrc(ctx, String(m.properties.Preprocessor))
 	m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License))
 
-	srcFiles := android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
+	m.srcPaths = android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
 	installDir := getCurrentIncludePath(ctx).Join(ctx, String(m.properties.To))
-	for _, src := range srcFiles {
+	for _, src := range m.srcPaths {
 		installPath := installDir.Join(ctx, src.Base())
 		m.installPaths = append(m.installPaths, installPath)
 
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 9281aeb..b201dd8 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -43,11 +43,17 @@
 			CommandDeps: []string{"$ndkStubGenerator"},
 		}, "arch", "apiLevel", "apiMap", "flags")
 
+	// $headersList should include paths to public headers. All types
+	// that are defined outside of public headers will be excluded from
+	// ABI monitoring.
+	//
+	// STG tool doesn't access content of files listed in $headersList,
+	// so there is no need to add them to dependencies.
 	stg = pctx.AndroidStaticRule("stg",
 		blueprint.RuleParams{
-			Command:     "$stg -S :$symbolList --elf $in -o $out",
+			Command:     "$stg -S :$symbolList --file-filter :$headersList --elf $in -o $out",
 			CommandDeps: []string{"$stg"},
-		}, "symbolList")
+		}, "symbolList", "headersList")
 
 	stgdiff = pctx.AndroidStaticRule("stgdiff",
 		blueprint.RuleParams{
@@ -347,14 +353,19 @@
 	this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx,
 		this.apiLevel.String(), ctx.Arch().ArchType.String(),
 		this.libraryName(ctx), "abi.stg")
+	headersList := getNdkABIHeadersFile(ctx)
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        stg,
 		Description: fmt.Sprintf("stg %s", implementationLibrary),
 		Input:       implementationLibrary,
-		Implicit:    symbolList,
-		Output:      this.abiDumpPath,
+		Implicits: []android.Path{
+			symbolList,
+			headersList,
+		},
+		Output: this.abiDumpPath,
 		Args: map[string]string{
-			"symbolList": symbolList.String(),
+			"symbolList":  symbolList.String(),
+			"headersList": headersList.String(),
 		},
 	})
 }
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
index d3a0a00..c2382b3 100644
--- a/cc/ndk_prebuilt.go
+++ b/cc/ndk_prebuilt.go
@@ -15,9 +15,11 @@
 package cc
 
 import (
+	"path/filepath"
 	"strings"
 
 	"android/soong/android"
+	"android/soong/bazel"
 )
 
 func init() {
@@ -64,6 +66,7 @@
 	module.Properties.Sdk_version = StringPtr("minimum")
 	module.Properties.AlwaysSdk = true
 	module.stl.Properties.Stl = StringPtr("none")
+	module.bazelable = true
 	return module.Init()
 }
 
@@ -84,12 +87,16 @@
 	module.Properties.AlwaysSdk = true
 	module.Properties.Sdk_version = StringPtr("current")
 	module.stl.Properties.Stl = StringPtr("none")
+	module.bazelable = true
 	return module.Init()
 }
 
+const (
+	libDir = "current/sources/cxx-stl/llvm-libc++/libs"
+)
+
 func getNdkStlLibDir(ctx android.ModuleContext) android.SourcePath {
-	libDir := "prebuilts/ndk/current/sources/cxx-stl/llvm-libc++/libs"
-	return android.PathForSource(ctx, libDir).Join(ctx, ctx.Arch().Abi[0])
+	return android.PathForSource(ctx, ctx.ModuleDir(), libDir).Join(ctx, ctx.Arch().Abi[0])
 }
 
 func (ndk *ndkPrebuiltStlLinker) link(ctx ModuleContext, flags Flags,
@@ -128,3 +135,81 @@
 
 	return lib
 }
+
+var (
+	archToAbiDirMap = map[string]string{
+		"android_arm":     "armeabi-v7a",
+		"android_arm64":   "arm64-v8a",
+		"android_riscv64": "riscv64",
+		"android_x86":     "x86",
+		"android_x86_64":  "x86_64",
+	}
+)
+
+// stlSrcBp2build returns a bazel label for the checked-in .so/.a file
+// It contains a select statement for each ABI
+func stlSrcBp2build(ctx android.TopDownMutatorContext, c *Module) bazel.LabelAttribute {
+	libName := strings.TrimPrefix(c.Name(), "ndk_")
+	libExt := ".so" // TODO - b/201079053: Support windows
+	if ctx.ModuleType() == "ndk_prebuilt_static_stl" {
+		libExt = ".a"
+	}
+	src := bazel.LabelAttribute{}
+	for arch, abiDir := range archToAbiDirMap {
+		srcPath := filepath.Join(libDir, abiDir, libName+libExt)
+		src.SetSelectValue(
+			bazel.OsArchConfigurationAxis,
+			arch,
+			android.BazelLabelForModuleSrcSingle(ctx, srcPath),
+		)
+	}
+	return src
+}
+
+// stlIncludesBp2build returns the includes exported by the STL
+func stlIncludesBp2build(c *Module) bazel.StringListAttribute {
+	linker, _ := c.linker.(*ndkPrebuiltStlLinker)
+	includeDirs := append(
+		[]string{},
+		linker.libraryDecorator.flagExporter.Properties.Export_include_dirs...,
+	)
+	includeDirs = append(
+		includeDirs,
+		linker.libraryDecorator.flagExporter.Properties.Export_system_include_dirs...,
+	)
+	return bazel.MakeStringListAttribute(android.FirstUniqueStrings(includeDirs))
+}
+
+func ndkPrebuiltStlBp2build(ctx android.TopDownMutatorContext, c *Module) {
+	if ctx.ModuleType() == "ndk_prebuilt_static_stl" {
+		ndkPrebuiltStaticStlBp2build(ctx, c)
+	} else {
+		ndkPrebuiltSharedStlBp2build(ctx, c)
+	}
+}
+
+func ndkPrebuiltStaticStlBp2build(ctx android.TopDownMutatorContext, c *Module) {
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "cc_prebuilt_library_static",
+		Bzl_load_location: "//build/bazel/rules/cc:cc_prebuilt_library_static.bzl",
+	}
+	attrs := &bazelPrebuiltLibraryStaticAttributes{
+		Static_library:         stlSrcBp2build(ctx, c),
+		Export_system_includes: stlIncludesBp2build(c), // The exports are always as system
+	}
+	// TODO: min_sdk_version
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: c.Name()}, attrs)
+}
+
+func ndkPrebuiltSharedStlBp2build(ctx android.TopDownMutatorContext, c *Module) {
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "cc_prebuilt_library_shared",
+		Bzl_load_location: "//build/bazel/rules/cc:cc_prebuilt_library_shared.bzl",
+	}
+	attrs := &bazelPrebuiltLibrarySharedAttributes{
+		Shared_library:         stlSrcBp2build(ctx, c),
+		Export_system_includes: stlIncludesBp2build(c), // The exports are always as system
+	}
+	// TODO: min_sdk_version
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: c.Name()}, attrs)
+}
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index feb3880..9ec2ae4 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -54,6 +54,7 @@
 
 import (
 	"android/soong/android"
+	"strings"
 )
 
 func init() {
@@ -96,15 +97,56 @@
 	return android.PathForOutput(ctx, "ndk.timestamp")
 }
 
+// The list of all NDK headers as they are located in the repo.
+// Used for ABI monitoring to track only structures defined in NDK headers.
+func getNdkABIHeadersFile(ctx android.PathContext) android.WritablePath {
+	return android.PathForOutput(ctx, "ndk_abi_headers.txt")
+}
+
 func NdkSingleton() android.Singleton {
 	return &ndkSingleton{}
 }
 
+// Collect all NDK exported headers paths into a file that is used to
+// detect public types that should be ABI monitored.
+//
+// Assume that we have the following code in exported header:
+//
+//	typedef struct Context Context;
+//	typedef struct Output {
+//	    ...
+//	} Output;
+//	void DoSomething(Context* ctx, Output* output);
+//
+// If none of public headers exported to end-users contain definition of
+// "struct Context", then "struct Context" layout and members shouldn't be
+// monitored. However we use DWARF information from a real library, which
+// may have access to the definition of "string Context" from
+// implementation headers, and it will leak to ABI.
+//
+// STG tool doesn't access source and header files, only DWARF information
+// from compiled library. And the DWARF contains file name where a type is
+// defined. So we need a rule to build a list of paths to public headers,
+// so STG can distinguish private types from public and do not monitor
+// private types that are not accessible to library users.
+func writeNdkAbiSrcFilter(ctx android.BuilderContext,
+	headerSrcPaths android.Paths, outputFile android.WritablePath) {
+	var filterBuilder strings.Builder
+	filterBuilder.WriteString("[decl_file_allowlist]\n")
+	for _, headerSrcPath := range headerSrcPaths {
+		filterBuilder.WriteString(headerSrcPath.String())
+		filterBuilder.WriteString("\n")
+	}
+
+	android.WriteFileRule(ctx, outputFile, filterBuilder.String())
+}
+
 type ndkSingleton struct{}
 
 func (n *ndkSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var staticLibInstallPaths android.Paths
-	var headerPaths android.Paths
+	var headerSrcPaths android.Paths
+	var headerInstallPaths android.Paths
 	var installPaths android.Paths
 	var licensePaths android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
@@ -113,19 +155,22 @@
 		}
 
 		if m, ok := module.(*headerModule); ok {
-			headerPaths = append(headerPaths, m.installPaths...)
+			headerSrcPaths = append(headerSrcPaths, m.srcPaths...)
+			headerInstallPaths = append(headerInstallPaths, m.installPaths...)
 			installPaths = append(installPaths, m.installPaths...)
 			licensePaths = append(licensePaths, m.licensePath)
 		}
 
 		if m, ok := module.(*versionedHeaderModule); ok {
-			headerPaths = append(headerPaths, m.installPaths...)
+			headerSrcPaths = append(headerSrcPaths, m.srcPaths...)
+			headerInstallPaths = append(headerInstallPaths, m.installPaths...)
 			installPaths = append(installPaths, m.installPaths...)
 			licensePaths = append(licensePaths, m.licensePath)
 		}
 
 		if m, ok := module.(*preprocessedHeadersModule); ok {
-			headerPaths = append(headerPaths, m.installPaths...)
+			headerSrcPaths = append(headerSrcPaths, m.srcPaths...)
+			headerInstallPaths = append(headerInstallPaths, m.installPaths...)
 			installPaths = append(installPaths, m.installPaths...)
 			licensePaths = append(licensePaths, m.licensePath)
 		}
@@ -175,9 +220,11 @@
 	ctx.Build(pctx, android.BuildParams{
 		Rule:      android.Touch,
 		Output:    getNdkHeadersTimestampFile(ctx),
-		Implicits: headerPaths,
+		Implicits: headerInstallPaths,
 	})
 
+	writeNdkAbiSrcFilter(ctx, headerSrcPaths, getNdkABIHeadersFile(ctx))
+
 	fullDepPaths := append(staticLibInstallPaths, getNdkBaseTimestampFile(ctx))
 
 	// There's a phony "ndk" rule defined in core/main.mk that depends on this.
diff --git a/cc/testing.go b/cc/testing.go
index d1632aa..24d6b0f 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -558,7 +558,7 @@
 	// This includes files that are needed by all, or at least most, instances of a cc module type.
 	android.MockFS{
 		// Needed for ndk_prebuilt_(shared|static)_stl.
-		"prebuilts/ndk/current/sources/cxx-stl/llvm-libc++/libs": nil,
+		"defaults/cc/common/current/sources/cxx-stl/llvm-libc++/libs": nil,
 	}.AddToFixture(),
 )
 
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 626f076..d20847b 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -21,7 +21,6 @@
 	"fmt"
 	"os"
 	"path/filepath"
-	"regexp"
 	"strings"
 	"time"
 
@@ -617,43 +616,6 @@
 	return excluded
 }
 
-// buildTargetsByPackage parses Bazel BUILD.bazel and BUILD files under
-// the workspace, and returns a map containing names of Bazel targets defined in
-// these BUILD files.
-// For example, maps "//foo/bar" to ["baz", "qux"] if `//foo/bar:{baz,qux}` exist.
-func buildTargetsByPackage(ctx *android.Context) map[string][]string {
-	existingBazelFiles, err := getExistingBazelRelatedFiles(topDir)
-	maybeQuit(err, "Error determining existing Bazel-related files")
-
-	result := map[string][]string{}
-
-	// Search for instances of `name = "$NAME"` (with arbitrary spacing).
-	targetNameRegex := regexp.MustCompile(`(?m)^\s*name\s*=\s*\"([^\"]+)\"`)
-
-	for _, path := range existingBazelFiles {
-		if !ctx.Config().Bp2buildPackageConfig.ShouldKeepExistingBuildFileForDir(filepath.Dir(path)) {
-			continue
-		}
-		fullPath := shared.JoinPath(topDir, path)
-		sourceDir := filepath.Dir(path)
-		fileInfo, err := os.Stat(fullPath)
-		maybeQuit(err, "Error accessing Bazel file '%s'", fullPath)
-
-		if !fileInfo.IsDir() &&
-			(fileInfo.Name() == "BUILD" || fileInfo.Name() == "BUILD.bazel") {
-			// Process this BUILD file.
-			buildFileContent, err := os.ReadFile(fullPath)
-			maybeQuit(err, "Error reading Bazel file '%s'", fullPath)
-
-			matches := targetNameRegex.FindAllStringSubmatch(string(buildFileContent), -1)
-			for _, match := range matches {
-				result[sourceDir] = append(result[sourceDir], match[1])
-			}
-		}
-	}
-	return result
-}
-
 // 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.
@@ -662,7 +624,11 @@
 	ctx.EventHandler.Do("bp2build", func() {
 
 		ctx.EventHandler.Do("read_build", func() {
-			ctx.Config().SetBazelBuildFileTargets(buildTargetsByPackage(ctx))
+			existingBazelFiles, err := getExistingBazelRelatedFiles(topDir)
+			maybeQuit(err, "Error determining existing Bazel-related files")
+
+			err = ctx.RegisterExistingBazelTargets(topDir, existingBazelFiles)
+			maybeQuit(err, "Error parsing existing Bazel-related files")
 		})
 
 		// Propagate "allow misssing dependencies" bit. This is normally set in
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 4097e8a..0b542fe 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -289,31 +289,12 @@
 		}
 	}
 
-	// Fix up the source tree due to a repo bug where it doesn't remove
-	// linkfiles that have been removed
-	fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
-	fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
-
 	// Create a source finder.
 	f := build.NewSourceFinder(buildCtx, config)
 	defer f.Shutdown()
 	build.FindSources(buildCtx, config, f)
 }
 
-func fixBadDanglingLink(ctx build.Context, name string) {
-	_, err := os.Lstat(name)
-	if err != nil {
-		return
-	}
-	_, err = os.Stat(name)
-	if os.IsNotExist(err) {
-		err = os.Remove(name)
-		if err != nil {
-			ctx.Fatalf("Failed to remove dangling link %q: %v", name, err)
-		}
-	}
-}
-
 func dumpVar(ctx build.Context, config build.Config, args []string) {
 	logAndSymlinkSetup(ctx, config)
 	flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
diff --git a/compliance/OWNERS b/compliance/OWNERS
deleted file mode 100644
index f52e201..0000000
--- a/compliance/OWNERS
+++ /dev/null
@@ -1,8 +0,0 @@
-# OSEP Build
-bbadour@google.com
-kanouche@google.com
-napier@google.com
-
-# Open Source Compliance Tools
-rtp@google.com
-austinyuan@google.com
diff --git a/java/app_import.go b/java/app_import.go
index 1718d93..c5d09fd 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -19,6 +19,7 @@
 import (
 	"fmt"
 	"reflect"
+	"strings"
 
 	"github.com/google/blueprint"
 
@@ -51,27 +52,11 @@
 		Description: "Uncompress dex files",
 	})
 
-	checkDexLibsAreUncompressedRule = pctx.AndroidStaticRule("check-dex-libs-are-uncompressed", blueprint.RuleParams{
-		// grep -v ' stor ' will search for lines that don't have ' stor '. stor means the file is stored uncompressed
-		Command: "if (zipinfo $in '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then " +
-			"echo $in: Contains compressed JNI libraries and/or dex files >&2;" +
-			"exit 1; " +
-			"else " +
-			"touch $out; " +
-			"fi",
-		Description: "Check for compressed JNI libs or dex files",
-	})
-
-	checkJniLibsAreUncompressedRule = pctx.AndroidStaticRule("check-jni-libs-are-uncompressed", blueprint.RuleParams{
-		// grep -v ' stor ' will search for lines that don't have ' stor '. stor means the file is stored uncompressed
-		Command: "if (zipinfo $in 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then " +
-			"echo $in: Contains compressed JNI libraries >&2;" +
-			"exit 1; " +
-			"else " +
-			"touch $out; " +
-			"fi",
-		Description: "Check for compressed JNI libs or dex files",
-	})
+	checkPresignedApkRule = pctx.AndroidStaticRule("check-presigned-apk", blueprint.RuleParams{
+		Command:     "build/soong/scripts/check_prebuilt_presigned_apk.py --aapt2 ${config.Aapt2Cmd} --zipalign ${config.ZipAlign} $extraArgs $in $out",
+		CommandDeps: []string{"build/soong/scripts/check_prebuilt_presigned_apk.py", "${config.Aapt2Cmd}", "${config.ZipAlign}"},
+		Description: "Check presigned apk",
+	}, "extraArgs")
 )
 
 func RegisterAppImportBuildComponents(ctx android.RegistrationContext) {
@@ -352,20 +337,14 @@
 	// Sign or align the package if package has not been preprocessed
 
 	if proptools.Bool(a.properties.Preprocessed) {
-		var output android.WritablePath
-		if !proptools.Bool(a.properties.Skip_preprocessed_apk_checks) {
-			output = android.PathForModuleOut(ctx, "validated-prebuilt", apkFilename)
-			a.validatePreprocessedApk(ctx, srcApk, output)
-		} else {
-			// If using the input APK unmodified, still make a copy of it so that the output filename has the
-			// right basename.
-			output = android.PathForModuleOut(ctx, apkFilename)
-			ctx.Build(pctx, android.BuildParams{
-				Rule:   android.Cp,
-				Input:  srcApk,
-				Output: output,
-			})
-		}
+		validationStamp := a.validatePresignedApk(ctx, srcApk)
+		output := android.PathForModuleOut(ctx, apkFilename)
+		ctx.Build(pctx, android.BuildParams{
+			Rule:       android.Cp,
+			Input:      srcApk,
+			Output:     output,
+			Validation: validationStamp,
+		})
 		a.outputFile = output
 		a.certificate = PresignedCertificate
 	} else if !Bool(a.properties.Presigned) {
@@ -384,13 +363,9 @@
 		SignAppPackage(ctx, signed, jnisUncompressed, certificates, nil, lineageFile, rotationMinSdkVersion)
 		a.outputFile = signed
 	} else {
-		// Presigned without Preprocessed shouldn't really be a thing, currently we disallow
-		// it for apps with targetSdk >= 30, because on those targetSdks you must be using signature
-		// v2 or later, and signature v2 would be wrecked by uncompressing libs / zipaligning.
-		// But ideally we would disallow it for all prebuilt apks, and remove the presigned property.
-		targetSdkCheck := a.validateTargetSdkLessThan30(ctx, srcApk)
+		validationStamp := a.validatePresignedApk(ctx, srcApk)
 		alignedApk := android.PathForModuleOut(ctx, "zip-aligned", apkFilename)
-		TransformZipAlign(ctx, alignedApk, jnisUncompressed, []android.Path{targetSdkCheck})
+		TransformZipAlign(ctx, alignedApk, jnisUncompressed, []android.Path{validationStamp})
 		a.outputFile = alignedApk
 		a.certificate = PresignedCertificate
 	}
@@ -406,52 +381,28 @@
 	// TODO: androidmk converter jni libs
 }
 
-func (a *AndroidAppImport) validatePreprocessedApk(ctx android.ModuleContext, srcApk android.Path, dstApk android.WritablePath) {
-	var validations android.Paths
-
-	alignmentStamp := android.PathForModuleOut(ctx, "validated-prebuilt", "alignment.stamp")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:   checkZipAlignment,
-		Input:  srcApk,
-		Output: alignmentStamp,
-	})
-
-	validations = append(validations, alignmentStamp)
-	jniCompressionStamp := android.PathForModuleOut(ctx, "validated-prebuilt", "jni_compression.stamp")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:   checkJniLibsAreUncompressedRule,
-		Input:  srcApk,
-		Output: jniCompressionStamp,
-	})
-	validations = append(validations, jniCompressionStamp)
-
+func (a *AndroidAppImport) validatePresignedApk(ctx android.ModuleContext, srcApk android.Path) android.Path {
+	stamp := android.PathForModuleOut(ctx, "validated-prebuilt", "check.stamp")
+	var extraArgs []string
 	if a.Privileged() {
-		// It's ok for non-privileged apps to have compressed dex files, see go/gms-uncompressed-jni-slides
-		dexCompressionStamp := android.PathForModuleOut(ctx, "validated-prebuilt", "dex_compression.stamp")
-		ctx.Build(pctx, android.BuildParams{
-			Rule:   checkDexLibsAreUncompressedRule,
-			Input:  srcApk,
-			Output: dexCompressionStamp,
-		})
-		validations = append(validations, dexCompressionStamp)
+		extraArgs = append(extraArgs, "--privileged")
+	}
+	if proptools.Bool(a.properties.Skip_preprocessed_apk_checks) {
+		extraArgs = append(extraArgs, "--skip-preprocessed-apk-checks")
+	}
+	if proptools.Bool(a.properties.Preprocessed) {
+		extraArgs = append(extraArgs, "--preprocessed")
 	}
 
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        android.Cp,
-		Input:       srcApk,
-		Output:      dstApk,
-		Validations: validations,
-	})
-}
-
-func (a *AndroidAppImport) validateTargetSdkLessThan30(ctx android.ModuleContext, srcApk android.Path) android.Path {
-	alignmentStamp := android.PathForModuleOut(ctx, "validated-prebuilt", "old_target_sdk.stamp")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:   checkBelowTargetSdk30ForNonPreprocessedApks,
+		Rule:   checkPresignedApkRule,
 		Input:  srcApk,
-		Output: alignmentStamp,
+		Output: stamp,
+		Args: map[string]string{
+			"extraArgs": strings.Join(extraArgs, " "),
+		},
 	})
-	return alignmentStamp
+	return stamp
 }
 
 func (a *AndroidAppImport) Prebuilt() *android.Prebuilt {
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 506c734..8f29bb3 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -659,14 +659,19 @@
 
 	apkName := "foo.apk"
 	variant := ctx.ModuleForTests("foo", "android_common")
-	outputBuildParams := variant.Output("validated-prebuilt/" + apkName).BuildParams
+	outputBuildParams := variant.Output(apkName).BuildParams
 	if outputBuildParams.Rule.String() != android.Cp.String() {
 		t.Errorf("Unexpected prebuilt android_app_import rule: " + outputBuildParams.Rule.String())
 	}
 
 	// Make sure compression and aligning were validated.
-	if len(outputBuildParams.Validations) != 2 {
-		t.Errorf("Expected compression/alignment validation rules, found %d validations", len(outputBuildParams.Validations))
+	if outputBuildParams.Validation == nil {
+		t.Errorf("Expected validation rule, but was not found")
+	}
+
+	validationBuildParams := variant.Output("validated-prebuilt/check.stamp").BuildParams
+	if validationBuildParams.Rule.String() != checkPresignedApkRule.String() {
+		t.Errorf("Unexpected validation rule: " + validationBuildParams.Rule.String())
 	}
 }
 
diff --git a/java/builder.go b/java/builder.go
index bf91917..ee7e225 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -259,32 +259,11 @@
 		},
 	)
 
-	checkZipAlignment = pctx.AndroidStaticRule("checkzipalign",
-		blueprint.RuleParams{
-			Command: "if ! ${config.ZipAlign} -c -p 4 $in > /dev/null; then " +
-				"echo $in: Improper package alignment >&2; " +
-				"exit 1; " +
-				"else " +
-				"touch $out; " +
-				"fi",
-			CommandDeps: []string{"${config.ZipAlign}"},
-			Description: "Check zip alignment",
-		},
-	)
-
 	convertImplementationJarToHeaderJarRule = pctx.AndroidStaticRule("convertImplementationJarToHeaderJar",
 		blueprint.RuleParams{
 			Command:     `${config.Zip2ZipCmd} -i ${in} -o ${out} -x 'META-INF/services/**/*'`,
 			CommandDeps: []string{"${config.Zip2ZipCmd}"},
 		})
-
-	checkBelowTargetSdk30ForNonPreprocessedApks = pctx.AndroidStaticRule("checkBelowTargetSdk30ForNonPreprocessedApks",
-		blueprint.RuleParams{
-			Command:     "build/soong/scripts/check_target_sdk_less_than_30.py ${config.Aapt2Cmd} $in $out",
-			CommandDeps: []string{"build/soong/scripts/check_target_sdk_less_than_30.py", "${config.Aapt2Cmd}"},
-			Description: "Check prebuilt target sdk version",
-		},
-	)
 )
 
 func init() {
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 3ba3065..fe0643a 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -22,6 +22,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel"
 	"android/soong/java/config"
 )
 
@@ -844,6 +845,7 @@
 
 type ExportedDroiddocDir struct {
 	android.ModuleBase
+	android.BazelModuleBase
 
 	properties ExportedDroiddocDirProperties
 
@@ -856,6 +858,7 @@
 	module := &ExportedDroiddocDir{}
 	module.AddProperties(&module.properties)
 	android.InitAndroidModule(module)
+	android.InitBazelModule(module)
 	return module
 }
 
@@ -867,6 +870,28 @@
 	d.deps = android.PathsForModuleSrc(ctx, []string{filepath.Join(path, "**/*")})
 }
 
+// ConvertWithBp2build implements android.BazelModule.
+func (d *ExportedDroiddocDir) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	props := bazel.BazelTargetModuleProperties{
+		// Use the native py_library rule.
+		Rule_class:        "droiddoc_exported_dir",
+		Bzl_load_location: "//build/bazel/rules/droiddoc:droiddoc_exported_dir.bzl",
+	}
+
+	type BazelAttrs struct {
+		Dir  *string
+		Srcs bazel.LabelListAttribute
+	}
+
+	attrs := &BazelAttrs{
+		Dir:  proptools.StringPtr(*d.properties.Path),
+		Srcs: bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, []string{filepath.Join(*d.properties.Path, "**/*")})),
+	}
+
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: d.Name()}, attrs)
+
+}
+
 // Defaults
 type DocDefaults struct {
 	android.ModuleBase
diff --git a/rust/config/global.go b/rust/config/global.go
index 4bd495d..c976617 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -25,7 +25,7 @@
 	pctx         = android.NewPackageContext("android/soong/rust/config")
 	exportedVars = android.NewExportedVariables(pctx)
 
-	RustDefaultVersion = "1.71.0"
+	RustDefaultVersion = "1.72.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
diff --git a/scripts/check_prebuilt_presigned_apk.py b/scripts/check_prebuilt_presigned_apk.py
new file mode 100755
index 0000000..abedfb7
--- /dev/null
+++ b/scripts/check_prebuilt_presigned_apk.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3
+
+import subprocess
+import argparse
+import re
+import sys
+import zipfile
+
+def check_target_sdk_less_than_30(args):
+    if not args.aapt2:
+        sys.exit('--aapt2 is required')
+    regex = re.compile(r"targetSdkVersion: *'([0-9]+)'")
+    output = subprocess.check_output([args.aapt2, "dump", "badging", args.apk], text=True)
+    targetSdkVersion = None
+    for line in output.splitlines():
+        match = regex.fullmatch(line.strip())
+        if match:
+            targetSdkVersion = int(match.group(1))
+            break
+
+    if targetSdkVersion is None or targetSdkVersion >= 30:
+        sys.exit(args.apk + ": Prebuilt, presigned apks with targetSdkVersion >= 30 (or a codename targetSdkVersion) must set preprocessed: true in the Android.bp definition (because they must be signed with signature v2, and the build system would wreck that signature otherwise)")
+
+def has_preprocessed_issues(args, *, fail=False):
+    if not args.zipalign:
+        sys.exit('--zipalign is required')
+    ret = subprocess.run([args.zipalign, '-c', '-p', '4', args.apk], stdout=subprocess.DEVNULL).returncode
+    if ret != 0:
+        if fail:
+            sys.exit(args.apk + ': Improper zip alignment')
+        return True
+
+    with zipfile.ZipFile(args.apk) as zf:
+        for info in zf.infolist():
+            if info.filename.startswith('lib/') and info.filename.endswith('.so') and info.compress_type != zipfile.ZIP_STORED:
+                if fail:
+                    sys.exit(args.apk + ': Contains compressed JNI libraries')
+                return True
+            # It's ok for non-privileged apps to have compressed dex files, see go/gms-uncompressed-jni-slides
+            if args.privileged:
+                if info.filename.endswith('.dex') and info.compress_type != zipfile.ZIP_STORED:
+                    if fail:
+                        sys.exit(args.apk + ': Contains compressed dex files and is privileged')
+                    return True
+    return False
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--aapt2', help = "the path to the aapt2 executable")
+    parser.add_argument('--zipalign', help = "the path to the zipalign executable")
+    parser.add_argument('--skip-preprocessed-apk-checks', action = 'store_true', help = "the value of the soong property with the same name")
+    parser.add_argument('--preprocessed', action = 'store_true', help = "the value of the soong property with the same name")
+    parser.add_argument('--privileged', action = 'store_true', help = "the value of the soong property with the same name")
+    parser.add_argument('apk', help = "the apk to check")
+    parser.add_argument('stampfile', help = "a file to touch if successful")
+    args = parser.parse_args()
+
+    if not args.preprocessed:
+        check_target_sdk_less_than_30(args)
+    elif args.skip_preprocessed_apk_checks:
+        if not has_preprocessed_issues(args):
+            sys.exit('This module sets `skip_preprocessed_apk_checks: true`, but does not actually have any issues. Please remove `skip_preprocessed_apk_checks`.')
+    else:
+        has_preprocessed_issues(args, fail=True)
+
+    subprocess.check_call(["touch", args.stampfile])
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/check_target_sdk_less_than_30.py b/scripts/check_target_sdk_less_than_30.py
deleted file mode 100755
index 69b0bf0..0000000
--- a/scripts/check_target_sdk_less_than_30.py
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/env python3
-
-import subprocess
-import argparse
-import re
-import sys
-
-def main():
-    parser = argparse.ArgumentParser()
-    parser.add_argument('aapt2', help = "the path to the aapt2 executable")
-    parser.add_argument('apk', help = "the apk to check")
-    parser.add_argument('stampfile', help = "a file to touch if successful")
-    args = parser.parse_args()
-
-    regex = re.compile(r"targetSdkVersion: *'([0-9]+)'")
-    output = subprocess.check_output([args.aapt2, "dump", "badging", args.apk], text=True)
-    targetSdkVersion = None
-    for line in output.splitlines():
-        match = regex.fullmatch(line.strip())
-        if match:
-            targetSdkVersion = int(match.group(1))
-            break
-
-    if targetSdkVersion is None or targetSdkVersion >= 30:
-        sys.exit(args.apk + ": Prebuilt, presigned apks with targetSdkVersion >= 30 (or a codename targetSdkVersion) must set preprocessed: true in the Android.bp definition (because they must be signed with signature v2, and the build system would wreck that signature otherwise)")
-
-    subprocess.check_call(["touch", args.stampfile])
-
-if __name__ == "__main__":
-    main()